Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

5828 lines
155 KiB

  1. //// RSRC - Win32 command line resource manager
  2. //
  3. // Copyright (c) 1996-9, Microsoft Corporation. All rights reserved.
  4. //
  5. // David C Brown [dbrown] 29th October 1998.
  6. ///// RSRC Command line
  7. //
  8. //c RSRC Executable [-l LocLang] [-u UnlocLang] [-i Types] [-q]
  9. //c [ [-t|-d] TextOutput [-c UnlocExecutable]
  10. //c | [-a|-r] text-input [-s symbols] [-v] ]
  11. //
  12. //p Executable: Win32 binary to analyse (default), to generate tokens (-t)
  13. // or dump (-d) from, or containing resources to be replaced (-r)
  14. // or appended to (-a).
  15. //
  16. //p -l LocLang: Restrict processing to the specified localized language. LangId
  17. // should be specified as a full hex NLS Language id, for example use
  18. // '-l 409' for US English. Required for replace (-r) operation.
  19. //
  20. //p -u UnlocLang: Specifies unlocalized language, defaults to 409 (US English).
  21. //
  22. //p -i Types: Restrict processing to listed types. Each type is indicated by a letter
  23. // as below:
  24. //
  25. //t Letter | Type | Letter | Type | Letter | Type
  26. //t ------ | ---- | ------ | ---- | ------ | ----
  27. //t c | Cursors | g | Message tables | n | INFs
  28. //t b | Bitmaps | v | Version info | h | HTML
  29. //t i | Icons | a | Accelerators | x | Binary data
  30. //t m | Menus | f | Font directories | |
  31. //t d | Dialogs | t | Fonts | o | All others
  32. //t s | Strings | r | RCDATA | a | All (default)
  33. //
  34. //
  35. //p -q: Quiet. By default Rsrc displays summary statistics of types and languages
  36. // of resources processed. -q suppresses all output except warning and error messages.
  37. //
  38. //p -t TextOutput: Generate tokens in checkin format.
  39. //
  40. //p -d TextOutput: Dump resources in Hex & ASCII format.
  41. //
  42. //p -c UnlocExecutable: Compare with unlocalized (English) resources - localised
  43. // resources in executable are compared with English resources in
  44. // UnlocExecutable. When the localised resource is bit for bit identical
  45. // with the English resource RSRC writes a one line unloc
  46. // token instead of the full resource. Valid only with tokenise (-t)
  47. // option.
  48. //
  49. //p -a TextInput: Append resources from text input file. Every resource in the
  50. // text file is added to the executable. Resources already in the executable
  51. // are not removed. When a resource from the token file has the same type, id
  52. // and language as one in the executable, the executable resource is replaced
  53. // by the token resource.
  54. //
  55. //p -r TextInput: Replace English resources in executable by localised resources
  56. // from text file. Requires -l parameter to specify localisation language.
  57. // When a resource from the token file has the same type and id as one in the
  58. // executable, and the executable resource is US English (409) and the localised
  59. // resource is in the language specified on the -l parameter, the US English
  60. // resource is removed.
  61. //
  62. //p -s Symbols: Symbol file (.dbg format). When RSRC updates the header checksum
  63. // in executable, it will also do so in the named symbol file. Valid only
  64. // with the replace (-r) and append (-a) options.
  65. //
  66. //
  67. // Miscellaneous options
  68. //
  69. //p -v: Update file and product version. By default any file and product version
  70. // in the token file is ignored during update/append, the file and product
  71. // versions from the original unlocalised resources are retained.
  72. //
  73. ///// Definitions
  74. //
  75. //p Resource key: The combination of resource type, resource id and
  76. // resource language. The resource key uniquely identifies the
  77. // resource. A Win32 executable can contain any combination of
  78. // languages, ids and types so long as no two resources have the
  79. // same type, key and language.
  80. //
  81. //p Resource type: A numeric or string value. Some numeric values are
  82. // predefined, for example menus and dialogs, but apps can and
  83. // do use any value they choose.
  84. //
  85. //p Resource id: A numeric or string value. Used by an application to
  86. // identify the resource when calling FindResource, LoadString etc.
  87. //
  88. //p Resource language: An NLS LANGID, i.e. a combination of primary and
  89. // sub-language, such as 0409 (US English).
  90. //
  91. //p Unloc token: A line in the token file specifying a localised resource
  92. // key followed by '=lang,cksum' where lang is the unlocalised
  93. // language (usually 0409) and cksum is the checksum of the unlocalised
  94. // resource. Used when the only difference between the localised and
  95. // unlocalised resource is the language in the resource key.
  96. ///// Use during localisation checkin process
  97. //
  98. //c RSRC LocalisedExecutable -c UnlocExecutable -t Tokens -l LocLang [-u UnlocLang]
  99. //
  100. // Extracts localized tokens for the specified langauge.
  101. //
  102. // Where a resource in the localised executable is bit for bit identical
  103. // to a resource in the unlocalized executable, the resource content is not
  104. // written to the token file. In its place RSRC writes an unloc token
  105. // giving the checksum of the resource and specifying the target language.
  106. //
  107. // Warnings are generated if the localised executable contains resources
  108. // in languages other than that specified by the -l parameter.
  109. //
  110. // Unlocalised resources for comparison are looked up in the unlocalised
  111. // executable in the language specified on the -u parameter, default 409
  112. // (US ENglish).
  113. ///// Use during the build to update a single language executable
  114. //
  115. //c RSRC Executable [-u UnlocLang] -r Tokens -l LocLang -s SymbolFile
  116. //
  117. // Each localised resource in the token file is added to the executable.
  118. //
  119. // Each corresponding unlocalized resource is removed from the executable.
  120. //
  121. // For each unloc token the unlocalized resource is found in the executable
  122. // and its language is updated to the target localized language recorded
  123. // in the unloc token.
  124. //
  125. // Tokens of other than the specified localised language are not
  126. // processed, but generate warnings.
  127. //
  128. // Warnings are generated for any resources not appearing in both the
  129. // executable and token files.
  130. //
  131. // Warnings are also generated for resources of other than the unlocalised
  132. // language found in the original executable, and resources of other than
  133. // the localised language in the token file.
  134. //
  135. // The unlocalised language defaults to 409 (US English).
  136. ///// Use during the build to add resources to a multi-language executable
  137. //
  138. //c RSRC Executable [-u UnlocLang] -a Tokens [-l Language] -s SymbolFile
  139. //
  140. // Localised resources from the token file are added to the executable.
  141. //
  142. // For each unloc token the unlocalised resource is found in the executable
  143. // and copied for the localised language recorded in the unloc token.
  144. //
  145. // If '-l Languge' is specified, only tokens of that language are added.
  146. // When used with the append (-a) option, '-l Language' applies only to
  147. // the token file: pre-existing resources in the executable are not affected.
  148. //
  149. // If a resource from the token file matches a resource already in the
  150. // executable in type, name and language, the executable resource
  151. // is replaced.
  152. //
  153. // Warnings are generated if any token in the executable is replaced, or
  154. // if the unlocalised resource corresponding to an unloc token is missing
  155. // or has a checksum which differs from the unlocalised resource that was
  156. // passed on the '-u' parameter when the toke file was created.
  157. //
  158. // If the '-l Language' option is used, warnings are generated for any
  159. // resources of other languages found in the token file.
  160. ///// Token format - resource key and header
  161. //
  162. // A resource may be represented by one or more lines. When
  163. // a resource is spread over more than one line, all lines except the
  164. // first are indented by three spaces.
  165. //
  166. // The first line of every resource starts with the resource key as follows:
  167. //
  168. // type,id,language;
  169. //
  170. // This is followed by the codepage recorded in the resource directory.
  171. // Note that the codepage is not part of the resource key, and is not
  172. // maintained consistently by all software. In particular:
  173. //
  174. // o RC writes the codepage as zero
  175. // o The NT UpdateResource API writes the codepage as 1252
  176. // o Locstudio writes a codepage that corresponds to the resource language
  177. //
  178. // Winnt.h documents the codepage as follows:
  179. //
  180. // "Each resource data entry ... contains ... a CodePage that should be
  181. // used when decoding code point values within the resource data.
  182. // Typically for new applications the code page would be the unicode
  183. // code page.'
  184. //
  185. // In practise I have never seen the codepage value set to Unicode (1200).
  186. //
  187. // If the '-c' (unlocalised comparison) parameter was provided on the
  188. // rsrc command, and there was an unlocalised resource with the same type
  189. // and id, the language and checksum of that unlocalised resource are
  190. // appended.
  191. //
  192. // Finally, the resource data is represented in one of the forms below,
  193. // or as 'unloc' if the resource data exactly matches the unlocalised resource
  194. // found in the file passed by 'c'.
  195. //
  196. //
  197. // There are thus three possible token key/header formats as follows:
  198. //
  199. //c type,id,language;codepage;resource-data
  200. //
  201. // Resource recorded in full, either no '-c' parameter specified, or
  202. // resource does not exist in unlocalised file.
  203. //
  204. //
  205. //c type,id,language;codepage,unlocalised-checksum,language;resource-data
  206. //
  207. // Resource recorded in full, '-c' parameter was specified, and localised
  208. // resource image differed from unlocalised resource image.
  209. //
  210. //
  211. //c type,id,language;codepage,unlocalised-checksum,language;'Unloc'
  212. //
  213. // Resource recorded in full, '-c' parameter was specified, and localised
  214. // resource image was identical to unlocalised resource image.
  215. ///// Token samples - default hex format
  216. //
  217. //
  218. // For most resource types, RSRC generates resources
  219. // as a string of hex digits.
  220. //
  221. // For example, the following represents an accelerator resource.
  222. //
  223. //c 0009,0002,0409;00000000;Hex;00000020:030074008e00000003002e00840000000b0044008400000087002e0084000000
  224. //
  225. // o Type 0x0009 (Accelerator)
  226. // o Id 0x0002
  227. // o Language 0x0409 (LANG_ENGLISH, SUBLANG_US)
  228. // o Codepage 0 (implies resource was probably generated by RC)
  229. // o Length in bytes 0x0020
  230. //
  231. // The resource is short, so its hex representation follows the length.
  232. //
  233. //
  234. // A larger binary resource is represented on multiple lines as follows:
  235. //
  236. //c 000a,4000,0409;00000000;Hex;0000016a
  237. //c 00000000:0000640100004c0000000114020000000000c000000000000046830000003508000050130852c8e0bd0170493f38ace1bd016044d085c9e0bd01003000000000000001000000000000000000000000000000590014001f0fe04fd020ea3a6910a2d808002b30309d190023563a5c000000000000000000000000000000000065
  238. //c 00000080:7c15003100000000003025a49e308857696e6e74000015003100000000002f25d3863508466f6e747300000000490000001c000000010000001c0000003900000000000000480000001d0000000300000063de7d98100000005f535445504853544550485f00563a5c57494e4e545c466f6e7473000010000000050000a02400
  239. //c 00000100:00004200000060000000030000a05800000000000000737465706800000000000000000000004255867d3048d211b5d8d085029b1cfa4119c94a9f4dd211871f0000000000004255867d3048d211b5d8d085029b1cfa4119c94a9f4dd211871f00000000000000000000
  240. //
  241. // o Type 0x000a (RCDATA)
  242. // o Id 0x4000
  243. // o Language 0x0409 (LANG_ENGLISH, SUBLANG_US)
  244. // o Codepage 0
  245. // o Length in bytes 0x016a
  246. //
  247. // The hex representation is split onto multiple lines each of 128 bytes.
  248. ///// Warnings and errors
  249. //
  250. //
  251. //
  252. //
  253. //
  254. // o warning RSRC100: Localised resource has no corresponding unlocalised resource in %s
  255. // o warning RSRC110: Unlocalised resource from token file appended to executable
  256. // o warning RSRC111: Unlocalised resource from token file replaced unlocalised resource in executable
  257. // o warning RSRC112: Localised resource from token file replaced localised resource already present in executable
  258. // o warning RSRC113: Localised resource from token file appended to executable - there was no matching unlocalised resource
  259. //
  260. // o warning RSRC120: Token file resource does not match specified language - ignored
  261. // o warning RSRC121: Token file resource is not a requested resource type - ignored
  262. // o warning RSRC122: executable unlocalised resource checksum does not match checksum recorded in token file for resource %s
  263. // o warning RSRC124: missing executable unlocalised resource for %s
  264. // o warning RSRC125: executable contains no unlocalised resource corresponding to resource %s
  265. //
  266. // o warning RSRC160: Symbol file does not match exectable
  267. // o warning RSRC161: Symbol file not processed
  268. // o warning RSRC162: Could not reopen executable %s to update checksum
  269. // o warning RSRC163: Failed to write updated symbol checksum
  270. //
  271. // o warning RSRC170: No localizable resources in %s
  272. // o warning RSRC171: could not close executable
  273. //
  274. //
  275. // o error RSRC230: 'Unloc' token is missing unlocalised resource information for %s
  276. // o error RSRC231: Failed to apply unloc token
  277. // o error RSRC232: Failed to apply token
  278. //
  279. // o error RSRC300: Hex digit expected
  280. // o error RSRC301: Hex value too large
  281. // o error RSRC302: Unexpected end of file
  282. // o error RSRC303: \'%s\' expected
  283. // o error RSRC304: newline expected
  284. // o error RSRC310: Unrecognised resource type for resource %s
  285. //
  286. // o error RSRC400: -t (tokenise) option excludes -d, -a, -r, and -s
  287. // o error RSRC401: -d (dump) option excludes -t, -u, -a, -r, and -s
  288. // o error RSRC402: -a (append) option excludes -t, -d, -u, and -r
  289. // o error RSRC403: -r (replace) option excludes -t, -d, -u, and -a
  290. // o error RSRC404: -r (replace) option requires -l (LangId)
  291. // o error RSRC405: Analysis excludes -s
  292. //
  293. // o error RSRC420: Update failed.
  294. // o error RSRC421: Token extraction failed.
  295. // o error RSRC422: Analysis failed.
  296. //
  297. // o error RSRC500: Corrupt executable - resource appears more than once
  298. // o error RSRC501: %s is not an executable file
  299. // o error RSRC502: %s is not a Win32 executable file
  300. // o error RSRC503: No resources in %s
  301. //
  302. // o error RSRC510: Cannot open executable file %s
  303. // o error RSRC511: cannot find resource directory in %s
  304. // o error RSRC512: Cannot create resource token file %s
  305. // o error RSRC513: Cannot open unlocalised executable file %s
  306. // o error RSRC514: cannot find resource directory in unlocalised executable %s
  307. // o error RSRC515: cannot write delta token file %s
  308. // o error RSRC516: cannot write stand alone token file %s
  309. //
  310. // o error RSRC520: Cannot open resource token file %s
  311. // o error RSRC521: UTF8 BOM missing from token file
  312. //
  313. // o error RSRC530: Cannot read executable resources from %s
  314. // o error RSRC531: Failed reading update tokens
  315. //
  316. // o error RSRC600: BeginUpdateResource failed on %s
  317. // o error RSRC601: UpdateResourceW failed on %s
  318. // o error RSRC602: EndUpdateResourceW failed on %s
  319. //// From Adina
  320. //
  321. // Here is my follow up on 2.
  322. //
  323. // Abstract:
  324. // The build team needs the new tool eventually run with build.exe, i.e.
  325. // we need build.exe recognize the errors, warnings, and simple output
  326. // messages from rsrc.exe and write them to build.err, build.wrn and
  327. // build.log files respectively.
  328. //
  329. // Solution:
  330. // All we need is RSRC complain to the general rule for the MS tools.
  331. // That is (\\orville\razzle\src\sdktools\build\buildexe.c):
  332. // {toolname} : {number}: {text}
  333. //
  334. // where:
  335. //
  336. // toolname If possible, the container and specific module that has
  337. // the error. For instance, the compiler uses
  338. // filename(linenum), the linker uses library(objname), etc.
  339. // If unable to provide a container, use the tool name.
  340. // number A number, prefixed with some tool identifier (C for
  341. // compiler, LNK for linker, LIB for librarian, N for nmake,
  342. // etc).
  343. // test The descriptive text of the message/error.
  344. //
  345. // Accepted String formats are:
  346. // container(module): error/warning NUM ...
  347. // container(module) : error/warning NUM ...
  348. // container (module): error/warning NUM ...
  349. // container (module) : error/warning NUM ...
  350. //
  351. // Ex. of RSRC error:
  352. //
  353. // RSRC : error RSRC2001: unable to open file d:\nt\binaries\jpn\ntdll.dll
  354. //
  355. // Ex. of RSRC warning:
  356. //
  357. // RSRC : warning RSRC5000: unable to find symbol file
  358. // d:\nt\binaries\jpn\retail\dll\ntdll.dbg
  359. //
  360. // Be aware that the error number after "error/warning" is NOT optional.
  361. // As the format above says, you can also display any information you
  362. // consider useful (for example the name of the binary being processed,
  363. // or the line number in the token file that caused the error/warning)
  364. // immediately after the name of the tool: RSRC(<info>).
  365. //
  366. // I confirm that RSRC_OK=0, RSRC_WRN=1, RSRC_ERR=2 are fine with us as
  367. // return values. Also, it does not make any difference if you write the
  368. // output to stderr ot stdout, but I would suggest to write the tool's
  369. // usage and all the warning and error message lines to stderr, and any
  370. // other text to stdout (based on other ms tools we're using, like
  371. // rebase.exe, binplace.exe, etc).
  372. //
  373. // I can make the changes to build.exe so that it recognizes RSRC.
  374. //
  375. // Please let me know if you have any questions.
  376. //
  377. // Thank you
  378. // Adina
  379. /// Following meeting Joerg. here are my action items:
  380. //
  381. // Meet with Joerg, Uwe, Majd, Hideki, Adina to plan usage in bidi NT5
  382. // build process and consider use for odd jobs in other languages.
  383. //
  384. // P1. Implement option to skip updating file and product version, and
  385. // to omit these from token file.
  386. // P1. Implement separate error code for detecting unhandled binary
  387. // format (such as win16).
  388. //
  389. // P2. Add CRC to each resource to detect SLM or editor corruption.
  390. // (Delete CRC in token file always accepted to allow hand modification).
  391. // P2. Option to disable header comment in token file
  392. //
  393. // P3. Interpret MSGTBL, ACCELERATOR and RCDATA - RCDATA as string
  394. // depending on option.
  395. //
  396. // Thanks -- Dave.
  397. //// From Joerg
  398. //
  399. // Howdy,
  400. // I'm playing with rsrc and ran into problems with ParseToken(): if rsrc
  401. // is located in a directory with spaces (e.g. Program Files),
  402. // the function fails to skip the command name, since it's quoted and
  403. // ParseToken stops at the first blank within the quotes.
  404. // I also had trouble compiling it (so I can step thru and see what it's
  405. // doing) under VC5 because there is no default constructor
  406. // for the class "LangId", so I just added a dummy constructor.
  407. //
  408. // J�rg
  409. //// Following meeting planning bidi build, Wednesday 2nd Dec.
  410. //
  411. // Checksum protection against user changes to tok file
  412. // Include length in warning comparison
  413. // Will need alpha build
  414. // Default file name - add .rsrc
  415. // Don't extract file or product version
  416. // => If version resource updated use file and product version from
  417. // US at write time
  418. // Diagnose version only resources
  419. // Diagnose not win32
  420. // Warning for no translations on tokenisation
  421. // Warning no no translations on update, and don't touch executable
  422. // Ability to -r any unlocalised language
  423. //// Resultant priorities (8th Dec):
  424. //
  425. // � 1. Use unlocalised file/product version if updating version resource
  426. // � 2. Analyse mode diagnoses no localisable resources and unhandled binary formats
  427. // 3. Warn when no translations, don't touch executable if updating
  428. // � 4. Support -r from any language to any language
  429. // 5. Allocate error numbers, clarify error messages
  430. //
  431. // 6. Include length in unloc token
  432. // � 7. Handle quoted installation directory and default filenames
  433. // 8. Add checksum protection against corruption of token file
  434. // 9. Option to interpret RCDATA as Unicode string (for kernel)
  435. // 10. Interpret MSGTBL and ACCELERATOR
  436. // 11. Support Win16 binaries
  437. // 12. ? Option to disable token file header
  438. #pragma warning( disable : 4786 ) // map creates some ridiculously long debug identifiers
  439. #include "stdio.h"
  440. #include "windows.h"
  441. #include "imagehlp.h"
  442. #include "time.h"
  443. #include <map>
  444. using namespace std ;
  445. using namespace std::rel_ops ;
  446. #define DBG 1
  447. //// OK and ASSERT macros
  448. //
  449. // All functions return an HRESULT.
  450. // All function calls are wrapped in 'OK()'.
  451. // OK checks for a failed HRESULT and if so returns that HRESULT directly.
  452. // Thus all errors propagate back up the call chain.
  453. //
  454. // MUST issues a message if an HRESULT is not S_OK and returns E_FAIL
  455. // back up the call chain.
  456. void __cdecl DebugMsg(char *fmt, ...) {
  457. va_list vargs;
  458. va_start(vargs, fmt);
  459. vfprintf(stderr, fmt, vargs);
  460. }
  461. #define MUST(a,b) {HRESULT hr; hr = (a); if (hr!= S_OK) {if (!g_fError) DebugMsg b; g_fError = TRUE; return E_FAIL;};}
  462. #define SHOULD(a,b) {HRESULT hr; hr = (a); if (hr!= S_OK) {DebugMsg b; g_fWarn = TRUE; return S_FALSE;};}
  463. #if DBG
  464. #pragma message("Checked build")
  465. #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;};}
  466. #define ASSERT(a) {if (!(a)) {DebugMsg("%s(%d): error RSRC999 : Assertion failed: "#a"\n", __FILE__, __LINE__); return E_UNEXPECTED;};}
  467. #else
  468. #pragma message ("Free build")
  469. #define OK(a) {HRESULT hr; hr = (a); if (hr != S_OK) return hr;}
  470. #define ASSERT(a) {if (!(a)) {return E_UNEXPECTED;};}
  471. #endif
  472. const int MAXPATH = 128;
  473. const char HexDigit[] = "0123456789abcdef";
  474. const BYTE bZeroPad[] = { 0, 0, 0, 0};
  475. const int MAXHEXLINELEN=128;
  476. const int OPTHELP = 0x00000001;
  477. const int OPTQUIET = 0x00000002;
  478. const int OPTEXTRACT = 0x00000004;
  479. const int OPTUNLOC = 0x00000008;
  480. const int OPTHEXDUMP = 0x00000010;
  481. const int OPTAPPEND = 0x00000020;
  482. const int OPTREPLACE = 0x00000040;
  483. const int OPTSYMBOLS = 0x00000080;
  484. const int OPTVERSION = 0x00000100;
  485. const int PROCESSCUR = 0x00000001;
  486. const int PROCESSBMP = 0x00000002;
  487. const int PROCESSICO = 0x00000004;
  488. const int PROCESSMNU = 0x00000008;
  489. const int PROCESSDLG = 0x00000010;
  490. const int PROCESSSTR = 0x00000020;
  491. const int PROCESSFDR = 0x00000040;
  492. const int PROCESSFNT = 0x00000080;
  493. const int PROCESSACC = 0x00000100;
  494. const int PROCESSRCD = 0x00000200;
  495. const int PROCESSMSG = 0x00000400;
  496. const int PROCESSVER = 0x00000800;
  497. const int PROCESSBIN = 0x00001000;
  498. const int PROCESSINF = 0x00002000;
  499. const int PROCESSOTH = 0x00004000;
  500. const int PROCESSALL = 0xFFFFFFFF;
  501. DWORD g_dwOptions = 0;
  502. DWORD g_dwProcess = 0;
  503. LANGID g_LangId = 0xffff;
  504. BOOL g_fWarn = FALSE;
  505. BOOL g_fError = FALSE;
  506. LANGID g_liUnlocalized = 0x0409; // Standard unlocalized language
  507. int g_cResourcesIgnored = 0;
  508. int g_cResourcesUpdated = 0; // Simple replacement
  509. int g_cResourcesTranslated = 0; // Changed from unloc language to loc language
  510. int g_cResourcesAppended = 0; // Added without affecting existing resources
  511. int g_cResourcesExtracted = 0; // Extracted to token file
  512. char g_szTypes [MAXPATH];
  513. char g_szExecutable [MAXPATH]; // Name of executable being analysed, tokenised or updated
  514. char g_szResources [MAXPATH]; // Name of resource token file
  515. char g_szUnloc [MAXPATH]; // Name of unlocalized executable for comparison
  516. int HexCharVal(char c) {
  517. switch (c) {
  518. case '0':
  519. case '1':
  520. case '2':
  521. case '3':
  522. case '4':
  523. case '5':
  524. case '6':
  525. case '7':
  526. case '8':
  527. case '9':
  528. return c - '0';
  529. case 'a':
  530. case 'b':
  531. case 'c':
  532. case 'd':
  533. case 'e':
  534. case 'f':
  535. return c - 'a' + 10;
  536. case 'A':
  537. case 'B':
  538. case 'C':
  539. case 'D':
  540. case 'E':
  541. case 'F':
  542. return c - 'A' + 10;
  543. }
  544. return -1; // Not a hex value
  545. }
  546. //// Scanner
  547. //
  548. // A structure for scanning through a block of memory
  549. class Scanner {
  550. protected:
  551. const BYTE *m_pStart;
  552. const BYTE *m_pRead;
  553. const BYTE *m_pLimit;
  554. public:
  555. Scanner() {m_pStart = NULL; m_pRead = NULL; m_pLimit = NULL;}
  556. Scanner(const BYTE *pb, DWORD dwLen) {m_pStart = pb; m_pRead = pb; m_pLimit = pb+dwLen;}
  557. ~Scanner() {m_pStart = NULL; m_pRead = NULL; m_pLimit=NULL;}
  558. const BYTE* GetRead() {return m_pRead;}
  559. const BYTE* GetLimit() {return m_pLimit;}
  560. HRESULT Advance(UINT cBytes) {
  561. ASSERT(m_pStart != NULL);
  562. ASSERT(m_pRead+cBytes <= m_pLimit);
  563. m_pRead += cBytes;
  564. return S_OK;
  565. }
  566. HRESULT Align(const BYTE *pb, int iAlignment) {
  567. // Advance until read position is a multiple of iAlignment
  568. // past pb. iAlignment MUST be a power of 2.
  569. // Does not advance past limit.
  570. // Ensure iAlignment is a power of 2
  571. // This seems like a good test, though I'm not sure I could prove it!
  572. ASSERT((iAlignment | iAlignment-1) == iAlignment*2 - 1);
  573. if (m_pRead - pb & iAlignment - 1) {
  574. m_pRead += (iAlignment - (m_pRead - pb & iAlignment - 1));
  575. if (m_pRead > m_pLimit) {
  576. m_pRead = m_pLimit;
  577. }
  578. }
  579. return S_OK;
  580. }
  581. HRESULT SetRead(const BYTE *pb) {
  582. ASSERT(m_pRead != NULL);
  583. ASSERT(pb >= m_pStart);
  584. ASSERT(pb < m_pLimit);
  585. m_pRead = pb;
  586. return S_OK;
  587. }
  588. };
  589. class TextScanner : public Scanner {
  590. protected:
  591. const BYTE *m_pLine; // Start of current line
  592. int m_iLine; // Current line
  593. char m_szTextPos[40];
  594. public:
  595. TextScanner() {m_pLine = NULL; m_iLine = 0; Scanner();}
  596. virtual char *GetTextPos() {
  597. sprintf(m_szTextPos, "%d:%d", m_iLine, m_pRead-m_pLine+1);
  598. return m_szTextPos;
  599. }
  600. //// ReadString
  601. //
  602. // Translates UTF8 to Unicode
  603. // Removes '\' escapes as necessary
  604. // Always returns a new zero terminated string
  605. HRESULT ReadString(WCHAR **ppwcString, int *piLen) {
  606. char *pc;
  607. WCHAR *pwc;
  608. int iLen;
  609. ASSERT(*((char*)m_pRead) == '\"');
  610. OK(Advance(1));
  611. pc = (char*)m_pRead;
  612. iLen = 0;
  613. // Count the number of unicode codepoints represented by the string
  614. while ( *pc != '\"'
  615. && pc < (char*)m_pLimit) {
  616. while ( pc < (char*)m_pLimit
  617. && *pc != '\\'
  618. && *pc != '\"' ) {
  619. if (*pc < 128) {
  620. pc++;
  621. } else {
  622. ASSERT(*pc >= 0xC0); // 80-BF reserved for trailing bytes
  623. if (*pc < 0xE0) {
  624. pc+=2;
  625. } else if (*pc < 0xF0) {
  626. pc+=3;
  627. } else {
  628. iLen++; // Additional Unicode codepoint required for surrogate
  629. pc+=4;
  630. }
  631. ASSERT(pc <= (char*)m_pLimit);
  632. }
  633. iLen++;
  634. }
  635. if (*pc == '\\') {
  636. pc++;
  637. if (pc < (char*)m_pLimit) {
  638. pc++;
  639. iLen++;
  640. }
  641. }
  642. }
  643. // Create a Unicode copy of the string
  644. *ppwcString = new WCHAR[iLen+1];
  645. ASSERT(*ppwcString != NULL);
  646. pwc = *ppwcString;
  647. while (*((char*)m_pRead) != '\"') {
  648. while ( *((char*)m_pRead) != '\\'
  649. && *((char*)m_pRead) != '\"') {
  650. if (*m_pRead < 0x80) {
  651. *pwc++ = *(char*)m_pRead;
  652. m_pRead++;
  653. } else {
  654. if (*m_pRead < 0xE0) {
  655. *pwc++ = (WCHAR)(*m_pRead & 0x1F) << 6
  656. | (WCHAR)(*(m_pRead+1) & 0x3F);
  657. m_pRead+=2;
  658. } else if (*m_pRead < 0xF0) {
  659. *pwc++ = (WCHAR)(*m_pRead & 0x0F) << 12
  660. | (WCHAR)(*(m_pRead+1) & 0x3F) << 6
  661. | (WCHAR)(*(m_pRead+2) & 0x3F);
  662. m_pRead+=3;
  663. } else {
  664. *pwc++ = 0xd800
  665. | (( (WCHAR)(*m_pRead & 0x07 << 2)
  666. | (WCHAR)(*(m_pRead+1) & 0x30 >> 4)) - 1) << 6
  667. | (WCHAR)(*(m_pRead+1) & 0x0F) << 2
  668. | (WCHAR)(*(m_pRead+2) & 0x30) >> 4;
  669. *pwc++ = 0xdc00
  670. | (WCHAR)(*(m_pRead+2) & 0x0f) << 6
  671. | (WCHAR)(*(m_pRead+3) & 0x3f);
  672. m_pRead+=4;
  673. }
  674. }
  675. }
  676. if (*(char*)m_pRead == '\\') {
  677. m_pRead++;
  678. if (m_pRead < m_pLimit) {
  679. switch (*(char*)m_pRead) {
  680. case 'r': *pwc++ = '\r'; break;
  681. case 'n': *pwc++ = '\n'; break;
  682. case 't': *pwc++ = '\t'; break;
  683. case 'z': *pwc++ = 0; break;
  684. case 'L': *pwc++ = 0x2028; break; // Line separator
  685. case 'P': *pwc++ = 0x2029; break; // Paragraph separator
  686. default: *pwc++ = *(char*)m_pRead;
  687. }
  688. m_pRead++;
  689. }
  690. }
  691. }
  692. *pwc = 0; // Add zero terminator
  693. m_pRead ++;
  694. *piLen = pwc - *ppwcString;
  695. ASSERT(m_pRead <= m_pLimit);
  696. return S_OK;
  697. }
  698. //// ReadHex
  699. //
  700. // Reads all characters up to he first non-hex digit and returns
  701. // the value represented by the sequence as a DWORD
  702. HRESULT ReadHex(DWORD *pdwVal) {
  703. *pdwVal = 0;
  704. MUST(HexCharVal(*(char*)m_pRead) >= 0
  705. ? S_OK : E_FAIL,
  706. ("%s: error RSRC300: Hex digit expected\n", GetTextPos()));
  707. while ( HexCharVal(*(char*)m_pRead) >= 0
  708. && m_pRead < m_pLimit) {
  709. MUST(*pdwVal < 0x10000000
  710. ? S_OK : E_FAIL,
  711. ("%s: error RSRC301: Hex value too large\n", GetTextPos()));
  712. *pdwVal = *pdwVal << 4 | HexCharVal(*(char*)m_pRead);
  713. OK(Advance(1));
  714. }
  715. return S_OK;
  716. }
  717. //// ReadHexByte - Read exactly 2 hex digits
  718. HRESULT ReadHexByte(BYTE *pb) {
  719. int n1,n2; // The two nibbles.
  720. n1 = HexCharVal(*(char*)m_pRead);
  721. n2 = HexCharVal(*(char*)(m_pRead+1));
  722. MUST( n1 >= 0
  723. && n2 >= 0
  724. ? S_OK : E_FAIL,
  725. ("%s: error RSRC300: Hex digit expected\n", GetTextPos()));
  726. *pb = (n1 << 4) + n2;
  727. MUST(Advance(2),
  728. ("%s: error RSRC302: Unexpected end of file\n", GetTextPos()));
  729. return S_OK;
  730. }
  731. HRESULT Expect(const char *pc) {
  732. while ( *pc
  733. && m_pRead+1 <= m_pLimit) {
  734. MUST(*(char*)m_pRead == *pc
  735. ? S_OK : E_FAIL,
  736. ("%s: error RSRC303: \'%s\' expected\n", GetTextPos(), pc));
  737. m_pRead++;
  738. pc++;
  739. }
  740. MUST(*pc == 0
  741. ? S_OK : E_FAIL,
  742. ("%s: error RSRC303: \'%s\' expected\n", GetTextPos(), pc));
  743. return S_OK;
  744. }
  745. //// SkipLn
  746. //
  747. // Skip to beginning of next non empty, non comment line.
  748. HRESULT SkipLn() {
  749. ASSERT(m_pRead != NULL);
  750. while (m_pRead < m_pLimit) {
  751. if (*(char*)m_pRead == '\r') {
  752. m_pRead++;
  753. if (m_pRead < m_pLimit && *(char*)m_pRead == '\n') {
  754. m_pRead++;
  755. m_pLine = m_pRead;
  756. m_iLine++;
  757. if ( m_pRead < m_pLimit
  758. && *(char*)m_pRead != '#'
  759. && *(char*)m_pRead != '\r') {
  760. break;
  761. }
  762. }
  763. } else {
  764. m_pRead++;
  765. }
  766. }
  767. return S_OK;
  768. }
  769. //// ExpectLn
  770. //
  771. // Expect end of line, preceeded by any whitespace
  772. //
  773. // Also skips trailing comments and whole line comments
  774. //
  775. // Any parameter is passed to Expect to vb found at the beginning of the new line
  776. HRESULT ExpectLn(const char *pc) {
  777. ASSERT(m_pRead != NULL);
  778. while ( m_pRead < m_pLimit
  779. && ( *(char*)m_pRead == ' '
  780. || *(char*)m_pRead == '\t')) {
  781. m_pRead++;
  782. }
  783. if ( m_pRead < m_pLimit
  784. && ( *(char*)m_pRead == '\r'
  785. || *(char*)m_pRead == '#')) {
  786. // Condition satisfied, skip to first non comment line
  787. SkipLn();
  788. } else {
  789. MUST(E_FAIL,("%s: error RSRC304: newline expected\n", GetTextPos()));
  790. }
  791. if (pc) {
  792. return Expect(pc);
  793. }
  794. return S_OK;
  795. }
  796. };
  797. //// Mapped files
  798. //
  799. // File mapping is used to read executable and token files.
  800. //
  801. // File mapping is also used to update in place checksum information
  802. // in executable and symbol files.
  803. class MappedFile : public TextScanner {
  804. HANDLE m_hFileMapping;
  805. BOOL fRW; // True when writeable
  806. char m_szFileName[MAXPATH];
  807. char m_szTextPos[MAXPATH+40];
  808. public:
  809. MappedFile() {m_hFileMapping = NULL; TextScanner();}
  810. const BYTE* GetFile() {return m_pStart;}
  811. virtual char *GetTextPos() {
  812. sprintf(m_szTextPos, "%s(%s)", m_szFileName, TextScanner::GetTextPos());
  813. return m_szTextPos;
  814. }
  815. HRESULT Open(const char *pcFileName, BOOL fWrite) {
  816. HANDLE hFile;
  817. m_pStart = NULL;
  818. m_pRead = NULL;
  819. m_pLimit = NULL;
  820. strcpy(m_szFileName, pcFileName);
  821. hFile = CreateFileA(
  822. pcFileName,
  823. GENERIC_READ | (fWrite ? GENERIC_WRITE : 0),
  824. FILE_SHARE_READ | (fWrite ? FILE_SHARE_WRITE | FILE_SHARE_DELETE : 0 ),
  825. NULL,
  826. OPEN_EXISTING,
  827. FILE_ATTRIBUTE_NORMAL,
  828. NULL);
  829. ASSERT(hFile != INVALID_HANDLE_VALUE);
  830. m_hFileMapping = CreateFileMapping(
  831. hFile,
  832. NULL,
  833. fWrite ? PAGE_READWRITE : PAGE_WRITECOPY,
  834. 0,0, NULL);
  835. ASSERT(m_hFileMapping != NULL);
  836. m_pStart = (BYTE*) MapViewOfFile(
  837. m_hFileMapping,
  838. fWrite ? FILE_MAP_WRITE : FILE_MAP_READ,
  839. 0,0, 0);
  840. ASSERT(m_pStart != NULL);
  841. m_pRead = m_pStart;
  842. m_pLine = m_pStart;
  843. m_pLimit = m_pStart + GetFileSize(hFile, NULL);
  844. m_iLine = 1;
  845. CloseHandle(hFile);
  846. fRW = fWrite;
  847. return S_OK;
  848. }
  849. DWORD CalcChecksum() {
  850. DWORD dwHeaderSum;
  851. DWORD dwCheckSum;
  852. if (CheckSumMappedFile((void*)m_pStart, m_pLimit-m_pStart, &dwHeaderSum, &dwCheckSum) == NULL) {
  853. return 0;
  854. } else {
  855. return dwCheckSum;
  856. }
  857. }
  858. HRESULT Close() {
  859. if (m_pStart) {
  860. UnmapViewOfFile(m_pStart);
  861. CloseHandle(m_hFileMapping);
  862. m_hFileMapping = NULL;
  863. m_pStart = NULL;
  864. }
  865. return S_OK;
  866. }
  867. };
  868. //// NewFile - services for writing a new text otr binary file
  869. //
  870. //
  871. class NewFile {
  872. HANDLE hFile;
  873. DWORD cbWrite; // Bytes written
  874. BYTE buf[4096]; // Performance buffer
  875. int iBufUsed;
  876. public:
  877. NewFile() {iBufUsed = 0;}
  878. int GetWrite() {return cbWrite;}
  879. HRESULT OpenWrite(char *pcFileName) {
  880. cbWrite = 0; // Bytes written
  881. hFile = CreateFileA(
  882. pcFileName,
  883. GENERIC_READ | GENERIC_WRITE,
  884. 0, // Not shared
  885. NULL,
  886. CREATE_ALWAYS,
  887. FILE_ATTRIBUTE_NORMAL,
  888. NULL);
  889. ASSERT(hFile != INVALID_HANDLE_VALUE);
  890. return S_OK;
  891. }
  892. HRESULT WriteBytes(const BYTE *pb, DWORD dwLen) {
  893. DWORD dwWritten;
  894. if (iBufUsed + dwLen <= sizeof(buf)) {
  895. memcpy(buf+iBufUsed, pb, dwLen);
  896. iBufUsed += dwLen;
  897. } else {
  898. ASSERT(hFile != NULL);
  899. if (iBufUsed > 0) {
  900. ASSERT(WriteFile(hFile, buf, iBufUsed, &dwWritten, NULL));
  901. ASSERT(dwWritten == iBufUsed);
  902. iBufUsed = 0;
  903. }
  904. if (dwLen <= sizeof(buf)) {
  905. memcpy(buf, pb, dwLen);
  906. iBufUsed = dwLen;
  907. } else {
  908. ASSERT(WriteFile(hFile, pb, dwLen, &dwWritten, NULL));
  909. ASSERT(dwWritten == dwLen);
  910. }
  911. }
  912. cbWrite += dwLen;
  913. return S_OK;
  914. }
  915. HRESULT WriteS(const char *pc) {
  916. return WriteBytes((BYTE*)pc, strlen(pc));
  917. }
  918. //// WriteString
  919. //
  920. // Translates Unicode to UTF8
  921. // Adds '\' escapes as necessary
  922. HRESULT WriteString(const WCHAR *pwc, int iLen) {
  923. BYTE buf[3];
  924. const WCHAR *pwcLimit;
  925. pwcLimit = pwc + iLen;
  926. OK(WriteBytes((BYTE*)"\"", 1));
  927. while (pwc < pwcLimit) {
  928. switch (*pwc) {
  929. case 0: OK(WriteS("\\z")); break;
  930. case '\r': OK(WriteS("\\r")); break;
  931. case '\n': OK(WriteS("\\n")); break;
  932. case '\t': OK(WriteS("\\t")); break;
  933. case 0x2028: OK(WriteS("\\L")); break; // Line separator
  934. case 0x2029: OK(WriteS("\\P")); break; // Paragraph separator
  935. case '\"': OK(WriteS("\\\"")); break;
  936. case '\\': OK(WriteS("\\\\")); break;
  937. default:
  938. if (*pwc < 128) {
  939. OK(WriteBytes((BYTE*)pwc, 1));
  940. } else if (*pwc < 0x7FF) {
  941. buf[0] = 0xC0 | *pwc >> 6;
  942. buf[1] = 0x80 | *pwc & 0x3F;
  943. OK(WriteBytes(buf, 2));
  944. } else {
  945. // Note - should code surrogates properly, this doesn't
  946. buf[0] = 0xE0 | *pwc>>12 & 0x0F;
  947. buf[1] = 0x80 | *pwc>>6 & 0x3F;
  948. buf[2] = 0x80 | *pwc & 0x3F;
  949. OK(WriteBytes(buf, 3));
  950. }
  951. }
  952. pwc++;
  953. }
  954. OK(WriteBytes((BYTE*)"\"", 1));
  955. return S_OK;
  956. }
  957. //// WriteHex
  958. //
  959. // Writes the given value in the given number of digits.
  960. //
  961. // If cDigits is zero, uses as many as necessary.
  962. HRESULT WriteHex(DWORD dw, int cDigits) {
  963. int i;
  964. char cBuf[8];
  965. i = 7;
  966. while (dw && i >= 0) {
  967. cBuf[i] = HexDigit[dw & 0xf];
  968. dw >>= 4;
  969. i--;
  970. }
  971. if (cDigits != 0) {
  972. while (i >= (8-cDigits)) {
  973. cBuf[i] = '0';
  974. i--;
  975. }
  976. }
  977. OK(WriteBytes((BYTE*)(cBuf+i+1), 7-i));
  978. return S_OK;
  979. }
  980. //// WriteHexBuffer
  981. //
  982. // Writes a buffer of up to 256 bytes as a continuous stream of hex digits
  983. HRESULT WriteHexBuffer(const BYTE *pb, DWORD dwLength) {
  984. DWORD i;
  985. char cBuf[512];
  986. ASSERT(hFile);
  987. ASSERT(dwLength <= 256);
  988. for (i=0; i<dwLength; i++) {
  989. cBuf[2*i] = HexDigit[*pb >> 4 & 0xf];
  990. cBuf[2*i+1] = HexDigit[*pb & 0xf];
  991. pb++;
  992. }
  993. OK(WriteBytes((BYTE*)cBuf, 2*dwLength));
  994. cbWrite += 2*dwLength;
  995. return S_OK;
  996. }
  997. //// WriteLn
  998. //
  999. // Write end of line mark (CR,LF)
  1000. HRESULT WriteLn() {
  1001. return WriteS("\r\n");
  1002. }
  1003. HRESULT Close() {
  1004. DWORD dwWritten;
  1005. if (hFile) {
  1006. if (iBufUsed > 0) {
  1007. ASSERT(WriteFile(hFile, buf, iBufUsed, &dwWritten, NULL));
  1008. ASSERT(dwWritten == iBufUsed);
  1009. }
  1010. CloseHandle(hFile);
  1011. hFile = NULL;
  1012. }
  1013. return S_OK;
  1014. }
  1015. };
  1016. //// Resource structures
  1017. //
  1018. // Each resource structure has an internal representation for the
  1019. // resource that may be read and written to/from both text and
  1020. // executable files.
  1021. //
  1022. // The ReadTok and WriteTok functions handle formatting and parsing
  1023. // of the token file.
  1024. //
  1025. // The ReadBin function unpacks a resource from a memory mapped
  1026. // executable into the internal representation.
  1027. //
  1028. // The cbBin function returns the unpadded length required for the
  1029. // item in executable (packed) format.
  1030. //
  1031. // The CopyBin function packs the internal representation into a
  1032. // target buffer.
  1033. class Resource {
  1034. public:
  1035. virtual HRESULT ReadTok (TextScanner &mfText) = 0;
  1036. virtual HRESULT ReadBin (Scanner &mfBin, DWORD dwLen) = 0;
  1037. virtual HRESULT WriteTok (NewFile &nfText) const = 0;
  1038. virtual size_t cbBin () const = 0;
  1039. virtual HRESULT CopyBin (BYTE **ppb) const = 0;
  1040. // For statistics
  1041. virtual int GetItems () const = 0;
  1042. virtual int GetWords () const = 0;
  1043. };
  1044. //// ResourceBYTE
  1045. //
  1046. // BYTE value represented in hex digits.
  1047. class ResourceBYTE {
  1048. public:
  1049. BYTE b;
  1050. HRESULT ReadBin(Scanner *pmf) {
  1051. b = *((BYTE*)(pmf->GetRead()));
  1052. OK(pmf->Advance(sizeof(BYTE)));
  1053. return S_OK;
  1054. }
  1055. size_t cbBin() const {
  1056. return 1;
  1057. }
  1058. HRESULT CopyBin(BYTE **ppb) const {
  1059. **ppb = b;
  1060. (*ppb)++;
  1061. return S_OK;
  1062. }
  1063. HRESULT ReadTok(TextScanner *pmf) {
  1064. DWORD dw;
  1065. OK(pmf->ReadHex(&dw));
  1066. ASSERT(dw < 0x100);
  1067. b = (BYTE)dw;
  1068. return S_OK;
  1069. }
  1070. HRESULT WriteTok(NewFile *pmf) const {
  1071. OK(pmf->WriteHex(b, 2));
  1072. return S_OK;
  1073. }
  1074. };
  1075. //// ResoureWORD
  1076. //
  1077. // WORD value represented in hex digits.
  1078. class ResourceWORD {
  1079. public:
  1080. WORD w;
  1081. HRESULT ReadBin(Scanner *pmf) {
  1082. w = *((WORD*)(pmf->GetRead()));
  1083. OK(pmf->Advance(sizeof(WORD)));
  1084. return S_OK;
  1085. }
  1086. size_t cbBin() const {
  1087. return sizeof(WORD);
  1088. }
  1089. HRESULT CopyBin(BYTE **ppb) const {
  1090. *(WORD*)*ppb = w;
  1091. *ppb += sizeof(WORD);
  1092. return S_OK;
  1093. }
  1094. HRESULT ReadTok(TextScanner *pmf) {
  1095. DWORD dw;
  1096. OK(pmf->ReadHex(&dw));
  1097. ASSERT(dw < 0x10000);
  1098. w = (WORD)dw;
  1099. return S_OK;
  1100. }
  1101. HRESULT WriteTok(NewFile *pmf) const {
  1102. OK(pmf->WriteHex(w, 4));
  1103. return S_OK;
  1104. }
  1105. };
  1106. //// ResourceDWORD
  1107. //
  1108. // DWORD value represented in hex digits.
  1109. class ResourceDWORD {
  1110. public:
  1111. DWORD dw;
  1112. HRESULT ReadBin(Scanner *pmf) {
  1113. dw = *((DWORD*)(pmf->GetRead()));
  1114. OK(pmf->Advance(sizeof(DWORD)));
  1115. return S_OK;
  1116. }
  1117. size_t cbBin() const {
  1118. return sizeof(DWORD);
  1119. }
  1120. HRESULT CopyBin(BYTE **ppb) const {
  1121. *(DWORD*)*ppb = dw;
  1122. *ppb += sizeof(DWORD);
  1123. return S_OK;
  1124. }
  1125. HRESULT ReadTok(TextScanner *pmf) {
  1126. OK(pmf->ReadHex(&dw));
  1127. return S_OK;
  1128. }
  1129. HRESULT WriteTok(NewFile *pmf) const {
  1130. OK(pmf->WriteHex(dw, 8));
  1131. return S_OK;
  1132. }
  1133. };
  1134. //// ResourceString
  1135. //
  1136. // String displayed with quotes. May be zero terminated or length
  1137. // word.
  1138. const WCHAR wcZero = 0;
  1139. class ResourceString {
  1140. WCHAR *pwcString;
  1141. WORD iLen;
  1142. void rsFree() {
  1143. if (pwcString)
  1144. delete [] pwcString;
  1145. pwcString = NULL;
  1146. iLen = 0;
  1147. }
  1148. public:
  1149. ResourceString() {pwcString = NULL; iLen = 0;}
  1150. ~ResourceString() {rsFree();}
  1151. ResourceString& operator= (const ResourceString &rs) {
  1152. iLen = rs.iLen;
  1153. pwcString = new WCHAR[iLen+1];
  1154. memcpy(pwcString, rs.pwcString, 2*(iLen+1));
  1155. return *this;
  1156. }
  1157. ResourceString(const ResourceString &rs) {
  1158. *this = rs;
  1159. }
  1160. const WCHAR *GetString() const {return pwcString;}
  1161. const int GetLength() const {return iLen;};
  1162. void SetEmpty () {iLen = 0; pwcString = NULL;}
  1163. HRESULT ReadBinL(Scanner *pmf) {
  1164. rsFree();
  1165. iLen = *((WORD*)(pmf->GetRead()));
  1166. OK(pmf->Advance(sizeof(WORD)));
  1167. pwcString = new WCHAR[iLen+1];
  1168. ASSERT(pwcString != NULL);
  1169. memcpy(pwcString, (WCHAR*)pmf->GetRead(), 2*iLen);
  1170. pwcString[iLen] = 0;
  1171. OK(pmf->Advance(iLen * sizeof(WCHAR)));
  1172. return S_OK;
  1173. }
  1174. size_t cbBinL() const {
  1175. return iLen * sizeof(WCHAR) + sizeof(WORD);
  1176. }
  1177. HRESULT CopyBinL(BYTE **ppb) const {
  1178. *(WORD*)*ppb = iLen;
  1179. *ppb += sizeof(WORD);
  1180. memcpy(*ppb, pwcString, sizeof(WCHAR)*iLen);
  1181. *ppb += sizeof(WCHAR)*iLen;
  1182. return S_OK;
  1183. }
  1184. // Zero terminated
  1185. HRESULT ReadBinZ(Scanner *pmf) {
  1186. const WCHAR* pwc;
  1187. rsFree();
  1188. pwc = (WCHAR*)pmf->GetRead();
  1189. while (*(WCHAR*)pmf->GetRead() != 0) {
  1190. OK(pmf->Advance(2));
  1191. }
  1192. iLen = (WCHAR*)pmf->GetRead() - pwc;
  1193. OK(pmf->Advance(2));
  1194. pwcString = new WCHAR[iLen+1];
  1195. ASSERT(pwcString != NULL);
  1196. memcpy(pwcString, pwc, 2*(iLen+1));
  1197. return S_OK;
  1198. }
  1199. size_t cbBinZ() const {
  1200. return (iLen+1) * sizeof(WCHAR);
  1201. }
  1202. // Known length (dwLen excludes zero terminator)
  1203. HRESULT ReadBin(Scanner *pmf, DWORD dwLen) {
  1204. rsFree();
  1205. iLen = dwLen;
  1206. pwcString = new WCHAR[dwLen+1];
  1207. ASSERT(pwcString != NULL);
  1208. memcpy(pwcString, pmf->GetRead(), 2*dwLen);
  1209. pwcString[dwLen] = 0;
  1210. OK(pmf->Advance(2*dwLen));
  1211. return S_OK;
  1212. }
  1213. size_t cbBin() const {
  1214. return iLen * sizeof(WCHAR);
  1215. }
  1216. HRESULT CopyBinZ(BYTE **ppb) const {
  1217. memcpy(*ppb, pwcString, sizeof(WCHAR)*iLen);
  1218. *ppb += sizeof(WCHAR)*iLen;
  1219. *(WCHAR*)*ppb = 0;
  1220. *ppb += sizeof(WCHAR);
  1221. return S_OK;
  1222. }
  1223. HRESULT ReadTok(TextScanner *pmf) {
  1224. int l;
  1225. rsFree();
  1226. OK(pmf->ReadString(&pwcString, &l));
  1227. ASSERT(l < 0x10000 && l >= 0);
  1228. iLen = (WORD) l;
  1229. return S_OK;
  1230. }
  1231. HRESULT WriteTok(NewFile *pmf) const {
  1232. OK(pmf->WriteString(pwcString, iLen));
  1233. return S_OK;
  1234. }
  1235. int GetWords() const {
  1236. int i;
  1237. int wc;
  1238. i = 0;
  1239. wc = 0;
  1240. while (i<iLen) {
  1241. while ( i < iLen
  1242. && pwcString[i] <= ' ') {
  1243. i++;
  1244. }
  1245. if (i<iLen) {
  1246. wc++;
  1247. }
  1248. while ( i < iLen
  1249. && pwcString[i] > ' ') {
  1250. i++;
  1251. }
  1252. }
  1253. return wc;
  1254. }
  1255. };
  1256. //// ResourceVariant
  1257. //
  1258. // A widely used alternative of either a Unicode string or a WORD value.
  1259. class ResourceVariant {
  1260. ResourceString *prs;
  1261. ResourceWORD rw;
  1262. BOOL fString;
  1263. void rvFree() {
  1264. if (fString && prs)
  1265. delete prs;
  1266. prs = NULL;
  1267. fString=FALSE;
  1268. }
  1269. public:
  1270. ResourceVariant() {fString=FALSE; prs=NULL;}
  1271. ~ResourceVariant() {rvFree();}
  1272. // Copy and assignment constructors required as this is used as the key in an STL map
  1273. ResourceVariant& operator= (const ResourceVariant &rv) {
  1274. fString = rv.fString;
  1275. if (fString) {
  1276. prs = new ResourceString(*rv.prs);
  1277. } else {
  1278. prs = NULL;
  1279. rw = rv.rw;
  1280. }
  1281. return *this;
  1282. }
  1283. ResourceVariant(const ResourceVariant &rv) {
  1284. *this = rv;
  1285. }
  1286. void fprint(FILE *fh) const {
  1287. if (fString) {
  1288. fprintf(fh, "%S", prs->GetString());
  1289. } else {
  1290. fprintf(fh, "%x", rw.w);
  1291. }
  1292. }
  1293. const BOOL GetfString() const {return fString;}
  1294. const WORD GetW() const {return rw.w;}
  1295. void SetW(WORD w) {if (fString) {delete prs; fString = FALSE;}rw.w = w;}
  1296. const WCHAR *GetString() const {return prs->GetString();}
  1297. const int GetLength() const {return prs->GetLength();}
  1298. const int GetWords() const {return fString ? prs->GetWords() : 0;}
  1299. HRESULT ReadBinFFFFZ(Scanner *pmf) {
  1300. rvFree();
  1301. fString = *(WORD*)pmf->GetRead() != 0xffff;
  1302. if (fString) {
  1303. prs = new ResourceString;
  1304. OK(prs->ReadBinZ(pmf));
  1305. } else {
  1306. OK(pmf->Advance(sizeof(WORD)));
  1307. OK(rw.ReadBin(pmf));
  1308. }
  1309. return S_OK;
  1310. }
  1311. size_t cbBinFFFFZ() const {
  1312. return fString ? prs->cbBinZ() : rw.cbBin() + sizeof(WORD);
  1313. }
  1314. HRESULT CopyBinFFFFZ(BYTE **ppb) const {
  1315. if (fString) {
  1316. return prs->CopyBinZ(ppb);
  1317. } else {
  1318. *(WORD*)*ppb = 0xFFFF; // Mark as value
  1319. (*ppb) += sizeof(WORD);
  1320. return rw.CopyBin(ppb);
  1321. }
  1322. }
  1323. HRESULT ReadBinFFFFL(Scanner *pmf) {
  1324. rvFree();
  1325. fString = *(WORD*)pmf->GetRead() != 0xffff;
  1326. if (fString) {
  1327. prs = new ResourceString;
  1328. OK(prs->ReadBinL(pmf));
  1329. } else {
  1330. OK(pmf->Advance(sizeof(WORD)));
  1331. OK(rw.ReadBin(pmf));
  1332. }
  1333. return S_OK;
  1334. }
  1335. size_t cbBinFFFFL() const {
  1336. return fString ? prs->cbBinL() : rw.cbBin() + sizeof(WORD);
  1337. }
  1338. HRESULT CopyBinFFFFL(BYTE **ppb) const {
  1339. if (fString) {
  1340. return prs->CopyBinL(ppb);
  1341. } else {
  1342. *(WORD*)*ppb = 0xFFFF; // Mark as value
  1343. (*ppb) += sizeof(WORD);
  1344. return rw.CopyBin(ppb);
  1345. }
  1346. }
  1347. HRESULT ReadTok(TextScanner *pmf) {
  1348. rvFree();
  1349. fString = *(char*)pmf->GetRead() == '\"';
  1350. if (fString) {
  1351. prs = new ResourceString;
  1352. OK(prs->ReadTok(pmf));
  1353. } else {
  1354. OK(rw.ReadTok(pmf));
  1355. }
  1356. return S_OK;
  1357. }
  1358. HRESULT WriteTok(NewFile *pmf) const {
  1359. if (fString) {
  1360. OK(prs->WriteTok(pmf));
  1361. } else {
  1362. OK(rw.WriteTok(pmf));
  1363. }
  1364. return S_OK;
  1365. }
  1366. HRESULT ReadWin32ResDirEntry(
  1367. Scanner *pmf,
  1368. const BYTE *pRsrc,
  1369. IMAGE_RESOURCE_DIRECTORY_ENTRY *pirde) {
  1370. rvFree();
  1371. fString = pirde->NameIsString;
  1372. if (fString) {
  1373. prs = new ResourceString;
  1374. OK(pmf->SetRead(pRsrc + pirde->NameOffset));
  1375. OK(prs->ReadBinL(pmf));
  1376. } else {
  1377. OK(pmf->SetRead((BYTE*)&pirde->Id));
  1378. OK(rw.ReadBin(pmf));
  1379. }
  1380. return S_OK;
  1381. }
  1382. bool operator< (const ResourceVariant &rv) const {
  1383. int l,c;
  1384. if (fString != rv.GetfString()) {
  1385. return !fString; // Numerics before strings
  1386. } else if (!fString) {
  1387. return rw.w < rv.GetW();
  1388. } else {
  1389. l = prs->GetLength();
  1390. if (l > rv.GetLength()) {
  1391. l = rv.GetLength();
  1392. }
  1393. c = wcsncmp(prs->GetString(), rv.GetString(), l);
  1394. if (c==0) {
  1395. return prs->GetLength() < rv.GetLength();
  1396. } else {
  1397. return c < 0;
  1398. }
  1399. }
  1400. return FALSE; // Equal at all depths
  1401. }
  1402. };
  1403. //// ResourceKey
  1404. //
  1405. // The resource key is the unique identifier of a resource, containing
  1406. // a resource type, a programmer defined unique id for the resource, and
  1407. // a language identifier.
  1408. class ResourceKey {
  1409. public:
  1410. int iDepth;
  1411. ResourceVariant *prvId[3];
  1412. ResourceKey() {iDepth=0;}
  1413. ResourceKey& operator= (const ResourceKey &rk) {
  1414. int i;
  1415. iDepth = rk.iDepth;
  1416. for (i=0; i<iDepth; i++) {
  1417. prvId[i] = new ResourceVariant(*rk.prvId[i]);
  1418. }
  1419. return *this;
  1420. }
  1421. ResourceKey(const ResourceKey& rk) {
  1422. *this = rk;
  1423. }
  1424. void fprint(FILE *fh) const {
  1425. prvId[0]->fprint(fh);
  1426. fprintf(fh, ",");
  1427. prvId[1]->fprint(fh);
  1428. fprintf(fh, ",");
  1429. prvId[2]->fprint(fh);
  1430. }
  1431. LPCWSTR GetResName(int i) const {
  1432. if (i >= iDepth) {
  1433. return (LPCWSTR) 0;
  1434. }
  1435. if (prvId[i]->GetfString()) {
  1436. return prvId[i]->GetString();
  1437. } else {
  1438. return (LPCWSTR)prvId[i]->GetW();
  1439. }
  1440. }
  1441. HRESULT SetLanguage(WORD lid) {
  1442. ASSERT(iDepth == 3);
  1443. ASSERT(prvId[2]->GetfString() == FALSE);
  1444. prvId[2]->SetW(lid);
  1445. return S_OK;
  1446. }
  1447. HRESULT ReadTok(TextScanner *pmf) {
  1448. prvId[0] = new ResourceVariant;
  1449. ASSERT(prvId[0] != NULL);
  1450. OK(prvId[0]->ReadTok(pmf));
  1451. iDepth = 1;
  1452. while (*(char*)pmf->GetRead() == ',') {
  1453. OK(pmf->Advance(1));
  1454. prvId[iDepth] = new ResourceVariant;
  1455. ASSERT(prvId[iDepth] != NULL);
  1456. OK(prvId[iDepth]->ReadTok(pmf));
  1457. iDepth++;
  1458. }
  1459. return S_OK;
  1460. }
  1461. HRESULT WriteTok(NewFile *pmf) const {
  1462. int i;
  1463. OK(prvId[0]->WriteTok(pmf));
  1464. for (i=1; i<iDepth; i++) {
  1465. OK(pmf->WriteS(","));
  1466. OK(prvId[i]->WriteTok(pmf));
  1467. }
  1468. return S_OK;
  1469. }
  1470. bool operator< (const ResourceKey &rk) const {
  1471. int i,l,c;
  1472. if (iDepth != rk.iDepth) {
  1473. return iDepth < rk.iDepth; // Lower depths come first
  1474. } else {
  1475. for (i=0; i<iDepth; i++) {
  1476. if (prvId[i]->GetfString() != rk.prvId[i]->GetfString()) {
  1477. return prvId[i]->GetfString() ? true : false; // Strings come before values
  1478. } else {
  1479. if (prvId[i]->GetfString()) {
  1480. // Compare strings
  1481. l = prvId[i]->GetLength();
  1482. if (l > rk.prvId[i]->GetLength()) {
  1483. l = rk.prvId[i]->GetLength();
  1484. }
  1485. c = wcsncmp(prvId[i]->GetString(), rk.prvId[i]->GetString(), l);
  1486. if (c == 0) {
  1487. if (prvId[i]->GetLength() != rk.prvId[i]->GetLength()) {
  1488. return prvId[i]->GetLength() < rk.prvId[i]->GetLength();
  1489. }
  1490. } else {
  1491. return c < 0;
  1492. }
  1493. } else {
  1494. // Compare numeric values
  1495. if (prvId[i]->GetW() != rk.prvId[i]->GetW()) {
  1496. return prvId[i]->GetW() < rk.prvId[i]->GetW();
  1497. }
  1498. }
  1499. }
  1500. }
  1501. return FALSE; // Equal at all depths
  1502. }
  1503. }
  1504. };
  1505. //// ResourceBinary
  1506. //
  1507. // Arbitrary binary resource
  1508. //
  1509. // Formatted as lines of hex digits
  1510. class ResourceBinary : public Resource {
  1511. protected: // Accessed by ResourceHexDump
  1512. BYTE *pb;
  1513. DWORD dwLength;
  1514. void rbFree() {if (pb) {delete[] pb; pb=NULL;}dwLength = 0;}
  1515. public:
  1516. ResourceBinary() {pb = NULL; dwLength = 0;}
  1517. ~ResourceBinary() {rbFree();}
  1518. DWORD GetLength() const {return dwLength;}
  1519. HRESULT ReadTok(TextScanner &mfText) {
  1520. DWORD i;
  1521. DWORD dwOffset;
  1522. DWORD dwCheckOffset;
  1523. rbFree();
  1524. OK(mfText.Expect("Hex;"));
  1525. OK(mfText.ReadHex(&dwLength));
  1526. pb = new BYTE[dwLength];
  1527. ASSERT(pb != NULL);
  1528. if (dwLength <= MAXHEXLINELEN) {
  1529. // Hex follows on same line
  1530. OK(mfText.Expect(":"));
  1531. for (i=0; i<dwLength; i++) {
  1532. OK(mfText.ReadHexByte(pb+i));
  1533. }
  1534. } else {
  1535. // Hex follows on subsequent lines
  1536. dwOffset = 0;
  1537. while (dwLength - dwOffset > MAXHEXLINELEN) {
  1538. OK(mfText.ExpectLn(" "));
  1539. OK(mfText.ReadHex(&dwCheckOffset));
  1540. ASSERT(dwOffset == dwCheckOffset);
  1541. OK(mfText.Expect(":"));
  1542. for (i=0; i<MAXHEXLINELEN; i++) {
  1543. OK(mfText.ReadHexByte(pb+dwOffset+i));
  1544. }
  1545. dwOffset += MAXHEXLINELEN;
  1546. }
  1547. OK(mfText.ExpectLn(" "));
  1548. OK(mfText.ReadHex(&dwCheckOffset));
  1549. ASSERT(dwOffset == dwCheckOffset);
  1550. OK(mfText.Expect(":"));
  1551. for (i=0; i<dwLength - dwOffset; i++) {
  1552. OK(mfText.ReadHexByte(pb+dwOffset+i));
  1553. }
  1554. }
  1555. return S_OK;
  1556. }
  1557. HRESULT ReadBin(Scanner &mfText, DWORD dwLen) {
  1558. rbFree();
  1559. dwLength = dwLen;
  1560. pb = new BYTE[dwLength];
  1561. memcpy(pb, mfText.GetRead(), dwLength);
  1562. OK(mfText.Advance(dwLen));
  1563. return S_OK;
  1564. }
  1565. HRESULT WriteTok(NewFile &nfText) const {
  1566. DWORD dwOffset;
  1567. // Write binary resource in lines of up to 256 bytes
  1568. OK(nfText.WriteS("Hex;"));
  1569. OK(nfText.WriteHex(dwLength, 8));
  1570. if (dwLength <= MAXHEXLINELEN) {
  1571. // Write <= MAXHEXLINELEN bytes on same line
  1572. OK(nfText.WriteS(":"));
  1573. OK(nfText.WriteHexBuffer(pb, dwLength));
  1574. } else {
  1575. // write MAXHEXLINELEN bytes per line on subsequent lines
  1576. dwOffset = 0;
  1577. while (dwLength - dwOffset > MAXHEXLINELEN) {
  1578. OK(nfText.WriteS("\r\n "));
  1579. OK(nfText.WriteHex(dwOffset, 8));
  1580. OK(nfText.WriteS(":"));
  1581. OK(nfText.WriteHexBuffer(pb+dwOffset, MAXHEXLINELEN));
  1582. dwOffset += MAXHEXLINELEN;
  1583. }
  1584. // Write remaining bytes, if any
  1585. OK(nfText.WriteS("\r\n "));
  1586. OK(nfText.WriteHex(dwOffset, 8));
  1587. OK(nfText.WriteS(":"));
  1588. OK(nfText.WriteHexBuffer(pb+dwOffset, dwLength - dwOffset));
  1589. }
  1590. return S_OK;
  1591. }
  1592. size_t cbBin() const {
  1593. return dwLength;
  1594. }
  1595. HRESULT CopyBin(BYTE **ppb) const {
  1596. if (dwLength > 0) {
  1597. memcpy(*ppb, pb, dwLength);
  1598. *ppb += dwLength;
  1599. }
  1600. return S_OK;
  1601. }
  1602. int GetItems() const {
  1603. return 0;
  1604. }
  1605. int GetWords() const {
  1606. return 0;
  1607. }
  1608. BOOL CompareBin(const BYTE *pbComp, DWORD dwLen) const {
  1609. if (dwLength != dwLen) return FALSE;
  1610. if (dwLength == 0) return TRUE;
  1611. if (pb ==pbComp) return true;
  1612. return !memcmp(pb, pbComp, dwLength);
  1613. }
  1614. };
  1615. //// ResourceHexDump
  1616. //
  1617. // Special version of ResourceBinary for generating a hex dump analysis
  1618. class ResourceHexDump : public ResourceBinary {
  1619. public:
  1620. HRESULT WriteTok(NewFile &nfText) const {
  1621. DWORD i,j;
  1622. ResourceDWORD rdw;
  1623. OK(nfText.WriteS("Hexdump,"));
  1624. OK(nfText.WriteHex(dwLength, 8));
  1625. OK(nfText.WriteS(":"));
  1626. for (i=0; i<dwLength; i++) {
  1627. if (i % 4 == 0) {
  1628. OK(nfText.WriteS(" "));
  1629. }
  1630. if (i % 8 == 0) {
  1631. OK(nfText.WriteS(" "));
  1632. }
  1633. if (i % 16 == 0) {
  1634. if (i>0) {
  1635. // Append ASCII interpretation
  1636. for (j=i-16; j<i; j++) {
  1637. if (pb[j] > 31) {
  1638. OK(nfText.WriteBytes(pb+j, 1));
  1639. } else {
  1640. OK(nfText.WriteS("."));
  1641. }
  1642. }
  1643. }
  1644. OK(nfText.WriteS("\r\n "));
  1645. rdw.dw = i; OK(rdw.WriteTok(&nfText));
  1646. OK(nfText.WriteS(": "));
  1647. }
  1648. OK(nfText.WriteHex(pb[i], 2));
  1649. OK(nfText.WriteS(" "));
  1650. }
  1651. // Append ANSI interpretation to last line
  1652. if (dwLength % 16 > 0) {
  1653. for (i = dwLength % 16 ; i < 16; i++) {
  1654. if (i % 4 == 0) {
  1655. OK(nfText.WriteS(" "));
  1656. }
  1657. if (i % 8 == 0) {
  1658. OK(nfText.WriteS(" "));
  1659. }
  1660. OK(nfText.WriteS(" "));
  1661. }
  1662. }
  1663. OK(nfText.WriteS(" "));
  1664. for (j=dwLength-1 & 0xfffffff0; j<dwLength; j++) {
  1665. if (pb[j] > 31) {
  1666. OK(nfText.WriteBytes(pb+j, 1));
  1667. } else {
  1668. OK(nfText.WriteS("."));
  1669. }
  1670. }
  1671. OK(nfText.WriteLn());
  1672. return S_OK;
  1673. }
  1674. };
  1675. //// Menu32
  1676. //
  1677. //
  1678. class MenuItem32 {
  1679. ResourceDWORD rdwType;
  1680. ResourceDWORD rdwState;
  1681. ResourceDWORD rdwId; // Extended ID
  1682. ResourceWORD rwId; // Non-extended ID
  1683. ResourceWORD rwFlags;
  1684. ResourceDWORD rdwHelpId;
  1685. ResourceString rsCaption;
  1686. BOOL fExtended;
  1687. public:
  1688. void SetExtended(BOOL f) {fExtended = f;}
  1689. int GetWords() const {return rsCaption.GetWords();}
  1690. virtual HRESULT ReadTok(TextScanner &mfText) {
  1691. if (!fExtended) {
  1692. OK(rwFlags.ReadTok(&mfText)); OK(mfText.Expect(","));
  1693. if (!(rwFlags.w & MF_POPUP)) {
  1694. OK(rwId .ReadTok(&mfText)); OK(mfText.Expect(","));
  1695. }
  1696. } else {
  1697. OK(rdwType .ReadTok(&mfText)); OK(mfText.Expect(","));
  1698. OK(rdwState .ReadTok(&mfText)); OK(mfText.Expect(","));
  1699. OK(rdwId .ReadTok(&mfText)); OK(mfText.Expect(","));
  1700. OK(rwFlags .ReadTok(&mfText)); OK(mfText.Expect(","));
  1701. if (rwFlags.w & 1) {
  1702. OK(rdwHelpId.ReadTok(&mfText)); OK(mfText.Expect(","));
  1703. }
  1704. }
  1705. OK(rsCaption.ReadTok(&mfText));
  1706. return S_OK;
  1707. }
  1708. virtual HRESULT ReadBin(Scanner &mfBin) {
  1709. const BYTE *pb; // For tracking
  1710. pb = mfBin.GetRead();
  1711. if (!fExtended) {
  1712. OK(rwFlags.ReadBin(&mfBin));
  1713. if (!(rwFlags.w & MF_POPUP)) {
  1714. OK(rwId .ReadBin(&mfBin));
  1715. }
  1716. } else {
  1717. OK(rdwType .ReadBin(&mfBin));
  1718. OK(rdwState.ReadBin(&mfBin));
  1719. OK(rdwId .ReadBin(&mfBin));
  1720. OK(rwFlags .ReadBin(&mfBin));
  1721. }
  1722. OK(rsCaption.ReadBinZ(&mfBin));
  1723. if (fExtended && rwFlags.w & 1) {
  1724. OK(mfBin.Align(pb, 4));
  1725. OK(rdwHelpId.ReadBin(&mfBin));
  1726. }
  1727. return S_OK;
  1728. }
  1729. virtual HRESULT WriteTok(NewFile &nfText) const {
  1730. if (!fExtended) {
  1731. OK(rwFlags.WriteTok(&nfText)); OK(nfText.WriteS(","));
  1732. if (!(rwFlags.w & MF_POPUP)) {
  1733. OK(rwId .WriteTok(&nfText)); OK(nfText.WriteS(","));
  1734. }
  1735. } else {
  1736. OK(rdwType .WriteTok(&nfText)); OK(nfText.WriteS(","));
  1737. OK(rdwState .WriteTok(&nfText)); OK(nfText.WriteS(","));
  1738. OK(rdwId .WriteTok(&nfText)); OK(nfText.WriteS(","));
  1739. OK(rwFlags .WriteTok(&nfText)); OK(nfText.WriteS(","));
  1740. if (rwFlags.w & 1) {
  1741. OK(rdwHelpId.WriteTok(&nfText)); OK(nfText.WriteS(","));
  1742. }
  1743. }
  1744. OK(rsCaption.WriteTok(&nfText));
  1745. return S_OK;
  1746. }
  1747. virtual size_t cbBin() const {
  1748. size_t cb;
  1749. if (!fExtended) {
  1750. cb = rwFlags.cbBin()
  1751. + rsCaption.cbBinZ();
  1752. if (!(rwFlags.w & MF_POPUP)) {
  1753. cb += rwId.cbBin();
  1754. }
  1755. } else {
  1756. cb = rdwType.cbBin()
  1757. + rdwState.cbBin()
  1758. + rdwId.cbBin()
  1759. + rwFlags.cbBin()
  1760. + rsCaption.cbBinZ();
  1761. if (rwFlags.w & 1) {
  1762. cb = cb + 3 & ~3;
  1763. cb += rdwHelpId.cbBin();
  1764. }
  1765. }
  1766. return cb;
  1767. }
  1768. virtual HRESULT CopyBin (BYTE **ppb) const {
  1769. const BYTE * pb;
  1770. pb = *ppb;
  1771. if (!fExtended) {
  1772. OK(rwFlags.CopyBin(ppb));
  1773. if (!(rwFlags.w & MF_POPUP)) {
  1774. OK(rwId .CopyBin(ppb));
  1775. }
  1776. } else {
  1777. OK(rdwType .CopyBin(ppb));
  1778. OK(rdwState.CopyBin(ppb));
  1779. OK(rdwId .CopyBin(ppb));
  1780. OK(rwFlags .CopyBin(ppb));
  1781. }
  1782. OK(rsCaption.CopyBinZ(ppb));
  1783. if (fExtended && rwFlags.w & 1) {
  1784. while (*ppb - pb & 3) {
  1785. **ppb = 0;
  1786. (*ppb)++;
  1787. }
  1788. OK(rdwHelpId.CopyBin(ppb));
  1789. }
  1790. return S_OK;
  1791. }
  1792. };
  1793. class Menu32 : public Resource {
  1794. ResourceWORD rwVer;
  1795. ResourceWORD rwHdrSize;
  1796. ResourceBinary rbHeader;
  1797. MenuItem32 *pMnuItm;
  1798. DWORD cItems;
  1799. BOOL fExtended;
  1800. public:
  1801. virtual HRESULT ReadTok(TextScanner &mfText) {
  1802. DWORD i, iItem;
  1803. OK(mfText.Expect("Mnu32"));
  1804. fExtended = *(char*)mfText.GetRead() == 'X';
  1805. if (fExtended) {
  1806. OK(mfText.Expect("X;"));
  1807. } else {
  1808. OK(mfText.Expect("N;"));
  1809. }
  1810. OK(rwVer .ReadTok(&mfText)); OK(mfText.Expect(","));
  1811. OK(rwHdrSize.ReadTok(&mfText)); OK(mfText.Expect(","));
  1812. if (fExtended && rwHdrSize.w > 0) {
  1813. OK(rbHeader.ReadTok(mfText)); OK(mfText.Expect(","));
  1814. }
  1815. OK(mfText.ReadHex(&cItems)); OK(mfText.Expect(":"));
  1816. pMnuItm = new MenuItem32 [cItems];
  1817. ASSERT(pMnuItm != NULL);
  1818. for (i=0; i<cItems; i++) {
  1819. OK(mfText.ExpectLn(" ")); OK(mfText.ReadHex(&iItem));
  1820. ASSERT(i == iItem);
  1821. pMnuItm[i].SetExtended(fExtended);
  1822. OK(mfText.Expect(";")); OK(pMnuItm[i].ReadTok(mfText));
  1823. }
  1824. return S_OK;
  1825. }
  1826. virtual HRESULT ReadBin(Scanner &mfBin, DWORD dwLen) {
  1827. const BYTE *pb; // For tracking
  1828. MenuItem32 mi; // For counting menu items
  1829. const BYTE *pbFirstItem;
  1830. int i;
  1831. cItems = 0;
  1832. pb = mfBin.GetRead();
  1833. OK(rwVer .ReadBin(&mfBin));
  1834. OK(rwHdrSize.ReadBin(&mfBin));
  1835. ASSERT(rwVer.w == 0 || rwVer.w == 1);
  1836. fExtended = rwVer.w;
  1837. if (fExtended && rwHdrSize.w > 0) {
  1838. rbHeader.ReadBin(mfBin, rwHdrSize.w);
  1839. }
  1840. ASSERT(mfBin.GetRead() - pb < dwLen);
  1841. // Count menu items
  1842. if (fExtended) {
  1843. OK(mfBin.Align(pb, 4));
  1844. }
  1845. pbFirstItem = mfBin.GetRead();
  1846. mi.SetExtended(fExtended);
  1847. while (mfBin.GetRead() - pb < dwLen) {
  1848. OK(mi.ReadBin(mfBin));
  1849. cItems++;
  1850. if (fExtended) {
  1851. OK(mfBin.Align(pb, 4));
  1852. }
  1853. }
  1854. pMnuItm = new MenuItem32 [cItems];
  1855. ASSERT(pMnuItm != NULL);
  1856. // Record the menus
  1857. OK(mfBin.SetRead(pbFirstItem));
  1858. for (i=0; i<cItems; i++) {
  1859. if (fExtended) {
  1860. OK(mfBin.Align(pb, 4));
  1861. }
  1862. pMnuItm[i].SetExtended(fExtended);
  1863. OK(pMnuItm[i].ReadBin(mfBin));
  1864. }
  1865. ASSERT(mfBin.GetRead() - pb <= dwLen);
  1866. return S_OK;
  1867. }
  1868. virtual HRESULT WriteTok(NewFile &nfText) const {
  1869. DWORD i;
  1870. OK(nfText.WriteS(fExtended ? "Mnu32X;": "Mnu32N;"));
  1871. OK(rwVer .WriteTok(&nfText)); OK(nfText.WriteS(","));
  1872. OK(rwHdrSize.WriteTok(&nfText)); OK(nfText.WriteS(","));
  1873. if (fExtended && rwHdrSize.w > 0) {
  1874. OK(rbHeader.WriteTok(nfText)); OK(nfText.WriteS(","));
  1875. }
  1876. OK(nfText.WriteHex(cItems,4)); OK(nfText.WriteS(":"));
  1877. for (i=0; i<cItems; i++) {
  1878. OK(nfText.WriteS("\r\n "));
  1879. OK(nfText.WriteHex(i, 4));
  1880. OK(nfText.WriteS(";"));
  1881. OK(pMnuItm[i].WriteTok(nfText));
  1882. }
  1883. return S_OK;
  1884. }
  1885. virtual size_t cbBin() const {
  1886. int i;
  1887. size_t cb;
  1888. cb = rwVer.cbBin()
  1889. + rwHdrSize.cbBin();
  1890. if (fExtended && rwHdrSize.w > 0) {
  1891. cb += rbHeader.cbBin();
  1892. }
  1893. for (i=0; i<cItems; i++) {
  1894. if (fExtended) {
  1895. cb = cb + 3 & ~3;
  1896. }
  1897. cb += pMnuItm[i].cbBin();
  1898. }
  1899. return cb;
  1900. }
  1901. virtual HRESULT CopyBin (BYTE **ppb) const {
  1902. const BYTE *pb; // For tracking
  1903. int i;
  1904. pb = *ppb;
  1905. OK(rwVer .CopyBin(ppb));
  1906. OK(rwHdrSize.CopyBin(ppb));
  1907. if (fExtended && rwHdrSize.w > 0) {
  1908. rbHeader.CopyBin(ppb);
  1909. }
  1910. for (i=0; i<cItems; i++) {
  1911. if (fExtended) {
  1912. while (*ppb - pb & 3) {
  1913. **ppb = 0;
  1914. (*ppb)++;
  1915. }
  1916. }
  1917. OK(pMnuItm[i].CopyBin(ppb));
  1918. }
  1919. return S_OK;
  1920. }
  1921. int GetItems() const {
  1922. return cItems;
  1923. }
  1924. int GetWords() const {
  1925. int i;
  1926. int wc;
  1927. wc = 0;
  1928. for (i=0; i<cItems; i++) {
  1929. wc += pMnuItm[i].GetWords();
  1930. }
  1931. return wc;
  1932. }
  1933. };
  1934. //// String32
  1935. //
  1936. // Strings are represented as a sequence of WCHARS, each string
  1937. // preceeded by its length. Each resource contains 16 strings.
  1938. class String32 : public Resource {
  1939. ResourceString rs[16];
  1940. DWORD cStrings;
  1941. DWORD cNonEmpty;
  1942. public:
  1943. virtual HRESULT ReadTok(TextScanner &mfText) {
  1944. DWORD i, iString, cLoaded;
  1945. OK(mfText.Expect("Str;"));
  1946. OK(mfText.ReadHex(&cStrings));
  1947. OK(mfText.Expect(","));
  1948. OK(mfText.ReadHex(&cNonEmpty));
  1949. OK(mfText.Expect(":"));
  1950. ASSERT(cStrings == 16);
  1951. ASSERT(cNonEmpty <= cStrings);
  1952. i=0;
  1953. cLoaded = 0;
  1954. while (cLoaded < cNonEmpty) {
  1955. OK(mfText.ExpectLn(" "));
  1956. OK(mfText.ReadHex(&iString));
  1957. OK(mfText.Expect(":"));
  1958. ASSERT(iString >= i);
  1959. ASSERT(iString < cStrings);
  1960. while (i<iString) {
  1961. rs[i].SetEmpty();
  1962. i++;
  1963. }
  1964. OK(rs[i].ReadTok(&mfText));
  1965. i++;
  1966. cLoaded++;
  1967. }
  1968. while (i<cStrings) {
  1969. rs[i].SetEmpty();
  1970. i++;
  1971. }
  1972. return S_OK;
  1973. }
  1974. virtual HRESULT ReadBin(Scanner &mfBin, DWORD dwLen) {
  1975. const BYTE *pb; // For tracking
  1976. cStrings = 0;
  1977. cNonEmpty = 0;
  1978. pb = mfBin.GetRead();
  1979. while ( cStrings < 16
  1980. && mfBin.GetRead() - pb < dwLen) {
  1981. rs[cStrings].ReadBinL(&mfBin);
  1982. if (rs[cStrings].GetLength() > 0) {
  1983. cNonEmpty++;
  1984. }
  1985. cStrings++;
  1986. }
  1987. ASSERT(mfBin.GetRead() - pb <= dwLen);
  1988. ASSERT(cStrings == 16);
  1989. return S_OK;
  1990. }
  1991. virtual HRESULT WriteTok(NewFile &nfText) const {
  1992. int i;
  1993. ASSERT(cStrings <= 16);
  1994. OK(nfText.WriteS("Str;"));
  1995. OK(nfText.WriteHex(cStrings, 2));
  1996. OK(nfText.WriteS(","));
  1997. OK(nfText.WriteHex(cNonEmpty, 2));
  1998. OK(nfText.WriteS(":"));
  1999. for (i=0; i<cStrings; i++) {
  2000. if (rs[i].GetLength() > 0) {
  2001. OK(nfText.WriteS("\r\n "));
  2002. OK(nfText.WriteHex(i, 1));
  2003. OK(nfText.WriteS(":"));
  2004. OK(rs[i].WriteTok(&nfText));
  2005. }
  2006. }
  2007. return S_OK;
  2008. }
  2009. virtual size_t cbBin() const {
  2010. int i;
  2011. size_t cb;
  2012. cb = 0;
  2013. for (i=0; i<cStrings; i++) {
  2014. cb += rs[i].cbBinL();
  2015. }
  2016. return cb;
  2017. }
  2018. virtual HRESULT CopyBin (BYTE **ppb) const {
  2019. int i;
  2020. for (i=0; i<cStrings; i++) {
  2021. OK(rs[i].CopyBinL(ppb));
  2022. }
  2023. return S_OK;
  2024. }
  2025. int GetItems() const {
  2026. return cNonEmpty;
  2027. }
  2028. int GetWords() const {
  2029. int i, wc;
  2030. wc = 0;
  2031. for (i=0; i<cStrings; i++) {
  2032. wc += rs[i].GetWords();
  2033. }
  2034. return wc;
  2035. }
  2036. };
  2037. class DialogHeader32 {
  2038. BOOL fExtended;
  2039. ResourceDWORD rdwStyle;
  2040. ResourceDWORD rdwSignature;
  2041. ResourceDWORD rdwHelpId;
  2042. ResourceDWORD rdwExStyle;
  2043. ResourceWORD rwcDit; // Count of dialog items
  2044. ResourceWORD rwX;
  2045. ResourceWORD rwY;
  2046. ResourceWORD rwCx;
  2047. ResourceWORD rwCy;
  2048. ResourceVariant rvMenu;
  2049. ResourceVariant rvClass;
  2050. ResourceVariant rvTitle;
  2051. ResourceWORD rwPointSize;
  2052. ResourceWORD rwWeight;
  2053. ResourceBYTE rbItalic;
  2054. ResourceBYTE rbCharSet;
  2055. ResourceString rsFaceName;
  2056. public:
  2057. WORD GetItemCount() const {return rwcDit.w;}
  2058. BOOL GetExtended() const {return fExtended;}
  2059. int GetWords() const {return rvTitle.GetWords();}
  2060. HRESULT ReadTok(TextScanner *pmf) {
  2061. OK(rwcDit .ReadTok(pmf)); OK(pmf->Expect(","));
  2062. OK(rdwStyle .ReadTok(pmf)); OK(pmf->Expect(","));
  2063. OK(rdwExStyle .ReadTok(pmf)); OK(pmf->Expect(","));
  2064. OK(rdwSignature.ReadTok(pmf)); OK(pmf->Expect(","));
  2065. OK(rdwHelpId .ReadTok(pmf)); OK(pmf->Expect(","));
  2066. OK(rwX .ReadTok(pmf)); OK(pmf->Expect(","));
  2067. OK(rwY .ReadTok(pmf)); OK(pmf->Expect(","));
  2068. OK(rwCx .ReadTok(pmf)); OK(pmf->Expect(","));
  2069. OK(rwCy .ReadTok(pmf)); OK(pmf->Expect(","));
  2070. OK(rvMenu .ReadTok(pmf)); OK(pmf->Expect(","));
  2071. OK(rvClass .ReadTok(pmf)); OK(pmf->Expect(","));
  2072. OK(rvTitle .ReadTok(pmf));
  2073. if (rdwStyle.dw & DS_SETFONT) {
  2074. OK(pmf->Expect(","));
  2075. OK(rwPointSize.ReadTok(pmf)); OK(pmf->Expect(","));
  2076. OK(rwWeight .ReadTok(pmf)); OK(pmf->Expect(","));
  2077. OK(rbItalic .ReadTok(pmf)); OK(pmf->Expect(","));
  2078. OK(rbCharSet .ReadTok(pmf)); OK(pmf->Expect(","));
  2079. OK(rsFaceName .ReadTok(pmf));
  2080. }
  2081. fExtended = rdwSignature.dw != 0;
  2082. return S_OK;
  2083. }
  2084. HRESULT ReadBin(Scanner *pmf) {
  2085. OK(rdwSignature.ReadBin(pmf));
  2086. fExtended = HIWORD(rdwSignature.dw) == 0xFFFF;
  2087. if (!fExtended) {
  2088. rdwStyle.dw = rdwSignature.dw;
  2089. OK(rdwExStyle.ReadBin(pmf));
  2090. rdwSignature.dw = 0;
  2091. rdwHelpId.dw = 0;
  2092. } else {
  2093. // Extended dialog adds signature and HelpID
  2094. OK(rdwHelpId.ReadBin(pmf));
  2095. OK(rdwExStyle.ReadBin(pmf));
  2096. OK(rdwStyle.ReadBin(pmf));
  2097. }
  2098. OK(rwcDit .ReadBin(pmf));
  2099. OK(rwX .ReadBin(pmf));
  2100. OK(rwY .ReadBin(pmf));
  2101. OK(rwCx .ReadBin(pmf));
  2102. OK(rwCy .ReadBin(pmf));
  2103. OK(rvMenu .ReadBinFFFFZ(pmf));
  2104. OK(rvClass .ReadBinFFFFZ(pmf));
  2105. OK(rvTitle .ReadBinFFFFZ(pmf));
  2106. if (rdwStyle.dw & DS_SETFONT) {
  2107. OK(rwPointSize.ReadBin(pmf));
  2108. if (!fExtended) {
  2109. rwWeight.w = 0;
  2110. rbItalic.b = 0;
  2111. rbCharSet.b = 0;
  2112. } else {
  2113. OK(rwWeight .ReadBin(pmf));
  2114. OK(rbItalic .ReadBin(pmf));
  2115. OK(rbCharSet .ReadBin(pmf));
  2116. }
  2117. OK(rsFaceName .ReadBinZ(pmf));
  2118. }
  2119. return S_OK;
  2120. }
  2121. size_t cbBin() const {
  2122. size_t cb;
  2123. cb = rdwStyle .cbBin() // Basics for all dialogs
  2124. + rdwExStyle .cbBin()
  2125. + rwcDit .cbBin()
  2126. + rwX .cbBin()
  2127. + rwY .cbBin()
  2128. + rwCx .cbBin()
  2129. + rwCy .cbBin()
  2130. + rvMenu .cbBinFFFFZ()
  2131. + rvClass .cbBinFFFFZ()
  2132. + rvTitle .cbBinFFFFZ();
  2133. if (rdwStyle.dw & DS_SETFONT) { // Facname additions
  2134. cb += rwPointSize .cbBin()
  2135. + rsFaceName .cbBinZ();
  2136. }
  2137. if (fExtended) { // Extended dialog addtions
  2138. cb += rdwSignature .cbBin()
  2139. + rdwHelpId .cbBin();
  2140. if (rdwStyle.dw & DS_SETFONT) {
  2141. cb += rwWeight .cbBin()
  2142. + rbItalic .cbBin()
  2143. + rbCharSet .cbBin();
  2144. }
  2145. }
  2146. return cb;
  2147. }
  2148. HRESULT CopyBin(BYTE **ppb) const {
  2149. BYTE *pbOriginal;
  2150. pbOriginal = *ppb;
  2151. if (!fExtended) {
  2152. OK(rdwStyle .CopyBin(ppb));
  2153. OK(rdwExStyle.CopyBin(ppb));
  2154. } else {
  2155. OK(rdwSignature.CopyBin(ppb));
  2156. OK(rdwHelpId .CopyBin(ppb));
  2157. OK(rdwExStyle .CopyBin(ppb));
  2158. OK(rdwStyle .CopyBin(ppb));
  2159. }
  2160. OK(rwcDit .CopyBin(ppb));
  2161. OK(rwX .CopyBin(ppb));
  2162. OK(rwY .CopyBin(ppb));
  2163. OK(rwCx .CopyBin(ppb));
  2164. OK(rwCy .CopyBin(ppb));
  2165. OK(rvMenu .CopyBinFFFFZ(ppb));
  2166. OK(rvClass .CopyBinFFFFZ(ppb));
  2167. OK(rvTitle .CopyBinFFFFZ(ppb));
  2168. if (rdwStyle.dw & DS_SETFONT) {
  2169. OK(rwPointSize.CopyBin(ppb));
  2170. if (fExtended) {
  2171. OK(rwWeight .CopyBin(ppb));
  2172. OK(rbItalic .CopyBin(ppb));
  2173. OK(rbCharSet.CopyBin(ppb));
  2174. }
  2175. OK(rsFaceName .CopyBinZ(ppb));
  2176. }
  2177. return S_OK;
  2178. }
  2179. HRESULT WriteTok(NewFile *pmf) const {
  2180. OK(rwcDit .WriteTok(pmf)); OK(pmf->WriteS(","));
  2181. OK(rdwStyle .WriteTok(pmf)); OK(pmf->WriteS(","));
  2182. OK(rdwExStyle .WriteTok(pmf)); OK(pmf->WriteS(","));
  2183. OK(rdwSignature.WriteTok(pmf)); OK(pmf->WriteS(","));
  2184. OK(rdwHelpId .WriteTok(pmf)); OK(pmf->WriteS(","));
  2185. OK(rwX .WriteTok(pmf)); OK(pmf->WriteS(","));
  2186. OK(rwY .WriteTok(pmf)); OK(pmf->WriteS(","));
  2187. OK(rwCx .WriteTok(pmf)); OK(pmf->WriteS(","));
  2188. OK(rwCy .WriteTok(pmf)); OK(pmf->WriteS(","));
  2189. OK(rvMenu .WriteTok(pmf)); OK(pmf->WriteS(","));
  2190. OK(rvClass .WriteTok(pmf)); OK(pmf->WriteS(","));
  2191. OK(rvTitle .WriteTok(pmf));
  2192. if (rdwStyle.dw & DS_SETFONT) {
  2193. OK(pmf->WriteS(","));
  2194. OK(rwPointSize.WriteTok(pmf)); OK(pmf->WriteS(","));
  2195. OK(rwWeight .WriteTok(pmf)); OK(pmf->WriteS(","));
  2196. OK(rbItalic .WriteTok(pmf)); OK(pmf->WriteS(","));
  2197. OK(rbCharSet .WriteTok(pmf)); OK(pmf->WriteS(","));
  2198. OK(rsFaceName .WriteTok(pmf));
  2199. }
  2200. return S_OK;
  2201. }
  2202. };
  2203. class DialogItem32 {
  2204. BOOL fExtended;
  2205. ResourceDWORD rdwStyle;
  2206. ResourceDWORD rdwHelpId;
  2207. ResourceDWORD rdwExStyle;
  2208. ResourceWORD rwX;
  2209. ResourceWORD rwY;
  2210. ResourceWORD rwCx;
  2211. ResourceWORD rwCy;
  2212. ResourceWORD rwId; // Normal
  2213. ResourceDWORD rdwId; // Extended
  2214. ResourceVariant rvClass;
  2215. ResourceVariant rvTitle;
  2216. ResourceWORD rwcbRawData; // Raw data size (extended only)
  2217. ResourceBinary rbRawData;
  2218. ResourceWORD rwDummy; // Replaces raw data on normal dialogs
  2219. public:
  2220. void SetExtended(BOOL f) {fExtended = f;}
  2221. int GetWords() const {return rvTitle.GetWords();}
  2222. HRESULT ReadTok(TextScanner *pmf) {
  2223. if (fExtended) {
  2224. OK(rdwId.ReadTok(pmf)); OK(pmf->Expect(","));
  2225. } else {
  2226. OK(rwId.ReadTok(pmf)); OK(pmf->Expect(","));
  2227. }
  2228. OK(rdwStyle .ReadTok(pmf)); OK(pmf->Expect(","));
  2229. OK(rdwExStyle .ReadTok(pmf)); OK(pmf->Expect(","));
  2230. OK(rdwHelpId .ReadTok(pmf)); OK(pmf->Expect(","));
  2231. OK(rwX .ReadTok(pmf)); OK(pmf->Expect(","));
  2232. OK(rwY .ReadTok(pmf)); OK(pmf->Expect(","));
  2233. OK(rwCx .ReadTok(pmf)); OK(pmf->Expect(","));
  2234. OK(rwCy .ReadTok(pmf)); OK(pmf->Expect(","));
  2235. OK(rvClass .ReadTok(pmf)); OK(pmf->Expect(","));
  2236. OK(rvTitle .ReadTok(pmf)); OK(pmf->Expect(","));
  2237. if (fExtended) {
  2238. OK(rbRawData.ReadTok(*pmf));
  2239. ASSERT(rbRawData.GetLength() < 0x10000);
  2240. rwcbRawData.w = (WORD)rbRawData.GetLength();
  2241. } else {
  2242. OK(rwDummy.ReadTok(pmf));
  2243. }
  2244. return S_OK;
  2245. }
  2246. HRESULT ReadBin(Scanner *pmf) {
  2247. if (!fExtended) {
  2248. OK(rdwStyle.ReadBin(pmf));
  2249. OK(rdwExStyle.ReadBin(pmf));
  2250. rdwHelpId.dw = 0;
  2251. } else {
  2252. OK(rdwHelpId.ReadBin(pmf));
  2253. OK(rdwExStyle.ReadBin(pmf));
  2254. OK(rdwStyle.ReadBin(pmf));
  2255. }
  2256. OK(rwX .ReadBin(pmf));
  2257. OK(rwY .ReadBin(pmf));
  2258. OK(rwCx.ReadBin(pmf));
  2259. OK(rwCy.ReadBin(pmf));
  2260. if (fExtended) {
  2261. OK(rdwId.ReadBin(pmf));
  2262. } else {
  2263. OK(rwId.ReadBin(pmf));
  2264. }
  2265. OK(rvClass.ReadBinFFFFZ(pmf));
  2266. OK(rvTitle.ReadBinFFFFZ(pmf));
  2267. if (fExtended) {
  2268. OK(rwcbRawData.ReadBin(pmf));
  2269. OK(rbRawData.ReadBin(*pmf, rwcbRawData.w));
  2270. } else {
  2271. OK(rwDummy.ReadBin(pmf));
  2272. }
  2273. return S_OK;
  2274. }
  2275. size_t cbBin() const {
  2276. size_t cb;
  2277. cb = rdwStyle .cbBin()
  2278. + rdwExStyle .cbBin()
  2279. + rwX .cbBin()
  2280. + rwY .cbBin()
  2281. + rwCx .cbBin()
  2282. + rwCy .cbBin()
  2283. + rvClass .cbBinFFFFZ()
  2284. + rvTitle .cbBinFFFFZ();
  2285. if (!fExtended) {
  2286. cb += rwId .cbBin()
  2287. + rwDummy .cbBin();
  2288. } else {
  2289. cb += rdwId .cbBin()
  2290. + rdwHelpId .cbBin()
  2291. + rbRawData .cbBin()
  2292. + rwcbRawData .cbBin();
  2293. }
  2294. return cb;
  2295. }
  2296. HRESULT CopyBin(BYTE **ppb) const {
  2297. BYTE *pbOriginal;
  2298. pbOriginal = *ppb;
  2299. if (!fExtended) {
  2300. OK(rdwStyle.CopyBin(ppb));
  2301. OK(rdwExStyle.CopyBin(ppb));
  2302. } else {
  2303. OK(rdwHelpId.CopyBin(ppb));
  2304. OK(rdwExStyle.CopyBin(ppb));
  2305. OK(rdwStyle.CopyBin(ppb));
  2306. }
  2307. OK(rwX .CopyBin(ppb));
  2308. OK(rwY .CopyBin(ppb));
  2309. OK(rwCx.CopyBin(ppb));
  2310. OK(rwCy.CopyBin(ppb));
  2311. if (fExtended) {
  2312. OK(rdwId.CopyBin(ppb));
  2313. } else {
  2314. OK(rwId.CopyBin(ppb));
  2315. }
  2316. OK(rvClass.CopyBinFFFFZ(ppb));
  2317. OK(rvTitle.CopyBinFFFFZ(ppb));
  2318. if (fExtended) {
  2319. OK(rwcbRawData.CopyBin(ppb));
  2320. OK(rbRawData.CopyBin(ppb));
  2321. } else {
  2322. OK(rwDummy.CopyBin(ppb));
  2323. }
  2324. return S_OK;
  2325. }
  2326. HRESULT WriteTok(NewFile *pmf) const {
  2327. if (fExtended) {
  2328. OK(rdwId.WriteTok(pmf)); OK(pmf->WriteS(","));
  2329. } else {
  2330. OK(rwId.WriteTok(pmf)); OK(pmf->WriteS(","));
  2331. }
  2332. OK(rdwStyle .WriteTok(pmf)); OK(pmf->WriteS(","));
  2333. OK(rdwExStyle .WriteTok(pmf)); OK(pmf->WriteS(","));
  2334. OK(rdwHelpId .WriteTok(pmf)); OK(pmf->WriteS(","));
  2335. OK(rwX .WriteTok(pmf)); OK(pmf->WriteS(","));
  2336. OK(rwY .WriteTok(pmf)); OK(pmf->WriteS(","));
  2337. OK(rwCx .WriteTok(pmf)); OK(pmf->WriteS(","));
  2338. OK(rwCy .WriteTok(pmf)); OK(pmf->WriteS(","));
  2339. OK(rvClass .WriteTok(pmf)); OK(pmf->WriteS(","));
  2340. OK(rvTitle .WriteTok(pmf)); OK(pmf->WriteS(","));
  2341. if (fExtended) {
  2342. OK(rbRawData.WriteTok(*pmf));
  2343. } else {
  2344. OK(rwDummy.WriteTok(pmf));
  2345. }
  2346. return S_OK;
  2347. }
  2348. };
  2349. //// Dialog32
  2350. //
  2351. //
  2352. class Dialog32 : public Resource {
  2353. DialogHeader32 DlgHdr; // Header
  2354. DialogItem32 *pDlgItm; // Array of items
  2355. BOOL fExtended;
  2356. int cItems;
  2357. public:
  2358. Dialog32() {pDlgItm = NULL;};
  2359. virtual HRESULT ReadTok(TextScanner &mfText) {
  2360. DWORD i, dwSeq;
  2361. OK(mfText.Expect("Dlg32"));
  2362. fExtended = *(char*)mfText.GetRead() == 'X';
  2363. if (fExtended) {
  2364. OK(mfText.Expect("X;"));
  2365. } else {
  2366. OK(mfText.Expect("N;"));
  2367. }
  2368. OK(DlgHdr.ReadTok(&mfText));
  2369. ASSERT(fExtended == DlgHdr.GetExtended());
  2370. cItems = DlgHdr.GetItemCount();
  2371. pDlgItm = new DialogItem32 [cItems];
  2372. ASSERT(pDlgItm != NULL);
  2373. for (i=0; i<cItems; i++) {
  2374. OK(mfText.ExpectLn(" "));
  2375. OK(mfText.ReadHex(&dwSeq));
  2376. ASSERT(dwSeq == i+1);
  2377. OK(mfText.Expect(";"));
  2378. pDlgItm[i].SetExtended(fExtended);
  2379. OK(pDlgItm[i].ReadTok(&mfText));
  2380. }
  2381. return S_OK;
  2382. }
  2383. virtual HRESULT WriteTok(NewFile &nfText) const {
  2384. DWORD i;
  2385. OK(nfText.WriteS(fExtended ? "Dlg32X;": "Dlg32N;"));
  2386. OK(DlgHdr.WriteTok(&nfText));
  2387. for (i=0; i<cItems; i++) {
  2388. OK(nfText.WriteS("\r\n "));
  2389. OK(nfText.WriteHex(i+1, 4));
  2390. OK(nfText.WriteS(";"));
  2391. OK(pDlgItm[i].WriteTok(&nfText));
  2392. }
  2393. return S_OK;
  2394. }
  2395. virtual HRESULT ReadBin(Scanner &mfBinary, DWORD dwLen) {
  2396. const BYTE *pb; // File pointer for tracking alignment
  2397. int i;
  2398. pb = mfBinary.GetRead();
  2399. OK(DlgHdr.ReadBin(&mfBinary));
  2400. fExtended = DlgHdr.GetExtended();
  2401. cItems = DlgHdr.GetItemCount();
  2402. pDlgItm = new DialogItem32 [cItems];
  2403. ASSERT(pDlgItm != NULL);
  2404. // Read items
  2405. for (i=0; i<cItems; i++) {
  2406. OK(mfBinary.Align(pb, 4)); // Advance over any alignment padding
  2407. pDlgItm[i].SetExtended(fExtended);
  2408. OK(pDlgItm[i].ReadBin(&mfBinary));
  2409. ASSERT(mfBinary.GetRead() - pb <= dwLen);
  2410. }
  2411. return S_OK;
  2412. }
  2413. virtual size_t cbBin() const {
  2414. size_t cb;
  2415. int i;
  2416. cb = DlgHdr.cbBin();
  2417. for (i=0; i<cItems; i++) {
  2418. cb = cb + 3 & ~3; // alignment padding
  2419. cb += pDlgItm[i].cbBin();
  2420. }
  2421. return cb;
  2422. }
  2423. virtual HRESULT CopyBin (BYTE **ppb) const {
  2424. BYTE *pb; // Pointer for tracking alignment
  2425. int i;
  2426. pb = *ppb;
  2427. DlgHdr.CopyBin(ppb);
  2428. for (i=0; i<cItems; i++) {
  2429. // Insert alignment padding
  2430. while (*ppb - pb & 3) {
  2431. **ppb = 0;
  2432. (*ppb)++;
  2433. }
  2434. pDlgItm[i].CopyBin(ppb);
  2435. }
  2436. return S_OK;
  2437. }
  2438. int GetItems() const {
  2439. return cItems;
  2440. }
  2441. int GetWords() const {
  2442. int i, wc;
  2443. wc = DlgHdr.GetWords();
  2444. for (i=0; i<cItems; i++) {
  2445. wc += pDlgItm[i].GetWords();
  2446. }
  2447. return wc;
  2448. }
  2449. };
  2450. //// VersionInfo
  2451. //
  2452. // The documentation in the Win32 SDK doesn't clearly capture the
  2453. // usage of block headers, or the nesting of blocks in the Version resource.
  2454. //
  2455. // Each block has the following format
  2456. //
  2457. // wLength Total length including key, value and subblocks
  2458. // wValueLength Length of value in bytes or characters according to bText
  2459. // bText Whether value is in bytes or zero terminated WCHARs
  2460. // szKey Zero terminated WCHAR key, padded with zeros to next DWORD boundary
  2461. // Value Size determined by bText and wValueLength, padded to DWORD boundary
  2462. // Sub-blocks Remaining space (if any, up to wLength) is an array of sub blocks
  2463. class VersionInfo : public Resource {
  2464. struct VersionBlock {
  2465. VersionBlock *pNext; // Next block at this level
  2466. VersionBlock *pSub; // First contained subblock
  2467. int iDepth; // Starts at zero
  2468. DWORD cSub; // Number of contained subblocks
  2469. BOOL bValue; // Set if a vlue is present
  2470. ResourceWORD rwbText;
  2471. ResourceString rsKey;
  2472. ResourceString rsValue; // Value when a string
  2473. ResourceBinary rbValue; // Value when bytes
  2474. };
  2475. VersionBlock *pvb; // First root level block
  2476. DWORD cBlocks; // Number of root level blocks
  2477. HRESULT ReadBinVersionBlocks(
  2478. Scanner &mfBinary,
  2479. DWORD dwLength, // Length of binary to read
  2480. VersionBlock **ppvb,
  2481. int iDepth,
  2482. DWORD *cSub) {
  2483. const BYTE *pbBlock;
  2484. const BYTE *pbResource;
  2485. ResourceWORD rwLength;
  2486. WORD wValueLength;
  2487. pbResource = mfBinary.GetRead();
  2488. (*cSub) = 0;
  2489. while (mfBinary.GetRead() < pbResource + dwLength) {
  2490. // Read one version block
  2491. pbBlock = mfBinary.GetRead();
  2492. OK(rwLength.ReadBin(&mfBinary));
  2493. ASSERT(pbBlock + rwLength.w <= mfBinary.GetLimit());
  2494. //OK((*ppvb)->rwValueLength.ReadBin(&mfBinary));
  2495. wValueLength = *(WORD*)mfBinary.GetRead();
  2496. OK(mfBinary.Advance(2));
  2497. if (rwLength.w > 0) {
  2498. // Block is not empty
  2499. *ppvb = new VersionBlock;
  2500. ASSERT(*ppvb != NULL);
  2501. (*ppvb)->pNext = NULL;
  2502. (*ppvb)->pSub = NULL;
  2503. (*ppvb)->iDepth = iDepth;
  2504. OK((*ppvb)->rwbText.ReadBin(&mfBinary));
  2505. OK((*ppvb)->rsKey.ReadBinZ(&mfBinary));
  2506. OK(mfBinary.Align(pbResource, 4));
  2507. (*ppvb)->bValue = wValueLength > 0;
  2508. if ((*ppvb)->bValue) {
  2509. if ((*ppvb)->rwbText.w == 0) {
  2510. // Binary value
  2511. OK((*ppvb)->rbValue.ReadBin(mfBinary, wValueLength));
  2512. } else {
  2513. // WCHAR string.
  2514. // Some writers include a zero terminator, some don't.
  2515. // Some incode zero codepoints inside the string
  2516. // Some writers get the length right, some dont.
  2517. // msvcrt20.dll text lengths are too long.
  2518. // Choose a length that is min(ValueLength, length remaining),
  2519. // and then drop any trailing zeros.
  2520. // Clip ValueLength to length remaining
  2521. ASSERT(mfBinary.GetRead() < pbBlock + rwLength.w);
  2522. if (wValueLength > (pbBlock + rwLength.w - mfBinary.GetRead()) / 2) {
  2523. wValueLength = (pbBlock + rwLength.w - mfBinary.GetRead()) / 2;
  2524. }
  2525. // Clip trailing zeros
  2526. while ( wValueLength > 0
  2527. && ((WCHAR*)mfBinary.GetRead())[wValueLength-1] == 0) {
  2528. wValueLength--;
  2529. }
  2530. // Extract whatever remains
  2531. OK((*ppvb)->rsValue.ReadBin(&mfBinary, wValueLength));
  2532. // Check that there's nothing being lost between the end of
  2533. // the string and the end of the block.
  2534. // Note that we assume here that blocks containing text values
  2535. // cannot have variety of messes that value text is stored in
  2536. // in exisiting executables.
  2537. while (mfBinary.GetRead() < pbBlock + rwLength.w) {
  2538. ASSERT(*(WCHAR*)mfBinary.GetRead() == 0);
  2539. OK(mfBinary.Advance(2));
  2540. }
  2541. }
  2542. OK(mfBinary.Align(pbResource, 4));
  2543. }
  2544. if (mfBinary.GetRead() - pbBlock < rwLength.w) {
  2545. ASSERT(mfBinary.GetLimit() > mfBinary.GetRead());
  2546. // Read subblocks
  2547. OK(ReadBinVersionBlocks(
  2548. mfBinary,
  2549. rwLength.w - (mfBinary.GetRead() - pbBlock),
  2550. &((*ppvb)->pSub),
  2551. iDepth + 1,
  2552. &(*ppvb)->cSub));
  2553. }
  2554. if (mfBinary.GetRead() < pbResource + dwLength) {
  2555. // Prepare to read more blocks at this level
  2556. ppvb = &((*ppvb)->pNext);
  2557. }
  2558. }
  2559. (*cSub)++;
  2560. }
  2561. return S_OK;
  2562. }
  2563. HRESULT WriteTokVersionBlocks(
  2564. NewFile &nfText,
  2565. VersionBlock *pvb) const {
  2566. while (pvb) {
  2567. OK(nfText.WriteS("\r\n "));
  2568. OK(nfText.WriteHex(pvb->iDepth, 2));
  2569. OK(nfText.WriteS(","));
  2570. OK(pvb->rsKey.WriteTok(&nfText));
  2571. if (pvb->bValue) {
  2572. OK(nfText.WriteS("="));
  2573. if (pvb->rwbText.w == 0) {
  2574. OK(pvb->rbValue.WriteTok(nfText)); // Binary value
  2575. } else {
  2576. OK(pvb->rsValue.WriteTok(&nfText)); // String value
  2577. }
  2578. }
  2579. if (pvb->pSub) {
  2580. OK(nfText.WriteS(";"));
  2581. OK(nfText.WriteHex(pvb->cSub,4));
  2582. OK(WriteTokVersionBlocks(nfText, pvb->pSub));
  2583. }
  2584. pvb = pvb->pNext;
  2585. }
  2586. return S_OK;
  2587. }
  2588. HRESULT ReadTokVersionBlocks(
  2589. TextScanner &mfText,
  2590. VersionBlock **ppvb,
  2591. int iDepth,
  2592. DWORD *pcBlocks) {
  2593. int i;
  2594. DWORD dwRecordedDepth;
  2595. OK(mfText.ReadHex(pcBlocks));
  2596. for (i=0; i<*pcBlocks; i++) {
  2597. *ppvb = new VersionBlock;
  2598. ASSERT(*ppvb != NULL);
  2599. (*ppvb)->pNext = NULL;
  2600. (*ppvb)->pSub = NULL;
  2601. (*ppvb)->iDepth = iDepth;
  2602. (*ppvb)->cSub = 0;
  2603. OK(mfText.ExpectLn(" "));
  2604. OK(mfText.ReadHex(&dwRecordedDepth));
  2605. ASSERT(dwRecordedDepth == iDepth);
  2606. OK(mfText.Expect(","));
  2607. OK((*ppvb)->rsKey.ReadTok(&mfText));
  2608. if (*(char*)mfText.GetRead() != '=') {
  2609. // No value
  2610. (*ppvb)->rwbText.w = 1;
  2611. (*ppvb)->bValue = FALSE;
  2612. } else {
  2613. OK(mfText.Expect("="));
  2614. (*ppvb)->bValue = TRUE;
  2615. if (*(char*)mfText.GetRead() == '\"') {
  2616. // String value
  2617. (*ppvb)->rwbText.w = 1;
  2618. OK((*ppvb)->rsValue.ReadTok(&mfText));
  2619. } else {
  2620. // Binary value
  2621. (*ppvb)->rwbText.w = 0;
  2622. OK((*ppvb)->rbValue.ReadTok(mfText));
  2623. }
  2624. }
  2625. if (*(char*)mfText.GetRead() == ';') {
  2626. // Process subkeys
  2627. OK(mfText.Expect(";"));
  2628. OK(ReadTokVersionBlocks(
  2629. mfText,
  2630. &(*ppvb)->pSub,
  2631. iDepth+1,
  2632. &(*ppvb)->cSub));
  2633. }
  2634. // Prepare to add another block
  2635. ppvb = &(*ppvb)->pNext;
  2636. }
  2637. return S_OK;
  2638. }
  2639. size_t cbBinVersionBlocks(const VersionBlock *pvb) const {
  2640. size_t cb;
  2641. cb = 6; // Header
  2642. cb += pvb->rsKey.cbBinZ();
  2643. cb = cb+3 & ~3; // DWORD align
  2644. if (pvb->bValue) {
  2645. if (pvb->rwbText.w) {
  2646. cb += pvb->rsValue.cbBinZ();
  2647. } else {
  2648. cb += pvb->rbValue.cbBin();
  2649. }
  2650. cb = cb + 3 & ~3; // DWORD align
  2651. }
  2652. if (pvb->pSub != NULL) {
  2653. pvb = pvb->pSub;
  2654. while (pvb) {
  2655. cb += cbBinVersionBlocks(pvb);
  2656. pvb = pvb->pNext;
  2657. }
  2658. }
  2659. return cb;
  2660. }
  2661. HRESULT CopyBinVersionBlocks(
  2662. BYTE **ppb,
  2663. const VersionBlock *pvb) const {
  2664. const BYTE *pbResource;
  2665. size_t cb;
  2666. pbResource = *ppb;
  2667. while (pvb != NULL) {
  2668. cb = cbBinVersionBlocks(pvb);
  2669. ASSERT(cb < 0x1000);
  2670. *((WORD*)(*ppb)) = (WORD)cb;
  2671. (*ppb) += 2;
  2672. // Generate value length
  2673. if (pvb->bValue) {
  2674. if (pvb->rwbText.w) {
  2675. *((WORD*)(*ppb)) = pvb->rsValue.GetLength()+1;
  2676. } else {
  2677. *((WORD*)(*ppb)) = pvb->rbValue.GetLength();
  2678. }
  2679. } else {
  2680. *((WORD*)(*ppb)) = 0;
  2681. }
  2682. (*ppb) += 2;
  2683. OK(pvb->rwbText.CopyBin(ppb));
  2684. OK(pvb->rsKey.CopyBinZ(ppb));
  2685. while (*ppb - pbResource & 3) {
  2686. **ppb = 0;
  2687. (*ppb)++;
  2688. }
  2689. if (pvb->bValue) {
  2690. if (pvb->rwbText.w) {
  2691. OK(pvb->rsValue.CopyBinZ(ppb));
  2692. } else {
  2693. OK(pvb->rbValue.CopyBin(ppb));
  2694. }
  2695. while (*ppb - pbResource & 3) {
  2696. **ppb = 0;
  2697. (*ppb)++;
  2698. }
  2699. }
  2700. if (pvb->pSub) {
  2701. OK(CopyBinVersionBlocks(ppb, pvb->pSub));
  2702. }
  2703. pvb = pvb->pNext;
  2704. }
  2705. return S_OK;
  2706. }
  2707. int GetItemsVersionBlocks(const VersionBlock *pvb) const {
  2708. int iItems = 0;
  2709. while (pvb != NULL) {
  2710. if ( pvb->bValue
  2711. && pvb->rwbText.w != 0) {
  2712. iItems++;
  2713. }
  2714. iItems += GetItemsVersionBlocks(pvb->pSub);
  2715. pvb = pvb->pNext;
  2716. }
  2717. return iItems;
  2718. }
  2719. int GetWordsVersionBlocks(const VersionBlock *pvb) const {
  2720. int iWords = 0;
  2721. while (pvb != NULL) {
  2722. if ( pvb->bValue
  2723. && pvb->rwbText.w != 0) {
  2724. iWords += pvb->rsValue.GetWords();
  2725. }
  2726. iWords += GetWordsVersionBlocks(pvb->pSub);
  2727. pvb = pvb->pNext;
  2728. }
  2729. return iWords;
  2730. }
  2731. public:
  2732. virtual HRESULT ReadTok(TextScanner &mfText) {
  2733. OK(mfText.Expect("Ver;"));
  2734. return ReadTokVersionBlocks(mfText, &pvb, 0, &cBlocks);
  2735. }
  2736. virtual HRESULT WriteTok(NewFile &nfText) const {
  2737. OK(nfText.WriteS("Ver;"));
  2738. OK(nfText.WriteHex(cBlocks,4));
  2739. return WriteTokVersionBlocks(nfText, pvb);
  2740. }
  2741. virtual HRESULT ReadBin(Scanner &mfBinary, DWORD dwLen) {
  2742. return ReadBinVersionBlocks(mfBinary, dwLen, &pvb, 0, &cBlocks);
  2743. }
  2744. virtual size_t cbBin() const {
  2745. const VersionBlock *pvbTop;
  2746. size_t cb;
  2747. cb = 0;
  2748. pvbTop = pvb;
  2749. while (pvbTop) {
  2750. cb += cbBinVersionBlocks(pvbTop);
  2751. pvbTop = pvbTop->pNext;
  2752. }
  2753. return cb;
  2754. }
  2755. virtual HRESULT CopyBin (BYTE **ppb) const {
  2756. return CopyBinVersionBlocks(ppb, pvb);
  2757. }
  2758. int GetItems() const {
  2759. return GetItemsVersionBlocks(pvb);
  2760. }
  2761. int GetWords() const {
  2762. return GetWordsVersionBlocks(pvb);
  2763. }
  2764. VersionBlock *FindStringFileInfo(WCHAR* pwcStr) const {
  2765. VersionBlock *pvbRider;
  2766. if ( pvb
  2767. && pvb->pSub
  2768. && pvb->pSub->pSub
  2769. && pvb->pSub->pSub->pSub) {
  2770. pvbRider = pvb->pSub->pSub->pSub;
  2771. while ( pvbRider
  2772. && wcscmp(pvbRider->rsKey.GetString(), pwcStr) != 0) {
  2773. pvbRider = pvbRider->pNext;
  2774. }
  2775. return pvbRider;
  2776. } else {
  2777. return NULL;
  2778. }
  2779. }
  2780. ResourceString* GetStringFileInfo(WCHAR *pwcStr) {
  2781. VersionBlock *pvbStringFileInfo;
  2782. pvbStringFileInfo = FindStringFileInfo(pwcStr);
  2783. if (pvbStringFileInfo) {
  2784. return &pvbStringFileInfo->rsValue;
  2785. } else {
  2786. return NULL;
  2787. }
  2788. }
  2789. void SetStringFileInfo(WCHAR *pwcStr, ResourceString *prs) {
  2790. VersionBlock *pvbStringFileInfo;
  2791. pvbStringFileInfo = FindStringFileInfo(pwcStr);
  2792. if (pvbStringFileInfo) {
  2793. pvbStringFileInfo->rsValue = *prs;
  2794. }
  2795. }
  2796. ResourceBinary* GetBinaryInfo() const {
  2797. if (pvb) {
  2798. return &pvb->rbValue;
  2799. }
  2800. return NULL;
  2801. }
  2802. void SetBinaryInfo(const ResourceBinary *prb) {
  2803. if (pvb) {
  2804. pvb->rbValue = *prb;
  2805. }
  2806. }
  2807. };
  2808. //// Statistic collection
  2809. //
  2810. //
  2811. struct ResourceStats {
  2812. int cResources; // Number of resources with this resource type
  2813. int cItems; // Number of items with this resource type
  2814. int cWords; // Number of words in strings in this resource type
  2815. int cBytes; // Number of bytes used by resources of this type
  2816. };
  2817. typedef map < ResourceVariant, ResourceStats, less<ResourceVariant> > MappedResourceStats;
  2818. MappedResourceStats ResourceStatsMap;
  2819. //// Define our own LangId class so that primary languages sort together.
  2820. //
  2821. //
  2822. class LangId {
  2823. public:
  2824. DWORD dwLang;
  2825. LangId(DWORD dwL) {dwLang = dwL;};
  2826. bool operator< (LangId li) const {
  2827. if (PRIMARYLANGID(dwLang) != PRIMARYLANGID(li.dwLang)) {
  2828. return PRIMARYLANGID(dwLang) < PRIMARYLANGID(li.dwLang) ? true : false;
  2829. } else {
  2830. return SUBLANGID(dwLang) < SUBLANGID(li.dwLang) ? true : false;
  2831. }
  2832. }
  2833. };
  2834. typedef map < LangId, ResourceStats, less<LangId> > MappedLanguageStats;
  2835. MappedLanguageStats LanguageStatsMap;
  2836. //// UpdateStats
  2837. //
  2838. //
  2839. const ResourceStats ZeroStats = {0};
  2840. HRESULT UpdateStats(
  2841. const ResourceKey &rk,
  2842. int cItems,
  2843. int cWords,
  2844. int cBytes) {
  2845. if (ResourceStatsMap.count(*rk.prvId[0]) == 0) {
  2846. ResourceStatsMap[*rk.prvId[0]] = ZeroStats;
  2847. }
  2848. if (LanguageStatsMap.count(rk.prvId[2]->GetW()) == 0) {
  2849. LanguageStatsMap[rk.prvId[2]->GetW()] = ZeroStats;
  2850. }
  2851. ResourceStatsMap[*rk.prvId[0]].cResources += 1;
  2852. ResourceStatsMap[*rk.prvId[0]].cItems += cItems;
  2853. ResourceStatsMap[*rk.prvId[0]].cWords += cWords;
  2854. ResourceStatsMap[*rk.prvId[0]].cBytes += cBytes;
  2855. LanguageStatsMap[rk.prvId[2]->GetW()].cResources += 1;
  2856. LanguageStatsMap[rk.prvId[2]->GetW()].cItems += cItems;
  2857. LanguageStatsMap[rk.prvId[2]->GetW()].cWords += cWords;
  2858. LanguageStatsMap[rk.prvId[2]->GetW()].cBytes += cBytes;
  2859. return S_OK;
  2860. }
  2861. //// IsResourceWanted
  2862. //
  2863. // Returns whether a given resource key was requested on the command line
  2864. BOOL IsResourceWanted(const ResourceKey &rk) {
  2865. if (rk.prvId[0]->GetfString()) {
  2866. return g_dwProcess & PROCESSOTH;
  2867. } else {
  2868. switch (rk.prvId[0]->GetW()) {
  2869. case 1: return g_dwProcess & PROCESSCUR;
  2870. case 2: return g_dwProcess & PROCESSBMP;
  2871. case 3: return g_dwProcess & PROCESSICO;
  2872. case 4: return g_dwProcess & PROCESSMNU;
  2873. case 5: return g_dwProcess & PROCESSDLG;
  2874. case 6: return g_dwProcess & PROCESSSTR;
  2875. case 7: return g_dwProcess & PROCESSFDR;
  2876. case 8: return g_dwProcess & PROCESSFNT;
  2877. case 9: return g_dwProcess & PROCESSACC;
  2878. case 10: return g_dwProcess & PROCESSRCD;
  2879. case 11: return g_dwProcess & PROCESSMSG;
  2880. case 16: return g_dwProcess & PROCESSVER;
  2881. case 240:
  2882. case 1024:
  2883. case 23:
  2884. case 2110: return g_dwProcess & PROCESSBIN;
  2885. case 2200: return g_dwProcess & PROCESSINF;
  2886. default: return g_dwProcess & PROCESSOTH;
  2887. }
  2888. }
  2889. return FALSE;
  2890. }
  2891. //// NewResource
  2892. //
  2893. // Returns a pointer to a newly allocated subclass of Resource
  2894. // suitable for the given resource type.
  2895. Resource *NewResource(const ResourceVariant &rv) {
  2896. if (rv.GetfString()) {
  2897. return new ResourceBinary;
  2898. } else {
  2899. switch (rv.GetW()) {
  2900. case 1: return new ResourceBinary;
  2901. case 2: return new ResourceBinary;
  2902. case 3: return new ResourceBinary;
  2903. case 4: return new Menu32;
  2904. case 5: return new Dialog32;
  2905. case 6: return new String32;
  2906. case 7: return new ResourceBinary;
  2907. case 8: return new ResourceBinary;
  2908. case 9: return new ResourceBinary;
  2909. case 10: return new ResourceBinary;
  2910. case 11: return new ResourceBinary;
  2911. case 16: return new VersionInfo;
  2912. case 240:
  2913. case 1024:
  2914. case 23:
  2915. case 2110: return new ResourceBinary;
  2916. case 2200: return new ResourceBinary;
  2917. default: return new ResourceBinary;
  2918. }
  2919. }
  2920. }
  2921. //// Rsrc internal resource directory
  2922. //
  2923. // Rsrc stores resources in an STL 'map' structure.
  2924. class ResourceValue {
  2925. public:
  2926. const BYTE *pb; // Pointer into mapped file
  2927. DWORD cb; // Count of bytes in the value
  2928. Resource *pResource;
  2929. DWORD dwCodePage; // Codepage from Win32 resource index - not very useful!
  2930. ResourceValue() {pb = NULL; pResource = NULL; cb=0; dwCodePage=0;}
  2931. /*
  2932. ~ResourceValue() {}; // Don't destroy content on destruction
  2933. ResourceValue& operator= (const ResourceValue &rv) {
  2934. pb = rv.pb;
  2935. cb = rv.cb;
  2936. pResource = rv.pResource;
  2937. dwCodePage = rv.dwCodePage;
  2938. return *this;
  2939. }
  2940. ResourceValue(const ResourceValue &rv) {
  2941. *this = rv;
  2942. }
  2943. */
  2944. //// CreateImage
  2945. //
  2946. // Convert interpreted resource to binary image.
  2947. // Used to prepare resources read from tokens for
  2948. // comparison and update.
  2949. HRESULT CreateImage() {
  2950. BYTE *pbBuf;
  2951. ASSERT(pb == NULL);
  2952. ASSERT(pResource != NULL);
  2953. cb = pResource->cbBin();
  2954. pbBuf = new BYTE [cb];
  2955. ASSERT(pbBuf != NULL);
  2956. pb = pbBuf;
  2957. OK(pResource->CopyBin(&pbBuf));
  2958. ASSERT(pbBuf - pb == cb); // This may be too strong? It has not failed yet!
  2959. ASSERT(pbBuf - pb <= cb); // This must be true - otherwise we wrote past the end of the buffer
  2960. return S_OK;
  2961. }
  2962. //// InterpretImage
  2963. //
  2964. // Convert binary image to interpreted resource.
  2965. // Used to prepare resources read from executable for
  2966. // writing as tokens.
  2967. HRESULT InterpretImage(const ResourceKey &rk) {
  2968. ASSERT(pb != NULL);
  2969. ASSERT(pResource == NULL);
  2970. ASSERT(rk.iDepth == 3);
  2971. ASSERT(!rk.prvId[2]->GetfString());
  2972. if (g_dwOptions & OPTHEXDUMP) {
  2973. pResource = new ResourceHexDump;
  2974. } else {
  2975. // This is a resource extraction to tokens so interpret content
  2976. pResource = NewResource(*rk.prvId[0]);
  2977. }
  2978. ASSERT(pResource != NULL);
  2979. OK(pResource->ReadBin(Scanner(pb, cb), cb));
  2980. pb = NULL;
  2981. cb = 0;
  2982. return S_OK;
  2983. }
  2984. //// Checksum
  2985. //
  2986. // Returns DWORD checksum of binary content of resource
  2987. DWORD Checksum() {
  2988. DWORD dw;
  2989. DWORD *pdw;
  2990. int i,l;
  2991. ASSERT(pb != NULL);
  2992. l = cb >> 2; // Length in whole DWORDS
  2993. pdw = (DWORD*)pb;
  2994. dw = 0;
  2995. for (i=0; i<l; i++) {
  2996. dw ^= pdw[i];
  2997. }
  2998. l = cb - (l << 2); // Remaining length in bytes
  2999. if (l>2) dw ^= pb[cb-3] << 16;
  3000. if (l>1) dw ^= pb[cb-2] << 8;
  3001. if (l>0) dw ^= pb[cb-1];
  3002. return dw;
  3003. }
  3004. };
  3005. class ResourceMap : public map < ResourceKey, ResourceValue*, less<ResourceKey> > {
  3006. public:
  3007. //// AddResource
  3008. //
  3009. //
  3010. HRESULT AddResource(ResourceKey &rk, const BYTE *pb, DWORD cb, DWORD dwCodePage) {
  3011. ResourceValue *prv;
  3012. // Build a resource structure
  3013. prv = new ResourceValue;
  3014. prv->pb = pb;
  3015. prv->cb = cb;
  3016. prv->dwCodePage = dwCodePage;
  3017. prv->pResource = NULL;
  3018. // Process add options
  3019. if (IsResourceWanted(rk)) {
  3020. // Insert resource details into STL map
  3021. if (this->count(rk) != 0) {
  3022. fprintf(stderr, "%s(", g_szExecutable);
  3023. rk.fprint(stderr);
  3024. fprintf(stderr, "): error RSRC500: Corrupt executable - resource appears more than once\n");
  3025. g_fError = TRUE;
  3026. return E_FAIL;
  3027. }
  3028. (*this)[rk] = prv;
  3029. } else {
  3030. g_cResourcesIgnored++;
  3031. }
  3032. return S_OK;
  3033. }
  3034. //// CopyResources
  3035. //
  3036. // Takes a copy so the original mapped file can be closed
  3037. HRESULT CopyResources() {
  3038. iterator rmi;
  3039. BYTE *pb;
  3040. for (rmi = begin(); rmi != end(); rmi++) {
  3041. pb = new BYTE[rmi->second->cb];
  3042. ASSERT(pb != NULL);
  3043. memcpy(pb, rmi->second->pb, rmi->second->cb);
  3044. rmi->second->pb = pb;
  3045. }
  3046. return S_OK;
  3047. }
  3048. //// WriteTokens
  3049. //
  3050. // Writes the content of the map as a token file.
  3051. //
  3052. // If an unlocalised map is provided, bit for bit identical
  3053. // resources are written as a reference to the unlocalised
  3054. // version language, rather than in full.
  3055. HRESULT WriteTokens(NewFile &nfText, ResourceMap *prmUnlocalised) {
  3056. iterator rmi;
  3057. iterator rmiUnlocalised;
  3058. ResourceKey rkUnlocalised;
  3059. for (rmi = begin(); rmi != end(); rmi++) {
  3060. g_cResourcesExtracted++;
  3061. // Write resource key and codepage
  3062. OK(rmi->first.WriteTok(&nfText));
  3063. OK(nfText.WriteS(";"));
  3064. OK(nfText.WriteHex(rmi->second->dwCodePage, 8));
  3065. if (prmUnlocalised) {
  3066. // Add unlocalised checksum and language
  3067. rkUnlocalised = rmi->first;
  3068. rkUnlocalised.SetLanguage(g_liUnlocalized);
  3069. rmiUnlocalised = prmUnlocalised->find(rkUnlocalised);
  3070. if (rmiUnlocalised == prmUnlocalised->end()) {
  3071. fprintf(stderr, "%s(", g_szResources);
  3072. rmi->first.fprint(stderr);
  3073. fprintf(stderr, "): warning RSRC100: Localised resource has no corresponding unlocalised resource in %s\n", g_szUnloc);
  3074. g_fWarn = TRUE;
  3075. } else {
  3076. // Put out details of the unlocalised resource
  3077. OK(nfText.WriteS(","));
  3078. OK(nfText.WriteHex(rmiUnlocalised->second->Checksum(), 8));
  3079. OK(nfText.WriteS(","));
  3080. OK(nfText.WriteHex(g_liUnlocalized, 4));
  3081. }
  3082. }
  3083. OK(nfText.WriteS(";"));
  3084. // Check whether resource needs to be written in full
  3085. if ( prmUnlocalised
  3086. && rmiUnlocalised != prmUnlocalised->end()
  3087. && rmiUnlocalised->second->cb == rmi->second->cb
  3088. && memcmp(rmi->second->pb, rmiUnlocalised->second->pb, rmi->second->cb) == 0) {
  3089. // Bit for bit match with unlocalised executable
  3090. OK(nfText.WriteS("Unloc"));
  3091. } else {
  3092. // Doesn't match - write it in full
  3093. OK(rmi->second->InterpretImage(rmi->first));
  3094. OK(rmi->second->pResource->WriteTok(nfText));
  3095. }
  3096. OK(nfText.WriteLn());
  3097. }
  3098. return S_OK;
  3099. }
  3100. //// UpdateWin32Executable
  3101. //
  3102. //
  3103. HRESULT UpdateWin32Executable(char *pExecutable) {
  3104. iterator rmi;
  3105. HANDLE hUpdate;
  3106. hUpdate = BeginUpdateResourceA(pExecutable, TRUE); // Will replace all resources
  3107. MUST(hUpdate != NULL ? S_OK : E_FAIL,
  3108. ("RSRC : error RSRC600: BeginUpdateResource failed on %s\n", pExecutable));
  3109. for (rmi = begin(); rmi != end(); rmi++) {
  3110. ASSERT(rmi->first.iDepth == 3);
  3111. ASSERT(!rmi->first.prvId[2]->GetfString());
  3112. // Create binary image of resource if necessary
  3113. if (rmi->second->pb == NULL) {
  3114. OK(rmi->second->CreateImage());
  3115. }
  3116. // Use NT resource API to update resource binary image in executable
  3117. if (!UpdateResourceW(
  3118. hUpdate,
  3119. rmi->first.GetResName(0),
  3120. rmi->first.GetResName(1),
  3121. rmi->first.prvId[2]->GetW(),
  3122. (void*)rmi->second->pb,
  3123. rmi->second->cb)) {
  3124. EndUpdateResourceW(hUpdate, TRUE); // Discard all requested updates
  3125. g_fError = TRUE;
  3126. fprintf(stderr, "RSRC : error RSRC601: UpdateResourceW failed on %s\n", pExecutable);
  3127. return HRESULT_FROM_WIN32(GetLastError());
  3128. }
  3129. }
  3130. if (!EndUpdateResourceW(hUpdate, FALSE)) { // Apply all requested updates
  3131. fprintf(stderr, "RSRC : error RSRC602: EndUpdateResourceW failed on %s\n", pExecutable);
  3132. g_fError = TRUE;
  3133. return HRESULT_FROM_WIN32(GetLastError());
  3134. }
  3135. return S_OK;
  3136. }
  3137. };
  3138. class SymbolFile {
  3139. MappedFile *m_pmfSymbolFile;
  3140. IMAGE_SEPARATE_DEBUG_HEADER *m_pDebugHeader;
  3141. public:
  3142. DWORD GetChecksum() const {return m_pDebugHeader->CheckSum;}
  3143. DWORD GetTimeDateStamp() const {return m_pDebugHeader->TimeDateStamp;}
  3144. DWORD GetImageBase() const {return m_pDebugHeader->ImageBase;}
  3145. DWORD GetSizeOfImage() const {return m_pDebugHeader->SizeOfImage;}
  3146. void SetChecksum (DWORD dwChecksum) {m_pDebugHeader->CheckSum = dwChecksum;}
  3147. void SetTimeDateStamp (DWORD dwTimeDateStamp) {m_pDebugHeader->TimeDateStamp = dwTimeDateStamp;}
  3148. void SetImageBase (DWORD dwImageBase) {m_pDebugHeader->ImageBase = dwImageBase;}
  3149. void SetSizeOfImage (DWORD dwSizeOfImage) {m_pDebugHeader->SizeOfImage = dwSizeOfImage;}
  3150. HRESULT Open(MappedFile *pmfSymbolFile) {
  3151. m_pmfSymbolFile = pmfSymbolFile;
  3152. m_pDebugHeader = (IMAGE_SEPARATE_DEBUG_HEADER*) pmfSymbolFile->GetFile();
  3153. ASSERT(m_pDebugHeader->Signature == IMAGE_SEPARATE_DEBUG_SIGNATURE);
  3154. return S_OK;
  3155. }
  3156. };
  3157. class Win32Executable : public MappedFile {
  3158. IMAGE_NT_HEADERS *m_pNtHeader;
  3159. IMAGE_SECTION_HEADER *m_pSections;
  3160. int m_iSectionRsrc;
  3161. int m_iSectionRsrc1;
  3162. // For scanning
  3163. ResourceKey m_rk; // Current resource key
  3164. HRESULT MapDirectory(
  3165. ResourceMap &rm,
  3166. const BYTE *pbRsrc, // Resource block
  3167. int dwOffset, // Directory offset relative to m_pbRsrc
  3168. int iLevel) { // Directory level being scanned
  3169. IMAGE_RESOURCE_DIRECTORY *pird;
  3170. IMAGE_RESOURCE_DIRECTORY_ENTRY *pEntries;
  3171. IMAGE_RESOURCE_DATA_ENTRY *pirde;
  3172. const BYTE *pb;
  3173. int i;
  3174. pird = (IMAGE_RESOURCE_DIRECTORY*) (pbRsrc+dwOffset);
  3175. pEntries = (IMAGE_RESOURCE_DIRECTORY_ENTRY*) (pird+1);
  3176. for (i=0; i<pird->NumberOfNamedEntries + pird->NumberOfIdEntries; i++) {
  3177. // Read the ID from the directory
  3178. ASSERT(iLevel<3);
  3179. m_rk.iDepth = iLevel+1;
  3180. m_rk.prvId[iLevel] = new ResourceVariant;
  3181. ASSERT(m_rk.prvId[iLevel] != NULL);
  3182. OK(m_rk.prvId[iLevel]->ReadWin32ResDirEntry(this, pbRsrc, pEntries+i));
  3183. if (pEntries[i].DataIsDirectory) {
  3184. // This is a directory node. Recurse to scan that directory.
  3185. OK(MapDirectory(rm, pbRsrc, pEntries[i].OffsetToDirectory, iLevel+1));
  3186. } else {
  3187. // We've reached a leaf node, establish the data address and
  3188. // add the resource to the map.
  3189. pirde = (IMAGE_RESOURCE_DATA_ENTRY*) (pbRsrc + pEntries[i].OffsetToData);
  3190. // Note that even when the resource data is in .rsrc1, the
  3191. // directory entry is usually in .rsrc.
  3192. if (pirde->OffsetToData < m_pSections[m_iSectionRsrc].VirtualAddress
  3193. + m_pSections[m_iSectionRsrc].SizeOfRawData) {
  3194. // Data is in section .rsrc
  3195. ASSERT(pirde->OffsetToData >= m_pSections[m_iSectionRsrc].VirtualAddress);
  3196. pb = GetFile()
  3197. + m_pSections[m_iSectionRsrc].PointerToRawData
  3198. + pirde->OffsetToData
  3199. - m_pSections[m_iSectionRsrc].VirtualAddress;
  3200. } else {
  3201. // Data is in section .rsrc1
  3202. ASSERT(pirde->OffsetToData >= m_pSections[m_iSectionRsrc1].VirtualAddress);
  3203. ASSERT(pirde->OffsetToData < m_pSections[m_iSectionRsrc1].VirtualAddress
  3204. + m_pSections[m_iSectionRsrc1].SizeOfRawData);
  3205. pb = GetFile()
  3206. + m_pSections[m_iSectionRsrc1].PointerToRawData
  3207. + pirde->OffsetToData
  3208. - m_pSections[m_iSectionRsrc1].VirtualAddress;
  3209. }
  3210. OK(rm.AddResource(m_rk, pb, pirde->Size, pirde->CodePage));
  3211. }
  3212. }
  3213. return S_OK;
  3214. }
  3215. public:
  3216. DWORD GetChecksum() const {return m_pNtHeader->OptionalHeader.CheckSum;}
  3217. DWORD GetTimeDateStamp() const {return m_pNtHeader->FileHeader.TimeDateStamp;}
  3218. DWORD GetImageBase() const {return m_pNtHeader->OptionalHeader.ImageBase;}
  3219. DWORD GetSizeOfImage() const {return m_pNtHeader->OptionalHeader.SizeOfImage;}
  3220. BOOL Is64BitImage() const {return m_pNtHeader->OptionalHeader.Magic == IMAGE_NT_OPTIONAL_HDR64_MAGIC;}
  3221. void SetChecksum(DWORD dwChecksum) {m_pNtHeader->OptionalHeader.CheckSum=dwChecksum;}
  3222. HRESULT Open(const char *pcFileName, BOOL fWrite) {
  3223. int i;
  3224. OK(MappedFile::Open(pcFileName, fWrite));
  3225. MUST(( *(WORD*)m_pStart == IMAGE_DOS_SIGNATURE
  3226. && *(WORD*)(m_pStart+0x18) >= 0x40) // WinVer >= 4
  3227. ? S_OK : E_FAIL,
  3228. ("RSRC : error RSRC501: %s is not an executable file\n", pcFileName));
  3229. m_pNtHeader = (IMAGE_NT_HEADERS*)(m_pStart + *(WORD*)(m_pStart+0x3c));
  3230. MUST((m_pNtHeader->Signature == IMAGE_NT_SIGNATURE)
  3231. ? S_OK : E_FAIL,
  3232. ("RSRC : error RSRC502: %s is not a Win32 executable file\n", pcFileName));
  3233. if (Is64BitImage()) {
  3234. m_pSections = (IMAGE_SECTION_HEADER*)( (BYTE *) (m_pNtHeader+1) +
  3235. (IMAGE_SIZEOF_NT_OPTIONAL64_HEADER - IMAGE_SIZEOF_NT_OPTIONAL32_HEADER));
  3236. } else {
  3237. m_pSections = (IMAGE_SECTION_HEADER*)(m_pNtHeader+1);
  3238. }
  3239. m_iSectionRsrc = -1;
  3240. m_iSectionRsrc1 = -1;
  3241. // Locate the one or two resource sections
  3242. for (i=0; i<m_pNtHeader->FileHeader.NumberOfSections; i++) {
  3243. if (strcmp((char*)m_pSections[i].Name, ".rsrc") == 0) {
  3244. m_iSectionRsrc = i;
  3245. } else if (strcmp((char*)m_pSections[i].Name, ".rsrc") == 0) {
  3246. m_iSectionRsrc1 = i;
  3247. }
  3248. }
  3249. MUST(m_iSectionRsrc >= 0
  3250. ? S_OK : E_FAIL,
  3251. ("RSRC : error RSRC503: No resources in %s\n", pcFileName));
  3252. ASSERT(m_iSectionRsrc > -1); // Check for presence of resources
  3253. return S_OK;
  3254. }
  3255. //// MapResourceDirectory
  3256. //
  3257. // Extract the resource directory into an STL map.
  3258. HRESULT MapResourceDirectory(ResourceMap &rm) {
  3259. OK(MapDirectory(
  3260. rm,
  3261. m_pStart + m_pSections[m_iSectionRsrc].PointerToRawData,
  3262. 0, 0));
  3263. if (m_iSectionRsrc1 >= 0) {
  3264. OK(MapDirectory(
  3265. rm,
  3266. m_pStart + m_pSections[m_iSectionRsrc1].PointerToRawData,
  3267. 0, 0));
  3268. }
  3269. return S_OK;
  3270. }
  3271. };
  3272. //// High level operation
  3273. //
  3274. // Controlling routines for the various modes of operation
  3275. ResourceMap rmExecutable; // Read and/or update
  3276. ResourceMap rmUnlocalised; // '-u' option - unlocalised resources for comparison
  3277. //// ApplyResource
  3278. //
  3279. // Applies a given key and value to the executable resource map.
  3280. //
  3281. // Tokens are merged with those already loaded from the executable
  3282. // according to the update mode (append or replace).
  3283. HRESULT ApplyResource(ResourceKey &rk, ResourceValue *prv) {
  3284. ResourceKey rkUnloc;
  3285. VersionInfo *pviLoc;
  3286. VersionInfo *pviUnloc;
  3287. ResourceMap::iterator rmiUnloc;
  3288. // Establish equivalent unlocalised key
  3289. rkUnloc = rk;
  3290. rkUnloc.SetLanguage(g_liUnlocalized);
  3291. // First ensure that we keep the unlocalised version info, if we can
  3292. if ( !(g_dwOptions & OPTVERSION)
  3293. && !rk.prvId[0]->GetfString()
  3294. && rk.prvId[0]->GetW() == 16 // VersionInfo
  3295. && (rmiUnloc=rmExecutable.find(rkUnloc)) != NULL
  3296. && rmiUnloc != rmExecutable.end()) {
  3297. // Special case - keep unlocalised file and product versions
  3298. if (rmiUnloc->second->pResource == NULL) {
  3299. rmiUnloc->second->InterpretImage(rmiUnloc->first);
  3300. }
  3301. pviLoc = static_cast<VersionInfo*>(prv->pResource);
  3302. pviUnloc = static_cast<VersionInfo*>(rmiUnloc->second->pResource);
  3303. if (pviLoc && pviUnloc) {
  3304. pviLoc->SetStringFileInfo(L"FileVersion", pviUnloc->GetStringFileInfo(L"FileVersion"));
  3305. pviLoc->SetStringFileInfo(L"ProductVersion", pviUnloc->GetStringFileInfo(L"ProductVersion"));
  3306. pviLoc->SetBinaryInfo(pviUnloc->GetBinaryInfo());
  3307. }
  3308. }
  3309. if (rk.prvId[2]->GetW() == g_liUnlocalized) {
  3310. // New token is not localized
  3311. fprintf(stderr, "%s(", g_szResources);
  3312. rk.fprint(stderr);
  3313. if (rmExecutable.count(rk) == 0) {
  3314. fprintf(stderr, "): warning RSRC110: Unlocalised resource from token file appended to executable\n");
  3315. g_fWarn = TRUE;
  3316. g_cResourcesAppended++;
  3317. } else {
  3318. fprintf(stderr, "): warning RSRC111: Unlocalised resource from token file replaced unlocalised resource in executable\n");
  3319. g_fWarn = TRUE;
  3320. g_cResourcesUpdated++;
  3321. }
  3322. } else if (rmExecutable.count(rk) > 0) {
  3323. // New token already exists in executable
  3324. fprintf(stderr, "%s(", g_szResources);
  3325. rk.fprint(stderr);
  3326. fprintf(stderr, "): warning RSRC112: Localised resource from token file replaced localised resource already present in executable\n");
  3327. g_fWarn = TRUE;
  3328. g_cResourcesUpdated++;
  3329. } else if (g_dwOptions & OPTREPLACE) {
  3330. // Replace operation
  3331. //
  3332. // Replace unlocalised resource with localised translation
  3333. if (rmExecutable.count(rkUnloc) == 0) {
  3334. fprintf(stderr, "%s(", g_szResources);
  3335. rk.fprint(stderr);
  3336. fprintf(stderr, "): warning RSRC113: Localised resource from token file appended to executable - there was no matching unlocalised resource\n");
  3337. g_fWarn = TRUE;
  3338. g_cResourcesAppended++;
  3339. } else {
  3340. // Normal operation: remove unlocalised resource from executable
  3341. rmExecutable.erase(rkUnloc);
  3342. g_cResourcesTranslated++;
  3343. }
  3344. } else {
  3345. // Append operation
  3346. g_cResourcesAppended++;
  3347. }
  3348. rmExecutable[rk] = prv;
  3349. return S_OK;
  3350. }
  3351. //// ReadTokens
  3352. //
  3353. // Scans the token file.
  3354. //
  3355. // Selected resources are passed to ApplyResource
  3356. HRESULT ReadTokens(TextScanner &mfText) {
  3357. ResourceKey rk;
  3358. ResourceValue *prv;
  3359. ResourceKey rkUnlocalised;
  3360. DWORD dwCodePage;
  3361. DWORD dwUnlocChecksum;
  3362. ResourceMap::iterator rmiUnlocalised;
  3363. DWORD liUnlocalised; // Unlocalised language referenced by token
  3364. while (mfText.GetRead() < mfText.GetLimit()) {
  3365. OK(rk.ReadTok(&mfText)); // Read resource key
  3366. OK(mfText.Expect(";"));
  3367. if ( ( g_LangId != 0xffff
  3368. && rk.prvId[2]->GetW() != g_LangId)
  3369. || !IsResourceWanted(rk)) {
  3370. // Ignore this token
  3371. g_cResourcesIgnored++;
  3372. fprintf(stderr, "%s(", g_szResources);
  3373. rk.fprint(stderr);
  3374. if (g_LangId != 0xffff && rk.prvId[2]->GetW() != g_LangId) {
  3375. fprintf(stderr, "): warning RSRC120: Token file resource does not match specified language - ignored\n");
  3376. g_fWarn = TRUE;
  3377. } else {
  3378. fprintf(stderr, "): warning RSRC121: Token file resource is not a requested resource type - ignored\n");
  3379. g_fWarn = TRUE;
  3380. }
  3381. // Skip unwanted resource
  3382. OK(mfText.SkipLn());
  3383. while (*(char*)mfText.GetRead() == ' ') {
  3384. OK(mfText.SkipLn());
  3385. }
  3386. } else {
  3387. rmiUnlocalised = NULL;
  3388. OK(mfText.ReadHex(&dwCodePage));
  3389. if (*(char*)mfText.GetRead() == ',') {
  3390. // There is unlocalised resource information available
  3391. OK(mfText.Expect(","));
  3392. OK(mfText.ReadHex(&dwUnlocChecksum));
  3393. OK(mfText.Expect(","));
  3394. OK(mfText.ReadHex(&liUnlocalised));
  3395. // Check whether the unlocalised resource still exists in the
  3396. // current executable, and has the same checksum,
  3397. rkUnlocalised = rk;
  3398. rkUnlocalised.SetLanguage(liUnlocalised);
  3399. rmiUnlocalised = rmExecutable.find(rkUnlocalised);
  3400. if ( rmiUnlocalised != rmExecutable.end()
  3401. && dwUnlocChecksum != rmiUnlocalised->second->Checksum()) {
  3402. fprintf(stderr, "%s: warning RSRC122: executable unlocalised resource checksum does not match checksum recorded in token file for resource ", mfText.GetTextPos());
  3403. rk.fprint(stderr);
  3404. fprintf(stderr, "\n");
  3405. g_fWarn = TRUE;
  3406. }
  3407. }
  3408. OK(mfText.Expect(";"));
  3409. if (*(char*)mfText.GetRead() == 'U') {
  3410. // No resource content provided in token file
  3411. // Use unlocalised resource from executable
  3412. if (rmiUnlocalised == NULL) {
  3413. fprintf(stderr, "%s: error RSRC230: 'Unloc' token is missing unlocalised resource information for ", mfText.GetTextPos());
  3414. rk.fprint(stderr);
  3415. fprintf(stderr, "\n");
  3416. g_fError = TRUE;
  3417. return E_FAIL;
  3418. }
  3419. OK(mfText.Expect("Unloc"));
  3420. OK(mfText.ExpectLn(""));
  3421. if (rmiUnlocalised == rmExecutable.end()) {
  3422. fprintf(stderr, "%s: warning RSRC124: missing executable unlocalised resource for ", mfText.GetTextPos());
  3423. rk.fprint(stderr);
  3424. fprintf(stderr, " - localisation skipped\n");
  3425. g_fWarn = TRUE;
  3426. } else {
  3427. MUST(ApplyResource(rk, rmiUnlocalised->second), ("%s: error RSRC231: Failed to apply unloc token\n", mfText.GetTextPos()));
  3428. }
  3429. } else {
  3430. // Resource content is provided in token file
  3431. if (rmiUnlocalised == rmExecutable.end()) {
  3432. fprintf(stderr, "%s: warning RSRC125: executable contains no unlocalised resource corresponding to resource ", mfText.GetTextPos());
  3433. rk.fprint(stderr);
  3434. fprintf(stderr, "\n");
  3435. g_fWarn = TRUE;
  3436. }
  3437. prv = new ResourceValue;
  3438. ASSERT(prv != NULL);
  3439. prv->dwCodePage = dwCodePage;
  3440. prv->pb = NULL;
  3441. prv->cb = 0;
  3442. switch (*(char*)mfText.GetRead()) {
  3443. case 'H': prv->pResource = new ResourceBinary; break;
  3444. case 'D': prv->pResource = new Dialog32; break;
  3445. case 'M': prv->pResource = new Menu32; break;
  3446. case 'S': prv->pResource = new String32; break;
  3447. case 'V': prv->pResource = new VersionInfo; break;
  3448. default:
  3449. fprintf(stderr, "%s: error RSRC310: Unrecognised resource type for resource ", mfText.GetTextPos());
  3450. rk.fprint(stderr);
  3451. fprintf(stderr, "\n");
  3452. g_fError = TRUE;
  3453. return E_FAIL;
  3454. }
  3455. ASSERT(prv->pResource != NULL);
  3456. // Parse selected resource
  3457. OK(prv->pResource->ReadTok(mfText));
  3458. OK(mfText.ExpectLn(NULL));
  3459. // Save parsed resource in STL map
  3460. MUST(ApplyResource(rk, prv), ("%s: error RSRC232: Failed to apply token\n", mfText.GetTextPos()));
  3461. }
  3462. }
  3463. }
  3464. return S_OK;
  3465. }
  3466. //// Stats
  3467. //
  3468. //
  3469. HRESULT Analyse(char *pExecutable) {
  3470. Win32Executable w32x;
  3471. NewFile nfText;
  3472. ResourceMap::iterator rmi;
  3473. MappedResourceStats::iterator mrsi;
  3474. MappedLanguageStats::iterator mlsi;
  3475. char key[100];
  3476. int i;
  3477. const WCHAR *pwc;
  3478. BOOL fLocalizable;
  3479. MUST(w32x.Open(pExecutable, FALSE),
  3480. ("RSRC : error RSRC510: Cannot open executable file %s\n", pExecutable));
  3481. MUST(w32x.MapResourceDirectory(rmExecutable),
  3482. ("RSRC : error RSRC511: cannot find resource directory in %s\n, pExecutable"));
  3483. // Scan through the resources updating the stats
  3484. fLocalizable = FALSE;
  3485. for (rmi = rmExecutable.begin(); rmi != rmExecutable.end(); rmi++) {
  3486. if ( rmi->first.prvId[0]->GetfString()
  3487. || rmi->first.prvId[0]->GetW() != 16) {
  3488. fLocalizable = TRUE;
  3489. }
  3490. OK(rmi->second->InterpretImage(rmi->first));
  3491. UpdateStats(rmi->first,
  3492. rmi->second->pResource->GetItems(),
  3493. rmi->second->pResource->GetWords(),
  3494. rmi->second->pResource->cbBin());
  3495. }
  3496. if (!(g_dwOptions & OPTQUIET)) {
  3497. fprintf(stdout, "\n Resource type Count Items Words Bytes\n");
  3498. fprintf(stdout, " ------------ ------ ------ ------ --------\n");
  3499. for (mrsi = ResourceStatsMap.begin(); mrsi != ResourceStatsMap.end(); mrsi++) {
  3500. if (mrsi->first.GetfString()) {
  3501. key[0] = '\"';
  3502. i=0;
  3503. pwc = mrsi->first.GetString();
  3504. while (i < min(10, mrsi->first.GetLength())) {
  3505. key[i+1] = (char) pwc[i];
  3506. i++;
  3507. }
  3508. key[i+1] = '\"';
  3509. key[i+2] = 0;
  3510. fprintf(stdout, " %-12.12s ", key);
  3511. } else {
  3512. switch (mrsi->first.GetW()) {
  3513. case 1: fprintf(stdout, " 1 (Cursor) "); break;
  3514. case 2: fprintf(stdout, " 2 (Bitmap) "); break;
  3515. case 3: fprintf(stdout, " 3 (Icon) "); break;
  3516. case 4: fprintf(stdout, " 4 (Menu) "); break;
  3517. case 5: fprintf(stdout, " 5 (Dialog) "); break;
  3518. case 6: fprintf(stdout, " 6 (String) "); break;
  3519. case 7: fprintf(stdout, " 7 (Fnt dir) "); break;
  3520. case 8: fprintf(stdout, " 8 (Font) "); break;
  3521. case 9: fprintf(stdout, " 9 (Accel) "); break;
  3522. case 10: fprintf(stdout, " a (RCDATA) "); break;
  3523. case 11: fprintf(stdout, " b (Msgtbl) "); break;
  3524. case 16: fprintf(stdout, " 10 (Version) "); break;
  3525. default: fprintf(stdout, " %-12x ", mrsi->first.GetW());
  3526. }
  3527. }
  3528. fprintf(stdout, "%6d ", mrsi->second.cResources);
  3529. if (mrsi->second.cItems > 0) {
  3530. fprintf(stdout, "%6d ", mrsi->second.cItems);
  3531. } else {
  3532. fprintf(stdout, " ");
  3533. }
  3534. if (mrsi->second.cWords > 0) {
  3535. fprintf(stdout, "%6d ", mrsi->second.cWords);
  3536. } else {
  3537. fprintf(stdout, " ");
  3538. }
  3539. fprintf(stdout, "%8d\n", mrsi->second.cBytes);
  3540. }
  3541. fprintf(stdout, "\n Language Resources Items Words Bytes\n");
  3542. fprintf(stdout, " -------- --------- ------ ------ --------\n");
  3543. for (mlsi = LanguageStatsMap.begin(); mlsi != LanguageStatsMap.end(); mlsi++) {
  3544. fprintf(stdout, " %8x %9d ",
  3545. mlsi->first, mlsi->second.cResources);
  3546. if (mlsi->second.cItems > 0) {
  3547. fprintf(stdout, "%6d ", mlsi->second.cItems);
  3548. } else {
  3549. fprintf(stdout, " ");
  3550. }
  3551. if (mlsi->second.cWords > 0) {
  3552. fprintf(stdout, "%6d ", mlsi->second.cWords);
  3553. } else {
  3554. fprintf(stdout, " ");
  3555. }
  3556. fprintf(stdout, "%8d\n", mlsi->second.cBytes);
  3557. }
  3558. fprintf(stdout, "\n");
  3559. }
  3560. if (!fLocalizable) {
  3561. fprintf(stderr, "RSRC : warning RSRC170: No localizable resources in %s\n", pExecutable);
  3562. g_fWarn = TRUE;
  3563. }
  3564. SHOULD(w32x.Close(), ("RSRC : warning RSRC171: could not close executable\n"));
  3565. return S_OK;
  3566. }
  3567. HRESULT ExtractResources(char *pExecutable, char *pResources) {
  3568. Win32Executable w32x;
  3569. Win32Executable w32xUnloc;
  3570. NewFile nfText;
  3571. char str[100];
  3572. DWORD dw;
  3573. MUST(w32x.Open(g_szExecutable, FALSE),
  3574. ("RSRC : error RSRC510: Cannot open executable file %s\n", g_szExecutable));
  3575. MUST(nfText.OpenWrite(g_szResources),
  3576. ("RSRC : error RSRC512: Cannot create resource token file %s\n", g_szResources));
  3577. // Write header
  3578. if (!(g_dwOptions & OPTHEXDUMP)) {
  3579. OK(nfText.WriteS("\xef\xbb\xbf\r\n")); // UTF-8 mark for notepad, richedit etc.
  3580. }
  3581. OK(nfText.WriteS("### "));
  3582. OK(nfText.WriteS(g_szResources));
  3583. OK(nfText.WriteS("\r\n#\r\n# Extracted: "));
  3584. GetDateFormatA(
  3585. MAKELCID(LANG_ENGLISH, SORT_DEFAULT),
  3586. 0, NULL,
  3587. "yyyy/MM/dd ",
  3588. str, sizeof(str));
  3589. OK(nfText.WriteS(str));
  3590. GetTimeFormatA(
  3591. MAKELCID(LANG_ENGLISH, SORT_DEFAULT),
  3592. 0, NULL,
  3593. "HH:mm:ss\'\r\n# By: \'",
  3594. str, sizeof(str));
  3595. OK(nfText.WriteS(str));
  3596. dw = sizeof(str);
  3597. GetComputerNameA(str, &dw);
  3598. OK(nfText.WriteS(str));
  3599. OK(nfText.WriteS("\r\n# Executable: "));
  3600. OK(nfText.WriteS(g_szExecutable));
  3601. if (g_LangId != 0xffff) {
  3602. OK(nfText.WriteS("\r\n# Language: "));
  3603. OK(nfText.WriteHex(g_LangId, 3));
  3604. }
  3605. if (g_dwProcess != PROCESSALL) {
  3606. OK(nfText.WriteS("\r\n# Res types: "));
  3607. OK(nfText.WriteS(g_szTypes));
  3608. }
  3609. OK(nfText.WriteS("\r\n\r\n"));
  3610. MUST(w32x.MapResourceDirectory(rmExecutable),
  3611. ("RSRC : error RSRC511: cannot find resource directory in %s\n, g_szExecutable"));
  3612. if (g_dwOptions & OPTUNLOC) {
  3613. // Write tokens that differ from specified unlocalised executable
  3614. MUST(w32xUnloc.Open(g_szUnloc, FALSE),
  3615. ("RSRC : error RSRC513: Cannot open unlocalised executable file %s\n", g_szUnloc));
  3616. MUST(w32xUnloc.MapResourceDirectory(rmUnlocalised),
  3617. ("RSRC : error RSRC514: cannot find resource directory in unlocalised executable %s\n, g_szUnloc"));
  3618. MUST(rmExecutable.WriteTokens(nfText, &rmUnlocalised),
  3619. ("RSRC : error RSRC515: cannot write delta token file %s\n, g_szResources"));
  3620. w32xUnloc.Close();
  3621. } else {
  3622. MUST(rmExecutable.WriteTokens(nfText, NULL),
  3623. ("RSRC : error RSRC516: cannot write stand alone token file %s\n, g_szResources"));
  3624. }
  3625. if (!(g_dwOptions & OPTQUIET)) {
  3626. fprintf(stdout, "\n%d resource(s) %s.\n", g_cResourcesExtracted, g_dwOptions & OPTHEXDUMP ? "dumped" : "tokenized");
  3627. if (g_cResourcesIgnored) {
  3628. fprintf(stdout, "%d resource(s) ignored.\n", g_cResourcesIgnored);
  3629. }
  3630. }
  3631. OK(w32x.Close());
  3632. OK(nfText.Close());
  3633. return S_OK;
  3634. }
  3635. //// UpdateResources
  3636. //
  3637. // Update resources in executable with tokens from given text
  3638. //
  3639. // Processing
  3640. //
  3641. // 1. Existing resources are loaded into the map as ResourceBinaries.
  3642. // 2. Resources are merged in from the token file according to
  3643. // command line selected processing options
  3644. // 3. The NT UpdateResource API set is used to replace all the resources
  3645. // in the executable with the merged resources in the map.
  3646. HRESULT UpdateResources(char *pExecutable, char *pResources, char* pSymbols) {
  3647. Win32Executable w32x;
  3648. SymbolFile symf;
  3649. MappedFile mfText;
  3650. MappedFile mfSymbols;
  3651. DWORD dwCheckSum;
  3652. MUST(w32x.Open(pExecutable, FALSE),
  3653. ("RSRC : error RSRC510: Cannot open executable file %s\n", pExecutable));
  3654. MUST(mfText.Open(pResources, FALSE),
  3655. ("RSRC : error RSRC520: Cannot open resource token file %s\n", pResources));
  3656. MUST(mfText.Expect("\xef\xbb\xbf"),
  3657. ("RSRC : error RSRC521: UTF8 BOM missing from token file\n")); // UTF-8 mark for notepad, richedit etc.
  3658. OK(mfText.ExpectLn("")); // Skip over header comments
  3659. if (g_dwOptions & OPTSYMBOLS) {
  3660. if ( SUCCEEDED(mfSymbols.Open(pSymbols, TRUE))
  3661. && SUCCEEDED(symf.Open(&mfSymbols))) {
  3662. if ( symf.GetChecksum() != w32x.GetChecksum()
  3663. || symf.GetImageBase() != w32x.GetImageBase()) {
  3664. time_t tsTime = symf.GetTimeDateStamp();
  3665. time_t teTime = w32x.GetTimeDateStamp();
  3666. char ssTime[30]; strcpy(ssTime, ctime(&tsTime)); ssTime[19] = 0;
  3667. char seTime[30]; strcpy(seTime, ctime(&teTime)); seTime[19] = 0;
  3668. fprintf(stderr, "\n Symbol mismatch: Executable Symbol file\n");
  3669. fprintf(stderr, " ImageBase: %8x %8x\n", w32x.GetImageBase(), symf.GetImageBase());
  3670. fprintf(stderr, " Checksum: %8x %8x\n", w32x.GetChecksum(), symf.GetChecksum());
  3671. fprintf(stderr, " Timestamp: %-15.15s %-15.15s\n\n", ssTime+4, seTime+4);
  3672. fprintf(stderr, "RSRC : warning RSRC160: Symbol file does not match exectable\n");
  3673. g_fWarn = TRUE;
  3674. }
  3675. } else {
  3676. fprintf(stderr, "RSRC : warning RSRC161: Symbol file not processed\n");
  3677. g_fWarn = TRUE;
  3678. g_dwOptions &= ~OPTSYMBOLS;
  3679. }
  3680. }
  3681. // Load existing resources
  3682. MUST(w32x.MapResourceDirectory(rmExecutable),
  3683. ("RSRC : error RSRC530: Cannot read executable resources from %s\n", pExecutable));
  3684. OK(rmExecutable.CopyResources()); // Take local copy before closing the mapped file
  3685. OK(w32x.Close());
  3686. // Merge in resources from token file
  3687. MUST(ReadTokens(mfText), ("RSRC : error RSRC531: Failed reading update tokens\n"));
  3688. OK(rmExecutable.UpdateWin32Executable(pExecutable));
  3689. // Update was succesful, Recalculate checksum
  3690. SHOULD(w32x.Open(pExecutable, TRUE),
  3691. ("RSRC : warning RSRC162: Could not reopen executable %s to update checksum\n", pExecutable));
  3692. dwCheckSum = w32x.CalcChecksum();
  3693. w32x.SetChecksum(dwCheckSum);
  3694. if (g_dwOptions & OPTSYMBOLS) {
  3695. symf.SetChecksum(dwCheckSum);
  3696. symf.SetTimeDateStamp(w32x.GetTimeDateStamp());
  3697. symf.SetSizeOfImage(w32x.GetSizeOfImage());
  3698. SHOULD(mfSymbols.Close(), ("RSRC : warning RSRC163: Failed to write updated symbol checksum\n"));
  3699. }
  3700. w32x.Close();
  3701. if (!(g_dwOptions & OPTQUIET)) {
  3702. fprintf(stdout, "\n");
  3703. if (g_cResourcesTranslated) {
  3704. fprintf(stdout, "%d resource(s) translated.\n", g_cResourcesTranslated);
  3705. }
  3706. if (g_cResourcesAppended) {
  3707. fprintf(stdout, "%d resource(s) appended.\n", g_cResourcesAppended);
  3708. }
  3709. if (g_cResourcesUpdated) {
  3710. fprintf(stdout, "%d resource(s) updated.\n", g_cResourcesUpdated);
  3711. }
  3712. if (g_cResourcesIgnored) {
  3713. fprintf(stdout, "%d resource(s) ignored.\n", g_cResourcesIgnored);
  3714. }
  3715. }
  3716. mfText.Close();
  3717. return S_OK;
  3718. }
  3719. //// Parameter parsing
  3720. //
  3721. //
  3722. char g_cSwitch = '-'; // Switch character is recorded the first time one is seen
  3723. void SkipWhitespace(char** p, char* pE) {
  3724. while ((*p<pE) && ((**p==' ')||(**p==9))) (*p)++;
  3725. }
  3726. void ParseToken(char** p, char* pE, char* s, int l) {
  3727. // Parse up to whitespace into string s
  3728. // Guarantee zero terminator and modify no more than l chars
  3729. // Return with p beyond whitespace
  3730. if (*p < pE && **p == '\"') {
  3731. // Quoted parameter
  3732. (*p)++; // Skip over leading quote
  3733. while (l>0 && *p<pE && **p!='\"') {
  3734. *s=**p; s++; (*p)++; l--;
  3735. }
  3736. // Skip any part of token that didn't fit s
  3737. while (*p<pE && **p!='\"') { // Skip up to terminating quote
  3738. (*p)++;
  3739. }
  3740. if (*p<pE) { // Skip over terminating quote
  3741. (*p)++;
  3742. }
  3743. } else {
  3744. // Unquoted parameter
  3745. while ((l>0) && (*p<pE) && (**p>' ')) {
  3746. *s=**p; s++; (*p)++;
  3747. l--;
  3748. }
  3749. // Skip any part of token that didn't fit into s
  3750. while ((*p<pE) && (**p>' ')) (*p)++;
  3751. }
  3752. if (l>0)
  3753. *s++ = 0;
  3754. else
  3755. *(s-1) = 0;
  3756. SkipWhitespace(p, pE);
  3757. }
  3758. void ParseName(char** p, char* pE, char* s, int l) {
  3759. // Uses ParseToken to parse a name such as a filename.
  3760. // If the name starts with '/' or '-' it is assumed to be
  3761. // an option rather than a filename and ParseName returns
  3762. // a zero length string.
  3763. if (*p<pE && **p==g_cSwitch) {
  3764. // This is an option and should not be treated as a name argument
  3765. s[0] = 0;
  3766. } else {
  3767. ParseToken(p, pE, s, l);
  3768. }
  3769. }
  3770. void DisplayUsage() {
  3771. fprintf(stdout, "Usage: rsrc -h\n");
  3772. fprintf(stdout, " or: rsrc executable [-l LangId] [-i include-opts] [-q]\n");
  3773. fprintf(stdout, " [ [-t|-d] [text-output] [-c unloc]\n");
  3774. fprintf(stdout, " | [-a|-r] [text-input] [-s symbols] ]\n");
  3775. }
  3776. void DisplayArgs() {
  3777. fprintf(stdout, "\nArguments\n\n");
  3778. fprintf(stdout, " -h Help\n");
  3779. fprintf(stdout, " -q Quiet (default is to show resource stats)\n");
  3780. fprintf(stdout, " -t tokens Write resources in checkin format to token file\n");
  3781. fprintf(stdout, " -c unloc Unlocalised executable for comparison\n");
  3782. fprintf(stdout, " -d tokens Write resources in hex dump format to token file\n");
  3783. fprintf(stdout, " -a tokens Append resources from token file to executable (multi-language update)\n");
  3784. fprintf(stdout, " -r tokens Replace executable resources from token file (single language update)\n");
  3785. fprintf(stdout, " -s symbol Symbol file whose checksum is to track the executable checksum\n");
  3786. fprintf(stdout, " -l lang Restrict processing to language specified in hex\n");
  3787. fprintf(stdout, " -u unlocl Unlocalised langauge, default 409\n");
  3788. fprintf(stdout, " -i opts Include only resource types specified:\n\n");
  3789. fprintf(stdout, " c - Cursors t - Fonts\n");
  3790. fprintf(stdout, " b - Bitmaps a - Accelerators\n");
  3791. fprintf(stdout, " i - Icons r - RCDATAs\n");
  3792. fprintf(stdout, " m - Menus g - Message tables\n");
  3793. fprintf(stdout, " d - Dialogs v - Versions info\n");
  3794. fprintf(stdout, " s - Strings x - Binary data\n");
  3795. fprintf(stdout, " f - Font directories n - INFs\n");
  3796. fprintf(stdout, " o - all others a - All (default)\n\n");
  3797. fprintf(stdout, " Examples\n\n");
  3798. fprintf(stdout, " rsrc notepad.exe - Show resource stats for notepad.exe\n");
  3799. fprintf(stdout, " rsrc notepad.exe -t - Extract tokens to notepad.exe.rsrc\n");
  3800. fprintf(stdout, " rsrc notepad.exe -r -l 401 - Translate from US using Arabic tokens in notepad.exe.rsrc\n");
  3801. fprintf(stdout, " rsrc notepad.exe -d dmp -i im - Hexdump of Icons and Menus to dmp\n\n");
  3802. }
  3803. HRESULT ProcessParameters() {
  3804. char *p; // Current command line character
  3805. char *pE; // End of command line
  3806. char *pcStop;
  3807. char token [MAXPATH];
  3808. char arg [MAXPATH];
  3809. char symbols [MAXPATH] = "";
  3810. int i,j;
  3811. int cFiles;
  3812. DWORD cRes;
  3813. BOOL fArgError;
  3814. p = GetCommandLine();
  3815. pE = p+strlen((char *)p);
  3816. g_dwOptions = 0;
  3817. g_dwProcess = 0;
  3818. cFiles = 0;
  3819. fArgError = FALSE;
  3820. g_szResources[0] = 0;
  3821. // Skip command name
  3822. ParseToken(&p, pE, token, sizeof(token));
  3823. while (p<pE) {
  3824. ParseToken(&p, pE, token, sizeof(token));
  3825. if ( token[0] == '-'
  3826. || token[0] == '/') {
  3827. // Process command option(s)
  3828. i = 1;
  3829. g_cSwitch = token[0]; // Argument may start with the other switch character
  3830. CharLower((char*)token);
  3831. while (token[i]) {
  3832. switch (token[i]) {
  3833. case '?':
  3834. case 'h': g_dwOptions |= OPTHELP; break;
  3835. case 'v': g_dwOptions |= OPTVERSION; break;
  3836. case 'q': g_dwOptions |= OPTQUIET; break;
  3837. case 't': g_dwOptions |= OPTEXTRACT; ParseName(&p, pE, g_szResources, sizeof(g_szResources)); break;
  3838. case 'c': g_dwOptions |= OPTUNLOC; ParseName(&p, pE, g_szUnloc, sizeof(g_szUnloc)); break;
  3839. case 'd': g_dwOptions |= OPTHEXDUMP; ParseName(&p, pE, g_szResources, sizeof(g_szResources)); break;
  3840. case 'a': g_dwOptions |= OPTAPPEND; ParseName(&p, pE, g_szResources, sizeof(g_szResources)); break;
  3841. case 'r': g_dwOptions |= OPTREPLACE; ParseName(&p, pE, g_szResources, sizeof(g_szResources)); break;
  3842. case 's': g_dwOptions |= OPTSYMBOLS; ParseName(&p, pE, symbols, sizeof(g_szResources)); break;
  3843. case 'l':
  3844. ParseToken(&p, pE, arg, sizeof(arg));
  3845. g_LangId = strtol(arg, &pcStop, 16);
  3846. if (*pcStop != 0) {
  3847. fprintf(stderr, "Localized language id contains invalid hex digit '%c'.\n", *pcStop);
  3848. fArgError = TRUE;
  3849. }
  3850. break;
  3851. case 'u':
  3852. ParseToken(&p, pE, arg, sizeof(arg));
  3853. g_liUnlocalized = strtol(arg, &pcStop, 16);
  3854. if (*pcStop != 0) {
  3855. fprintf(stderr, "Unlocalized language id contains invalid hex digit '%c'.\n", *pcStop);
  3856. fArgError = TRUE;
  3857. }
  3858. break;
  3859. case 'i':
  3860. ParseToken(&p, pE, g_szTypes, sizeof(g_szTypes));
  3861. g_dwProcess = 0;
  3862. j = 0;
  3863. while (g_szTypes[j]) {
  3864. switch (g_szTypes[j]) {
  3865. case 'c': g_dwProcess |= PROCESSCUR; break;
  3866. case 'b': g_dwProcess |= PROCESSBMP; break;
  3867. case 'i': g_dwProcess |= PROCESSICO; break;
  3868. case 'm': g_dwProcess |= PROCESSMNU; break;
  3869. case 'd': g_dwProcess |= PROCESSDLG; break;
  3870. case 's': g_dwProcess |= PROCESSSTR; break;
  3871. case 'f': g_dwProcess |= PROCESSFDR; break;
  3872. case 't': g_dwProcess |= PROCESSFNT; break;
  3873. case 'a': g_dwProcess |= PROCESSACC; break;
  3874. case 'r': g_dwProcess |= PROCESSRCD; break;
  3875. case 'g': g_dwProcess |= PROCESSMSG; break;
  3876. case 'v': g_dwProcess |= PROCESSVER; break;
  3877. case 'x': g_dwProcess |= PROCESSBIN; break;
  3878. case 'n': g_dwProcess |= PROCESSINF; break;
  3879. case 'o': g_dwProcess |= PROCESSOTH; break;
  3880. case 'A': g_dwProcess |= PROCESSALL; break;
  3881. default:
  3882. fprintf(stderr, "Unrecognised resource type '%c'.\n", g_szTypes[j]);
  3883. fArgError = TRUE;
  3884. }
  3885. j++;
  3886. }
  3887. break;
  3888. default:
  3889. fprintf(stderr, "Unrecognised argument '%c'.\n", token[i]);
  3890. fArgError = TRUE;
  3891. break;
  3892. }
  3893. i++;
  3894. }
  3895. } else {
  3896. // Process filename
  3897. switch (cFiles) {
  3898. case 0: strcpy(g_szExecutable, token); break;
  3899. }
  3900. cFiles++;
  3901. }
  3902. }
  3903. if (g_dwOptions & OPTHELP) {
  3904. fprintf(stderr, "\nRsrc - Manage Win32 executable resources.\n\n");
  3905. DisplayUsage();
  3906. DisplayArgs();
  3907. return S_OK;
  3908. }
  3909. // Validate option combinations
  3910. if (g_dwOptions & OPTEXTRACT) {
  3911. if (g_dwOptions & (OPTHEXDUMP | OPTAPPEND | OPTREPLACE | OPTSYMBOLS)) {
  3912. fprintf(stderr, "RSRC : error RSRC400: -t (tokenise) option excludes -d, -a, -r, and -s\n");
  3913. fArgError = TRUE;
  3914. }
  3915. } else if (g_dwOptions & OPTHEXDUMP) {
  3916. if (g_dwOptions & (OPTEXTRACT | OPTUNLOC | OPTAPPEND | OPTREPLACE | OPTSYMBOLS)) {
  3917. fprintf(stderr, "RSRC : error RSRC401: -d (dump) option excludes -t, -u, -a, -r, and -s\n");
  3918. fArgError = TRUE;
  3919. }
  3920. } else if (g_dwOptions & OPTAPPEND) {
  3921. if (g_dwOptions & (OPTEXTRACT | OPTHEXDUMP | OPTUNLOC | OPTREPLACE)) {
  3922. fprintf(stderr, "RSRC : error RSRC402: -a (append) option excludes -t, -d, -u, and -r\n");
  3923. fArgError = TRUE;
  3924. }
  3925. } else if (g_dwOptions & OPTREPLACE) {
  3926. if (g_dwOptions & (OPTEXTRACT | OPTHEXDUMP | OPTUNLOC | OPTAPPEND)) {
  3927. fprintf(stderr, "RSRC : error RSRC403: -r (replace) option excludes -t, -d, -u, and -a\n");
  3928. fArgError = TRUE;
  3929. }
  3930. if (g_LangId == 0xFFFF) {
  3931. fprintf(stderr, "RSRC : error RSRC404: -r (replace) option requires -l (LangId)\n");
  3932. fArgError = TRUE;
  3933. }
  3934. } else {
  3935. if (g_dwOptions & (OPTSYMBOLS)) {
  3936. fprintf(stderr, "RSRC : error RSRC405: Analysis excludes -s\n");
  3937. fArgError = TRUE;
  3938. }
  3939. }
  3940. if (fArgError) {
  3941. DisplayUsage();
  3942. DisplayArgs();
  3943. return E_INVALIDARG;
  3944. } else if (cFiles != 1) {
  3945. fprintf(stderr, "\nRsrc : error RSRC406: must specify at least an executable file name.\n\n");
  3946. DisplayUsage();
  3947. return E_INVALIDARG;
  3948. } else {
  3949. // We have valid parameters
  3950. if (g_dwProcess == 0) {
  3951. g_dwProcess = PROCESSALL;
  3952. }
  3953. if (!(g_dwOptions & OPTQUIET)) {
  3954. fprintf(stdout, "\nRsrc - Manage executable resources.\n\n");
  3955. fprintf(stdout, " Executable file: %s\n", g_szExecutable);
  3956. if (g_szResources[0]) {
  3957. fprintf(stdout, " Resource file: %s\n", g_szResources);
  3958. }
  3959. if (symbols[0]) {
  3960. fprintf(stdout, " Symbol file: %s\n", symbols);
  3961. }
  3962. if (g_LangId != 0xffff) {
  3963. char szLang[50] = "";
  3964. char szCountry[50] = "";
  3965. GetLocaleInfoA(g_LangId, LOCALE_SENGLANGUAGE, szLang, sizeof(szLang));
  3966. GetLocaleInfoA(g_LangId, LOCALE_SENGCOUNTRY, szCountry, sizeof(szCountry));
  3967. fprintf(stdout, " Language: %x (%s - %s)\n", g_LangId, szLang, szCountry);
  3968. }
  3969. if (g_dwProcess != PROCESSALL) {
  3970. fprintf(stdout, " Include only: %s\n", g_szTypes);
  3971. }
  3972. }
  3973. cRes = 0;
  3974. // Handle default token file name
  3975. if (g_szResources[0] == 0) {
  3976. strcpy(g_szResources, g_szExecutable);
  3977. strcat(g_szResources, ".rsrc");
  3978. }
  3979. if (g_dwOptions & (OPTAPPEND | OPTREPLACE)) {
  3980. // Update an executable from tokens
  3981. MUST(UpdateResources(g_szExecutable, g_szResources, symbols), ("RSRC : error RSRC420: Update failed.\n"));
  3982. } else if (g_dwOptions & (OPTEXTRACT | OPTHEXDUMP)) {
  3983. // Generate tokens from an executable
  3984. MUST(ExtractResources(g_szExecutable, g_szResources), ("RSRC : error RSRC421: Token extraction failed.\n"));
  3985. } else {
  3986. // Analyse an executable
  3987. MUST(Analyse(g_szExecutable), ("RSRC : error RSRC422: Analysis failed.\n"));
  3988. }
  3989. return S_OK;
  3990. }
  3991. }
  3992. int _cdecl main(void) {
  3993. if (SUCCEEDED(ProcessParameters())) {
  3994. if (!g_fWarn) {
  3995. return 0; // No problems
  3996. } else {
  3997. return 1; // Warning(s) but no error(s)
  3998. }
  3999. } else {
  4000. return 2; // Error(s)
  4001. }
  4002. }