|
|
//// RSRC - Win32 command line resource manager
//
// Copyright (c) 1996-9, Microsoft Corporation. All rights reserved.
//
// David C Brown [dbrown] 29th October 1998.
///// RSRC Command line
//
//c RSRC Executable [-l LocLang] [-u UnlocLang] [-i Types] [-q]
//c [ [-t|-d] TextOutput [-c UnlocExecutable]
//c | [-a|-r] text-input [-s symbols] [-v] ]
//
//p Executable: Win32 binary to analyse (default), to generate tokens (-t)
// or dump (-d) from, or containing resources to be replaced (-r)
// or appended to (-a).
//
//p -l LocLang: Restrict processing to the specified localized language. LangId
// should be specified as a full hex NLS Language id, for example use
// '-l 409' for US English. Required for replace (-r) operation.
//
//p -u UnlocLang: Specifies unlocalized language, defaults to 409 (US English).
//
//p -i Types: Restrict processing to listed types. Each type is indicated by a letter
// as below:
//
//t Letter | Type | Letter | Type | Letter | Type
//t ------ | ---- | ------ | ---- | ------ | ----
//t c | Cursors | g | Message tables | n | INFs
//t b | Bitmaps | v | Version info | h | HTML
//t i | Icons | a | Accelerators | x | Binary data
//t m | Menus | f | Font directories | |
//t d | Dialogs | t | Fonts | o | All others
//t s | Strings | r | RCDATA | a | All (default)
//
//
//p -q: Quiet. By default Rsrc displays summary statistics of types and languages
// of resources processed. -q suppresses all output except warning and error messages.
//
//p -t TextOutput: Generate tokens in checkin format.
//
//p -d TextOutput: Dump resources in Hex & ASCII format.
//
//p -c UnlocExecutable: Compare with unlocalized (English) resources - localised
// resources in executable are compared with English resources in
// UnlocExecutable. When the localised resource is bit for bit identical
// with the English resource RSRC writes a one line unloc
// token instead of the full resource. Valid only with tokenise (-t)
// option.
//
//p -a TextInput: Append resources from text input file. Every resource in the
// text file is added to the executable. Resources already in the executable
// are not removed. When a resource from the token file has the same type, id
// and language as one in the executable, the executable resource is replaced
// by the token resource.
//
//p -r TextInput: Replace English resources in executable by localised resources
// from text file. Requires -l parameter to specify localisation language.
// When a resource from the token file has the same type and id as one in the
// executable, and the executable resource is US English (409) and the localised
// resource is in the language specified on the -l parameter, the US English
// resource is removed.
//
//p -s Symbols: Symbol file (.dbg format). When RSRC updates the header checksum
// in executable, it will also do so in the named symbol file. Valid only
// with the replace (-r) and append (-a) options.
//
//
// Miscellaneous options
//
//p -v: Update file and product version. By default any file and product version
// in the token file is ignored during update/append, the file and product
// versions from the original unlocalised resources are retained.
//
///// Definitions
//
//p Resource key: The combination of resource type, resource id and
// resource language. The resource key uniquely identifies the
// resource. A Win32 executable can contain any combination of
// languages, ids and types so long as no two resources have the
// same type, key and language.
//
//p Resource type: A numeric or string value. Some numeric values are
// predefined, for example menus and dialogs, but apps can and
// do use any value they choose.
//
//p Resource id: A numeric or string value. Used by an application to
// identify the resource when calling FindResource, LoadString etc.
//
//p Resource language: An NLS LANGID, i.e. a combination of primary and
// sub-language, such as 0409 (US English).
//
//p Unloc token: A line in the token file specifying a localised resource
// key followed by '=lang,cksum' where lang is the unlocalised
// language (usually 0409) and cksum is the checksum of the unlocalised
// resource. Used when the only difference between the localised and
// unlocalised resource is the language in the resource key.
///// Use during localisation checkin process
//
//c RSRC LocalisedExecutable -c UnlocExecutable -t Tokens -l LocLang [-u UnlocLang]
//
// Extracts localized tokens for the specified langauge.
//
// Where a resource in the localised executable is bit for bit identical
// to a resource in the unlocalized executable, the resource content is not
// written to the token file. In its place RSRC writes an unloc token
// giving the checksum of the resource and specifying the target language.
//
// Warnings are generated if the localised executable contains resources
// in languages other than that specified by the -l parameter.
//
// Unlocalised resources for comparison are looked up in the unlocalised
// executable in the language specified on the -u parameter, default 409
// (US ENglish).
///// Use during the build to update a single language executable
//
//c RSRC Executable [-u UnlocLang] -r Tokens -l LocLang -s SymbolFile
//
// Each localised resource in the token file is added to the executable.
//
// Each corresponding unlocalized resource is removed from the executable.
//
// For each unloc token the unlocalized resource is found in the executable
// and its language is updated to the target localized language recorded
// in the unloc token.
//
// Tokens of other than the specified localised language are not
// processed, but generate warnings.
//
// Warnings are generated for any resources not appearing in both the
// executable and token files.
//
// Warnings are also generated for resources of other than the unlocalised
// language found in the original executable, and resources of other than
// the localised language in the token file.
//
// The unlocalised language defaults to 409 (US English).
///// Use during the build to add resources to a multi-language executable
//
//c RSRC Executable [-u UnlocLang] -a Tokens [-l Language] -s SymbolFile
//
// Localised resources from the token file are added to the executable.
//
// For each unloc token the unlocalised resource is found in the executable
// and copied for the localised language recorded in the unloc token.
//
// If '-l Languge' is specified, only tokens of that language are added.
// When used with the append (-a) option, '-l Language' applies only to
// the token file: pre-existing resources in the executable are not affected.
//
// If a resource from the token file matches a resource already in the
// executable in type, name and language, the executable resource
// is replaced.
//
// Warnings are generated if any token in the executable is replaced, or
// if the unlocalised resource corresponding to an unloc token is missing
// or has a checksum which differs from the unlocalised resource that was
// passed on the '-u' parameter when the toke file was created.
//
// If the '-l Language' option is used, warnings are generated for any
// resources of other languages found in the token file.
///// Token format - resource key and header
//
// A resource may be represented by one or more lines. When
// a resource is spread over more than one line, all lines except the
// first are indented by three spaces.
//
// The first line of every resource starts with the resource key as follows:
//
// type,id,language;
//
// This is followed by the codepage recorded in the resource directory.
// Note that the codepage is not part of the resource key, and is not
// maintained consistently by all software. In particular:
//
// o RC writes the codepage as zero
// o The NT UpdateResource API writes the codepage as 1252
// o Locstudio writes a codepage that corresponds to the resource language
//
// Winnt.h documents the codepage as follows:
//
// "Each resource data entry ... contains ... a CodePage that should be
// used when decoding code point values within the resource data.
// Typically for new applications the code page would be the unicode
// code page.'
//
// In practise I have never seen the codepage value set to Unicode (1200).
//
// If the '-c' (unlocalised comparison) parameter was provided on the
// rsrc command, and there was an unlocalised resource with the same type
// and id, the language and checksum of that unlocalised resource are
// appended.
//
// Finally, the resource data is represented in one of the forms below,
// or as 'unloc' if the resource data exactly matches the unlocalised resource
// found in the file passed by 'c'.
//
//
// There are thus three possible token key/header formats as follows:
//
//c type,id,language;codepage;resource-data
//
// Resource recorded in full, either no '-c' parameter specified, or
// resource does not exist in unlocalised file.
//
//
//c type,id,language;codepage,unlocalised-checksum,language;resource-data
//
// Resource recorded in full, '-c' parameter was specified, and localised
// resource image differed from unlocalised resource image.
//
//
//c type,id,language;codepage,unlocalised-checksum,language;'Unloc'
//
// Resource recorded in full, '-c' parameter was specified, and localised
// resource image was identical to unlocalised resource image.
///// Token samples - default hex format
//
//
// For most resource types, RSRC generates resources
// as a string of hex digits.
//
// For example, the following represents an accelerator resource.
//
//c 0009,0002,0409;00000000;Hex;00000020:030074008e00000003002e00840000000b0044008400000087002e0084000000
//
// o Type 0x0009 (Accelerator)
// o Id 0x0002
// o Language 0x0409 (LANG_ENGLISH, SUBLANG_US)
// o Codepage 0 (implies resource was probably generated by RC)
// o Length in bytes 0x0020
//
// The resource is short, so its hex representation follows the length.
//
//
// A larger binary resource is represented on multiple lines as follows:
//
//c 000a,4000,0409;00000000;Hex;0000016a
//c 00000000:0000640100004c0000000114020000000000c000000000000046830000003508000050130852c8e0bd0170493f38ace1bd016044d085c9e0bd01003000000000000001000000000000000000000000000000590014001f0fe04fd020ea3a6910a2d808002b30309d190023563a5c000000000000000000000000000000000065
//c 00000080:7c15003100000000003025a49e308857696e6e74000015003100000000002f25d3863508466f6e747300000000490000001c000000010000001c0000003900000000000000480000001d0000000300000063de7d98100000005f535445504853544550485f00563a5c57494e4e545c466f6e7473000010000000050000a02400
//c 00000100:00004200000060000000030000a05800000000000000737465706800000000000000000000004255867d3048d211b5d8d085029b1cfa4119c94a9f4dd211871f0000000000004255867d3048d211b5d8d085029b1cfa4119c94a9f4dd211871f00000000000000000000
//
// o Type 0x000a (RCDATA)
// o Id 0x4000
// o Language 0x0409 (LANG_ENGLISH, SUBLANG_US)
// o Codepage 0
// o Length in bytes 0x016a
//
// The hex representation is split onto multiple lines each of 128 bytes.
///// Warnings and errors
//
//
//
//
//
// o warning RSRC100: Localised resource has no corresponding unlocalised resource in %s
// o warning RSRC110: Unlocalised resource from token file appended to executable
// o warning RSRC111: Unlocalised resource from token file replaced unlocalised resource in executable
// o warning RSRC112: Localised resource from token file replaced localised resource already present in executable
// o warning RSRC113: Localised resource from token file appended to executable - there was no matching unlocalised resource
//
// o warning RSRC120: Token file resource does not match specified language - ignored
// o warning RSRC121: Token file resource is not a requested resource type - ignored
// o warning RSRC122: executable unlocalised resource checksum does not match checksum recorded in token file for resource %s
// o warning RSRC124: missing executable unlocalised resource for %s
// o warning RSRC125: executable contains no unlocalised resource corresponding to resource %s
//
// o warning RSRC160: Symbol file does not match exectable
// o warning RSRC161: Symbol file not processed
// o warning RSRC162: Could not reopen executable %s to update checksum
// o warning RSRC163: Failed to write updated symbol checksum
//
// o warning RSRC170: No localizable resources in %s
// o warning RSRC171: could not close executable
//
//
// o error RSRC230: 'Unloc' token is missing unlocalised resource information for %s
// o error RSRC231: Failed to apply unloc token
// o error RSRC232: Failed to apply token
//
// o error RSRC300: Hex digit expected
// o error RSRC301: Hex value too large
// o error RSRC302: Unexpected end of file
// o error RSRC303: \'%s\' expected
// o error RSRC304: newline expected
// o error RSRC310: Unrecognised resource type for resource %s
//
// o error RSRC400: -t (tokenise) option excludes -d, -a, -r, and -s
// o error RSRC401: -d (dump) option excludes -t, -u, -a, -r, and -s
// o error RSRC402: -a (append) option excludes -t, -d, -u, and -r
// o error RSRC403: -r (replace) option excludes -t, -d, -u, and -a
// o error RSRC404: -r (replace) option requires -l (LangId)
// o error RSRC405: Analysis excludes -s
//
// o error RSRC420: Update failed.
// o error RSRC421: Token extraction failed.
// o error RSRC422: Analysis failed.
//
// o error RSRC500: Corrupt executable - resource appears more than once
// o error RSRC501: %s is not an executable file
// o error RSRC502: %s is not a Win32 executable file
// o error RSRC503: No resources in %s
//
// o error RSRC510: Cannot open executable file %s
// o error RSRC511: cannot find resource directory in %s
// o error RSRC512: Cannot create resource token file %s
// o error RSRC513: Cannot open unlocalised executable file %s
// o error RSRC514: cannot find resource directory in unlocalised executable %s
// o error RSRC515: cannot write delta token file %s
// o error RSRC516: cannot write stand alone token file %s
//
// o error RSRC520: Cannot open resource token file %s
// o error RSRC521: UTF8 BOM missing from token file
//
// o error RSRC530: Cannot read executable resources from %s
// o error RSRC531: Failed reading update tokens
//
// o error RSRC600: BeginUpdateResource failed on %s
// o error RSRC601: UpdateResourceW failed on %s
// o error RSRC602: EndUpdateResourceW failed on %s
//// From Adina
//
// Here is my follow up on 2.
//
// Abstract:
// The build team needs the new tool eventually run with build.exe, i.e.
// we need build.exe recognize the errors, warnings, and simple output
// messages from rsrc.exe and write them to build.err, build.wrn and
// build.log files respectively.
//
// Solution:
// All we need is RSRC complain to the general rule for the MS tools.
// That is (\\orville\razzle\src\sdktools\build\buildexe.c):
// {toolname} : {number}: {text}
//
// where:
//
// toolname If possible, the container and specific module that has
// the error. For instance, the compiler uses
// filename(linenum), the linker uses library(objname), etc.
// If unable to provide a container, use the tool name.
// number A number, prefixed with some tool identifier (C for
// compiler, LNK for linker, LIB for librarian, N for nmake,
// etc).
// test The descriptive text of the message/error.
//
// Accepted String formats are:
// container(module): error/warning NUM ...
// container(module) : error/warning NUM ...
// container (module): error/warning NUM ...
// container (module) : error/warning NUM ...
//
// Ex. of RSRC error:
//
// RSRC : error RSRC2001: unable to open file d:\nt\binaries\jpn\ntdll.dll
//
// Ex. of RSRC warning:
//
// RSRC : warning RSRC5000: unable to find symbol file
// d:\nt\binaries\jpn\retail\dll\ntdll.dbg
//
// Be aware that the error number after "error/warning" is NOT optional.
// As the format above says, you can also display any information you
// consider useful (for example the name of the binary being processed,
// or the line number in the token file that caused the error/warning)
// immediately after the name of the tool: RSRC(<info>).
//
// I confirm that RSRC_OK=0, RSRC_WRN=1, RSRC_ERR=2 are fine with us as
// return values. Also, it does not make any difference if you write the
// output to stderr ot stdout, but I would suggest to write the tool's
// usage and all the warning and error message lines to stderr, and any
// other text to stdout (based on other ms tools we're using, like
// rebase.exe, binplace.exe, etc).
//
// I can make the changes to build.exe so that it recognizes RSRC.
//
// Please let me know if you have any questions.
//
// Thank you
// Adina
/// Following meeting Joerg. here are my action items:
//
// Meet with Joerg, Uwe, Majd, Hideki, Adina to plan usage in bidi NT5
// build process and consider use for odd jobs in other languages.
//
// P1. Implement option to skip updating file and product version, and
// to omit these from token file.
// P1. Implement separate error code for detecting unhandled binary
// format (such as win16).
//
// P2. Add CRC to each resource to detect SLM or editor corruption.
// (Delete CRC in token file always accepted to allow hand modification).
// P2. Option to disable header comment in token file
//
// P3. Interpret MSGTBL, ACCELERATOR and RCDATA - RCDATA as string
// depending on option.
//
// Thanks -- Dave.
//// From Joerg
//
// Howdy,
// I'm playing with rsrc and ran into problems with ParseToken(): if rsrc
// is located in a directory with spaces (e.g. Program Files),
// the function fails to skip the command name, since it's quoted and
// ParseToken stops at the first blank within the quotes.
// I also had trouble compiling it (so I can step thru and see what it's
// doing) under VC5 because there is no default constructor
// for the class "LangId", so I just added a dummy constructor.
//
// J�rg
//// Following meeting planning bidi build, Wednesday 2nd Dec.
//
// Checksum protection against user changes to tok file
// Include length in warning comparison
// Will need alpha build
// Default file name - add .rsrc
// Don't extract file or product version
// => If version resource updated use file and product version from
// US at write time
// Diagnose version only resources
// Diagnose not win32
// Warning for no translations on tokenisation
// Warning no no translations on update, and don't touch executable
// Ability to -r any unlocalised language
//// Resultant priorities (8th Dec):
//
// � 1. Use unlocalised file/product version if updating version resource
// � 2. Analyse mode diagnoses no localisable resources and unhandled binary formats
// 3. Warn when no translations, don't touch executable if updating
// � 4. Support -r from any language to any language
// 5. Allocate error numbers, clarify error messages
//
// 6. Include length in unloc token
// � 7. Handle quoted installation directory and default filenames
// 8. Add checksum protection against corruption of token file
// 9. Option to interpret RCDATA as Unicode string (for kernel)
// 10. Interpret MSGTBL and ACCELERATOR
// 11. Support Win16 binaries
// 12. ? Option to disable token file header
#pragma warning( disable : 4786 ) // map creates some ridiculously long debug identifiers
#include "stdio.h"
#include "windows.h"
#include "imagehlp.h"
#include "time.h"
#include <map>
using namespace std ; using namespace std::rel_ops ;
#define DBG 1
//// OK and ASSERT macros
//
// All functions return an HRESULT.
// All function calls are wrapped in 'OK()'.
// OK checks for a failed HRESULT and if so returns that HRESULT directly.
// Thus all errors propagate back up the call chain.
//
// MUST issues a message if an HRESULT is not S_OK and returns E_FAIL
// back up the call chain.
void __cdecl DebugMsg(char *fmt, ...) {
va_list vargs;
va_start(vargs, fmt); vfprintf(stderr, fmt, vargs); }
#define MUST(a,b) {HRESULT hr; hr = (a); if (hr!= S_OK) {if (!g_fError) DebugMsg b; g_fError = TRUE; return E_FAIL;};}
#define SHOULD(a,b) {HRESULT hr; hr = (a); if (hr!= S_OK) {DebugMsg b; g_fWarn = TRUE; return S_FALSE;};}
#if DBG
#pragma message("Checked build")
#define OK(a) {HRESULT hr; hr = (a); if (hr!= S_OK) {DebugMsg("%s(%d): error RSRC999 : HRESULT not S_OK: "#a"\n", __FILE__, __LINE__); return hr;};}
#define ASSERT(a) {if (!(a)) {DebugMsg("%s(%d): error RSRC999 : Assertion failed: "#a"\n", __FILE__, __LINE__); return E_UNEXPECTED;};}
#else
#pragma message ("Free build")
#define OK(a) {HRESULT hr; hr = (a); if (hr != S_OK) return hr;}
#define ASSERT(a) {if (!(a)) {return E_UNEXPECTED;};}
#endif
const int MAXPATH = 128; const char HexDigit[] = "0123456789abcdef"; const BYTE bZeroPad[] = { 0, 0, 0, 0};
const int MAXHEXLINELEN=128;
const int OPTHELP = 0x00000001; const int OPTQUIET = 0x00000002; const int OPTEXTRACT = 0x00000004; const int OPTUNLOC = 0x00000008; const int OPTHEXDUMP = 0x00000010; const int OPTAPPEND = 0x00000020; const int OPTREPLACE = 0x00000040; const int OPTSYMBOLS = 0x00000080; const int OPTVERSION = 0x00000100;
const int PROCESSCUR = 0x00000001; const int PROCESSBMP = 0x00000002; const int PROCESSICO = 0x00000004; const int PROCESSMNU = 0x00000008; const int PROCESSDLG = 0x00000010; const int PROCESSSTR = 0x00000020; const int PROCESSFDR = 0x00000040; const int PROCESSFNT = 0x00000080; const int PROCESSACC = 0x00000100; const int PROCESSRCD = 0x00000200; const int PROCESSMSG = 0x00000400; const int PROCESSVER = 0x00000800; const int PROCESSBIN = 0x00001000; const int PROCESSINF = 0x00002000; const int PROCESSOTH = 0x00004000;
const int PROCESSALL = 0xFFFFFFFF;
DWORD g_dwOptions = 0; DWORD g_dwProcess = 0; LANGID g_LangId = 0xffff; BOOL g_fWarn = FALSE; BOOL g_fError = FALSE; LANGID g_liUnlocalized = 0x0409; // Standard unlocalized language
int g_cResourcesIgnored = 0; int g_cResourcesUpdated = 0; // Simple replacement
int g_cResourcesTranslated = 0; // Changed from unloc language to loc language
int g_cResourcesAppended = 0; // Added without affecting existing resources
int g_cResourcesExtracted = 0; // Extracted to token file
char g_szTypes [MAXPATH]; char g_szExecutable [MAXPATH]; // Name of executable being analysed, tokenised or updated
char g_szResources [MAXPATH]; // Name of resource token file
char g_szUnloc [MAXPATH]; // Name of unlocalized executable for comparison
int HexCharVal(char c) { switch (c) { case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': return c - '0'; case 'a': case 'b': case 'c': case 'd': case 'e': case 'f': return c - 'a' + 10; case 'A': case 'B': case 'C': case 'D': case 'E': case 'F': return c - 'A' + 10; } return -1; // Not a hex value
}
//// Scanner
//
// A structure for scanning through a block of memory
class Scanner {
protected:
const BYTE *m_pStart; const BYTE *m_pRead; const BYTE *m_pLimit;
public:
Scanner() {m_pStart = NULL; m_pRead = NULL; m_pLimit = NULL;} Scanner(const BYTE *pb, DWORD dwLen) {m_pStart = pb; m_pRead = pb; m_pLimit = pb+dwLen;} ~Scanner() {m_pStart = NULL; m_pRead = NULL; m_pLimit=NULL;}
const BYTE* GetRead() {return m_pRead;} const BYTE* GetLimit() {return m_pLimit;}
HRESULT Advance(UINT cBytes) { ASSERT(m_pStart != NULL); ASSERT(m_pRead+cBytes <= m_pLimit); m_pRead += cBytes; return S_OK; }
HRESULT Align(const BYTE *pb, int iAlignment) { // Advance until read position is a multiple of iAlignment
// past pb. iAlignment MUST be a power of 2.
// Does not advance past limit.
// Ensure iAlignment is a power of 2
// This seems like a good test, though I'm not sure I could prove it!
ASSERT((iAlignment | iAlignment-1) == iAlignment*2 - 1);
if (m_pRead - pb & iAlignment - 1) {
m_pRead += (iAlignment - (m_pRead - pb & iAlignment - 1));
if (m_pRead > m_pLimit) { m_pRead = m_pLimit; } } return S_OK; }
HRESULT SetRead(const BYTE *pb) { ASSERT(m_pRead != NULL); ASSERT(pb >= m_pStart); ASSERT(pb < m_pLimit); m_pRead = pb; return S_OK; } };
class TextScanner : public Scanner {
protected:
const BYTE *m_pLine; // Start of current line
int m_iLine; // Current line
char m_szTextPos[40];
public:
TextScanner() {m_pLine = NULL; m_iLine = 0; Scanner();}
virtual char *GetTextPos() { sprintf(m_szTextPos, "%d:%d", m_iLine, m_pRead-m_pLine+1); return m_szTextPos; }
//// ReadString
//
// Translates UTF8 to Unicode
// Removes '\' escapes as necessary
// Always returns a new zero terminated string
HRESULT ReadString(WCHAR **ppwcString, int *piLen) {
char *pc; WCHAR *pwc; int iLen;
ASSERT(*((char*)m_pRead) == '\"'); OK(Advance(1));
pc = (char*)m_pRead; iLen = 0;
// Count the number of unicode codepoints represented by the string
while ( *pc != '\"' && pc < (char*)m_pLimit) {
while ( pc < (char*)m_pLimit && *pc != '\\' && *pc != '\"' ) {
if (*pc < 128) { pc++; } else { ASSERT(*pc >= 0xC0); // 80-BF reserved for trailing bytes
if (*pc < 0xE0) { pc+=2; } else if (*pc < 0xF0) { pc+=3; } else { iLen++; // Additional Unicode codepoint required for surrogate
pc+=4; } ASSERT(pc <= (char*)m_pLimit); } iLen++; }
if (*pc == '\\') { pc++; if (pc < (char*)m_pLimit) { pc++; iLen++; } } }
// Create a Unicode copy of the string
*ppwcString = new WCHAR[iLen+1];
ASSERT(*ppwcString != NULL);
pwc = *ppwcString;
while (*((char*)m_pRead) != '\"') {
while ( *((char*)m_pRead) != '\\' && *((char*)m_pRead) != '\"') {
if (*m_pRead < 0x80) { *pwc++ = *(char*)m_pRead; m_pRead++; } else { if (*m_pRead < 0xE0) { *pwc++ = (WCHAR)(*m_pRead & 0x1F) << 6 | (WCHAR)(*(m_pRead+1) & 0x3F); m_pRead+=2; } else if (*m_pRead < 0xF0) { *pwc++ = (WCHAR)(*m_pRead & 0x0F) << 12 | (WCHAR)(*(m_pRead+1) & 0x3F) << 6 | (WCHAR)(*(m_pRead+2) & 0x3F); m_pRead+=3; } else { *pwc++ = 0xd800 | (( (WCHAR)(*m_pRead & 0x07 << 2) | (WCHAR)(*(m_pRead+1) & 0x30 >> 4)) - 1) << 6 | (WCHAR)(*(m_pRead+1) & 0x0F) << 2 | (WCHAR)(*(m_pRead+2) & 0x30) >> 4; *pwc++ = 0xdc00 | (WCHAR)(*(m_pRead+2) & 0x0f) << 6 | (WCHAR)(*(m_pRead+3) & 0x3f); m_pRead+=4; } } }
if (*(char*)m_pRead == '\\') { m_pRead++; if (m_pRead < m_pLimit) { switch (*(char*)m_pRead) { case 'r': *pwc++ = '\r'; break; case 'n': *pwc++ = '\n'; break; case 't': *pwc++ = '\t'; break; case 'z': *pwc++ = 0; break; case 'L': *pwc++ = 0x2028; break; // Line separator
case 'P': *pwc++ = 0x2029; break; // Paragraph separator
default: *pwc++ = *(char*)m_pRead; } m_pRead++; } } }
*pwc = 0; // Add zero terminator
m_pRead ++; *piLen = pwc - *ppwcString;
ASSERT(m_pRead <= m_pLimit); return S_OK; }
//// ReadHex
//
// Reads all characters up to he first non-hex digit and returns
// the value represented by the sequence as a DWORD
HRESULT ReadHex(DWORD *pdwVal) { *pdwVal = 0;
MUST(HexCharVal(*(char*)m_pRead) >= 0 ? S_OK : E_FAIL, ("%s: error RSRC300: Hex digit expected\n", GetTextPos()));
while ( HexCharVal(*(char*)m_pRead) >= 0 && m_pRead < m_pLimit) {
MUST(*pdwVal < 0x10000000 ? S_OK : E_FAIL, ("%s: error RSRC301: Hex value too large\n", GetTextPos()));
*pdwVal = *pdwVal << 4 | HexCharVal(*(char*)m_pRead); OK(Advance(1)); } return S_OK; }
//// ReadHexByte - Read exactly 2 hex digits
HRESULT ReadHexByte(BYTE *pb) { int n1,n2; // The two nibbles.
n1 = HexCharVal(*(char*)m_pRead); n2 = HexCharVal(*(char*)(m_pRead+1));
MUST( n1 >= 0 && n2 >= 0 ? S_OK : E_FAIL, ("%s: error RSRC300: Hex digit expected\n", GetTextPos()));
*pb = (n1 << 4) + n2;
MUST(Advance(2), ("%s: error RSRC302: Unexpected end of file\n", GetTextPos())); return S_OK; }
HRESULT Expect(const char *pc) { while ( *pc && m_pRead+1 <= m_pLimit) {
MUST(*(char*)m_pRead == *pc ? S_OK : E_FAIL, ("%s: error RSRC303: \'%s\' expected\n", GetTextPos(), pc)); m_pRead++; pc++; }
MUST(*pc == 0 ? S_OK : E_FAIL, ("%s: error RSRC303: \'%s\' expected\n", GetTextPos(), pc));
return S_OK; }
//// SkipLn
//
// Skip to beginning of next non empty, non comment line.
HRESULT SkipLn() {
ASSERT(m_pRead != NULL);
while (m_pRead < m_pLimit) {
if (*(char*)m_pRead == '\r') {
m_pRead++;
if (m_pRead < m_pLimit && *(char*)m_pRead == '\n') {
m_pRead++; m_pLine = m_pRead; m_iLine++;
if ( m_pRead < m_pLimit && *(char*)m_pRead != '#' && *(char*)m_pRead != '\r') {
break; } }
} else {
m_pRead++; } }
return S_OK; }
//// ExpectLn
//
// Expect end of line, preceeded by any whitespace
//
// Also skips trailing comments and whole line comments
//
// Any parameter is passed to Expect to vb found at the beginning of the new line
HRESULT ExpectLn(const char *pc) {
ASSERT(m_pRead != NULL);
while ( m_pRead < m_pLimit && ( *(char*)m_pRead == ' ' || *(char*)m_pRead == '\t')) {
m_pRead++; }
if ( m_pRead < m_pLimit && ( *(char*)m_pRead == '\r' || *(char*)m_pRead == '#')) {
// Condition satisfied, skip to first non comment line
SkipLn();
} else {
MUST(E_FAIL,("%s: error RSRC304: newline expected\n", GetTextPos())); }
if (pc) { return Expect(pc); }
return S_OK; } };
//// Mapped files
//
// File mapping is used to read executable and token files.
//
// File mapping is also used to update in place checksum information
// in executable and symbol files.
class MappedFile : public TextScanner {
HANDLE m_hFileMapping; BOOL fRW; // True when writeable
char m_szFileName[MAXPATH]; char m_szTextPos[MAXPATH+40];
public:
MappedFile() {m_hFileMapping = NULL; TextScanner();}
const BYTE* GetFile() {return m_pStart;} virtual char *GetTextPos() { sprintf(m_szTextPos, "%s(%s)", m_szFileName, TextScanner::GetTextPos()); return m_szTextPos; }
HRESULT Open(const char *pcFileName, BOOL fWrite) {
HANDLE hFile;
m_pStart = NULL; m_pRead = NULL; m_pLimit = NULL;
strcpy(m_szFileName, pcFileName);
hFile = CreateFileA( pcFileName, GENERIC_READ | (fWrite ? GENERIC_WRITE : 0), FILE_SHARE_READ | (fWrite ? FILE_SHARE_WRITE | FILE_SHARE_DELETE : 0 ), NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, NULL);
ASSERT(hFile != INVALID_HANDLE_VALUE);
m_hFileMapping = CreateFileMapping( hFile, NULL, fWrite ? PAGE_READWRITE : PAGE_WRITECOPY, 0,0, NULL);
ASSERT(m_hFileMapping != NULL);
m_pStart = (BYTE*) MapViewOfFile( m_hFileMapping, fWrite ? FILE_MAP_WRITE : FILE_MAP_READ, 0,0, 0);
ASSERT(m_pStart != NULL);
m_pRead = m_pStart; m_pLine = m_pStart; m_pLimit = m_pStart + GetFileSize(hFile, NULL); m_iLine = 1; CloseHandle(hFile);
fRW = fWrite; return S_OK; }
DWORD CalcChecksum() {
DWORD dwHeaderSum; DWORD dwCheckSum;
if (CheckSumMappedFile((void*)m_pStart, m_pLimit-m_pStart, &dwHeaderSum, &dwCheckSum) == NULL) { return 0; } else { return dwCheckSum; } }
HRESULT Close() { if (m_pStart) { UnmapViewOfFile(m_pStart); CloseHandle(m_hFileMapping); m_hFileMapping = NULL; m_pStart = NULL; } return S_OK; } };
//// NewFile - services for writing a new text otr binary file
//
//
class NewFile {
HANDLE hFile; DWORD cbWrite; // Bytes written
BYTE buf[4096]; // Performance buffer
int iBufUsed;
public:
NewFile() {iBufUsed = 0;}
int GetWrite() {return cbWrite;}
HRESULT OpenWrite(char *pcFileName) {
cbWrite = 0; // Bytes written
hFile = CreateFileA( pcFileName, GENERIC_READ | GENERIC_WRITE, 0, // Not shared
NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, NULL);
ASSERT(hFile != INVALID_HANDLE_VALUE);
return S_OK; }
HRESULT WriteBytes(const BYTE *pb, DWORD dwLen) {
DWORD dwWritten;
if (iBufUsed + dwLen <= sizeof(buf)) {
memcpy(buf+iBufUsed, pb, dwLen); iBufUsed += dwLen;
} else {
ASSERT(hFile != NULL);
if (iBufUsed > 0) { ASSERT(WriteFile(hFile, buf, iBufUsed, &dwWritten, NULL)); ASSERT(dwWritten == iBufUsed); iBufUsed = 0; }
if (dwLen <= sizeof(buf)) {
memcpy(buf, pb, dwLen); iBufUsed = dwLen;
} else {
ASSERT(WriteFile(hFile, pb, dwLen, &dwWritten, NULL)); ASSERT(dwWritten == dwLen); } }
cbWrite += dwLen;
return S_OK; }
HRESULT WriteS(const char *pc) { return WriteBytes((BYTE*)pc, strlen(pc)); }
//// WriteString
//
// Translates Unicode to UTF8
// Adds '\' escapes as necessary
HRESULT WriteString(const WCHAR *pwc, int iLen) {
BYTE buf[3]; const WCHAR *pwcLimit;
pwcLimit = pwc + iLen; OK(WriteBytes((BYTE*)"\"", 1)); while (pwc < pwcLimit) { switch (*pwc) { case 0: OK(WriteS("\\z")); break; case '\r': OK(WriteS("\\r")); break; case '\n': OK(WriteS("\\n")); break; case '\t': OK(WriteS("\\t")); break; case 0x2028: OK(WriteS("\\L")); break; // Line separator
case 0x2029: OK(WriteS("\\P")); break; // Paragraph separator
case '\"': OK(WriteS("\\\"")); break; case '\\': OK(WriteS("\\\\")); break; default: if (*pwc < 128) { OK(WriteBytes((BYTE*)pwc, 1)); } else if (*pwc < 0x7FF) { buf[0] = 0xC0 | *pwc >> 6; buf[1] = 0x80 | *pwc & 0x3F; OK(WriteBytes(buf, 2)); } else { // Note - should code surrogates properly, this doesn't
buf[0] = 0xE0 | *pwc>>12 & 0x0F; buf[1] = 0x80 | *pwc>>6 & 0x3F; buf[2] = 0x80 | *pwc & 0x3F; OK(WriteBytes(buf, 3)); } } pwc++; } OK(WriteBytes((BYTE*)"\"", 1)); return S_OK; }
//// WriteHex
//
// Writes the given value in the given number of digits.
//
// If cDigits is zero, uses as many as necessary.
HRESULT WriteHex(DWORD dw, int cDigits) { int i; char cBuf[8];
i = 7;
while (dw && i >= 0) { cBuf[i] = HexDigit[dw & 0xf]; dw >>= 4; i--; }
if (cDigits != 0) { while (i >= (8-cDigits)) { cBuf[i] = '0'; i--; } }
OK(WriteBytes((BYTE*)(cBuf+i+1), 7-i));
return S_OK; }
//// WriteHexBuffer
//
// Writes a buffer of up to 256 bytes as a continuous stream of hex digits
HRESULT WriteHexBuffer(const BYTE *pb, DWORD dwLength) { DWORD i; char cBuf[512];
ASSERT(hFile); ASSERT(dwLength <= 256);
for (i=0; i<dwLength; i++) { cBuf[2*i] = HexDigit[*pb >> 4 & 0xf]; cBuf[2*i+1] = HexDigit[*pb & 0xf]; pb++; }
OK(WriteBytes((BYTE*)cBuf, 2*dwLength)); cbWrite += 2*dwLength;
return S_OK; }
//// WriteLn
//
// Write end of line mark (CR,LF)
HRESULT WriteLn() { return WriteS("\r\n"); }
HRESULT Close() { DWORD dwWritten; if (hFile) { if (iBufUsed > 0) { ASSERT(WriteFile(hFile, buf, iBufUsed, &dwWritten, NULL)); ASSERT(dwWritten == iBufUsed); } CloseHandle(hFile); hFile = NULL; } return S_OK; } };
//// Resource structures
//
// Each resource structure has an internal representation for the
// resource that may be read and written to/from both text and
// executable files.
//
// The ReadTok and WriteTok functions handle formatting and parsing
// of the token file.
//
// The ReadBin function unpacks a resource from a memory mapped
// executable into the internal representation.
//
// The cbBin function returns the unpadded length required for the
// item in executable (packed) format.
//
// The CopyBin function packs the internal representation into a
// target buffer.
class Resource { public: virtual HRESULT ReadTok (TextScanner &mfText) = 0; virtual HRESULT ReadBin (Scanner &mfBin, DWORD dwLen) = 0;
virtual HRESULT WriteTok (NewFile &nfText) const = 0; virtual size_t cbBin () const = 0; virtual HRESULT CopyBin (BYTE **ppb) const = 0;
// For statistics
virtual int GetItems () const = 0; virtual int GetWords () const = 0; };
//// ResourceBYTE
//
// BYTE value represented in hex digits.
class ResourceBYTE {
public:
BYTE b;
HRESULT ReadBin(Scanner *pmf) { b = *((BYTE*)(pmf->GetRead())); OK(pmf->Advance(sizeof(BYTE))); return S_OK; }
size_t cbBin() const { return 1; }
HRESULT CopyBin(BYTE **ppb) const { **ppb = b; (*ppb)++; return S_OK; }
HRESULT ReadTok(TextScanner *pmf) { DWORD dw; OK(pmf->ReadHex(&dw)); ASSERT(dw < 0x100); b = (BYTE)dw; return S_OK; }
HRESULT WriteTok(NewFile *pmf) const { OK(pmf->WriteHex(b, 2)); return S_OK; } };
//// ResoureWORD
//
// WORD value represented in hex digits.
class ResourceWORD {
public:
WORD w;
HRESULT ReadBin(Scanner *pmf) { w = *((WORD*)(pmf->GetRead())); OK(pmf->Advance(sizeof(WORD))); return S_OK; }
size_t cbBin() const { return sizeof(WORD); }
HRESULT CopyBin(BYTE **ppb) const { *(WORD*)*ppb = w; *ppb += sizeof(WORD); return S_OK; }
HRESULT ReadTok(TextScanner *pmf) { DWORD dw; OK(pmf->ReadHex(&dw)); ASSERT(dw < 0x10000); w = (WORD)dw; return S_OK; }
HRESULT WriteTok(NewFile *pmf) const { OK(pmf->WriteHex(w, 4)); return S_OK; } };
//// ResourceDWORD
//
// DWORD value represented in hex digits.
class ResourceDWORD {
public:
DWORD dw;
HRESULT ReadBin(Scanner *pmf) { dw = *((DWORD*)(pmf->GetRead())); OK(pmf->Advance(sizeof(DWORD))); return S_OK; }
size_t cbBin() const { return sizeof(DWORD); }
HRESULT CopyBin(BYTE **ppb) const { *(DWORD*)*ppb = dw; *ppb += sizeof(DWORD); return S_OK; }
HRESULT ReadTok(TextScanner *pmf) { OK(pmf->ReadHex(&dw)); return S_OK; }
HRESULT WriteTok(NewFile *pmf) const { OK(pmf->WriteHex(dw, 8)); return S_OK; } };
//// ResourceString
//
// String displayed with quotes. May be zero terminated or length
// word.
const WCHAR wcZero = 0;
class ResourceString {
WCHAR *pwcString; WORD iLen;
void rsFree() { if (pwcString) delete [] pwcString; pwcString = NULL; iLen = 0; }
public:
ResourceString() {pwcString = NULL; iLen = 0;} ~ResourceString() {rsFree();}
ResourceString& operator= (const ResourceString &rs) { iLen = rs.iLen; pwcString = new WCHAR[iLen+1]; memcpy(pwcString, rs.pwcString, 2*(iLen+1)); return *this; }
ResourceString(const ResourceString &rs) { *this = rs; }
const WCHAR *GetString() const {return pwcString;} const int GetLength() const {return iLen;}; void SetEmpty () {iLen = 0; pwcString = NULL;}
HRESULT ReadBinL(Scanner *pmf) { rsFree();
iLen = *((WORD*)(pmf->GetRead())); OK(pmf->Advance(sizeof(WORD)));
pwcString = new WCHAR[iLen+1]; ASSERT(pwcString != NULL); memcpy(pwcString, (WCHAR*)pmf->GetRead(), 2*iLen); pwcString[iLen] = 0;
OK(pmf->Advance(iLen * sizeof(WCHAR))); return S_OK; }
size_t cbBinL() const { return iLen * sizeof(WCHAR) + sizeof(WORD); }
HRESULT CopyBinL(BYTE **ppb) const { *(WORD*)*ppb = iLen; *ppb += sizeof(WORD); memcpy(*ppb, pwcString, sizeof(WCHAR)*iLen); *ppb += sizeof(WCHAR)*iLen; return S_OK; }
// Zero terminated
HRESULT ReadBinZ(Scanner *pmf) {
const WCHAR* pwc; rsFree();
pwc = (WCHAR*)pmf->GetRead(); while (*(WCHAR*)pmf->GetRead() != 0) { OK(pmf->Advance(2)); }
iLen = (WCHAR*)pmf->GetRead() - pwc; OK(pmf->Advance(2));
pwcString = new WCHAR[iLen+1]; ASSERT(pwcString != NULL); memcpy(pwcString, pwc, 2*(iLen+1));
return S_OK; }
size_t cbBinZ() const { return (iLen+1) * sizeof(WCHAR); }
// Known length (dwLen excludes zero terminator)
HRESULT ReadBin(Scanner *pmf, DWORD dwLen) {
rsFree(); iLen = dwLen;
pwcString = new WCHAR[dwLen+1]; ASSERT(pwcString != NULL); memcpy(pwcString, pmf->GetRead(), 2*dwLen); pwcString[dwLen] = 0; OK(pmf->Advance(2*dwLen));
return S_OK; }
size_t cbBin() const { return iLen * sizeof(WCHAR); }
HRESULT CopyBinZ(BYTE **ppb) const { memcpy(*ppb, pwcString, sizeof(WCHAR)*iLen); *ppb += sizeof(WCHAR)*iLen; *(WCHAR*)*ppb = 0; *ppb += sizeof(WCHAR); return S_OK; }
HRESULT ReadTok(TextScanner *pmf) { int l; rsFree(); OK(pmf->ReadString(&pwcString, &l)); ASSERT(l < 0x10000 && l >= 0); iLen = (WORD) l; return S_OK; }
HRESULT WriteTok(NewFile *pmf) const { OK(pmf->WriteString(pwcString, iLen)); return S_OK; }
int GetWords() const {
int i; int wc;
i = 0; wc = 0;
while (i<iLen) {
while ( i < iLen && pwcString[i] <= ' ') { i++; }
if (i<iLen) { wc++; }
while ( i < iLen && pwcString[i] > ' ') { i++; } }
return wc; } };
//// ResourceVariant
//
// A widely used alternative of either a Unicode string or a WORD value.
class ResourceVariant {
ResourceString *prs; ResourceWORD rw; BOOL fString;
void rvFree() { if (fString && prs) delete prs; prs = NULL; fString=FALSE; }
public:
ResourceVariant() {fString=FALSE; prs=NULL;} ~ResourceVariant() {rvFree();}
// Copy and assignment constructors required as this is used as the key in an STL map
ResourceVariant& operator= (const ResourceVariant &rv) { fString = rv.fString; if (fString) { prs = new ResourceString(*rv.prs); } else { prs = NULL; rw = rv.rw; } return *this; }
ResourceVariant(const ResourceVariant &rv) { *this = rv; }
void fprint(FILE *fh) const { if (fString) { fprintf(fh, "%S", prs->GetString()); } else { fprintf(fh, "%x", rw.w); } }
const BOOL GetfString() const {return fString;} const WORD GetW() const {return rw.w;} void SetW(WORD w) {if (fString) {delete prs; fString = FALSE;}rw.w = w;} const WCHAR *GetString() const {return prs->GetString();} const int GetLength() const {return prs->GetLength();} const int GetWords() const {return fString ? prs->GetWords() : 0;}
HRESULT ReadBinFFFFZ(Scanner *pmf) { rvFree(); fString = *(WORD*)pmf->GetRead() != 0xffff; if (fString) { prs = new ResourceString; OK(prs->ReadBinZ(pmf)); } else { OK(pmf->Advance(sizeof(WORD))); OK(rw.ReadBin(pmf)); } return S_OK; }
size_t cbBinFFFFZ() const { return fString ? prs->cbBinZ() : rw.cbBin() + sizeof(WORD); }
HRESULT CopyBinFFFFZ(BYTE **ppb) const { if (fString) { return prs->CopyBinZ(ppb); } else { *(WORD*)*ppb = 0xFFFF; // Mark as value
(*ppb) += sizeof(WORD); return rw.CopyBin(ppb); } }
HRESULT ReadBinFFFFL(Scanner *pmf) { rvFree(); fString = *(WORD*)pmf->GetRead() != 0xffff; if (fString) { prs = new ResourceString; OK(prs->ReadBinL(pmf)); } else { OK(pmf->Advance(sizeof(WORD))); OK(rw.ReadBin(pmf)); } return S_OK; }
size_t cbBinFFFFL() const { return fString ? prs->cbBinL() : rw.cbBin() + sizeof(WORD); }
HRESULT CopyBinFFFFL(BYTE **ppb) const { if (fString) { return prs->CopyBinL(ppb); } else { *(WORD*)*ppb = 0xFFFF; // Mark as value
(*ppb) += sizeof(WORD); return rw.CopyBin(ppb); } }
HRESULT ReadTok(TextScanner *pmf) { rvFree(); fString = *(char*)pmf->GetRead() == '\"'; if (fString) { prs = new ResourceString; OK(prs->ReadTok(pmf)); } else { OK(rw.ReadTok(pmf)); } return S_OK; }
HRESULT WriteTok(NewFile *pmf) const { if (fString) { OK(prs->WriteTok(pmf)); } else { OK(rw.WriteTok(pmf)); } return S_OK; }
HRESULT ReadWin32ResDirEntry( Scanner *pmf, const BYTE *pRsrc, IMAGE_RESOURCE_DIRECTORY_ENTRY *pirde) {
rvFree(); fString = pirde->NameIsString;
if (fString) { prs = new ResourceString; OK(pmf->SetRead(pRsrc + pirde->NameOffset)); OK(prs->ReadBinL(pmf)); } else { OK(pmf->SetRead((BYTE*)&pirde->Id)); OK(rw.ReadBin(pmf)); } return S_OK; }
bool operator< (const ResourceVariant &rv) const {
int l,c;
if (fString != rv.GetfString()) {
return !fString; // Numerics before strings
} else if (!fString) {
return rw.w < rv.GetW();
} else {
l = prs->GetLength(); if (l > rv.GetLength()) { l = rv.GetLength(); }
c = wcsncmp(prs->GetString(), rv.GetString(), l);
if (c==0) { return prs->GetLength() < rv.GetLength(); } else { return c < 0; } }
return FALSE; // Equal at all depths
} };
//// ResourceKey
//
// The resource key is the unique identifier of a resource, containing
// a resource type, a programmer defined unique id for the resource, and
// a language identifier.
class ResourceKey {
public:
int iDepth; ResourceVariant *prvId[3];
ResourceKey() {iDepth=0;}
ResourceKey& operator= (const ResourceKey &rk) { int i; iDepth = rk.iDepth; for (i=0; i<iDepth; i++) { prvId[i] = new ResourceVariant(*rk.prvId[i]); } return *this; }
ResourceKey(const ResourceKey& rk) { *this = rk; }
void fprint(FILE *fh) const {
prvId[0]->fprint(fh); fprintf(fh, ","); prvId[1]->fprint(fh); fprintf(fh, ","); prvId[2]->fprint(fh); }
LPCWSTR GetResName(int i) const { if (i >= iDepth) { return (LPCWSTR) 0; } if (prvId[i]->GetfString()) { return prvId[i]->GetString(); } else { return (LPCWSTR)prvId[i]->GetW(); } }
HRESULT SetLanguage(WORD lid) {
ASSERT(iDepth == 3); ASSERT(prvId[2]->GetfString() == FALSE); prvId[2]->SetW(lid);
return S_OK; }
HRESULT ReadTok(TextScanner *pmf) { prvId[0] = new ResourceVariant; ASSERT(prvId[0] != NULL); OK(prvId[0]->ReadTok(pmf)); iDepth = 1; while (*(char*)pmf->GetRead() == ',') { OK(pmf->Advance(1)); prvId[iDepth] = new ResourceVariant; ASSERT(prvId[iDepth] != NULL); OK(prvId[iDepth]->ReadTok(pmf)); iDepth++; } return S_OK; }
HRESULT WriteTok(NewFile *pmf) const { int i; OK(prvId[0]->WriteTok(pmf)); for (i=1; i<iDepth; i++) { OK(pmf->WriteS(",")); OK(prvId[i]->WriteTok(pmf)); } return S_OK; }
bool operator< (const ResourceKey &rk) const { int i,l,c;
if (iDepth != rk.iDepth) { return iDepth < rk.iDepth; // Lower depths come first
} else { for (i=0; i<iDepth; i++) { if (prvId[i]->GetfString() != rk.prvId[i]->GetfString()) { return prvId[i]->GetfString() ? true : false; // Strings come before values
} else { if (prvId[i]->GetfString()) { // Compare strings
l = prvId[i]->GetLength(); if (l > rk.prvId[i]->GetLength()) { l = rk.prvId[i]->GetLength(); } c = wcsncmp(prvId[i]->GetString(), rk.prvId[i]->GetString(), l); if (c == 0) { if (prvId[i]->GetLength() != rk.prvId[i]->GetLength()) { return prvId[i]->GetLength() < rk.prvId[i]->GetLength(); } } else { return c < 0; } } else { // Compare numeric values
if (prvId[i]->GetW() != rk.prvId[i]->GetW()) { return prvId[i]->GetW() < rk.prvId[i]->GetW(); } }
} } return FALSE; // Equal at all depths
} } };
//// ResourceBinary
//
// Arbitrary binary resource
//
// Formatted as lines of hex digits
class ResourceBinary : public Resource {
protected: // Accessed by ResourceHexDump
BYTE *pb; DWORD dwLength;
void rbFree() {if (pb) {delete[] pb; pb=NULL;}dwLength = 0;}
public:
ResourceBinary() {pb = NULL; dwLength = 0;} ~ResourceBinary() {rbFree();}
DWORD GetLength() const {return dwLength;}
HRESULT ReadTok(TextScanner &mfText) { DWORD i; DWORD dwOffset; DWORD dwCheckOffset;
rbFree();
OK(mfText.Expect("Hex;")); OK(mfText.ReadHex(&dwLength));
pb = new BYTE[dwLength]; ASSERT(pb != NULL);
if (dwLength <= MAXHEXLINELEN) {
// Hex follows on same line
OK(mfText.Expect(":")); for (i=0; i<dwLength; i++) { OK(mfText.ReadHexByte(pb+i)); }
} else {
// Hex follows on subsequent lines
dwOffset = 0; while (dwLength - dwOffset > MAXHEXLINELEN) { OK(mfText.ExpectLn(" ")); OK(mfText.ReadHex(&dwCheckOffset)); ASSERT(dwOffset == dwCheckOffset); OK(mfText.Expect(":")); for (i=0; i<MAXHEXLINELEN; i++) { OK(mfText.ReadHexByte(pb+dwOffset+i)); } dwOffset += MAXHEXLINELEN; }
OK(mfText.ExpectLn(" ")); OK(mfText.ReadHex(&dwCheckOffset)); ASSERT(dwOffset == dwCheckOffset); OK(mfText.Expect(":")); for (i=0; i<dwLength - dwOffset; i++) { OK(mfText.ReadHexByte(pb+dwOffset+i)); } }
return S_OK; }
HRESULT ReadBin(Scanner &mfText, DWORD dwLen) { rbFree(); dwLength = dwLen; pb = new BYTE[dwLength]; memcpy(pb, mfText.GetRead(), dwLength); OK(mfText.Advance(dwLen)); return S_OK; }
HRESULT WriteTok(NewFile &nfText) const {
DWORD dwOffset;
// Write binary resource in lines of up to 256 bytes
OK(nfText.WriteS("Hex;")); OK(nfText.WriteHex(dwLength, 8));
if (dwLength <= MAXHEXLINELEN) {
// Write <= MAXHEXLINELEN bytes on same line
OK(nfText.WriteS(":")); OK(nfText.WriteHexBuffer(pb, dwLength));
} else {
// write MAXHEXLINELEN bytes per line on subsequent lines
dwOffset = 0; while (dwLength - dwOffset > MAXHEXLINELEN) { OK(nfText.WriteS("\r\n ")); OK(nfText.WriteHex(dwOffset, 8)); OK(nfText.WriteS(":")); OK(nfText.WriteHexBuffer(pb+dwOffset, MAXHEXLINELEN)); dwOffset += MAXHEXLINELEN; }
// Write remaining bytes, if any
OK(nfText.WriteS("\r\n ")); OK(nfText.WriteHex(dwOffset, 8)); OK(nfText.WriteS(":")); OK(nfText.WriteHexBuffer(pb+dwOffset, dwLength - dwOffset)); }
return S_OK; }
size_t cbBin() const { return dwLength; }
HRESULT CopyBin(BYTE **ppb) const { if (dwLength > 0) { memcpy(*ppb, pb, dwLength); *ppb += dwLength; } return S_OK; }
int GetItems() const { return 0; }
int GetWords() const { return 0; }
BOOL CompareBin(const BYTE *pbComp, DWORD dwLen) const {
if (dwLength != dwLen) return FALSE; if (dwLength == 0) return TRUE; if (pb ==pbComp) return true;
return !memcmp(pb, pbComp, dwLength); }
};
//// ResourceHexDump
//
// Special version of ResourceBinary for generating a hex dump analysis
class ResourceHexDump : public ResourceBinary {
public: HRESULT WriteTok(NewFile &nfText) const { DWORD i,j; ResourceDWORD rdw;
OK(nfText.WriteS("Hexdump,")); OK(nfText.WriteHex(dwLength, 8)); OK(nfText.WriteS(":")); for (i=0; i<dwLength; i++) { if (i % 4 == 0) { OK(nfText.WriteS(" ")); } if (i % 8 == 0) { OK(nfText.WriteS(" ")); } if (i % 16 == 0) { if (i>0) { // Append ASCII interpretation
for (j=i-16; j<i; j++) { if (pb[j] > 31) { OK(nfText.WriteBytes(pb+j, 1)); } else { OK(nfText.WriteS(".")); } } } OK(nfText.WriteS("\r\n ")); rdw.dw = i; OK(rdw.WriteTok(&nfText)); OK(nfText.WriteS(": ")); } OK(nfText.WriteHex(pb[i], 2)); OK(nfText.WriteS(" ")); }
// Append ANSI interpretation to last line
if (dwLength % 16 > 0) { for (i = dwLength % 16 ; i < 16; i++) { if (i % 4 == 0) { OK(nfText.WriteS(" ")); } if (i % 8 == 0) { OK(nfText.WriteS(" ")); } OK(nfText.WriteS(" ")); } } OK(nfText.WriteS(" "));
for (j=dwLength-1 & 0xfffffff0; j<dwLength; j++) { if (pb[j] > 31) { OK(nfText.WriteBytes(pb+j, 1)); } else { OK(nfText.WriteS(".")); } }
OK(nfText.WriteLn()); return S_OK; } };
//// Menu32
//
//
class MenuItem32 {
ResourceDWORD rdwType; ResourceDWORD rdwState; ResourceDWORD rdwId; // Extended ID
ResourceWORD rwId; // Non-extended ID
ResourceWORD rwFlags; ResourceDWORD rdwHelpId; ResourceString rsCaption;
BOOL fExtended;
public:
void SetExtended(BOOL f) {fExtended = f;} int GetWords() const {return rsCaption.GetWords();}
virtual HRESULT ReadTok(TextScanner &mfText) {
if (!fExtended) { OK(rwFlags.ReadTok(&mfText)); OK(mfText.Expect(",")); if (!(rwFlags.w & MF_POPUP)) { OK(rwId .ReadTok(&mfText)); OK(mfText.Expect(",")); } } else { OK(rdwType .ReadTok(&mfText)); OK(mfText.Expect(",")); OK(rdwState .ReadTok(&mfText)); OK(mfText.Expect(",")); OK(rdwId .ReadTok(&mfText)); OK(mfText.Expect(",")); OK(rwFlags .ReadTok(&mfText)); OK(mfText.Expect(",")); if (rwFlags.w & 1) { OK(rdwHelpId.ReadTok(&mfText)); OK(mfText.Expect(",")); } }
OK(rsCaption.ReadTok(&mfText));
return S_OK; }
virtual HRESULT ReadBin(Scanner &mfBin) {
const BYTE *pb; // For tracking
pb = mfBin.GetRead();
if (!fExtended) { OK(rwFlags.ReadBin(&mfBin)); if (!(rwFlags.w & MF_POPUP)) { OK(rwId .ReadBin(&mfBin)); } } else { OK(rdwType .ReadBin(&mfBin)); OK(rdwState.ReadBin(&mfBin)); OK(rdwId .ReadBin(&mfBin)); OK(rwFlags .ReadBin(&mfBin)); }
OK(rsCaption.ReadBinZ(&mfBin));
if (fExtended && rwFlags.w & 1) {
OK(mfBin.Align(pb, 4));
OK(rdwHelpId.ReadBin(&mfBin)); }
return S_OK; }
virtual HRESULT WriteTok(NewFile &nfText) const {
if (!fExtended) { OK(rwFlags.WriteTok(&nfText)); OK(nfText.WriteS(",")); if (!(rwFlags.w & MF_POPUP)) { OK(rwId .WriteTok(&nfText)); OK(nfText.WriteS(",")); } } else { OK(rdwType .WriteTok(&nfText)); OK(nfText.WriteS(",")); OK(rdwState .WriteTok(&nfText)); OK(nfText.WriteS(",")); OK(rdwId .WriteTok(&nfText)); OK(nfText.WriteS(",")); OK(rwFlags .WriteTok(&nfText)); OK(nfText.WriteS(",")); if (rwFlags.w & 1) { OK(rdwHelpId.WriteTok(&nfText)); OK(nfText.WriteS(",")); } }
OK(rsCaption.WriteTok(&nfText));
return S_OK; }
virtual size_t cbBin() const {
size_t cb;
if (!fExtended) {
cb = rwFlags.cbBin() + rsCaption.cbBinZ();
if (!(rwFlags.w & MF_POPUP)) { cb += rwId.cbBin(); }
} else {
cb = rdwType.cbBin() + rdwState.cbBin() + rdwId.cbBin() + rwFlags.cbBin() + rsCaption.cbBinZ();
if (rwFlags.w & 1) {
cb = cb + 3 & ~3; cb += rdwHelpId.cbBin(); } }
return cb; }
virtual HRESULT CopyBin (BYTE **ppb) const {
const BYTE * pb;
pb = *ppb;
if (!fExtended) { OK(rwFlags.CopyBin(ppb)); if (!(rwFlags.w & MF_POPUP)) { OK(rwId .CopyBin(ppb)); } } else { OK(rdwType .CopyBin(ppb)); OK(rdwState.CopyBin(ppb)); OK(rdwId .CopyBin(ppb)); OK(rwFlags .CopyBin(ppb)); }
OK(rsCaption.CopyBinZ(ppb));
if (fExtended && rwFlags.w & 1) {
while (*ppb - pb & 3) { **ppb = 0; (*ppb)++; }
OK(rdwHelpId.CopyBin(ppb)); }
return S_OK; }
};
class Menu32 : public Resource {
ResourceWORD rwVer; ResourceWORD rwHdrSize; ResourceBinary rbHeader; MenuItem32 *pMnuItm; DWORD cItems; BOOL fExtended;
public: virtual HRESULT ReadTok(TextScanner &mfText) {
DWORD i, iItem;
OK(mfText.Expect("Mnu32")); fExtended = *(char*)mfText.GetRead() == 'X'; if (fExtended) { OK(mfText.Expect("X;")); } else { OK(mfText.Expect("N;")); }
OK(rwVer .ReadTok(&mfText)); OK(mfText.Expect(",")); OK(rwHdrSize.ReadTok(&mfText)); OK(mfText.Expect(",")); if (fExtended && rwHdrSize.w > 0) { OK(rbHeader.ReadTok(mfText)); OK(mfText.Expect(",")); } OK(mfText.ReadHex(&cItems)); OK(mfText.Expect(":"));
pMnuItm = new MenuItem32 [cItems]; ASSERT(pMnuItm != NULL);
for (i=0; i<cItems; i++) {
OK(mfText.ExpectLn(" ")); OK(mfText.ReadHex(&iItem)); ASSERT(i == iItem); pMnuItm[i].SetExtended(fExtended); OK(mfText.Expect(";")); OK(pMnuItm[i].ReadTok(mfText)); }
return S_OK; }
virtual HRESULT ReadBin(Scanner &mfBin, DWORD dwLen) {
const BYTE *pb; // For tracking
MenuItem32 mi; // For counting menu items
const BYTE *pbFirstItem; int i;
cItems = 0; pb = mfBin.GetRead();
OK(rwVer .ReadBin(&mfBin)); OK(rwHdrSize.ReadBin(&mfBin));
ASSERT(rwVer.w == 0 || rwVer.w == 1); fExtended = rwVer.w;
if (fExtended && rwHdrSize.w > 0) { rbHeader.ReadBin(mfBin, rwHdrSize.w); }
ASSERT(mfBin.GetRead() - pb < dwLen);
// Count menu items
if (fExtended) { OK(mfBin.Align(pb, 4)); }
pbFirstItem = mfBin.GetRead(); mi.SetExtended(fExtended); while (mfBin.GetRead() - pb < dwLen) {
OK(mi.ReadBin(mfBin)); cItems++;
if (fExtended) { OK(mfBin.Align(pb, 4)); } }
pMnuItm = new MenuItem32 [cItems]; ASSERT(pMnuItm != NULL);
// Record the menus
OK(mfBin.SetRead(pbFirstItem)); for (i=0; i<cItems; i++) {
if (fExtended) { OK(mfBin.Align(pb, 4)); }
pMnuItm[i].SetExtended(fExtended); OK(pMnuItm[i].ReadBin(mfBin)); }
ASSERT(mfBin.GetRead() - pb <= dwLen);
return S_OK; }
virtual HRESULT WriteTok(NewFile &nfText) const {
DWORD i;
OK(nfText.WriteS(fExtended ? "Mnu32X;": "Mnu32N;"));
OK(rwVer .WriteTok(&nfText)); OK(nfText.WriteS(",")); OK(rwHdrSize.WriteTok(&nfText)); OK(nfText.WriteS(",")); if (fExtended && rwHdrSize.w > 0) { OK(rbHeader.WriteTok(nfText)); OK(nfText.WriteS(",")); } OK(nfText.WriteHex(cItems,4)); OK(nfText.WriteS(":"));
for (i=0; i<cItems; i++) {
OK(nfText.WriteS("\r\n ")); OK(nfText.WriteHex(i, 4)); OK(nfText.WriteS(";")); OK(pMnuItm[i].WriteTok(nfText)); }
return S_OK; }
virtual size_t cbBin() const { int i; size_t cb;
cb = rwVer.cbBin() + rwHdrSize.cbBin();
if (fExtended && rwHdrSize.w > 0) { cb += rbHeader.cbBin(); }
for (i=0; i<cItems; i++) {
if (fExtended) { cb = cb + 3 & ~3; }
cb += pMnuItm[i].cbBin(); }
return cb; }
virtual HRESULT CopyBin (BYTE **ppb) const {
const BYTE *pb; // For tracking
int i;
pb = *ppb;
OK(rwVer .CopyBin(ppb)); OK(rwHdrSize.CopyBin(ppb));
if (fExtended && rwHdrSize.w > 0) { rbHeader.CopyBin(ppb); }
for (i=0; i<cItems; i++) {
if (fExtended) { while (*ppb - pb & 3) { **ppb = 0; (*ppb)++; } }
OK(pMnuItm[i].CopyBin(ppb)); }
return S_OK; }
int GetItems() const { return cItems; }
int GetWords() const { int i; int wc;
wc = 0; for (i=0; i<cItems; i++) { wc += pMnuItm[i].GetWords(); }
return wc; } };
//// String32
//
// Strings are represented as a sequence of WCHARS, each string
// preceeded by its length. Each resource contains 16 strings.
class String32 : public Resource {
ResourceString rs[16]; DWORD cStrings; DWORD cNonEmpty;
public: virtual HRESULT ReadTok(TextScanner &mfText) {
DWORD i, iString, cLoaded;
OK(mfText.Expect("Str;")); OK(mfText.ReadHex(&cStrings)); OK(mfText.Expect(",")); OK(mfText.ReadHex(&cNonEmpty)); OK(mfText.Expect(":"));
ASSERT(cStrings == 16); ASSERT(cNonEmpty <= cStrings);
i=0; cLoaded = 0; while (cLoaded < cNonEmpty) {
OK(mfText.ExpectLn(" ")); OK(mfText.ReadHex(&iString)); OK(mfText.Expect(":")); ASSERT(iString >= i); ASSERT(iString < cStrings); while (i<iString) { rs[i].SetEmpty(); i++; } OK(rs[i].ReadTok(&mfText)); i++; cLoaded++; }
while (i<cStrings) { rs[i].SetEmpty(); i++; }
return S_OK; }
virtual HRESULT ReadBin(Scanner &mfBin, DWORD dwLen) {
const BYTE *pb; // For tracking
cStrings = 0; cNonEmpty = 0; pb = mfBin.GetRead();
while ( cStrings < 16 && mfBin.GetRead() - pb < dwLen) {
rs[cStrings].ReadBinL(&mfBin); if (rs[cStrings].GetLength() > 0) { cNonEmpty++; } cStrings++; }
ASSERT(mfBin.GetRead() - pb <= dwLen); ASSERT(cStrings == 16);
return S_OK; }
virtual HRESULT WriteTok(NewFile &nfText) const {
int i;
ASSERT(cStrings <= 16);
OK(nfText.WriteS("Str;")); OK(nfText.WriteHex(cStrings, 2)); OK(nfText.WriteS(",")); OK(nfText.WriteHex(cNonEmpty, 2)); OK(nfText.WriteS(":"));
for (i=0; i<cStrings; i++) { if (rs[i].GetLength() > 0) { OK(nfText.WriteS("\r\n ")); OK(nfText.WriteHex(i, 1)); OK(nfText.WriteS(":")); OK(rs[i].WriteTok(&nfText)); } }
return S_OK; }
virtual size_t cbBin() const { int i; size_t cb;
cb = 0;
for (i=0; i<cStrings; i++) { cb += rs[i].cbBinL(); }
return cb; }
virtual HRESULT CopyBin (BYTE **ppb) const { int i;
for (i=0; i<cStrings; i++) { OK(rs[i].CopyBinL(ppb)); }
return S_OK; }
int GetItems() const { return cNonEmpty; }
int GetWords() const {
int i, wc;
wc = 0; for (i=0; i<cStrings; i++) { wc += rs[i].GetWords(); }
return wc; } };
class DialogHeader32 {
BOOL fExtended;
ResourceDWORD rdwStyle; ResourceDWORD rdwSignature; ResourceDWORD rdwHelpId; ResourceDWORD rdwExStyle; ResourceWORD rwcDit; // Count of dialog items
ResourceWORD rwX; ResourceWORD rwY; ResourceWORD rwCx; ResourceWORD rwCy; ResourceVariant rvMenu; ResourceVariant rvClass; ResourceVariant rvTitle; ResourceWORD rwPointSize; ResourceWORD rwWeight; ResourceBYTE rbItalic; ResourceBYTE rbCharSet; ResourceString rsFaceName;
public:
WORD GetItemCount() const {return rwcDit.w;} BOOL GetExtended() const {return fExtended;} int GetWords() const {return rvTitle.GetWords();}
HRESULT ReadTok(TextScanner *pmf) {
OK(rwcDit .ReadTok(pmf)); OK(pmf->Expect(","));
OK(rdwStyle .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rdwExStyle .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rdwSignature.ReadTok(pmf)); OK(pmf->Expect(",")); OK(rdwHelpId .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rwX .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rwY .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rwCx .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rwCy .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rvMenu .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rvClass .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rvTitle .ReadTok(pmf)); if (rdwStyle.dw & DS_SETFONT) { OK(pmf->Expect(",")); OK(rwPointSize.ReadTok(pmf)); OK(pmf->Expect(",")); OK(rwWeight .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rbItalic .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rbCharSet .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rsFaceName .ReadTok(pmf)); }
fExtended = rdwSignature.dw != 0;
return S_OK; }
HRESULT ReadBin(Scanner *pmf) {
OK(rdwSignature.ReadBin(pmf));
fExtended = HIWORD(rdwSignature.dw) == 0xFFFF; if (!fExtended) {
rdwStyle.dw = rdwSignature.dw; OK(rdwExStyle.ReadBin(pmf)); rdwSignature.dw = 0; rdwHelpId.dw = 0;
} else {
// Extended dialog adds signature and HelpID
OK(rdwHelpId.ReadBin(pmf)); OK(rdwExStyle.ReadBin(pmf)); OK(rdwStyle.ReadBin(pmf)); }
OK(rwcDit .ReadBin(pmf)); OK(rwX .ReadBin(pmf)); OK(rwY .ReadBin(pmf)); OK(rwCx .ReadBin(pmf)); OK(rwCy .ReadBin(pmf)); OK(rvMenu .ReadBinFFFFZ(pmf)); OK(rvClass .ReadBinFFFFZ(pmf)); OK(rvTitle .ReadBinFFFFZ(pmf)); if (rdwStyle.dw & DS_SETFONT) { OK(rwPointSize.ReadBin(pmf)); if (!fExtended) { rwWeight.w = 0; rbItalic.b = 0; rbCharSet.b = 0; } else { OK(rwWeight .ReadBin(pmf)); OK(rbItalic .ReadBin(pmf)); OK(rbCharSet .ReadBin(pmf)); } OK(rsFaceName .ReadBinZ(pmf)); }
return S_OK; }
size_t cbBin() const { size_t cb; cb = rdwStyle .cbBin() // Basics for all dialogs
+ rdwExStyle .cbBin() + rwcDit .cbBin() + rwX .cbBin() + rwY .cbBin() + rwCx .cbBin() + rwCy .cbBin() + rvMenu .cbBinFFFFZ() + rvClass .cbBinFFFFZ() + rvTitle .cbBinFFFFZ();
if (rdwStyle.dw & DS_SETFONT) { // Facname additions
cb += rwPointSize .cbBin() + rsFaceName .cbBinZ(); }
if (fExtended) { // Extended dialog addtions
cb += rdwSignature .cbBin() + rdwHelpId .cbBin();
if (rdwStyle.dw & DS_SETFONT) { cb += rwWeight .cbBin() + rbItalic .cbBin() + rbCharSet .cbBin(); } }
return cb; }
HRESULT CopyBin(BYTE **ppb) const {
BYTE *pbOriginal;
pbOriginal = *ppb;
if (!fExtended) {
OK(rdwStyle .CopyBin(ppb)); OK(rdwExStyle.CopyBin(ppb));
} else {
OK(rdwSignature.CopyBin(ppb)); OK(rdwHelpId .CopyBin(ppb)); OK(rdwExStyle .CopyBin(ppb)); OK(rdwStyle .CopyBin(ppb)); } OK(rwcDit .CopyBin(ppb)); OK(rwX .CopyBin(ppb)); OK(rwY .CopyBin(ppb)); OK(rwCx .CopyBin(ppb)); OK(rwCy .CopyBin(ppb)); OK(rvMenu .CopyBinFFFFZ(ppb)); OK(rvClass .CopyBinFFFFZ(ppb)); OK(rvTitle .CopyBinFFFFZ(ppb)); if (rdwStyle.dw & DS_SETFONT) { OK(rwPointSize.CopyBin(ppb)); if (fExtended) { OK(rwWeight .CopyBin(ppb)); OK(rbItalic .CopyBin(ppb)); OK(rbCharSet.CopyBin(ppb)); } OK(rsFaceName .CopyBinZ(ppb)); }
return S_OK; }
HRESULT WriteTok(NewFile *pmf) const {
OK(rwcDit .WriteTok(pmf)); OK(pmf->WriteS(","));
OK(rdwStyle .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rdwExStyle .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rdwSignature.WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rdwHelpId .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rwX .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rwY .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rwCx .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rwCy .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rvMenu .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rvClass .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rvTitle .WriteTok(pmf)); if (rdwStyle.dw & DS_SETFONT) { OK(pmf->WriteS(",")); OK(rwPointSize.WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rwWeight .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rbItalic .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rbCharSet .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rsFaceName .WriteTok(pmf)); } return S_OK; } };
class DialogItem32 {
BOOL fExtended;
ResourceDWORD rdwStyle; ResourceDWORD rdwHelpId; ResourceDWORD rdwExStyle; ResourceWORD rwX; ResourceWORD rwY; ResourceWORD rwCx; ResourceWORD rwCy; ResourceWORD rwId; // Normal
ResourceDWORD rdwId; // Extended
ResourceVariant rvClass; ResourceVariant rvTitle;
ResourceWORD rwcbRawData; // Raw data size (extended only)
ResourceBinary rbRawData;
ResourceWORD rwDummy; // Replaces raw data on normal dialogs
public:
void SetExtended(BOOL f) {fExtended = f;} int GetWords() const {return rvTitle.GetWords();}
HRESULT ReadTok(TextScanner *pmf) {
if (fExtended) { OK(rdwId.ReadTok(pmf)); OK(pmf->Expect(",")); } else { OK(rwId.ReadTok(pmf)); OK(pmf->Expect(",")); }
OK(rdwStyle .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rdwExStyle .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rdwHelpId .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rwX .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rwY .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rwCx .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rwCy .ReadTok(pmf)); OK(pmf->Expect(","));
OK(rvClass .ReadTok(pmf)); OK(pmf->Expect(",")); OK(rvTitle .ReadTok(pmf)); OK(pmf->Expect(","));
if (fExtended) {
OK(rbRawData.ReadTok(*pmf)); ASSERT(rbRawData.GetLength() < 0x10000); rwcbRawData.w = (WORD)rbRawData.GetLength();
} else {
OK(rwDummy.ReadTok(pmf)); }
return S_OK; }
HRESULT ReadBin(Scanner *pmf) {
if (!fExtended) {
OK(rdwStyle.ReadBin(pmf)); OK(rdwExStyle.ReadBin(pmf)); rdwHelpId.dw = 0;
} else {
OK(rdwHelpId.ReadBin(pmf)); OK(rdwExStyle.ReadBin(pmf)); OK(rdwStyle.ReadBin(pmf)); }
OK(rwX .ReadBin(pmf)); OK(rwY .ReadBin(pmf)); OK(rwCx.ReadBin(pmf)); OK(rwCy.ReadBin(pmf));
if (fExtended) { OK(rdwId.ReadBin(pmf)); } else { OK(rwId.ReadBin(pmf)); }
OK(rvClass.ReadBinFFFFZ(pmf)); OK(rvTitle.ReadBinFFFFZ(pmf));
if (fExtended) {
OK(rwcbRawData.ReadBin(pmf)); OK(rbRawData.ReadBin(*pmf, rwcbRawData.w));
} else {
OK(rwDummy.ReadBin(pmf)); }
return S_OK; }
size_t cbBin() const { size_t cb;
cb = rdwStyle .cbBin() + rdwExStyle .cbBin() + rwX .cbBin() + rwY .cbBin() + rwCx .cbBin() + rwCy .cbBin() + rvClass .cbBinFFFFZ() + rvTitle .cbBinFFFFZ();
if (!fExtended) { cb += rwId .cbBin() + rwDummy .cbBin(); } else { cb += rdwId .cbBin() + rdwHelpId .cbBin() + rbRawData .cbBin() + rwcbRawData .cbBin(); } return cb; }
HRESULT CopyBin(BYTE **ppb) const {
BYTE *pbOriginal;
pbOriginal = *ppb;
if (!fExtended) {
OK(rdwStyle.CopyBin(ppb)); OK(rdwExStyle.CopyBin(ppb));
} else {
OK(rdwHelpId.CopyBin(ppb)); OK(rdwExStyle.CopyBin(ppb)); OK(rdwStyle.CopyBin(ppb)); }
OK(rwX .CopyBin(ppb)); OK(rwY .CopyBin(ppb)); OK(rwCx.CopyBin(ppb)); OK(rwCy.CopyBin(ppb));
if (fExtended) { OK(rdwId.CopyBin(ppb)); } else { OK(rwId.CopyBin(ppb)); }
OK(rvClass.CopyBinFFFFZ(ppb)); OK(rvTitle.CopyBinFFFFZ(ppb));
if (fExtended) {
OK(rwcbRawData.CopyBin(ppb)); OK(rbRawData.CopyBin(ppb));
} else {
OK(rwDummy.CopyBin(ppb)); }
return S_OK; }
HRESULT WriteTok(NewFile *pmf) const {
if (fExtended) { OK(rdwId.WriteTok(pmf)); OK(pmf->WriteS(",")); } else { OK(rwId.WriteTok(pmf)); OK(pmf->WriteS(",")); }
OK(rdwStyle .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rdwExStyle .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rdwHelpId .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rwX .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rwY .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rwCx .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rwCy .WriteTok(pmf)); OK(pmf->WriteS(","));
OK(rvClass .WriteTok(pmf)); OK(pmf->WriteS(",")); OK(rvTitle .WriteTok(pmf)); OK(pmf->WriteS(","));
if (fExtended) {
OK(rbRawData.WriteTok(*pmf));
} else {
OK(rwDummy.WriteTok(pmf)); }
return S_OK; } };
//// Dialog32
//
//
class Dialog32 : public Resource {
DialogHeader32 DlgHdr; // Header
DialogItem32 *pDlgItm; // Array of items
BOOL fExtended; int cItems;
public:
Dialog32() {pDlgItm = NULL;};
virtual HRESULT ReadTok(TextScanner &mfText) {
DWORD i, dwSeq;
OK(mfText.Expect("Dlg32")); fExtended = *(char*)mfText.GetRead() == 'X'; if (fExtended) { OK(mfText.Expect("X;")); } else { OK(mfText.Expect("N;")); }
OK(DlgHdr.ReadTok(&mfText)); ASSERT(fExtended == DlgHdr.GetExtended());
cItems = DlgHdr.GetItemCount();
pDlgItm = new DialogItem32 [cItems]; ASSERT(pDlgItm != NULL);
for (i=0; i<cItems; i++) {
OK(mfText.ExpectLn(" ")); OK(mfText.ReadHex(&dwSeq)); ASSERT(dwSeq == i+1); OK(mfText.Expect(";"));
pDlgItm[i].SetExtended(fExtended); OK(pDlgItm[i].ReadTok(&mfText)); }
return S_OK; }
virtual HRESULT WriteTok(NewFile &nfText) const {
DWORD i;
OK(nfText.WriteS(fExtended ? "Dlg32X;": "Dlg32N;"));
OK(DlgHdr.WriteTok(&nfText));
for (i=0; i<cItems; i++) {
OK(nfText.WriteS("\r\n ")); OK(nfText.WriteHex(i+1, 4)); OK(nfText.WriteS(";")); OK(pDlgItm[i].WriteTok(&nfText)); }
return S_OK; }
virtual HRESULT ReadBin(Scanner &mfBinary, DWORD dwLen) {
const BYTE *pb; // File pointer for tracking alignment
int i;
pb = mfBinary.GetRead();
OK(DlgHdr.ReadBin(&mfBinary)); fExtended = DlgHdr.GetExtended(); cItems = DlgHdr.GetItemCount();
pDlgItm = new DialogItem32 [cItems]; ASSERT(pDlgItm != NULL);
// Read items
for (i=0; i<cItems; i++) {
OK(mfBinary.Align(pb, 4)); // Advance over any alignment padding
pDlgItm[i].SetExtended(fExtended); OK(pDlgItm[i].ReadBin(&mfBinary));
ASSERT(mfBinary.GetRead() - pb <= dwLen); }
return S_OK; }
virtual size_t cbBin() const {
size_t cb; int i;
cb = DlgHdr.cbBin();
for (i=0; i<cItems; i++) {
cb = cb + 3 & ~3; // alignment padding
cb += pDlgItm[i].cbBin(); }
return cb; }
virtual HRESULT CopyBin (BYTE **ppb) const {
BYTE *pb; // Pointer for tracking alignment
int i;
pb = *ppb;
DlgHdr.CopyBin(ppb);
for (i=0; i<cItems; i++) {
// Insert alignment padding
while (*ppb - pb & 3) { **ppb = 0; (*ppb)++; }
pDlgItm[i].CopyBin(ppb); }
return S_OK; }
int GetItems() const { return cItems; }
int GetWords() const {
int i, wc;
wc = DlgHdr.GetWords(); for (i=0; i<cItems; i++) { wc += pDlgItm[i].GetWords(); } return wc; } };
//// VersionInfo
//
// The documentation in the Win32 SDK doesn't clearly capture the
// usage of block headers, or the nesting of blocks in the Version resource.
//
// Each block has the following format
//
// wLength Total length including key, value and subblocks
// wValueLength Length of value in bytes or characters according to bText
// bText Whether value is in bytes or zero terminated WCHARs
// szKey Zero terminated WCHAR key, padded with zeros to next DWORD boundary
// Value Size determined by bText and wValueLength, padded to DWORD boundary
// Sub-blocks Remaining space (if any, up to wLength) is an array of sub blocks
class VersionInfo : public Resource {
struct VersionBlock { VersionBlock *pNext; // Next block at this level
VersionBlock *pSub; // First contained subblock
int iDepth; // Starts at zero
DWORD cSub; // Number of contained subblocks
BOOL bValue; // Set if a vlue is present
ResourceWORD rwbText; ResourceString rsKey; ResourceString rsValue; // Value when a string
ResourceBinary rbValue; // Value when bytes
};
VersionBlock *pvb; // First root level block
DWORD cBlocks; // Number of root level blocks
HRESULT ReadBinVersionBlocks( Scanner &mfBinary, DWORD dwLength, // Length of binary to read
VersionBlock **ppvb, int iDepth, DWORD *cSub) {
const BYTE *pbBlock; const BYTE *pbResource; ResourceWORD rwLength; WORD wValueLength;
pbResource = mfBinary.GetRead(); (*cSub) = 0; while (mfBinary.GetRead() < pbResource + dwLength) {
// Read one version block
pbBlock = mfBinary.GetRead(); OK(rwLength.ReadBin(&mfBinary));
ASSERT(pbBlock + rwLength.w <= mfBinary.GetLimit());
//OK((*ppvb)->rwValueLength.ReadBin(&mfBinary));
wValueLength = *(WORD*)mfBinary.GetRead(); OK(mfBinary.Advance(2));
if (rwLength.w > 0) {
// Block is not empty
*ppvb = new VersionBlock; ASSERT(*ppvb != NULL);
(*ppvb)->pNext = NULL; (*ppvb)->pSub = NULL; (*ppvb)->iDepth = iDepth;
OK((*ppvb)->rwbText.ReadBin(&mfBinary)); OK((*ppvb)->rsKey.ReadBinZ(&mfBinary)); OK(mfBinary.Align(pbResource, 4));
(*ppvb)->bValue = wValueLength > 0;
if ((*ppvb)->bValue) {
if ((*ppvb)->rwbText.w == 0) {
// Binary value
OK((*ppvb)->rbValue.ReadBin(mfBinary, wValueLength));
} else {
// WCHAR string.
// Some writers include a zero terminator, some don't.
// Some incode zero codepoints inside the string
// Some writers get the length right, some dont.
// msvcrt20.dll text lengths are too long.
// Choose a length that is min(ValueLength, length remaining),
// and then drop any trailing zeros.
// Clip ValueLength to length remaining
ASSERT(mfBinary.GetRead() < pbBlock + rwLength.w);
if (wValueLength > (pbBlock + rwLength.w - mfBinary.GetRead()) / 2) { wValueLength = (pbBlock + rwLength.w - mfBinary.GetRead()) / 2; }
// Clip trailing zeros
while ( wValueLength > 0 && ((WCHAR*)mfBinary.GetRead())[wValueLength-1] == 0) { wValueLength--; }
// Extract whatever remains
OK((*ppvb)->rsValue.ReadBin(&mfBinary, wValueLength));
// Check that there's nothing being lost between the end of
// the string and the end of the block.
// Note that we assume here that blocks containing text values
// cannot have variety of messes that value text is stored in
// in exisiting executables.
while (mfBinary.GetRead() < pbBlock + rwLength.w) {
ASSERT(*(WCHAR*)mfBinary.GetRead() == 0); OK(mfBinary.Advance(2)); } } OK(mfBinary.Align(pbResource, 4)); }
if (mfBinary.GetRead() - pbBlock < rwLength.w) {
ASSERT(mfBinary.GetLimit() > mfBinary.GetRead());
// Read subblocks
OK(ReadBinVersionBlocks( mfBinary, rwLength.w - (mfBinary.GetRead() - pbBlock), &((*ppvb)->pSub), iDepth + 1, &(*ppvb)->cSub)); }
if (mfBinary.GetRead() < pbResource + dwLength) {
// Prepare to read more blocks at this level
ppvb = &((*ppvb)->pNext); } }
(*cSub)++; }
return S_OK; }
HRESULT WriteTokVersionBlocks( NewFile &nfText, VersionBlock *pvb) const {
while (pvb) {
OK(nfText.WriteS("\r\n ")); OK(nfText.WriteHex(pvb->iDepth, 2)); OK(nfText.WriteS(",")); OK(pvb->rsKey.WriteTok(&nfText));
if (pvb->bValue) {
OK(nfText.WriteS("="));
if (pvb->rwbText.w == 0) {
OK(pvb->rbValue.WriteTok(nfText)); // Binary value
} else {
OK(pvb->rsValue.WriteTok(&nfText)); // String value
}
}
if (pvb->pSub) { OK(nfText.WriteS(";")); OK(nfText.WriteHex(pvb->cSub,4)); OK(WriteTokVersionBlocks(nfText, pvb->pSub)); }
pvb = pvb->pNext; }
return S_OK; }
HRESULT ReadTokVersionBlocks( TextScanner &mfText, VersionBlock **ppvb, int iDepth, DWORD *pcBlocks) {
int i; DWORD dwRecordedDepth;
OK(mfText.ReadHex(pcBlocks));
for (i=0; i<*pcBlocks; i++) { *ppvb = new VersionBlock; ASSERT(*ppvb != NULL);
(*ppvb)->pNext = NULL; (*ppvb)->pSub = NULL; (*ppvb)->iDepth = iDepth; (*ppvb)->cSub = 0;
OK(mfText.ExpectLn(" ")); OK(mfText.ReadHex(&dwRecordedDepth)); ASSERT(dwRecordedDepth == iDepth); OK(mfText.Expect(",")); OK((*ppvb)->rsKey.ReadTok(&mfText));
if (*(char*)mfText.GetRead() != '=') {
// No value
(*ppvb)->rwbText.w = 1; (*ppvb)->bValue = FALSE;
} else {
OK(mfText.Expect("=")); (*ppvb)->bValue = TRUE;
if (*(char*)mfText.GetRead() == '\"') {
// String value
(*ppvb)->rwbText.w = 1; OK((*ppvb)->rsValue.ReadTok(&mfText));
} else {
// Binary value
(*ppvb)->rwbText.w = 0; OK((*ppvb)->rbValue.ReadTok(mfText)); } }
if (*(char*)mfText.GetRead() == ';') {
// Process subkeys
OK(mfText.Expect(";")); OK(ReadTokVersionBlocks( mfText, &(*ppvb)->pSub, iDepth+1, &(*ppvb)->cSub)); }
// Prepare to add another block
ppvb = &(*ppvb)->pNext; }
return S_OK; }
size_t cbBinVersionBlocks(const VersionBlock *pvb) const {
size_t cb;
cb = 6; // Header
cb += pvb->rsKey.cbBinZ();
cb = cb+3 & ~3; // DWORD align
if (pvb->bValue) {
if (pvb->rwbText.w) {
cb += pvb->rsValue.cbBinZ();
} else {
cb += pvb->rbValue.cbBin(); }
cb = cb + 3 & ~3; // DWORD align
}
if (pvb->pSub != NULL) {
pvb = pvb->pSub; while (pvb) { cb += cbBinVersionBlocks(pvb); pvb = pvb->pNext; } }
return cb; }
HRESULT CopyBinVersionBlocks( BYTE **ppb, const VersionBlock *pvb) const {
const BYTE *pbResource; size_t cb;
pbResource = *ppb;
while (pvb != NULL) {
cb = cbBinVersionBlocks(pvb); ASSERT(cb < 0x1000);
*((WORD*)(*ppb)) = (WORD)cb; (*ppb) += 2;
// Generate value length
if (pvb->bValue) { if (pvb->rwbText.w) { *((WORD*)(*ppb)) = pvb->rsValue.GetLength()+1; } else { *((WORD*)(*ppb)) = pvb->rbValue.GetLength(); } } else { *((WORD*)(*ppb)) = 0; } (*ppb) += 2;
OK(pvb->rwbText.CopyBin(ppb)); OK(pvb->rsKey.CopyBinZ(ppb));
while (*ppb - pbResource & 3) { **ppb = 0; (*ppb)++; }
if (pvb->bValue) {
if (pvb->rwbText.w) { OK(pvb->rsValue.CopyBinZ(ppb)); } else { OK(pvb->rbValue.CopyBin(ppb)); }
while (*ppb - pbResource & 3) { **ppb = 0; (*ppb)++; } }
if (pvb->pSub) { OK(CopyBinVersionBlocks(ppb, pvb->pSub)); }
pvb = pvb->pNext; }
return S_OK; }
int GetItemsVersionBlocks(const VersionBlock *pvb) const {
int iItems = 0;
while (pvb != NULL) {
if ( pvb->bValue && pvb->rwbText.w != 0) {
iItems++; }
iItems += GetItemsVersionBlocks(pvb->pSub);
pvb = pvb->pNext; }
return iItems; }
int GetWordsVersionBlocks(const VersionBlock *pvb) const {
int iWords = 0;
while (pvb != NULL) {
if ( pvb->bValue && pvb->rwbText.w != 0) {
iWords += pvb->rsValue.GetWords(); }
iWords += GetWordsVersionBlocks(pvb->pSub);
pvb = pvb->pNext; }
return iWords; }
public:
virtual HRESULT ReadTok(TextScanner &mfText) { OK(mfText.Expect("Ver;")); return ReadTokVersionBlocks(mfText, &pvb, 0, &cBlocks); }
virtual HRESULT WriteTok(NewFile &nfText) const { OK(nfText.WriteS("Ver;")); OK(nfText.WriteHex(cBlocks,4)); return WriteTokVersionBlocks(nfText, pvb); }
virtual HRESULT ReadBin(Scanner &mfBinary, DWORD dwLen) { return ReadBinVersionBlocks(mfBinary, dwLen, &pvb, 0, &cBlocks); }
virtual size_t cbBin() const {
const VersionBlock *pvbTop; size_t cb;
cb = 0; pvbTop = pvb;
while (pvbTop) { cb += cbBinVersionBlocks(pvbTop); pvbTop = pvbTop->pNext; } return cb; }
virtual HRESULT CopyBin (BYTE **ppb) const { return CopyBinVersionBlocks(ppb, pvb); }
int GetItems() const { return GetItemsVersionBlocks(pvb); }
int GetWords() const { return GetWordsVersionBlocks(pvb); }
VersionBlock *FindStringFileInfo(WCHAR* pwcStr) const { VersionBlock *pvbRider;
if ( pvb && pvb->pSub && pvb->pSub->pSub && pvb->pSub->pSub->pSub) {
pvbRider = pvb->pSub->pSub->pSub; while ( pvbRider && wcscmp(pvbRider->rsKey.GetString(), pwcStr) != 0) { pvbRider = pvbRider->pNext; } return pvbRider; } else { return NULL; } }
ResourceString* GetStringFileInfo(WCHAR *pwcStr) {
VersionBlock *pvbStringFileInfo;
pvbStringFileInfo = FindStringFileInfo(pwcStr);
if (pvbStringFileInfo) { return &pvbStringFileInfo->rsValue; } else { return NULL; } }
void SetStringFileInfo(WCHAR *pwcStr, ResourceString *prs) {
VersionBlock *pvbStringFileInfo;
pvbStringFileInfo = FindStringFileInfo(pwcStr);
if (pvbStringFileInfo) { pvbStringFileInfo->rsValue = *prs; } }
ResourceBinary* GetBinaryInfo() const { if (pvb) { return &pvb->rbValue; } return NULL; }
void SetBinaryInfo(const ResourceBinary *prb) { if (pvb) { pvb->rbValue = *prb; } } };
//// Statistic collection
//
//
struct ResourceStats { int cResources; // Number of resources with this resource type
int cItems; // Number of items with this resource type
int cWords; // Number of words in strings in this resource type
int cBytes; // Number of bytes used by resources of this type
};
typedef map < ResourceVariant, ResourceStats, less<ResourceVariant> > MappedResourceStats;
MappedResourceStats ResourceStatsMap;
//// Define our own LangId class so that primary languages sort together.
//
//
class LangId {
public: DWORD dwLang;
LangId(DWORD dwL) {dwLang = dwL;};
bool operator< (LangId li) const {
if (PRIMARYLANGID(dwLang) != PRIMARYLANGID(li.dwLang)) {
return PRIMARYLANGID(dwLang) < PRIMARYLANGID(li.dwLang) ? true : false;
} else {
return SUBLANGID(dwLang) < SUBLANGID(li.dwLang) ? true : false; } } };
typedef map < LangId, ResourceStats, less<LangId> > MappedLanguageStats;
MappedLanguageStats LanguageStatsMap;
//// UpdateStats
//
//
const ResourceStats ZeroStats = {0};
HRESULT UpdateStats( const ResourceKey &rk, int cItems, int cWords, int cBytes) {
if (ResourceStatsMap.count(*rk.prvId[0]) == 0) { ResourceStatsMap[*rk.prvId[0]] = ZeroStats; }
if (LanguageStatsMap.count(rk.prvId[2]->GetW()) == 0) { LanguageStatsMap[rk.prvId[2]->GetW()] = ZeroStats; }
ResourceStatsMap[*rk.prvId[0]].cResources += 1; ResourceStatsMap[*rk.prvId[0]].cItems += cItems; ResourceStatsMap[*rk.prvId[0]].cWords += cWords; ResourceStatsMap[*rk.prvId[0]].cBytes += cBytes;
LanguageStatsMap[rk.prvId[2]->GetW()].cResources += 1; LanguageStatsMap[rk.prvId[2]->GetW()].cItems += cItems; LanguageStatsMap[rk.prvId[2]->GetW()].cWords += cWords; LanguageStatsMap[rk.prvId[2]->GetW()].cBytes += cBytes;
return S_OK; }
//// IsResourceWanted
//
// Returns whether a given resource key was requested on the command line
BOOL IsResourceWanted(const ResourceKey &rk) {
if (rk.prvId[0]->GetfString()) {
return g_dwProcess & PROCESSOTH;
} else {
switch (rk.prvId[0]->GetW()) {
case 1: return g_dwProcess & PROCESSCUR; case 2: return g_dwProcess & PROCESSBMP; case 3: return g_dwProcess & PROCESSICO; case 4: return g_dwProcess & PROCESSMNU; case 5: return g_dwProcess & PROCESSDLG; case 6: return g_dwProcess & PROCESSSTR; case 7: return g_dwProcess & PROCESSFDR; case 8: return g_dwProcess & PROCESSFNT; case 9: return g_dwProcess & PROCESSACC; case 10: return g_dwProcess & PROCESSRCD; case 11: return g_dwProcess & PROCESSMSG; case 16: return g_dwProcess & PROCESSVER; case 240: case 1024: case 23: case 2110: return g_dwProcess & PROCESSBIN; case 2200: return g_dwProcess & PROCESSINF; default: return g_dwProcess & PROCESSOTH; } }
return FALSE; }
//// NewResource
//
// Returns a pointer to a newly allocated subclass of Resource
// suitable for the given resource type.
Resource *NewResource(const ResourceVariant &rv) {
if (rv.GetfString()) {
return new ResourceBinary;
} else {
switch (rv.GetW()) {
case 1: return new ResourceBinary; case 2: return new ResourceBinary; case 3: return new ResourceBinary; case 4: return new Menu32; case 5: return new Dialog32; case 6: return new String32; case 7: return new ResourceBinary; case 8: return new ResourceBinary; case 9: return new ResourceBinary; case 10: return new ResourceBinary; case 11: return new ResourceBinary; case 16: return new VersionInfo; case 240: case 1024: case 23: case 2110: return new ResourceBinary; case 2200: return new ResourceBinary;
default: return new ResourceBinary; } } }
//// Rsrc internal resource directory
//
// Rsrc stores resources in an STL 'map' structure.
class ResourceValue {
public:
const BYTE *pb; // Pointer into mapped file
DWORD cb; // Count of bytes in the value
Resource *pResource; DWORD dwCodePage; // Codepage from Win32 resource index - not very useful!
ResourceValue() {pb = NULL; pResource = NULL; cb=0; dwCodePage=0;}
/*
~ResourceValue() {}; // Don't destroy content on destruction
ResourceValue& operator= (const ResourceValue &rv) { pb = rv.pb; cb = rv.cb; pResource = rv.pResource; dwCodePage = rv.dwCodePage; return *this; }
ResourceValue(const ResourceValue &rv) { *this = rv; } */
//// CreateImage
//
// Convert interpreted resource to binary image.
// Used to prepare resources read from tokens for
// comparison and update.
HRESULT CreateImage() {
BYTE *pbBuf;
ASSERT(pb == NULL); ASSERT(pResource != NULL);
cb = pResource->cbBin(); pbBuf = new BYTE [cb]; ASSERT(pbBuf != NULL);
pb = pbBuf; OK(pResource->CopyBin(&pbBuf));
ASSERT(pbBuf - pb == cb); // This may be too strong? It has not failed yet!
ASSERT(pbBuf - pb <= cb); // This must be true - otherwise we wrote past the end of the buffer
return S_OK; }
//// InterpretImage
//
// Convert binary image to interpreted resource.
// Used to prepare resources read from executable for
// writing as tokens.
HRESULT InterpretImage(const ResourceKey &rk) {
ASSERT(pb != NULL); ASSERT(pResource == NULL);
ASSERT(rk.iDepth == 3); ASSERT(!rk.prvId[2]->GetfString());
if (g_dwOptions & OPTHEXDUMP) {
pResource = new ResourceHexDump;
} else {
// This is a resource extraction to tokens so interpret content
pResource = NewResource(*rk.prvId[0]); }
ASSERT(pResource != NULL);
OK(pResource->ReadBin(Scanner(pb, cb), cb));
pb = NULL; cb = 0;
return S_OK; }
//// Checksum
//
// Returns DWORD checksum of binary content of resource
DWORD Checksum() {
DWORD dw; DWORD *pdw; int i,l;
ASSERT(pb != NULL);
l = cb >> 2; // Length in whole DWORDS
pdw = (DWORD*)pb; dw = 0;
for (i=0; i<l; i++) {
dw ^= pdw[i]; }
l = cb - (l << 2); // Remaining length in bytes
if (l>2) dw ^= pb[cb-3] << 16; if (l>1) dw ^= pb[cb-2] << 8; if (l>0) dw ^= pb[cb-1];
return dw; } };
class ResourceMap : public map < ResourceKey, ResourceValue*, less<ResourceKey> > {
public:
//// AddResource
//
//
HRESULT AddResource(ResourceKey &rk, const BYTE *pb, DWORD cb, DWORD dwCodePage) {
ResourceValue *prv;
// Build a resource structure
prv = new ResourceValue;
prv->pb = pb; prv->cb = cb; prv->dwCodePage = dwCodePage; prv->pResource = NULL;
// Process add options
if (IsResourceWanted(rk)) {
// Insert resource details into STL map
if (this->count(rk) != 0) {
fprintf(stderr, "%s(", g_szExecutable); rk.fprint(stderr); fprintf(stderr, "): error RSRC500: Corrupt executable - resource appears more than once\n"); g_fError = TRUE; return E_FAIL; }
(*this)[rk] = prv;
} else {
g_cResourcesIgnored++; }
return S_OK; }
//// CopyResources
//
// Takes a copy so the original mapped file can be closed
HRESULT CopyResources() {
iterator rmi; BYTE *pb;
for (rmi = begin(); rmi != end(); rmi++) {
pb = new BYTE[rmi->second->cb]; ASSERT(pb != NULL);
memcpy(pb, rmi->second->pb, rmi->second->cb);
rmi->second->pb = pb; }
return S_OK; }
//// WriteTokens
//
// Writes the content of the map as a token file.
//
// If an unlocalised map is provided, bit for bit identical
// resources are written as a reference to the unlocalised
// version language, rather than in full.
HRESULT WriteTokens(NewFile &nfText, ResourceMap *prmUnlocalised) {
iterator rmi; iterator rmiUnlocalised; ResourceKey rkUnlocalised;
for (rmi = begin(); rmi != end(); rmi++) {
g_cResourcesExtracted++;
// Write resource key and codepage
OK(rmi->first.WriteTok(&nfText)); OK(nfText.WriteS(";")); OK(nfText.WriteHex(rmi->second->dwCodePage, 8));
if (prmUnlocalised) {
// Add unlocalised checksum and language
rkUnlocalised = rmi->first; rkUnlocalised.SetLanguage(g_liUnlocalized); rmiUnlocalised = prmUnlocalised->find(rkUnlocalised);
if (rmiUnlocalised == prmUnlocalised->end()) {
fprintf(stderr, "%s(", g_szResources); rmi->first.fprint(stderr); fprintf(stderr, "): warning RSRC100: Localised resource has no corresponding unlocalised resource in %s\n", g_szUnloc); g_fWarn = TRUE;
} else {
// Put out details of the unlocalised resource
OK(nfText.WriteS(",")); OK(nfText.WriteHex(rmiUnlocalised->second->Checksum(), 8)); OK(nfText.WriteS(",")); OK(nfText.WriteHex(g_liUnlocalized, 4)); } }
OK(nfText.WriteS(";"));
// Check whether resource needs to be written in full
if ( prmUnlocalised && rmiUnlocalised != prmUnlocalised->end() && rmiUnlocalised->second->cb == rmi->second->cb && memcmp(rmi->second->pb, rmiUnlocalised->second->pb, rmi->second->cb) == 0) {
// Bit for bit match with unlocalised executable
OK(nfText.WriteS("Unloc"));
} else {
// Doesn't match - write it in full
OK(rmi->second->InterpretImage(rmi->first)); OK(rmi->second->pResource->WriteTok(nfText)); }
OK(nfText.WriteLn()); }
return S_OK; }
//// UpdateWin32Executable
//
//
HRESULT UpdateWin32Executable(char *pExecutable) {
iterator rmi; HANDLE hUpdate;
hUpdate = BeginUpdateResourceA(pExecutable, TRUE); // Will replace all resources
MUST(hUpdate != NULL ? S_OK : E_FAIL, ("RSRC : error RSRC600: BeginUpdateResource failed on %s\n", pExecutable));
for (rmi = begin(); rmi != end(); rmi++) {
ASSERT(rmi->first.iDepth == 3); ASSERT(!rmi->first.prvId[2]->GetfString());
// Create binary image of resource if necessary
if (rmi->second->pb == NULL) { OK(rmi->second->CreateImage()); }
// Use NT resource API to update resource binary image in executable
if (!UpdateResourceW( hUpdate, rmi->first.GetResName(0), rmi->first.GetResName(1), rmi->first.prvId[2]->GetW(), (void*)rmi->second->pb, rmi->second->cb)) {
EndUpdateResourceW(hUpdate, TRUE); // Discard all requested updates
g_fError = TRUE; fprintf(stderr, "RSRC : error RSRC601: UpdateResourceW failed on %s\n", pExecutable); return HRESULT_FROM_WIN32(GetLastError()); } }
if (!EndUpdateResourceW(hUpdate, FALSE)) { // Apply all requested updates
fprintf(stderr, "RSRC : error RSRC602: EndUpdateResourceW failed on %s\n", pExecutable); g_fError = TRUE; return HRESULT_FROM_WIN32(GetLastError()); }
return S_OK; } };
class SymbolFile {
MappedFile *m_pmfSymbolFile; IMAGE_SEPARATE_DEBUG_HEADER *m_pDebugHeader;
public:
DWORD GetChecksum() const {return m_pDebugHeader->CheckSum;} DWORD GetTimeDateStamp() const {return m_pDebugHeader->TimeDateStamp;} DWORD GetImageBase() const {return m_pDebugHeader->ImageBase;} DWORD GetSizeOfImage() const {return m_pDebugHeader->SizeOfImage;}
void SetChecksum (DWORD dwChecksum) {m_pDebugHeader->CheckSum = dwChecksum;} void SetTimeDateStamp (DWORD dwTimeDateStamp) {m_pDebugHeader->TimeDateStamp = dwTimeDateStamp;} void SetImageBase (DWORD dwImageBase) {m_pDebugHeader->ImageBase = dwImageBase;} void SetSizeOfImage (DWORD dwSizeOfImage) {m_pDebugHeader->SizeOfImage = dwSizeOfImage;}
HRESULT Open(MappedFile *pmfSymbolFile) {
m_pmfSymbolFile = pmfSymbolFile; m_pDebugHeader = (IMAGE_SEPARATE_DEBUG_HEADER*) pmfSymbolFile->GetFile();
ASSERT(m_pDebugHeader->Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE);
return S_OK; }
};
class Win32Executable : public MappedFile {
IMAGE_NT_HEADERS *m_pNtHeader; IMAGE_SECTION_HEADER *m_pSections; int m_iSectionRsrc; int m_iSectionRsrc1;
// For scanning
ResourceKey m_rk; // Current resource key
HRESULT MapDirectory( ResourceMap &rm, const BYTE *pbRsrc, // Resource block
int dwOffset, // Directory offset relative to m_pbRsrc
int iLevel) { // Directory level being scanned
IMAGE_RESOURCE_DIRECTORY *pird; IMAGE_RESOURCE_DIRECTORY_ENTRY *pEntries; IMAGE_RESOURCE_DATA_ENTRY *pirde; const BYTE *pb; int i;
pird = (IMAGE_RESOURCE_DIRECTORY*) (pbRsrc+dwOffset); pEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY*) (pird+1);
for (i=0; i<pird->NumberOfNamedEntries + pird->NumberOfIdEntries; i++) {
// Read the ID from the directory
ASSERT(iLevel<3); m_rk.iDepth = iLevel+1;
m_rk.prvId[iLevel] = new ResourceVariant; ASSERT(m_rk.prvId[iLevel] != NULL); OK(m_rk.prvId[iLevel]->ReadWin32ResDirEntry(this, pbRsrc, pEntries+i));
if (pEntries[i].DataIsDirectory) {
// This is a directory node. Recurse to scan that directory.
OK(MapDirectory(rm, pbRsrc, pEntries[i].OffsetToDirectory, iLevel+1));
} else {
// We've reached a leaf node, establish the data address and
// add the resource to the map.
pirde = (IMAGE_RESOURCE_DATA_ENTRY*) (pbRsrc + pEntries[i].OffsetToData);
// Note that even when the resource data is in .rsrc1, the
// directory entry is usually in .rsrc.
if (pirde->OffsetToData < m_pSections[m_iSectionRsrc].VirtualAddress + m_pSections[m_iSectionRsrc].SizeOfRawData) {
// Data is in section .rsrc
ASSERT(pirde->OffsetToData >= m_pSections[m_iSectionRsrc].VirtualAddress);
pb = GetFile() + m_pSections[m_iSectionRsrc].PointerToRawData + pirde->OffsetToData - m_pSections[m_iSectionRsrc].VirtualAddress;
} else {
// Data is in section .rsrc1
ASSERT(pirde->OffsetToData >= m_pSections[m_iSectionRsrc1].VirtualAddress); ASSERT(pirde->OffsetToData < m_pSections[m_iSectionRsrc1].VirtualAddress + m_pSections[m_iSectionRsrc1].SizeOfRawData);
pb = GetFile() + m_pSections[m_iSectionRsrc1].PointerToRawData + pirde->OffsetToData - m_pSections[m_iSectionRsrc1].VirtualAddress; }
OK(rm.AddResource(m_rk, pb, pirde->Size, pirde->CodePage)); } } return S_OK; }
public:
DWORD GetChecksum() const {return m_pNtHeader->OptionalHeader.CheckSum;} DWORD GetTimeDateStamp() const {return m_pNtHeader->FileHeader.TimeDateStamp;} DWORD GetImageBase() const {return m_pNtHeader->OptionalHeader.ImageBase;} DWORD GetSizeOfImage() const {return m_pNtHeader->OptionalHeader.SizeOfImage;} BOOL Is64BitImage() const {return m_pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC;}
void SetChecksum(DWORD dwChecksum) {m_pNtHeader->OptionalHeader.CheckSum=dwChecksum;}
HRESULT Open(const char *pcFileName, BOOL fWrite) {
int i;
OK(MappedFile::Open(pcFileName, fWrite));
MUST(( *(WORD*)m_pStart == IMAGE_DOS_SIGNATURE && *(WORD*)(m_pStart+0x18) >= 0x40) // WinVer >= 4
? S_OK : E_FAIL, ("RSRC : error RSRC501: %s is not an executable file\n", pcFileName));
m_pNtHeader = (IMAGE_NT_HEADERS*)(m_pStart + *(WORD*)(m_pStart+0x3c));
MUST((m_pNtHeader->Signature == IMAGE_NT_SIGNATURE) ? S_OK : E_FAIL, ("RSRC : error RSRC502: %s is not a Win32 executable file\n", pcFileName));
if (Is64BitImage()) { m_pSections = (IMAGE_SECTION_HEADER*)( (BYTE *) (m_pNtHeader+1) + (IMAGE_SIZEOF_NT_OPTIONAL64_HEADER - IMAGE_SIZEOF_NT_OPTIONAL32_HEADER)); } else { m_pSections = (IMAGE_SECTION_HEADER*)(m_pNtHeader+1); } m_iSectionRsrc = -1; m_iSectionRsrc1 = -1;
// Locate the one or two resource sections
for (i=0; i<m_pNtHeader->FileHeader.NumberOfSections; i++) {
if (strcmp((char*)m_pSections[i].Name, ".rsrc") == 0) {
m_iSectionRsrc = i;
} else if (strcmp((char*)m_pSections[i].Name, ".rsrc") == 0) {
m_iSectionRsrc1 = i; } }
MUST(m_iSectionRsrc >= 0 ? S_OK : E_FAIL, ("RSRC : error RSRC503: No resources in %s\n", pcFileName)); ASSERT(m_iSectionRsrc > -1); // Check for presence of resources
return S_OK; }
//// MapResourceDirectory
//
// Extract the resource directory into an STL map.
HRESULT MapResourceDirectory(ResourceMap &rm) {
OK(MapDirectory( rm, m_pStart + m_pSections[m_iSectionRsrc].PointerToRawData, 0, 0));
if (m_iSectionRsrc1 >= 0) { OK(MapDirectory( rm, m_pStart + m_pSections[m_iSectionRsrc1].PointerToRawData, 0, 0)); }
return S_OK; } };
//// High level operation
//
// Controlling routines for the various modes of operation
ResourceMap rmExecutable; // Read and/or update
ResourceMap rmUnlocalised; // '-u' option - unlocalised resources for comparison
//// ApplyResource
//
// Applies a given key and value to the executable resource map.
//
// Tokens are merged with those already loaded from the executable
// according to the update mode (append or replace).
HRESULT ApplyResource(ResourceKey &rk, ResourceValue *prv) {
ResourceKey rkUnloc; VersionInfo *pviLoc; VersionInfo *pviUnloc; ResourceMap::iterator rmiUnloc;
// Establish equivalent unlocalised key
rkUnloc = rk; rkUnloc.SetLanguage(g_liUnlocalized);
// First ensure that we keep the unlocalised version info, if we can
if ( !(g_dwOptions & OPTVERSION) && !rk.prvId[0]->GetfString() && rk.prvId[0]->GetW() == 16 // VersionInfo
&& (rmiUnloc=rmExecutable.find(rkUnloc)) != NULL && rmiUnloc != rmExecutable.end()) {
// Special case - keep unlocalised file and product versions
if (rmiUnloc->second->pResource == NULL) { rmiUnloc->second->InterpretImage(rmiUnloc->first); }
pviLoc = static_cast<VersionInfo*>(prv->pResource); pviUnloc = static_cast<VersionInfo*>(rmiUnloc->second->pResource); if (pviLoc && pviUnloc) { pviLoc->SetStringFileInfo(L"FileVersion", pviUnloc->GetStringFileInfo(L"FileVersion")); pviLoc->SetStringFileInfo(L"ProductVersion", pviUnloc->GetStringFileInfo(L"ProductVersion")); pviLoc->SetBinaryInfo(pviUnloc->GetBinaryInfo()); } }
if (rk.prvId[2]->GetW() == g_liUnlocalized) {
// New token is not localized
fprintf(stderr, "%s(", g_szResources); rk.fprint(stderr);
if (rmExecutable.count(rk) == 0) {
fprintf(stderr, "): warning RSRC110: Unlocalised resource from token file appended to executable\n"); g_fWarn = TRUE; g_cResourcesAppended++;
} else {
fprintf(stderr, "): warning RSRC111: Unlocalised resource from token file replaced unlocalised resource in executable\n"); g_fWarn = TRUE; g_cResourcesUpdated++; }
} else if (rmExecutable.count(rk) > 0) {
// New token already exists in executable
fprintf(stderr, "%s(", g_szResources); rk.fprint(stderr); fprintf(stderr, "): warning RSRC112: Localised resource from token file replaced localised resource already present in executable\n"); g_fWarn = TRUE; g_cResourcesUpdated++;
} else if (g_dwOptions & OPTREPLACE) {
// Replace operation
//
// Replace unlocalised resource with localised translation
if (rmExecutable.count(rkUnloc) == 0) {
fprintf(stderr, "%s(", g_szResources); rk.fprint(stderr); fprintf(stderr, "): warning RSRC113: Localised resource from token file appended to executable - there was no matching unlocalised resource\n"); g_fWarn = TRUE; g_cResourcesAppended++;
} else {
// Normal operation: remove unlocalised resource from executable
rmExecutable.erase(rkUnloc);
g_cResourcesTranslated++; }
} else {
// Append operation
g_cResourcesAppended++; }
rmExecutable[rk] = prv;
return S_OK; }
//// ReadTokens
//
// Scans the token file.
//
// Selected resources are passed to ApplyResource
HRESULT ReadTokens(TextScanner &mfText) {
ResourceKey rk; ResourceValue *prv; ResourceKey rkUnlocalised; DWORD dwCodePage; DWORD dwUnlocChecksum; ResourceMap::iterator rmiUnlocalised; DWORD liUnlocalised; // Unlocalised language referenced by token
while (mfText.GetRead() < mfText.GetLimit()) {
OK(rk.ReadTok(&mfText)); // Read resource key
OK(mfText.Expect(";"));
if ( ( g_LangId != 0xffff && rk.prvId[2]->GetW() != g_LangId) || !IsResourceWanted(rk)) {
// Ignore this token
g_cResourcesIgnored++;
fprintf(stderr, "%s(", g_szResources); rk.fprint(stderr);
if (g_LangId != 0xffff && rk.prvId[2]->GetW() != g_LangId) {
fprintf(stderr, "): warning RSRC120: Token file resource does not match specified language - ignored\n"); g_fWarn = TRUE;
} else {
fprintf(stderr, "): warning RSRC121: Token file resource is not a requested resource type - ignored\n"); g_fWarn = TRUE; }
// Skip unwanted resource
OK(mfText.SkipLn()); while (*(char*)mfText.GetRead() == ' ') { OK(mfText.SkipLn()); }
} else {
rmiUnlocalised = NULL;
OK(mfText.ReadHex(&dwCodePage));
if (*(char*)mfText.GetRead() == ',') {
// There is unlocalised resource information available
OK(mfText.Expect(",")); OK(mfText.ReadHex(&dwUnlocChecksum)); OK(mfText.Expect(",")); OK(mfText.ReadHex(&liUnlocalised));
// Check whether the unlocalised resource still exists in the
// current executable, and has the same checksum,
rkUnlocalised = rk; rkUnlocalised.SetLanguage(liUnlocalised); rmiUnlocalised = rmExecutable.find(rkUnlocalised);
if ( rmiUnlocalised != rmExecutable.end() && dwUnlocChecksum != rmiUnlocalised->second->Checksum()) {
fprintf(stderr, "%s: warning RSRC122: executable unlocalised resource checksum does not match checksum recorded in token file for resource ", mfText.GetTextPos()); rk.fprint(stderr); fprintf(stderr, "\n"); g_fWarn = TRUE; } }
OK(mfText.Expect(";"));
if (*(char*)mfText.GetRead() == 'U') {
// No resource content provided in token file
// Use unlocalised resource from executable
if (rmiUnlocalised == NULL) {
fprintf(stderr, "%s: error RSRC230: 'Unloc' token is missing unlocalised resource information for ", mfText.GetTextPos()); rk.fprint(stderr); fprintf(stderr, "\n"); g_fError = TRUE; return E_FAIL; }
OK(mfText.Expect("Unloc")); OK(mfText.ExpectLn(""));
if (rmiUnlocalised == rmExecutable.end()) {
fprintf(stderr, "%s: warning RSRC124: missing executable unlocalised resource for ", mfText.GetTextPos()); rk.fprint(stderr); fprintf(stderr, " - localisation skipped\n"); g_fWarn = TRUE;
} else {
MUST(ApplyResource(rk, rmiUnlocalised->second), ("%s: error RSRC231: Failed to apply unloc token\n", mfText.GetTextPos())); }
} else {
// Resource content is provided in token file
if (rmiUnlocalised == rmExecutable.end()) {
fprintf(stderr, "%s: warning RSRC125: executable contains no unlocalised resource corresponding to resource ", mfText.GetTextPos()); rk.fprint(stderr); fprintf(stderr, "\n"); g_fWarn = TRUE; }
prv = new ResourceValue; ASSERT(prv != NULL);
prv->dwCodePage = dwCodePage; prv->pb = NULL; prv->cb = 0;
switch (*(char*)mfText.GetRead()) {
case 'H': prv->pResource = new ResourceBinary; break; case 'D': prv->pResource = new Dialog32; break; case 'M': prv->pResource = new Menu32; break; case 'S': prv->pResource = new String32; break; case 'V': prv->pResource = new VersionInfo; break;
default: fprintf(stderr, "%s: error RSRC310: Unrecognised resource type for resource ", mfText.GetTextPos()); rk.fprint(stderr); fprintf(stderr, "\n"); g_fError = TRUE; return E_FAIL; }
ASSERT(prv->pResource != NULL);
// Parse selected resource
OK(prv->pResource->ReadTok(mfText)); OK(mfText.ExpectLn(NULL));
// Save parsed resource in STL map
MUST(ApplyResource(rk, prv), ("%s: error RSRC232: Failed to apply token\n", mfText.GetTextPos())); } } }
return S_OK; }
//// Stats
//
//
HRESULT Analyse(char *pExecutable) {
Win32Executable w32x; NewFile nfText; ResourceMap::iterator rmi; MappedResourceStats::iterator mrsi; MappedLanguageStats::iterator mlsi; char key[100]; int i; const WCHAR *pwc; BOOL fLocalizable;
MUST(w32x.Open(pExecutable, FALSE), ("RSRC : error RSRC510: Cannot open executable file %s\n", pExecutable));
MUST(w32x.MapResourceDirectory(rmExecutable), ("RSRC : error RSRC511: cannot find resource directory in %s\n, pExecutable"));
// Scan through the resources updating the stats
fLocalizable = FALSE;
for (rmi = rmExecutable.begin(); rmi != rmExecutable.end(); rmi++) {
if ( rmi->first.prvId[0]->GetfString() || rmi->first.prvId[0]->GetW() != 16) { fLocalizable = TRUE; }
OK(rmi->second->InterpretImage(rmi->first));
UpdateStats(rmi->first, rmi->second->pResource->GetItems(), rmi->second->pResource->GetWords(), rmi->second->pResource->cbBin()); }
if (!(g_dwOptions & OPTQUIET)) { fprintf(stdout, "\n Resource type Count Items Words Bytes\n"); fprintf(stdout, " ------------ ------ ------ ------ --------\n");
for (mrsi = ResourceStatsMap.begin(); mrsi != ResourceStatsMap.end(); mrsi++) {
if (mrsi->first.GetfString()) {
key[0] = '\"'; i=0; pwc = mrsi->first.GetString(); while (i < min(10, mrsi->first.GetLength())) {
key[i+1] = (char) pwc[i]; i++; }
key[i+1] = '\"'; key[i+2] = 0;
fprintf(stdout, " %-12.12s ", key);
} else {
switch (mrsi->first.GetW()) { case 1: fprintf(stdout, " 1 (Cursor) "); break; case 2: fprintf(stdout, " 2 (Bitmap) "); break; case 3: fprintf(stdout, " 3 (Icon) "); break; case 4: fprintf(stdout, " 4 (Menu) "); break; case 5: fprintf(stdout, " 5 (Dialog) "); break; case 6: fprintf(stdout, " 6 (String) "); break; case 7: fprintf(stdout, " 7 (Fnt dir) "); break; case 8: fprintf(stdout, " 8 (Font) "); break; case 9: fprintf(stdout, " 9 (Accel) "); break; case 10: fprintf(stdout, " a (RCDATA) "); break; case 11: fprintf(stdout, " b (Msgtbl) "); break; case 16: fprintf(stdout, " 10 (Version) "); break; default: fprintf(stdout, " %-12x ", mrsi->first.GetW()); } }
fprintf(stdout, "%6d ", mrsi->second.cResources);
if (mrsi->second.cItems > 0) { fprintf(stdout, "%6d ", mrsi->second.cItems); } else { fprintf(stdout, " "); } if (mrsi->second.cWords > 0) { fprintf(stdout, "%6d ", mrsi->second.cWords); } else { fprintf(stdout, " "); } fprintf(stdout, "%8d\n", mrsi->second.cBytes); }
fprintf(stdout, "\n Language Resources Items Words Bytes\n"); fprintf(stdout, " -------- --------- ------ ------ --------\n");
for (mlsi = LanguageStatsMap.begin(); mlsi != LanguageStatsMap.end(); mlsi++) {
fprintf(stdout, " %8x %9d ", mlsi->first, mlsi->second.cResources);
if (mlsi->second.cItems > 0) { fprintf(stdout, "%6d ", mlsi->second.cItems); } else { fprintf(stdout, " "); }
if (mlsi->second.cWords > 0) { fprintf(stdout, "%6d ", mlsi->second.cWords); } else { fprintf(stdout, " "); }
fprintf(stdout, "%8d\n", mlsi->second.cBytes); }
fprintf(stdout, "\n"); }
if (!fLocalizable) { fprintf(stderr, "RSRC : warning RSRC170: No localizable resources in %s\n", pExecutable); g_fWarn = TRUE; }
SHOULD(w32x.Close(), ("RSRC : warning RSRC171: could not close executable\n"));
return S_OK; }
HRESULT ExtractResources(char *pExecutable, char *pResources) {
Win32Executable w32x; Win32Executable w32xUnloc; NewFile nfText; char str[100]; DWORD dw;
MUST(w32x.Open(g_szExecutable, FALSE), ("RSRC : error RSRC510: Cannot open executable file %s\n", g_szExecutable));
MUST(nfText.OpenWrite(g_szResources), ("RSRC : error RSRC512: Cannot create resource token file %s\n", g_szResources));
// Write header
if (!(g_dwOptions & OPTHEXDUMP)) { OK(nfText.WriteS("\xef\xbb\xbf\r\n")); // UTF-8 mark for notepad, richedit etc.
} OK(nfText.WriteS("### ")); OK(nfText.WriteS(g_szResources)); OK(nfText.WriteS("\r\n#\r\n# Extracted: ")); GetDateFormatA( MAKELCID(LANG_ENGLISH, SORT_DEFAULT), 0, NULL, "yyyy/MM/dd ", str, sizeof(str)); OK(nfText.WriteS(str)); GetTimeFormatA( MAKELCID(LANG_ENGLISH, SORT_DEFAULT), 0, NULL, "HH:mm:ss\'\r\n# By: \'", str, sizeof(str)); OK(nfText.WriteS(str)); dw = sizeof(str); GetComputerNameA(str, &dw); OK(nfText.WriteS(str)); OK(nfText.WriteS("\r\n# Executable: ")); OK(nfText.WriteS(g_szExecutable));
if (g_LangId != 0xffff) { OK(nfText.WriteS("\r\n# Language: ")); OK(nfText.WriteHex(g_LangId, 3)); }
if (g_dwProcess != PROCESSALL) { OK(nfText.WriteS("\r\n# Res types: ")); OK(nfText.WriteS(g_szTypes)); }
OK(nfText.WriteS("\r\n\r\n"));
MUST(w32x.MapResourceDirectory(rmExecutable), ("RSRC : error RSRC511: cannot find resource directory in %s\n, g_szExecutable"));
if (g_dwOptions & OPTUNLOC) {
// Write tokens that differ from specified unlocalised executable
MUST(w32xUnloc.Open(g_szUnloc, FALSE), ("RSRC : error RSRC513: Cannot open unlocalised executable file %s\n", g_szUnloc));
MUST(w32xUnloc.MapResourceDirectory(rmUnlocalised), ("RSRC : error RSRC514: cannot find resource directory in unlocalised executable %s\n, g_szUnloc"));
MUST(rmExecutable.WriteTokens(nfText, &rmUnlocalised), ("RSRC : error RSRC515: cannot write delta token file %s\n, g_szResources"));
w32xUnloc.Close();
} else {
MUST(rmExecutable.WriteTokens(nfText, NULL), ("RSRC : error RSRC516: cannot write stand alone token file %s\n, g_szResources")); }
if (!(g_dwOptions & OPTQUIET)) { fprintf(stdout, "\n%d resource(s) %s.\n", g_cResourcesExtracted, g_dwOptions & OPTHEXDUMP ? "dumped" : "tokenized");
if (g_cResourcesIgnored) { fprintf(stdout, "%d resource(s) ignored.\n", g_cResourcesIgnored); } }
OK(w32x.Close()); OK(nfText.Close());
return S_OK; }
//// UpdateResources
//
// Update resources in executable with tokens from given text
//
// Processing
//
// 1. Existing resources are loaded into the map as ResourceBinaries.
// 2. Resources are merged in from the token file according to
// command line selected processing options
// 3. The NT UpdateResource API set is used to replace all the resources
// in the executable with the merged resources in the map.
HRESULT UpdateResources(char *pExecutable, char *pResources, char* pSymbols) {
Win32Executable w32x; SymbolFile symf; MappedFile mfText; MappedFile mfSymbols; DWORD dwCheckSum;
MUST(w32x.Open(pExecutable, FALSE), ("RSRC : error RSRC510: Cannot open executable file %s\n", pExecutable));
MUST(mfText.Open(pResources, FALSE), ("RSRC : error RSRC520: Cannot open resource token file %s\n", pResources));
MUST(mfText.Expect("\xef\xbb\xbf"), ("RSRC : error RSRC521: UTF8 BOM missing from token file\n")); // UTF-8 mark for notepad, richedit etc.
OK(mfText.ExpectLn("")); // Skip over header comments
if (g_dwOptions & OPTSYMBOLS) { if ( SUCCEEDED(mfSymbols.Open(pSymbols, TRUE)) && SUCCEEDED(symf.Open(&mfSymbols))) {
if ( symf.GetChecksum() != w32x.GetChecksum() || symf.GetImageBase() != w32x.GetImageBase()) {
time_t tsTime = symf.GetTimeDateStamp(); time_t teTime = w32x.GetTimeDateStamp(); char ssTime[30]; strcpy(ssTime, ctime(&tsTime)); ssTime[19] = 0; char seTime[30]; strcpy(seTime, ctime(&teTime)); seTime[19] = 0;
fprintf(stderr, "\n Symbol mismatch: Executable Symbol file\n"); fprintf(stderr, " ImageBase: %8x %8x\n", w32x.GetImageBase(), symf.GetImageBase()); fprintf(stderr, " Checksum: %8x %8x\n", w32x.GetChecksum(), symf.GetChecksum()); fprintf(stderr, " Timestamp: %-15.15s %-15.15s\n\n", ssTime+4, seTime+4);
fprintf(stderr, "RSRC : warning RSRC160: Symbol file does not match exectable\n"); g_fWarn = TRUE; }
} else {
fprintf(stderr, "RSRC : warning RSRC161: Symbol file not processed\n"); g_fWarn = TRUE; g_dwOptions &= ~OPTSYMBOLS; }
}
// Load existing resources
MUST(w32x.MapResourceDirectory(rmExecutable), ("RSRC : error RSRC530: Cannot read executable resources from %s\n", pExecutable));
OK(rmExecutable.CopyResources()); // Take local copy before closing the mapped file
OK(w32x.Close());
// Merge in resources from token file
MUST(ReadTokens(mfText), ("RSRC : error RSRC531: Failed reading update tokens\n"));
OK(rmExecutable.UpdateWin32Executable(pExecutable));
// Update was succesful, Recalculate checksum
SHOULD(w32x.Open(pExecutable, TRUE), ("RSRC : warning RSRC162: Could not reopen executable %s to update checksum\n", pExecutable));
dwCheckSum = w32x.CalcChecksum();
w32x.SetChecksum(dwCheckSum);
if (g_dwOptions & OPTSYMBOLS) { symf.SetChecksum(dwCheckSum); symf.SetTimeDateStamp(w32x.GetTimeDateStamp()); symf.SetSizeOfImage(w32x.GetSizeOfImage()); SHOULD(mfSymbols.Close(), ("RSRC : warning RSRC163: Failed to write updated symbol checksum\n")); }
w32x.Close();
if (!(g_dwOptions & OPTQUIET)) {
fprintf(stdout, "\n");
if (g_cResourcesTranslated) { fprintf(stdout, "%d resource(s) translated.\n", g_cResourcesTranslated); }
if (g_cResourcesAppended) { fprintf(stdout, "%d resource(s) appended.\n", g_cResourcesAppended); }
if (g_cResourcesUpdated) { fprintf(stdout, "%d resource(s) updated.\n", g_cResourcesUpdated); }
if (g_cResourcesIgnored) { fprintf(stdout, "%d resource(s) ignored.\n", g_cResourcesIgnored); } }
mfText.Close();
return S_OK; }
//// Parameter parsing
//
//
char g_cSwitch = '-'; // Switch character is recorded the first time one is seen
void SkipWhitespace(char** p, char* pE) { while ((*p<pE) && ((**p==' ')||(**p==9))) (*p)++; }
void ParseToken(char** p, char* pE, char* s, int l) {
// Parse up to whitespace into string s
// Guarantee zero terminator and modify no more than l chars
// Return with p beyond whitespace
if (*p < pE && **p == '\"') {
// Quoted parameter
(*p)++; // Skip over leading quote
while (l>0 && *p<pE && **p!='\"') { *s=**p; s++; (*p)++; l--; }
// Skip any part of token that didn't fit s
while (*p<pE && **p!='\"') { // Skip up to terminating quote
(*p)++; }
if (*p<pE) { // Skip over terminating quote
(*p)++; }
} else {
// Unquoted parameter
while ((l>0) && (*p<pE) && (**p>' ')) { *s=**p; s++; (*p)++; l--; }
// Skip any part of token that didn't fit into s
while ((*p<pE) && (**p>' ')) (*p)++; }
if (l>0) *s++ = 0; else *(s-1) = 0;
SkipWhitespace(p, pE); }
void ParseName(char** p, char* pE, char* s, int l) {
// Uses ParseToken to parse a name such as a filename.
// If the name starts with '/' or '-' it is assumed to be
// an option rather than a filename and ParseName returns
// a zero length string.
if (*p<pE && **p==g_cSwitch) {
// This is an option and should not be treated as a name argument
s[0] = 0;
} else {
ParseToken(p, pE, s, l); } }
void DisplayUsage() { fprintf(stdout, "Usage: rsrc -h\n"); fprintf(stdout, " or: rsrc executable [-l LangId] [-i include-opts] [-q]\n"); fprintf(stdout, " [ [-t|-d] [text-output] [-c unloc]\n"); fprintf(stdout, " | [-a|-r] [text-input] [-s symbols] ]\n"); }
void DisplayArgs() { fprintf(stdout, "\nArguments\n\n"); fprintf(stdout, " -h Help\n"); fprintf(stdout, " -q Quiet (default is to show resource stats)\n"); fprintf(stdout, " -t tokens Write resources in checkin format to token file\n"); fprintf(stdout, " -c unloc Unlocalised executable for comparison\n"); fprintf(stdout, " -d tokens Write resources in hex dump format to token file\n"); fprintf(stdout, " -a tokens Append resources from token file to executable (multi-language update)\n"); fprintf(stdout, " -r tokens Replace executable resources from token file (single language update)\n"); fprintf(stdout, " -s symbol Symbol file whose checksum is to track the executable checksum\n"); fprintf(stdout, " -l lang Restrict processing to language specified in hex\n"); fprintf(stdout, " -u unlocl Unlocalised langauge, default 409\n"); fprintf(stdout, " -i opts Include only resource types specified:\n\n"); fprintf(stdout, " c - Cursors t - Fonts\n"); fprintf(stdout, " b - Bitmaps a - Accelerators\n"); fprintf(stdout, " i - Icons r - RCDATAs\n"); fprintf(stdout, " m - Menus g - Message tables\n"); fprintf(stdout, " d - Dialogs v - Versions info\n"); fprintf(stdout, " s - Strings x - Binary data\n"); fprintf(stdout, " f - Font directories n - INFs\n"); fprintf(stdout, " o - all others a - All (default)\n\n"); fprintf(stdout, " Examples\n\n"); fprintf(stdout, " rsrc notepad.exe - Show resource stats for notepad.exe\n"); fprintf(stdout, " rsrc notepad.exe -t - Extract tokens to notepad.exe.rsrc\n"); fprintf(stdout, " rsrc notepad.exe -r -l 401 - Translate from US using Arabic tokens in notepad.exe.rsrc\n"); fprintf(stdout, " rsrc notepad.exe -d dmp -i im - Hexdump of Icons and Menus to dmp\n\n"); }
HRESULT ProcessParameters() {
char *p; // Current command line character
char *pE; // End of command line
char *pcStop;
char token [MAXPATH]; char arg [MAXPATH]; char symbols [MAXPATH] = "";
int i,j; int cFiles; DWORD cRes; BOOL fArgError;
p = GetCommandLine(); pE = p+strlen((char *)p);
g_dwOptions = 0; g_dwProcess = 0; cFiles = 0; fArgError = FALSE; g_szResources[0] = 0;
// Skip command name
ParseToken(&p, pE, token, sizeof(token));
while (p<pE) { ParseToken(&p, pE, token, sizeof(token));
if ( token[0] == '-' || token[0] == '/') {
// Process command option(s)
i = 1; g_cSwitch = token[0]; // Argument may start with the other switch character
CharLower((char*)token); while (token[i]) { switch (token[i]) { case '?': case 'h': g_dwOptions |= OPTHELP; break; case 'v': g_dwOptions |= OPTVERSION; break; case 'q': g_dwOptions |= OPTQUIET; break;
case 't': g_dwOptions |= OPTEXTRACT; ParseName(&p, pE, g_szResources, sizeof(g_szResources)); break; case 'c': g_dwOptions |= OPTUNLOC; ParseName(&p, pE, g_szUnloc, sizeof(g_szUnloc)); break; case 'd': g_dwOptions |= OPTHEXDUMP; ParseName(&p, pE, g_szResources, sizeof(g_szResources)); break; case 'a': g_dwOptions |= OPTAPPEND; ParseName(&p, pE, g_szResources, sizeof(g_szResources)); break; case 'r': g_dwOptions |= OPTREPLACE; ParseName(&p, pE, g_szResources, sizeof(g_szResources)); break; case 's': g_dwOptions |= OPTSYMBOLS; ParseName(&p, pE, symbols, sizeof(g_szResources)); break;
case 'l': ParseToken(&p, pE, arg, sizeof(arg)); g_LangId = strtol(arg, &pcStop, 16); if (*pcStop != 0) { fprintf(stderr, "Localized language id contains invalid hex digit '%c'.\n", *pcStop); fArgError = TRUE; } break;
case 'u': ParseToken(&p, pE, arg, sizeof(arg)); g_liUnlocalized = strtol(arg, &pcStop, 16); if (*pcStop != 0) { fprintf(stderr, "Unlocalized language id contains invalid hex digit '%c'.\n", *pcStop); fArgError = TRUE; } break;
case 'i': ParseToken(&p, pE, g_szTypes, sizeof(g_szTypes)); g_dwProcess = 0; j = 0; while (g_szTypes[j]) { switch (g_szTypes[j]) { case 'c': g_dwProcess |= PROCESSCUR; break; case 'b': g_dwProcess |= PROCESSBMP; break; case 'i': g_dwProcess |= PROCESSICO; break; case 'm': g_dwProcess |= PROCESSMNU; break; case 'd': g_dwProcess |= PROCESSDLG; break; case 's': g_dwProcess |= PROCESSSTR; break; case 'f': g_dwProcess |= PROCESSFDR; break; case 't': g_dwProcess |= PROCESSFNT; break; case 'a': g_dwProcess |= PROCESSACC; break; case 'r': g_dwProcess |= PROCESSRCD; break; case 'g': g_dwProcess |= PROCESSMSG; break; case 'v': g_dwProcess |= PROCESSVER; break; case 'x': g_dwProcess |= PROCESSBIN; break; case 'n': g_dwProcess |= PROCESSINF; break; case 'o': g_dwProcess |= PROCESSOTH; break; case 'A': g_dwProcess |= PROCESSALL; break; default: fprintf(stderr, "Unrecognised resource type '%c'.\n", g_szTypes[j]); fArgError = TRUE; } j++; } break;
default: fprintf(stderr, "Unrecognised argument '%c'.\n", token[i]); fArgError = TRUE; break; } i++; }
} else {
// Process filename
switch (cFiles) { case 0: strcpy(g_szExecutable, token); break; } cFiles++; } }
if (g_dwOptions & OPTHELP) {
fprintf(stderr, "\nRsrc - Manage Win32 executable resources.\n\n"); DisplayUsage(); DisplayArgs(); return S_OK;
}
// Validate option combinations
if (g_dwOptions & OPTEXTRACT) {
if (g_dwOptions & (OPTHEXDUMP | OPTAPPEND | OPTREPLACE | OPTSYMBOLS)) {
fprintf(stderr, "RSRC : error RSRC400: -t (tokenise) option excludes -d, -a, -r, and -s\n"); fArgError = TRUE; }
} else if (g_dwOptions & OPTHEXDUMP) {
if (g_dwOptions & (OPTEXTRACT | OPTUNLOC | OPTAPPEND | OPTREPLACE | OPTSYMBOLS)) {
fprintf(stderr, "RSRC : error RSRC401: -d (dump) option excludes -t, -u, -a, -r, and -s\n"); fArgError = TRUE; }
} else if (g_dwOptions & OPTAPPEND) {
if (g_dwOptions & (OPTEXTRACT | OPTHEXDUMP | OPTUNLOC | OPTREPLACE)) {
fprintf(stderr, "RSRC : error RSRC402: -a (append) option excludes -t, -d, -u, and -r\n"); fArgError = TRUE; }
} else if (g_dwOptions & OPTREPLACE) {
if (g_dwOptions & (OPTEXTRACT | OPTHEXDUMP | OPTUNLOC | OPTAPPEND)) {
fprintf(stderr, "RSRC : error RSRC403: -r (replace) option excludes -t, -d, -u, and -a\n"); fArgError = TRUE; }
if (g_LangId == 0xFFFF) {
fprintf(stderr, "RSRC : error RSRC404: -r (replace) option requires -l (LangId)\n"); fArgError = TRUE; }
} else {
if (g_dwOptions & (OPTSYMBOLS)) {
fprintf(stderr, "RSRC : error RSRC405: Analysis excludes -s\n"); fArgError = TRUE; }
}
if (fArgError) {
DisplayUsage(); DisplayArgs(); return E_INVALIDARG;
} else if (cFiles != 1) {
fprintf(stderr, "\nRsrc : error RSRC406: must specify at least an executable file name.\n\n"); DisplayUsage(); return E_INVALIDARG;
} else {
// We have valid parameters
if (g_dwProcess == 0) { g_dwProcess = PROCESSALL; }
if (!(g_dwOptions & OPTQUIET)) { fprintf(stdout, "\nRsrc - Manage executable resources.\n\n"); fprintf(stdout, " Executable file: %s\n", g_szExecutable);
if (g_szResources[0]) { fprintf(stdout, " Resource file: %s\n", g_szResources); }
if (symbols[0]) { fprintf(stdout, " Symbol file: %s\n", symbols); }
if (g_LangId != 0xffff) { char szLang[50] = ""; char szCountry[50] = ""; GetLocaleInfoA(g_LangId, LOCALE_SENGLANGUAGE, szLang, sizeof(szLang)); GetLocaleInfoA(g_LangId, LOCALE_SENGCOUNTRY, szCountry, sizeof(szCountry)); fprintf(stdout, " Language: %x (%s - %s)\n", g_LangId, szLang, szCountry); }
if (g_dwProcess != PROCESSALL) { fprintf(stdout, " Include only: %s\n", g_szTypes); } }
cRes = 0;
// Handle default token file name
if (g_szResources[0] == 0) { strcpy(g_szResources, g_szExecutable); strcat(g_szResources, ".rsrc"); }
if (g_dwOptions & (OPTAPPEND | OPTREPLACE)) {
// Update an executable from tokens
MUST(UpdateResources(g_szExecutable, g_szResources, symbols), ("RSRC : error RSRC420: Update failed.\n"));
} else if (g_dwOptions & (OPTEXTRACT | OPTHEXDUMP)) {
// Generate tokens from an executable
MUST(ExtractResources(g_szExecutable, g_szResources), ("RSRC : error RSRC421: Token extraction failed.\n"));
} else {
// Analyse an executable
MUST(Analyse(g_szExecutable), ("RSRC : error RSRC422: Analysis failed.\n"));
}
return S_OK; } }
int _cdecl main(void) {
if (SUCCEEDED(ProcessParameters())) {
if (!g_fWarn) {
return 0; // No problems
} else {
return 1; // Warning(s) but no error(s)
}
} else {
return 2; // Error(s)
} }
|