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