5829 lines
155 KiB
C++
5829 lines
155 KiB
C++
|
//// 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):
|
|||
|
//
|
|||
|
// <20> 1. Use unlocalised file/product version if updating version resource
|
|||
|
// <20> 2. Analyse mode diagnoses no localisable resources and unhandled binary formats
|
|||
|
// 3. Warn when no translations, don't touch executable if updating
|
|||
|
// <20> 4. Support -r from any language to any language
|
|||
|
// 5. Allocate error numbers, clarify error messages
|
|||
|
//
|
|||
|
// 6. Include length in unloc token
|
|||
|
// <20> 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)
|
|||
|
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|