ResourceFileWriter.cpp 52 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567
  1. //===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
  2. //
  3. // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
  4. // See https://llvm.org/LICENSE.txt for license information.
  5. // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
  6. //
  7. //===---------------------------------------------------------------------===//
  8. //
  9. // This implements the visitor serializing resources to a .res stream.
  10. //
  11. //===---------------------------------------------------------------------===//
  12. #include "ResourceFileWriter.h"
  13. #include "llvm/Object/WindowsResource.h"
  14. #include "llvm/Support/ConvertUTF.h"
  15. #include "llvm/Support/Endian.h"
  16. #include "llvm/Support/EndianStream.h"
  17. #include "llvm/Support/FileSystem.h"
  18. #include "llvm/Support/MemoryBuffer.h"
  19. #include "llvm/Support/Path.h"
  20. #include "llvm/Support/Process.h"
  21. using namespace llvm::support;
  22. // Take an expression returning llvm::Error and forward the error if it exists.
  23. #define RETURN_IF_ERROR(Expr) \
  24. if (auto Err = (Expr)) \
  25. return Err;
  26. namespace llvm {
  27. namespace rc {
  28. // Class that employs RAII to save the current FileWriter object state
  29. // and revert to it as soon as we leave the scope. This is useful if resources
  30. // declare their own resource-local statements.
  31. class ContextKeeper {
  32. ResourceFileWriter *FileWriter;
  33. ResourceFileWriter::ObjectInfo SavedInfo;
  34. public:
  35. ContextKeeper(ResourceFileWriter *V)
  36. : FileWriter(V), SavedInfo(V->ObjectData) {}
  37. ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
  38. };
  39. static Error createError(const Twine &Message,
  40. std::errc Type = std::errc::invalid_argument) {
  41. return make_error<StringError>(Message, std::make_error_code(Type));
  42. }
  43. static Error checkNumberFits(uint32_t Number, size_t MaxBits,
  44. const Twine &FieldName) {
  45. assert(1 <= MaxBits && MaxBits <= 32);
  46. if (!(Number >> MaxBits))
  47. return Error::success();
  48. return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
  49. Twine(MaxBits) + " bits.",
  50. std::errc::value_too_large);
  51. }
  52. template <typename FitType>
  53. static Error checkNumberFits(uint32_t Number, const Twine &FieldName) {
  54. return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
  55. }
  56. // A similar function for signed integers.
  57. template <typename FitType>
  58. static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName,
  59. bool CanBeNegative) {
  60. int32_t SignedNum = Number;
  61. if (SignedNum < std::numeric_limits<FitType>::min() ||
  62. SignedNum > std::numeric_limits<FitType>::max())
  63. return createError(FieldName + " (" + Twine(SignedNum) +
  64. ") does not fit in " + Twine(sizeof(FitType) * 8) +
  65. "-bit signed integer type.",
  66. std::errc::value_too_large);
  67. if (!CanBeNegative && SignedNum < 0)
  68. return createError(FieldName + " (" + Twine(SignedNum) +
  69. ") cannot be negative.");
  70. return Error::success();
  71. }
  72. static Error checkRCInt(RCInt Number, const Twine &FieldName) {
  73. if (Number.isLong())
  74. return Error::success();
  75. return checkNumberFits<uint16_t>(Number, FieldName);
  76. }
  77. static Error checkIntOrString(IntOrString Value, const Twine &FieldName) {
  78. if (!Value.isInt())
  79. return Error::success();
  80. return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
  81. }
  82. static bool stripQuotes(StringRef &Str, bool &IsLongString) {
  83. if (!Str.contains('"'))
  84. return false;
  85. // Just take the contents of the string, checking if it's been marked long.
  86. IsLongString = Str.startswith_insensitive("L");
  87. if (IsLongString)
  88. Str = Str.drop_front();
  89. bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
  90. (void)StripSuccess;
  91. assert(StripSuccess && "Strings should be enclosed in quotes.");
  92. return true;
  93. }
  94. static UTF16 cp1252ToUnicode(unsigned char C) {
  95. static const UTF16 Map80[] = {
  96. 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
  97. 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
  98. 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
  99. 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
  100. };
  101. if (C >= 0x80 && C <= 0x9F)
  102. return Map80[C - 0x80];
  103. return C;
  104. }
  105. // Describes a way to handle '\0' characters when processing the string.
  106. // rc.exe tool sometimes behaves in a weird way in postprocessing.
  107. // If the string to be output is equivalent to a C-string (e.g. in MENU
  108. // titles), string is (predictably) truncated after first 0-byte.
  109. // When outputting a string table, the behavior is equivalent to appending
  110. // '\0\0' at the end of the string, and then stripping the string
  111. // before the first '\0\0' occurrence.
  112. // Finally, when handling strings in user-defined resources, 0-bytes
  113. // aren't stripped, nor do they terminate the string.
  114. enum class NullHandlingMethod {
  115. UserResource, // Don't terminate string on '\0'.
  116. CutAtNull, // Terminate string on '\0'.
  117. CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
  118. };
  119. // Parses an identifier or string and returns a processed version of it:
  120. // * Strip the string boundary quotes.
  121. // * Convert the input code page characters to UTF16.
  122. // * Squash "" to a single ".
  123. // * Replace the escape sequences with their processed version.
  124. // For identifiers, this is no-op.
  125. static Error processString(StringRef Str, NullHandlingMethod NullHandler,
  126. bool &IsLongString, SmallVectorImpl<UTF16> &Result,
  127. int CodePage) {
  128. bool IsString = stripQuotes(Str, IsLongString);
  129. SmallVector<UTF16, 128> Chars;
  130. // Convert the input bytes according to the chosen codepage.
  131. if (CodePage == CpUtf8) {
  132. convertUTF8ToUTF16String(Str, Chars);
  133. } else if (CodePage == CpWin1252) {
  134. for (char C : Str)
  135. Chars.push_back(cp1252ToUnicode((unsigned char)C));
  136. } else {
  137. // For other, unknown codepages, only allow plain ASCII input.
  138. for (char C : Str) {
  139. if ((unsigned char)C > 0x7F)
  140. return createError("Non-ASCII 8-bit codepoint (" + Twine(C) +
  141. ") can't be interpreted in the current codepage");
  142. Chars.push_back((unsigned char)C);
  143. }
  144. }
  145. if (!IsString) {
  146. // It's an identifier if it's not a string. Make all characters uppercase.
  147. for (UTF16 &Ch : Chars) {
  148. assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII");
  149. Ch = toupper(Ch);
  150. }
  151. Result.swap(Chars);
  152. return Error::success();
  153. }
  154. Result.reserve(Chars.size());
  155. size_t Pos = 0;
  156. auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error {
  157. if (!IsLongString) {
  158. if (NullHandler == NullHandlingMethod::UserResource) {
  159. // Narrow strings in user-defined resources are *not* output in
  160. // UTF-16 format.
  161. if (Char > 0xFF)
  162. return createError("Non-8-bit codepoint (" + Twine(Char) +
  163. ") can't occur in a user-defined narrow string");
  164. }
  165. }
  166. Result.push_back(Char);
  167. return Error::success();
  168. };
  169. auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error {
  170. if (!IsLongString) {
  171. // Escaped chars in narrow strings have to be interpreted according to
  172. // the chosen code page.
  173. if (Char > 0xFF)
  174. return createError("Non-8-bit escaped char (" + Twine(Char) +
  175. ") can't occur in narrow string");
  176. if (CodePage == CpUtf8) {
  177. if (Char >= 0x80)
  178. return createError("Unable to interpret single byte (" + Twine(Char) +
  179. ") as UTF-8");
  180. } else if (CodePage == CpWin1252) {
  181. Char = cp1252ToUnicode(Char);
  182. } else {
  183. // Unknown/unsupported codepage, only allow ASCII input.
  184. if (Char > 0x7F)
  185. return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) +
  186. ") can't "
  187. "occur in a non-Unicode string");
  188. }
  189. }
  190. return AddRes(Char);
  191. };
  192. while (Pos < Chars.size()) {
  193. UTF16 CurChar = Chars[Pos];
  194. ++Pos;
  195. // Strip double "".
  196. if (CurChar == '"') {
  197. if (Pos == Chars.size() || Chars[Pos] != '"')
  198. return createError("Expected \"\"");
  199. ++Pos;
  200. RETURN_IF_ERROR(AddRes('"'));
  201. continue;
  202. }
  203. if (CurChar == '\\') {
  204. UTF16 TypeChar = Chars[Pos];
  205. ++Pos;
  206. if (TypeChar == 'x' || TypeChar == 'X') {
  207. // Read a hex number. Max number of characters to read differs between
  208. // narrow and wide strings.
  209. UTF16 ReadInt = 0;
  210. size_t RemainingChars = IsLongString ? 4 : 2;
  211. // We don't want to read non-ASCII hex digits. std:: functions past
  212. // 0xFF invoke UB.
  213. //
  214. // FIXME: actually, Microsoft version probably doesn't check this
  215. // condition and uses their Unicode version of 'isxdigit'. However,
  216. // there are some hex-digit Unicode character outside of ASCII, and
  217. // some of these are actually accepted by rc.exe, the notable example
  218. // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written
  219. // instead of ASCII digits in \x... escape sequence and get accepted.
  220. // However, the resulting hexcodes seem totally unpredictable.
  221. // We think it's infeasible to try to reproduce this behavior, nor to
  222. // put effort in order to detect it.
  223. while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) {
  224. if (!isxdigit(Chars[Pos]))
  225. break;
  226. char Digit = tolower(Chars[Pos]);
  227. ++Pos;
  228. ReadInt <<= 4;
  229. if (isdigit(Digit))
  230. ReadInt |= Digit - '0';
  231. else
  232. ReadInt |= Digit - 'a' + 10;
  233. --RemainingChars;
  234. }
  235. RETURN_IF_ERROR(AddEscapedChar(ReadInt));
  236. continue;
  237. }
  238. if (TypeChar >= '0' && TypeChar < '8') {
  239. // Read an octal number. Note that we've already read the first digit.
  240. UTF16 ReadInt = TypeChar - '0';
  241. size_t RemainingChars = IsLongString ? 6 : 2;
  242. while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' &&
  243. Chars[Pos] < '8') {
  244. ReadInt <<= 3;
  245. ReadInt |= Chars[Pos] - '0';
  246. --RemainingChars;
  247. ++Pos;
  248. }
  249. RETURN_IF_ERROR(AddEscapedChar(ReadInt));
  250. continue;
  251. }
  252. switch (TypeChar) {
  253. case 'A':
  254. case 'a':
  255. // Windows '\a' translates into '\b' (Backspace).
  256. RETURN_IF_ERROR(AddRes('\b'));
  257. break;
  258. case 'n': // Somehow, RC doesn't recognize '\N' and '\R'.
  259. RETURN_IF_ERROR(AddRes('\n'));
  260. break;
  261. case 'r':
  262. RETURN_IF_ERROR(AddRes('\r'));
  263. break;
  264. case 'T':
  265. case 't':
  266. RETURN_IF_ERROR(AddRes('\t'));
  267. break;
  268. case '\\':
  269. RETURN_IF_ERROR(AddRes('\\'));
  270. break;
  271. case '"':
  272. // RC accepts \" only if another " comes afterwards; then, \"" means
  273. // a single ".
  274. if (Pos == Chars.size() || Chars[Pos] != '"')
  275. return createError("Expected \\\"\"");
  276. ++Pos;
  277. RETURN_IF_ERROR(AddRes('"'));
  278. break;
  279. default:
  280. // If TypeChar means nothing, \ is should be output to stdout with
  281. // following char. However, rc.exe consumes these characters when
  282. // dealing with wide strings.
  283. if (!IsLongString) {
  284. RETURN_IF_ERROR(AddRes('\\'));
  285. RETURN_IF_ERROR(AddRes(TypeChar));
  286. }
  287. break;
  288. }
  289. continue;
  290. }
  291. // If nothing interesting happens, just output the character.
  292. RETURN_IF_ERROR(AddRes(CurChar));
  293. }
  294. switch (NullHandler) {
  295. case NullHandlingMethod::CutAtNull:
  296. for (size_t Pos = 0; Pos < Result.size(); ++Pos)
  297. if (Result[Pos] == '\0')
  298. Result.resize(Pos);
  299. break;
  300. case NullHandlingMethod::CutAtDoubleNull:
  301. for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos)
  302. if (Result[Pos] == '\0' && Result[Pos + 1] == '\0')
  303. Result.resize(Pos);
  304. if (Result.size() > 0 && Result.back() == '\0')
  305. Result.pop_back();
  306. break;
  307. case NullHandlingMethod::UserResource:
  308. break;
  309. }
  310. return Error::success();
  311. }
  312. uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
  313. uint64_t Result = tell();
  314. FS->write((const char *)Data.begin(), Data.size());
  315. return Result;
  316. }
  317. Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
  318. SmallVector<UTF16, 128> ProcessedString;
  319. bool IsLongString;
  320. RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
  321. IsLongString, ProcessedString,
  322. Params.CodePage));
  323. for (auto Ch : ProcessedString)
  324. writeInt<uint16_t>(Ch);
  325. if (WriteTerminator)
  326. writeInt<uint16_t>(0);
  327. return Error::success();
  328. }
  329. Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
  330. return writeIntOrString(Ident);
  331. }
  332. Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
  333. if (!Value.isInt())
  334. return writeCString(Value.getString());
  335. writeInt<uint16_t>(0xFFFF);
  336. writeInt<uint16_t>(Value.getInt());
  337. return Error::success();
  338. }
  339. void ResourceFileWriter::writeRCInt(RCInt Value) {
  340. if (Value.isLong())
  341. writeInt<uint32_t>(Value);
  342. else
  343. writeInt<uint16_t>(Value);
  344. }
  345. Error ResourceFileWriter::appendFile(StringRef Filename) {
  346. bool IsLong;
  347. stripQuotes(Filename, IsLong);
  348. auto File = loadFile(Filename);
  349. if (!File)
  350. return File.takeError();
  351. *FS << (*File)->getBuffer();
  352. return Error::success();
  353. }
  354. void ResourceFileWriter::padStream(uint64_t Length) {
  355. assert(Length > 0);
  356. uint64_t Location = tell();
  357. Location %= Length;
  358. uint64_t Pad = (Length - Location) % Length;
  359. for (uint64_t i = 0; i < Pad; ++i)
  360. writeInt<uint8_t>(0);
  361. }
  362. Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) {
  363. if (Err)
  364. return joinErrors(createError("Error in " + Res->getResourceTypeName() +
  365. " statement (ID " + Twine(Res->ResName) +
  366. "): "),
  367. std::move(Err));
  368. return Error::success();
  369. }
  370. Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
  371. return writeResource(Res, &ResourceFileWriter::writeNullBody);
  372. }
  373. Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
  374. return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
  375. }
  376. Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) {
  377. return writeResource(Res, &ResourceFileWriter::writeBitmapBody);
  378. }
  379. Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
  380. return handleError(visitIconOrCursorResource(Res), Res);
  381. }
  382. Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
  383. return writeResource(Res, &ResourceFileWriter::writeDialogBody);
  384. }
  385. Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
  386. return handleError(visitIconOrCursorResource(Res), Res);
  387. }
  388. Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
  389. ObjectData.Caption = Stmt->Value;
  390. return Error::success();
  391. }
  392. Error ResourceFileWriter::visitClassStmt(const ClassStmt *Stmt) {
  393. ObjectData.Class = Stmt->Value;
  394. return Error::success();
  395. }
  396. Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
  397. return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
  398. }
  399. Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
  400. return writeResource(Res, &ResourceFileWriter::writeMenuBody);
  401. }
  402. Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
  403. const auto *Res = cast<StringTableResource>(Base);
  404. ContextKeeper RAII(this);
  405. RETURN_IF_ERROR(Res->applyStmts(this));
  406. for (auto &String : Res->Table) {
  407. RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"));
  408. uint16_t BundleID = String.first >> 4;
  409. StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
  410. auto &BundleData = StringTableData.BundleData;
  411. auto Iter = BundleData.find(Key);
  412. if (Iter == BundleData.end()) {
  413. // Need to create a bundle.
  414. StringTableData.BundleList.push_back(Key);
  415. auto EmplaceResult = BundleData.emplace(
  416. Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags));
  417. assert(EmplaceResult.second && "Could not create a bundle");
  418. Iter = EmplaceResult.first;
  419. }
  420. RETURN_IF_ERROR(
  421. insertStringIntoBundle(Iter->second, String.first, String.second));
  422. }
  423. return Error::success();
  424. }
  425. Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
  426. return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
  427. }
  428. Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
  429. return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
  430. }
  431. Error ResourceFileWriter::visitCharacteristicsStmt(
  432. const CharacteristicsStmt *Stmt) {
  433. ObjectData.Characteristics = Stmt->Value;
  434. return Error::success();
  435. }
  436. Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) {
  437. ObjectData.ExStyle = Stmt->Value;
  438. return Error::success();
  439. }
  440. Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
  441. RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
  442. RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
  443. RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
  444. ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
  445. Stmt->Charset};
  446. ObjectData.Font.emplace(Font);
  447. return Error::success();
  448. }
  449. Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
  450. RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
  451. RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
  452. ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
  453. return Error::success();
  454. }
  455. Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
  456. ObjectData.Style = Stmt->Value;
  457. return Error::success();
  458. }
  459. Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
  460. ObjectData.VersionInfo = Stmt->Value;
  461. return Error::success();
  462. }
  463. Error ResourceFileWriter::writeResource(
  464. const RCResource *Res,
  465. Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
  466. // We don't know the sizes yet.
  467. object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
  468. uint64_t HeaderLoc = writeObject(HeaderPrefix);
  469. auto ResType = Res->getResourceType();
  470. RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
  471. RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
  472. RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
  473. RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
  474. // Apply the resource-local optional statements.
  475. ContextKeeper RAII(this);
  476. RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
  477. padStream(sizeof(uint32_t));
  478. object::WinResHeaderSuffix HeaderSuffix{
  479. ulittle32_t(0), // DataVersion; seems to always be 0
  480. ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo),
  481. ulittle32_t(ObjectData.VersionInfo),
  482. ulittle32_t(ObjectData.Characteristics)};
  483. writeObject(HeaderSuffix);
  484. uint64_t DataLoc = tell();
  485. RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
  486. // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
  487. // Update the sizes.
  488. HeaderPrefix.DataSize = tell() - DataLoc;
  489. HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
  490. writeObjectAt(HeaderPrefix, HeaderLoc);
  491. padStream(sizeof(uint32_t));
  492. return Error::success();
  493. }
  494. // --- NullResource helpers. --- //
  495. Error ResourceFileWriter::writeNullBody(const RCResource *) {
  496. return Error::success();
  497. }
  498. // --- AcceleratorsResource helpers. --- //
  499. Error ResourceFileWriter::writeSingleAccelerator(
  500. const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
  501. using Accelerator = AcceleratorsResource::Accelerator;
  502. using Opt = Accelerator::Options;
  503. struct AccelTableEntry {
  504. ulittle16_t Flags;
  505. ulittle16_t ANSICode;
  506. ulittle16_t Id;
  507. uint16_t Padding;
  508. } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
  509. bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
  510. // Remove ASCII flags (which doesn't occur in .res files).
  511. Entry.Flags = Obj.Flags & ~Opt::ASCII;
  512. if (IsLastItem)
  513. Entry.Flags |= 0x80;
  514. RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
  515. Entry.Id = ulittle16_t(Obj.Id);
  516. auto createAccError = [&Obj](const char *Msg) {
  517. return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
  518. };
  519. if (IsASCII && IsVirtKey)
  520. return createAccError("Accelerator can't be both ASCII and VIRTKEY");
  521. if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
  522. return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
  523. " accelerators");
  524. if (Obj.Event.isInt()) {
  525. if (!IsASCII && !IsVirtKey)
  526. return createAccError(
  527. "Accelerator with a numeric event must be either ASCII"
  528. " or VIRTKEY");
  529. uint32_t EventVal = Obj.Event.getInt();
  530. RETURN_IF_ERROR(
  531. checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
  532. Entry.ANSICode = ulittle16_t(EventVal);
  533. writeObject(Entry);
  534. return Error::success();
  535. }
  536. StringRef Str = Obj.Event.getString();
  537. bool IsWide;
  538. stripQuotes(Str, IsWide);
  539. if (Str.size() == 0 || Str.size() > 2)
  540. return createAccError(
  541. "Accelerator string events should have length 1 or 2");
  542. if (Str[0] == '^') {
  543. if (Str.size() == 1)
  544. return createAccError("No character following '^' in accelerator event");
  545. if (IsVirtKey)
  546. return createAccError(
  547. "VIRTKEY accelerator events can't be preceded by '^'");
  548. char Ch = Str[1];
  549. if (Ch >= 'a' && Ch <= 'z')
  550. Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
  551. else if (Ch >= 'A' && Ch <= 'Z')
  552. Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
  553. else
  554. return createAccError("Control character accelerator event should be"
  555. " alphabetic");
  556. writeObject(Entry);
  557. return Error::success();
  558. }
  559. if (Str.size() == 2)
  560. return createAccError("Event string should be one-character, possibly"
  561. " preceded by '^'");
  562. uint8_t EventCh = Str[0];
  563. // The original tool just warns in this situation. We chose to fail.
  564. if (IsVirtKey && !isalnum(EventCh))
  565. return createAccError("Non-alphanumeric characters cannot describe virtual"
  566. " keys");
  567. if (EventCh > 0x7F)
  568. return createAccError("Non-ASCII description of accelerator");
  569. if (IsVirtKey)
  570. EventCh = toupper(EventCh);
  571. Entry.ANSICode = ulittle16_t(EventCh);
  572. writeObject(Entry);
  573. return Error::success();
  574. }
  575. Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
  576. auto *Res = cast<AcceleratorsResource>(Base);
  577. size_t AcceleratorId = 0;
  578. for (auto &Acc : Res->Accelerators) {
  579. ++AcceleratorId;
  580. RETURN_IF_ERROR(
  581. writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
  582. }
  583. return Error::success();
  584. }
  585. // --- BitmapResource helpers. --- //
  586. Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
  587. StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
  588. bool IsLong;
  589. stripQuotes(Filename, IsLong);
  590. auto File = loadFile(Filename);
  591. if (!File)
  592. return File.takeError();
  593. StringRef Buffer = (*File)->getBuffer();
  594. // Skip the 14 byte BITMAPFILEHEADER.
  595. constexpr size_t BITMAPFILEHEADER_size = 14;
  596. if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
  597. Buffer[1] != 'M')
  598. return createError("Incorrect bitmap file.");
  599. *FS << Buffer.substr(BITMAPFILEHEADER_size);
  600. return Error::success();
  601. }
  602. // --- CursorResource and IconResource helpers. --- //
  603. // ICONRESDIR structure. Describes a single icon in resource group.
  604. //
  605. // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
  606. struct IconResDir {
  607. uint8_t Width;
  608. uint8_t Height;
  609. uint8_t ColorCount;
  610. uint8_t Reserved;
  611. };
  612. // CURSORDIR structure. Describes a single cursor in resource group.
  613. //
  614. // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
  615. struct CursorDir {
  616. ulittle16_t Width;
  617. ulittle16_t Height;
  618. };
  619. // RESDIRENTRY structure, stripped from the last item. Stripping made
  620. // for compatibility with RESDIR.
  621. //
  622. // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
  623. struct ResourceDirEntryStart {
  624. union {
  625. CursorDir Cursor; // Used in CURSOR resources.
  626. IconResDir Icon; // Used in .ico and .cur files, and ICON resources.
  627. };
  628. ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource).
  629. ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
  630. ulittle32_t Size;
  631. // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only).
  632. // ulittle16_t IconID; // Resource icon ID (RESDIR only).
  633. };
  634. // BITMAPINFOHEADER structure. Describes basic information about the bitmap
  635. // being read.
  636. //
  637. // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
  638. struct BitmapInfoHeader {
  639. ulittle32_t Size;
  640. ulittle32_t Width;
  641. ulittle32_t Height;
  642. ulittle16_t Planes;
  643. ulittle16_t BitCount;
  644. ulittle32_t Compression;
  645. ulittle32_t SizeImage;
  646. ulittle32_t XPelsPerMeter;
  647. ulittle32_t YPelsPerMeter;
  648. ulittle32_t ClrUsed;
  649. ulittle32_t ClrImportant;
  650. };
  651. // Group icon directory header. Called ICONDIR in .ico/.cur files and
  652. // NEWHEADER in .res files.
  653. //
  654. // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
  655. struct GroupIconDir {
  656. ulittle16_t Reserved; // Always 0.
  657. ulittle16_t ResType; // 1 for icons, 2 for cursors.
  658. ulittle16_t ResCount; // Number of items.
  659. };
  660. enum class IconCursorGroupType { Icon, Cursor };
  661. class SingleIconCursorResource : public RCResource {
  662. public:
  663. IconCursorGroupType Type;
  664. const ResourceDirEntryStart &Header;
  665. ArrayRef<uint8_t> Image;
  666. SingleIconCursorResource(IconCursorGroupType ResourceType,
  667. const ResourceDirEntryStart &HeaderEntry,
  668. ArrayRef<uint8_t> ImageData, uint16_t Flags)
  669. : RCResource(Flags), Type(ResourceType), Header(HeaderEntry),
  670. Image(ImageData) {}
  671. Twine getResourceTypeName() const override { return "Icon/cursor image"; }
  672. IntOrString getResourceType() const override {
  673. return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
  674. }
  675. ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
  676. static bool classof(const RCResource *Res) {
  677. return Res->getKind() == RkSingleCursorOrIconRes;
  678. }
  679. };
  680. class IconCursorGroupResource : public RCResource {
  681. public:
  682. IconCursorGroupType Type;
  683. GroupIconDir Header;
  684. std::vector<ResourceDirEntryStart> ItemEntries;
  685. IconCursorGroupResource(IconCursorGroupType ResourceType,
  686. const GroupIconDir &HeaderData,
  687. std::vector<ResourceDirEntryStart> &&Entries)
  688. : Type(ResourceType), Header(HeaderData),
  689. ItemEntries(std::move(Entries)) {}
  690. Twine getResourceTypeName() const override { return "Icon/cursor group"; }
  691. IntOrString getResourceType() const override {
  692. return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
  693. }
  694. ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
  695. static bool classof(const RCResource *Res) {
  696. return Res->getKind() == RkCursorOrIconGroupRes;
  697. }
  698. };
  699. Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
  700. auto *Res = cast<SingleIconCursorResource>(Base);
  701. if (Res->Type == IconCursorGroupType::Cursor) {
  702. // In case of cursors, two WORDS are appended to the beginning
  703. // of the resource: HotspotX (Planes in RESDIRENTRY),
  704. // and HotspotY (BitCount).
  705. //
  706. // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
  707. // (Remarks section).
  708. writeObject(Res->Header.Planes);
  709. writeObject(Res->Header.BitCount);
  710. }
  711. writeObject(Res->Image);
  712. return Error::success();
  713. }
  714. Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
  715. auto *Res = cast<IconCursorGroupResource>(Base);
  716. writeObject(Res->Header);
  717. for (auto Item : Res->ItemEntries) {
  718. writeObject(Item);
  719. writeInt(IconCursorID++);
  720. }
  721. return Error::success();
  722. }
  723. Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
  724. return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
  725. }
  726. Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
  727. return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
  728. }
  729. Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
  730. IconCursorGroupType Type;
  731. StringRef FileStr;
  732. IntOrString ResName = Base->ResName;
  733. if (auto *IconRes = dyn_cast<IconResource>(Base)) {
  734. FileStr = IconRes->IconLoc;
  735. Type = IconCursorGroupType::Icon;
  736. } else {
  737. auto *CursorRes = dyn_cast<CursorResource>(Base);
  738. FileStr = CursorRes->CursorLoc;
  739. Type = IconCursorGroupType::Cursor;
  740. }
  741. bool IsLong;
  742. stripQuotes(FileStr, IsLong);
  743. auto File = loadFile(FileStr);
  744. if (!File)
  745. return File.takeError();
  746. BinaryStreamReader Reader((*File)->getBuffer(), support::little);
  747. // Read the file headers.
  748. // - At the beginning, ICONDIR/NEWHEADER header.
  749. // - Then, a number of RESDIR headers follow. These contain offsets
  750. // to data.
  751. const GroupIconDir *Header;
  752. RETURN_IF_ERROR(Reader.readObject(Header));
  753. if (Header->Reserved != 0)
  754. return createError("Incorrect icon/cursor Reserved field; should be 0.");
  755. uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
  756. if (Header->ResType != NeededType)
  757. return createError("Incorrect icon/cursor ResType field; should be " +
  758. Twine(NeededType) + ".");
  759. uint16_t NumItems = Header->ResCount;
  760. // Read single ico/cur headers.
  761. std::vector<ResourceDirEntryStart> ItemEntries;
  762. ItemEntries.reserve(NumItems);
  763. std::vector<uint32_t> ItemOffsets(NumItems);
  764. for (size_t ID = 0; ID < NumItems; ++ID) {
  765. const ResourceDirEntryStart *Object;
  766. RETURN_IF_ERROR(Reader.readObject(Object));
  767. ItemEntries.push_back(*Object);
  768. RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
  769. }
  770. // Now write each icon/cursors one by one. At first, all the contents
  771. // without ICO/CUR header. This is described by SingleIconCursorResource.
  772. for (size_t ID = 0; ID < NumItems; ++ID) {
  773. // Load the fragment of file.
  774. Reader.setOffset(ItemOffsets[ID]);
  775. ArrayRef<uint8_t> Image;
  776. RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
  777. SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image,
  778. Base->MemoryFlags);
  779. SingleRes.setName(IconCursorID + ID);
  780. RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
  781. }
  782. // Now, write all the headers concatenated into a separate resource.
  783. for (size_t ID = 0; ID < NumItems; ++ID) {
  784. // We need to rewrite the cursor headers, and fetch actual values
  785. // for Planes/BitCount.
  786. const auto &OldHeader = ItemEntries[ID];
  787. ResourceDirEntryStart NewHeader = OldHeader;
  788. if (Type == IconCursorGroupType::Cursor) {
  789. NewHeader.Cursor.Width = OldHeader.Icon.Width;
  790. // Each cursor in fact stores two bitmaps, one under another.
  791. // Height provided in cursor definition describes the height of the
  792. // cursor, whereas the value existing in resource definition describes
  793. // the height of the bitmap. Therefore, we need to double this height.
  794. NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
  795. // Two WORDs were written at the beginning of the resource (hotspot
  796. // location). This is reflected in Size field.
  797. NewHeader.Size += 2 * sizeof(uint16_t);
  798. }
  799. // Now, we actually need to read the bitmap header to find
  800. // the number of planes and the number of bits per pixel.
  801. Reader.setOffset(ItemOffsets[ID]);
  802. const BitmapInfoHeader *BMPHeader;
  803. RETURN_IF_ERROR(Reader.readObject(BMPHeader));
  804. if (BMPHeader->Size == sizeof(BitmapInfoHeader)) {
  805. NewHeader.Planes = BMPHeader->Planes;
  806. NewHeader.BitCount = BMPHeader->BitCount;
  807. } else {
  808. // A PNG .ico file.
  809. // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
  810. // "The image must be in 32bpp"
  811. NewHeader.Planes = 1;
  812. NewHeader.BitCount = 32;
  813. }
  814. ItemEntries[ID] = NewHeader;
  815. }
  816. IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
  817. HeaderRes.setName(ResName);
  818. if (Base->MemoryFlags & MfPreload) {
  819. HeaderRes.MemoryFlags |= MfPreload;
  820. HeaderRes.MemoryFlags &= ~MfPure;
  821. }
  822. RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
  823. return Error::success();
  824. }
  825. // --- DialogResource helpers. --- //
  826. Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
  827. bool IsExtended) {
  828. // Each control should be aligned to DWORD.
  829. padStream(sizeof(uint32_t));
  830. auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
  831. IntWithNotMask CtlStyle(TypeInfo.Style);
  832. CtlStyle |= Ctl.Style.getValueOr(RCInt(0));
  833. uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
  834. // DIALOG(EX) item header prefix.
  835. if (!IsExtended) {
  836. struct {
  837. ulittle32_t Style;
  838. ulittle32_t ExtStyle;
  839. } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)};
  840. writeObject(Prefix);
  841. } else {
  842. struct {
  843. ulittle32_t HelpID;
  844. ulittle32_t ExtStyle;
  845. ulittle32_t Style;
  846. } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
  847. ulittle32_t(CtlStyle.getValue())};
  848. writeObject(Prefix);
  849. }
  850. // Common fixed-length part.
  851. RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
  852. Ctl.X, "Dialog control x-coordinate", true));
  853. RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
  854. Ctl.Y, "Dialog control y-coordinate", true));
  855. RETURN_IF_ERROR(
  856. checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
  857. RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
  858. Ctl.Height, "Dialog control height", false));
  859. struct {
  860. ulittle16_t X;
  861. ulittle16_t Y;
  862. ulittle16_t Width;
  863. ulittle16_t Height;
  864. } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
  865. ulittle16_t(Ctl.Height)};
  866. writeObject(Middle);
  867. // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
  868. if (!IsExtended) {
  869. // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
  870. // want to refer to later.
  871. if (Ctl.ID != static_cast<uint32_t>(-1))
  872. RETURN_IF_ERROR(checkNumberFits<uint16_t>(
  873. Ctl.ID, "Control ID in simple DIALOG resource"));
  874. writeInt<uint16_t>(Ctl.ID);
  875. } else {
  876. writeInt<uint32_t>(Ctl.ID);
  877. }
  878. // Window class - either 0xFFFF + 16-bit integer or a string.
  879. RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
  880. // Element caption/reference ID. ID is preceded by 0xFFFF.
  881. RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
  882. RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
  883. // # bytes of extra creation data count. Don't pass any.
  884. writeInt<uint16_t>(0);
  885. return Error::success();
  886. }
  887. Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
  888. auto *Res = cast<DialogResource>(Base);
  889. // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
  890. const uint32_t DefaultStyle = 0x80880000;
  891. const uint32_t StyleFontFlag = 0x40;
  892. const uint32_t StyleCaptionFlag = 0x00C00000;
  893. uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
  894. if (ObjectData.Font)
  895. UsedStyle |= StyleFontFlag;
  896. else
  897. UsedStyle &= ~StyleFontFlag;
  898. // Actually, in case of empty (but existent) caption, the examined field
  899. // is equal to "\"\"". That's why empty captions are still noticed.
  900. if (ObjectData.Caption != "")
  901. UsedStyle |= StyleCaptionFlag;
  902. const uint16_t DialogExMagic = 0xFFFF;
  903. uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0);
  904. // Write DIALOG(EX) header prefix. These are pretty different.
  905. if (!Res->IsExtended) {
  906. // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
  907. // In such a case, whole object (in .res file) is equivalent to a
  908. // DIALOGEX. It might lead to access violation/segmentation fault in
  909. // resource readers. For example,
  910. // 1 DIALOG 0, 0, 0, 65432
  911. // STYLE 0xFFFF0001 {}
  912. // would be compiled to a DIALOGEX with 65432 controls.
  913. if ((UsedStyle >> 16) == DialogExMagic)
  914. return createError("16 higher bits of DIALOG resource style cannot be"
  915. " equal to 0xFFFF");
  916. struct {
  917. ulittle32_t Style;
  918. ulittle32_t ExtStyle;
  919. } Prefix{ulittle32_t(UsedStyle),
  920. ulittle32_t(ExStyle)};
  921. writeObject(Prefix);
  922. } else {
  923. struct {
  924. ulittle16_t Version;
  925. ulittle16_t Magic;
  926. ulittle32_t HelpID;
  927. ulittle32_t ExtStyle;
  928. ulittle32_t Style;
  929. } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
  930. ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)};
  931. writeObject(Prefix);
  932. }
  933. // Now, a common part. First, fixed-length fields.
  934. RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
  935. "Number of dialog controls"));
  936. RETURN_IF_ERROR(
  937. checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
  938. RETURN_IF_ERROR(
  939. checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
  940. RETURN_IF_ERROR(
  941. checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
  942. RETURN_IF_ERROR(
  943. checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
  944. struct {
  945. ulittle16_t Count;
  946. ulittle16_t PosX;
  947. ulittle16_t PosY;
  948. ulittle16_t DialogWidth;
  949. ulittle16_t DialogHeight;
  950. } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
  951. ulittle16_t(Res->Y), ulittle16_t(Res->Width),
  952. ulittle16_t(Res->Height)};
  953. writeObject(Middle);
  954. // MENU field. As of now, we don't keep them in the state and can peacefully
  955. // think there is no menu attached to the dialog.
  956. writeInt<uint16_t>(0);
  957. // Window CLASS field.
  958. RETURN_IF_ERROR(writeIntOrString(ObjectData.Class));
  959. // Window title or a single word equal to 0.
  960. RETURN_IF_ERROR(writeCString(ObjectData.Caption));
  961. // If there *is* a window font declared, output its data.
  962. auto &Font = ObjectData.Font;
  963. if (Font) {
  964. writeInt<uint16_t>(Font->Size);
  965. // Additional description occurs only in DIALOGEX.
  966. if (Res->IsExtended) {
  967. writeInt<uint16_t>(Font->Weight);
  968. writeInt<uint8_t>(Font->IsItalic);
  969. writeInt<uint8_t>(Font->Charset);
  970. }
  971. RETURN_IF_ERROR(writeCString(Font->Typeface));
  972. }
  973. auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
  974. if (!Err)
  975. return Error::success();
  976. return joinErrors(createError("Error in " + Twine(Ctl.Type) +
  977. " control (ID " + Twine(Ctl.ID) + "):"),
  978. std::move(Err));
  979. };
  980. for (auto &Ctl : Res->Controls)
  981. RETURN_IF_ERROR(
  982. handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
  983. return Error::success();
  984. }
  985. // --- HTMLResource helpers. --- //
  986. Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
  987. return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
  988. }
  989. // --- MenuResource helpers. --- //
  990. Error ResourceFileWriter::writeMenuDefinition(
  991. const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
  992. assert(Def);
  993. const MenuDefinition *DefPtr = Def.get();
  994. if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
  995. writeInt<uint16_t>(Flags);
  996. // Some resource files use -1, i.e. UINT32_MAX, for empty menu items.
  997. if (MenuItemPtr->Id != static_cast<uint32_t>(-1))
  998. RETURN_IF_ERROR(
  999. checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
  1000. writeInt<uint16_t>(MenuItemPtr->Id);
  1001. RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
  1002. return Error::success();
  1003. }
  1004. if (isa<MenuSeparator>(DefPtr)) {
  1005. writeInt<uint16_t>(Flags);
  1006. writeInt<uint32_t>(0);
  1007. return Error::success();
  1008. }
  1009. auto *PopupPtr = cast<PopupItem>(DefPtr);
  1010. writeInt<uint16_t>(Flags);
  1011. RETURN_IF_ERROR(writeCString(PopupPtr->Name));
  1012. return writeMenuDefinitionList(PopupPtr->SubItems);
  1013. }
  1014. Error ResourceFileWriter::writeMenuDefinitionList(
  1015. const MenuDefinitionList &List) {
  1016. for (auto &Def : List.Definitions) {
  1017. uint16_t Flags = Def->getResFlags();
  1018. // Last element receives an additional 0x80 flag.
  1019. const uint16_t LastElementFlag = 0x0080;
  1020. if (&Def == &List.Definitions.back())
  1021. Flags |= LastElementFlag;
  1022. RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
  1023. }
  1024. return Error::success();
  1025. }
  1026. Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
  1027. // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
  1028. // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
  1029. writeInt<uint32_t>(0);
  1030. return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
  1031. }
  1032. // --- StringTableResource helpers. --- //
  1033. class BundleResource : public RCResource {
  1034. public:
  1035. using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
  1036. BundleType Bundle;
  1037. BundleResource(const BundleType &StrBundle)
  1038. : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
  1039. IntOrString getResourceType() const override { return 6; }
  1040. ResourceKind getKind() const override { return RkStringTableBundle; }
  1041. static bool classof(const RCResource *Res) {
  1042. return Res->getKind() == RkStringTableBundle;
  1043. }
  1044. Twine getResourceTypeName() const override { return "STRINGTABLE"; }
  1045. };
  1046. Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
  1047. return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
  1048. }
  1049. Error ResourceFileWriter::insertStringIntoBundle(
  1050. StringTableInfo::Bundle &Bundle, uint16_t StringID,
  1051. const std::vector<StringRef> &String) {
  1052. uint16_t StringLoc = StringID & 15;
  1053. if (Bundle.Data[StringLoc])
  1054. return createError("Multiple STRINGTABLE strings located under ID " +
  1055. Twine(StringID));
  1056. Bundle.Data[StringLoc] = String;
  1057. return Error::success();
  1058. }
  1059. Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
  1060. auto *Res = cast<BundleResource>(Base);
  1061. for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
  1062. // The string format is a tiny bit different here. We
  1063. // first output the size of the string, and then the string itself
  1064. // (which is not null-terminated).
  1065. SmallVector<UTF16, 128> Data;
  1066. if (Res->Bundle.Data[ID]) {
  1067. bool IsLongString;
  1068. for (StringRef S : *Res->Bundle.Data[ID])
  1069. RETURN_IF_ERROR(processString(S, NullHandlingMethod::CutAtDoubleNull,
  1070. IsLongString, Data, Params.CodePage));
  1071. if (AppendNull)
  1072. Data.push_back('\0');
  1073. }
  1074. RETURN_IF_ERROR(
  1075. checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
  1076. writeInt<uint16_t>(Data.size());
  1077. for (auto Char : Data)
  1078. writeInt(Char);
  1079. }
  1080. return Error::success();
  1081. }
  1082. Error ResourceFileWriter::dumpAllStringTables() {
  1083. for (auto Key : StringTableData.BundleList) {
  1084. auto Iter = StringTableData.BundleData.find(Key);
  1085. assert(Iter != StringTableData.BundleData.end());
  1086. // For a moment, revert the context info to moment of bundle declaration.
  1087. ContextKeeper RAII(this);
  1088. ObjectData = Iter->second.DeclTimeInfo;
  1089. BundleResource Res(Iter->second);
  1090. // Bundle #(k+1) contains keys [16k, 16k + 15].
  1091. Res.setName(Key.first + 1);
  1092. RETURN_IF_ERROR(visitStringTableBundle(&Res));
  1093. }
  1094. return Error::success();
  1095. }
  1096. // --- UserDefinedResource helpers. --- //
  1097. Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
  1098. auto *Res = cast<UserDefinedResource>(Base);
  1099. if (Res->IsFileResource)
  1100. return appendFile(Res->FileLoc);
  1101. for (auto &Elem : Res->Contents) {
  1102. if (Elem.isInt()) {
  1103. RETURN_IF_ERROR(
  1104. checkRCInt(Elem.getInt(), "Number in user-defined resource"));
  1105. writeRCInt(Elem.getInt());
  1106. continue;
  1107. }
  1108. SmallVector<UTF16, 128> ProcessedString;
  1109. bool IsLongString;
  1110. RETURN_IF_ERROR(
  1111. processString(Elem.getString(), NullHandlingMethod::UserResource,
  1112. IsLongString, ProcessedString, Params.CodePage));
  1113. for (auto Ch : ProcessedString) {
  1114. if (IsLongString) {
  1115. writeInt(Ch);
  1116. continue;
  1117. }
  1118. RETURN_IF_ERROR(checkNumberFits<uint8_t>(
  1119. Ch, "Character in narrow string in user-defined resource"));
  1120. writeInt<uint8_t>(Ch);
  1121. }
  1122. }
  1123. return Error::success();
  1124. }
  1125. // --- VersionInfoResourceResource helpers. --- //
  1126. Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
  1127. // Output the header if the block has name.
  1128. bool OutputHeader = Blk.Name != "";
  1129. uint64_t LengthLoc;
  1130. padStream(sizeof(uint32_t));
  1131. if (OutputHeader) {
  1132. LengthLoc = writeInt<uint16_t>(0);
  1133. writeInt<uint16_t>(0);
  1134. writeInt<uint16_t>(1); // true
  1135. RETURN_IF_ERROR(writeCString(Blk.Name));
  1136. padStream(sizeof(uint32_t));
  1137. }
  1138. for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
  1139. VersionInfoStmt *ItemPtr = Item.get();
  1140. if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
  1141. RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
  1142. continue;
  1143. }
  1144. auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
  1145. RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
  1146. }
  1147. if (OutputHeader) {
  1148. uint64_t CurLoc = tell();
  1149. writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
  1150. }
  1151. return Error::success();
  1152. }
  1153. Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
  1154. // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
  1155. // is a mapping from the key (string) to the value (a sequence of ints or
  1156. // a sequence of strings).
  1157. //
  1158. // If integers are to be written: width of each integer written depends on
  1159. // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
  1160. // ValueLength defined in structure referenced below is then the total
  1161. // number of bytes taken by these integers.
  1162. //
  1163. // If strings are to be written: characters are always WORDs.
  1164. // Moreover, '\0' character is written after the last string, and between
  1165. // every two strings separated by comma (if strings are not comma-separated,
  1166. // they're simply concatenated). ValueLength is equal to the number of WORDs
  1167. // written (that is, half of the bytes written).
  1168. //
  1169. // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
  1170. bool HasStrings = false, HasInts = false;
  1171. for (auto &Item : Val.Values)
  1172. (Item.isInt() ? HasInts : HasStrings) = true;
  1173. assert((HasStrings || HasInts) && "VALUE must have at least one argument");
  1174. if (HasStrings && HasInts)
  1175. return createError(Twine("VALUE ") + Val.Key +
  1176. " cannot contain both strings and integers");
  1177. padStream(sizeof(uint32_t));
  1178. auto LengthLoc = writeInt<uint16_t>(0);
  1179. auto ValLengthLoc = writeInt<uint16_t>(0);
  1180. writeInt<uint16_t>(HasStrings);
  1181. RETURN_IF_ERROR(writeCString(Val.Key));
  1182. padStream(sizeof(uint32_t));
  1183. auto DataLoc = tell();
  1184. for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
  1185. auto &Item = Val.Values[Id];
  1186. if (Item.isInt()) {
  1187. auto Value = Item.getInt();
  1188. RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
  1189. writeRCInt(Value);
  1190. continue;
  1191. }
  1192. bool WriteTerminator =
  1193. Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
  1194. RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
  1195. }
  1196. auto CurLoc = tell();
  1197. auto ValueLength = CurLoc - DataLoc;
  1198. if (HasStrings) {
  1199. assert(ValueLength % 2 == 0);
  1200. ValueLength /= 2;
  1201. }
  1202. writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
  1203. writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
  1204. return Error::success();
  1205. }
  1206. template <typename Ty>
  1207. static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
  1208. const Ty &Default) {
  1209. auto Iter = Map.find(Key);
  1210. if (Iter != Map.end())
  1211. return Iter->getValue();
  1212. return Default;
  1213. }
  1214. Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
  1215. auto *Res = cast<VersionInfoResource>(Base);
  1216. const auto &FixedData = Res->FixedData;
  1217. struct /* VS_FIXEDFILEINFO */ {
  1218. ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
  1219. ulittle32_t StructVersion = ulittle32_t(0x10000);
  1220. // It's weird to have most-significant DWORD first on the little-endian
  1221. // machines, but let it be this way.
  1222. ulittle32_t FileVersionMS;
  1223. ulittle32_t FileVersionLS;
  1224. ulittle32_t ProductVersionMS;
  1225. ulittle32_t ProductVersionLS;
  1226. ulittle32_t FileFlagsMask;
  1227. ulittle32_t FileFlags;
  1228. ulittle32_t FileOS;
  1229. ulittle32_t FileType;
  1230. ulittle32_t FileSubtype;
  1231. // MS implementation seems to always set these fields to 0.
  1232. ulittle32_t FileDateMS = ulittle32_t(0);
  1233. ulittle32_t FileDateLS = ulittle32_t(0);
  1234. } FixedInfo;
  1235. // First, VS_VERSIONINFO.
  1236. auto LengthLoc = writeInt<uint16_t>(0);
  1237. writeInt<uint16_t>(sizeof(FixedInfo));
  1238. writeInt<uint16_t>(0);
  1239. cantFail(writeCString("VS_VERSION_INFO"));
  1240. padStream(sizeof(uint32_t));
  1241. using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
  1242. auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
  1243. static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
  1244. if (!FixedData.IsTypePresent[(int)Type])
  1245. return DefaultOut;
  1246. return FixedData.FixedInfo[(int)Type];
  1247. };
  1248. auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
  1249. RETURN_IF_ERROR(checkNumberFits<uint16_t>(
  1250. *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
  1251. FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
  1252. FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
  1253. auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
  1254. RETURN_IF_ERROR(checkNumberFits<uint16_t>(
  1255. *std::max_element(ProdVer.begin(), ProdVer.end()),
  1256. "PRODUCTVERSION fields"));
  1257. FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
  1258. FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
  1259. FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
  1260. FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
  1261. FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
  1262. FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
  1263. FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
  1264. writeObject(FixedInfo);
  1265. padStream(sizeof(uint32_t));
  1266. RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
  1267. // FIXME: check overflow?
  1268. writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
  1269. return Error::success();
  1270. }
  1271. Expected<std::unique_ptr<MemoryBuffer>>
  1272. ResourceFileWriter::loadFile(StringRef File) const {
  1273. SmallString<128> Path;
  1274. SmallString<128> Cwd;
  1275. std::unique_ptr<MemoryBuffer> Result;
  1276. // 0. The file path is absolute or has a root directory, so we shouldn't
  1277. // try to append it on top of other base directories. (An absolute path
  1278. // must have a root directory, but e.g. the path "\dir\file" on windows
  1279. // isn't considered absolute, but it does have a root directory. As long as
  1280. // sys::path::append doesn't handle appending an absolute path or a path
  1281. // starting with a root directory on top of a base, we must handle this
  1282. // case separately at the top. C++17's path::append handles that case
  1283. // properly though, so if using that to append paths below, this early
  1284. // exception case could be removed.)
  1285. if (sys::path::has_root_directory(File))
  1286. return errorOrToExpected(MemoryBuffer::getFile(
  1287. File, /*IsText=*/false, /*RequiresNullTerminator=*/false));
  1288. // 1. The current working directory.
  1289. sys::fs::current_path(Cwd);
  1290. Path.assign(Cwd.begin(), Cwd.end());
  1291. sys::path::append(Path, File);
  1292. if (sys::fs::exists(Path))
  1293. return errorOrToExpected(MemoryBuffer::getFile(
  1294. Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
  1295. // 2. The directory of the input resource file, if it is different from the
  1296. // current working directory.
  1297. StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
  1298. Path.assign(InputFileDir.begin(), InputFileDir.end());
  1299. sys::path::append(Path, File);
  1300. if (sys::fs::exists(Path))
  1301. return errorOrToExpected(MemoryBuffer::getFile(
  1302. Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
  1303. // 3. All of the include directories specified on the command line.
  1304. for (StringRef ForceInclude : Params.Include) {
  1305. Path.assign(ForceInclude.begin(), ForceInclude.end());
  1306. sys::path::append(Path, File);
  1307. if (sys::fs::exists(Path))
  1308. return errorOrToExpected(MemoryBuffer::getFile(
  1309. Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
  1310. }
  1311. if (!Params.NoInclude) {
  1312. if (auto Result = llvm::sys::Process::FindInEnvPath("INCLUDE", File))
  1313. return errorOrToExpected(MemoryBuffer::getFile(
  1314. *Result, /*IsText=*/false, /*RequiresNullTerminator=*/false));
  1315. }
  1316. return make_error<StringError>("error : file not found : " + Twine(File),
  1317. inconvertibleErrorCode());
  1318. }
  1319. } // namespace rc
  1320. } // namespace llvm