xref: /llvm-project/llvm/tools/llvm-rc/ResourceFileWriter.cpp (revision c1a67857ba0a6ba558818b589fe7c0fcc8f238ae)
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 =
486           BundleData.emplace(Key, StringTableInfo::Bundle(ObjectData));
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->getMemoryFlags()), 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)
789       : Type(ResourceType), Header(HeaderEntry), Image(ImageData) {}
790 
791   Twine getResourceTypeName() const override { return "Icon/cursor image"; }
792   IntOrString getResourceType() const override {
793     return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
794   }
795   uint16_t getMemoryFlags() const override {
796     return MfDiscardable | MfMoveable;
797   }
798   ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
799   static bool classof(const RCResource *Res) {
800     return Res->getKind() == RkSingleCursorOrIconRes;
801   }
802 };
803 
804 class IconCursorGroupResource : public RCResource {
805 public:
806   IconCursorGroupType Type;
807   GroupIconDir Header;
808   std::vector<ResourceDirEntryStart> ItemEntries;
809 
810   IconCursorGroupResource(IconCursorGroupType ResourceType,
811                           const GroupIconDir &HeaderData,
812                           std::vector<ResourceDirEntryStart> &&Entries)
813       : Type(ResourceType), Header(HeaderData),
814         ItemEntries(std::move(Entries)) {}
815 
816   Twine getResourceTypeName() const override { return "Icon/cursor group"; }
817   IntOrString getResourceType() const override {
818     return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
819   }
820   ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
821   static bool classof(const RCResource *Res) {
822     return Res->getKind() == RkCursorOrIconGroupRes;
823   }
824 };
825 
826 Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
827   auto *Res = cast<SingleIconCursorResource>(Base);
828   if (Res->Type == IconCursorGroupType::Cursor) {
829     // In case of cursors, two WORDS are appended to the beginning
830     // of the resource: HotspotX (Planes in RESDIRENTRY),
831     // and HotspotY (BitCount).
832     //
833     // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
834     //  (Remarks section).
835     writeObject(Res->Header.Planes);
836     writeObject(Res->Header.BitCount);
837   }
838 
839   writeObject(Res->Image);
840   return Error::success();
841 }
842 
843 Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
844   auto *Res = cast<IconCursorGroupResource>(Base);
845   writeObject(Res->Header);
846   for (auto Item : Res->ItemEntries) {
847     writeObject(Item);
848     writeInt(IconCursorID++);
849   }
850   return Error::success();
851 }
852 
853 Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
854   return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
855 }
856 
857 Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
858   return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
859 }
860 
861 Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
862   IconCursorGroupType Type;
863   StringRef FileStr;
864   IntOrString ResName = Base->ResName;
865 
866   if (auto *IconRes = dyn_cast<IconResource>(Base)) {
867     FileStr = IconRes->IconLoc;
868     Type = IconCursorGroupType::Icon;
869   } else {
870     auto *CursorRes = dyn_cast<CursorResource>(Base);
871     FileStr = CursorRes->CursorLoc;
872     Type = IconCursorGroupType::Cursor;
873   }
874 
875   bool IsLong;
876   stripQuotes(FileStr, IsLong);
877   auto File = loadFile(FileStr);
878 
879   if (!File)
880     return File.takeError();
881 
882   BinaryStreamReader Reader((*File)->getBuffer(), support::little);
883 
884   // Read the file headers.
885   //   - At the beginning, ICONDIR/NEWHEADER header.
886   //   - Then, a number of RESDIR headers follow. These contain offsets
887   //       to data.
888   const GroupIconDir *Header;
889 
890   RETURN_IF_ERROR(Reader.readObject(Header));
891   if (Header->Reserved != 0)
892     return createError("Incorrect icon/cursor Reserved field; should be 0.");
893   uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
894   if (Header->ResType != NeededType)
895     return createError("Incorrect icon/cursor ResType field; should be " +
896                        Twine(NeededType) + ".");
897 
898   uint16_t NumItems = Header->ResCount;
899 
900   // Read single ico/cur headers.
901   std::vector<ResourceDirEntryStart> ItemEntries;
902   ItemEntries.reserve(NumItems);
903   std::vector<uint32_t> ItemOffsets(NumItems);
904   for (size_t ID = 0; ID < NumItems; ++ID) {
905     const ResourceDirEntryStart *Object;
906     RETURN_IF_ERROR(Reader.readObject(Object));
907     ItemEntries.push_back(*Object);
908     RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
909   }
910 
911   // Now write each icon/cursors one by one. At first, all the contents
912   // without ICO/CUR header. This is described by SingleIconCursorResource.
913   for (size_t ID = 0; ID < NumItems; ++ID) {
914     // Load the fragment of file.
915     Reader.setOffset(ItemOffsets[ID]);
916     ArrayRef<uint8_t> Image;
917     RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
918     SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image);
919     SingleRes.setName(IconCursorID + ID);
920     RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
921   }
922 
923   // Now, write all the headers concatenated into a separate resource.
924   for (size_t ID = 0; ID < NumItems; ++ID) {
925     if (Type == IconCursorGroupType::Icon) {
926       // rc.exe seems to always set NumPlanes to 1. No idea why it happens.
927       ItemEntries[ID].Planes = 1;
928       continue;
929     }
930 
931     // We need to rewrite the cursor headers.
932     const auto &OldHeader = ItemEntries[ID];
933     ResourceDirEntryStart NewHeader;
934     NewHeader.Cursor.Width = OldHeader.Icon.Width;
935     // Each cursor in fact stores two bitmaps, one under another.
936     // Height provided in cursor definition describes the height of the
937     // cursor, whereas the value existing in resource definition describes
938     // the height of the bitmap. Therefore, we need to double this height.
939     NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
940 
941     // Now, we actually need to read the bitmap header to find
942     // the number of planes and the number of bits per pixel.
943     Reader.setOffset(ItemOffsets[ID]);
944     const BitmapInfoHeader *BMPHeader;
945     RETURN_IF_ERROR(Reader.readObject(BMPHeader));
946     NewHeader.Planes = BMPHeader->Planes;
947     NewHeader.BitCount = BMPHeader->BitCount;
948 
949     // Two WORDs were written at the beginning of the resource (hotspot
950     // location). This is reflected in Size field.
951     NewHeader.Size = OldHeader.Size + 2 * sizeof(uint16_t);
952 
953     ItemEntries[ID] = NewHeader;
954   }
955 
956   IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
957   HeaderRes.setName(ResName);
958   RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
959 
960   return Error::success();
961 }
962 
963 // --- DialogResource helpers. --- //
964 
965 Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
966                                                    bool IsExtended) {
967   // Each control should be aligned to DWORD.
968   padStream(sizeof(uint32_t));
969 
970   auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
971   uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0);
972   uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
973 
974   // DIALOG(EX) item header prefix.
975   if (!IsExtended) {
976     struct {
977       ulittle32_t Style;
978       ulittle32_t ExtStyle;
979     } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)};
980     writeObject(Prefix);
981   } else {
982     struct {
983       ulittle32_t HelpID;
984       ulittle32_t ExtStyle;
985       ulittle32_t Style;
986     } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
987              ulittle32_t(CtlStyle)};
988     writeObject(Prefix);
989   }
990 
991   // Common fixed-length part.
992   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
993       Ctl.X, "Dialog control x-coordinate", true));
994   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
995       Ctl.Y, "Dialog control y-coordinate", true));
996   RETURN_IF_ERROR(
997       checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
998   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
999       Ctl.Height, "Dialog control height", false));
1000   struct {
1001     ulittle16_t X;
1002     ulittle16_t Y;
1003     ulittle16_t Width;
1004     ulittle16_t Height;
1005   } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
1006            ulittle16_t(Ctl.Height)};
1007   writeObject(Middle);
1008 
1009   // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
1010   if (!IsExtended) {
1011     // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
1012     // want to refer to later.
1013     if (Ctl.ID != static_cast<uint32_t>(-1))
1014       RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1015           Ctl.ID, "Control ID in simple DIALOG resource"));
1016     writeInt<uint16_t>(Ctl.ID);
1017   } else {
1018     writeInt<uint32_t>(Ctl.ID);
1019   }
1020 
1021   // Window class - either 0xFFFF + 16-bit integer or a string.
1022   RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
1023 
1024   // Element caption/reference ID. ID is preceded by 0xFFFF.
1025   RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
1026   RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
1027 
1028   // # bytes of extra creation data count. Don't pass any.
1029   writeInt<uint16_t>(0);
1030 
1031   return Error::success();
1032 }
1033 
1034 Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
1035   auto *Res = cast<DialogResource>(Base);
1036 
1037   // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
1038   const uint32_t DefaultStyle = 0x80880000;
1039   const uint32_t StyleFontFlag = 0x40;
1040   const uint32_t StyleCaptionFlag = 0x00C00000;
1041 
1042   uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
1043   if (ObjectData.Font)
1044     UsedStyle |= StyleFontFlag;
1045   else
1046     UsedStyle &= ~StyleFontFlag;
1047 
1048   // Actually, in case of empty (but existent) caption, the examined field
1049   // is equal to "\"\"". That's why empty captions are still noticed.
1050   if (ObjectData.Caption != "")
1051     UsedStyle |= StyleCaptionFlag;
1052 
1053   const uint16_t DialogExMagic = 0xFFFF;
1054 
1055   // Write DIALOG(EX) header prefix. These are pretty different.
1056   if (!Res->IsExtended) {
1057     // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
1058     // In such a case, whole object (in .res file) is equivalent to a
1059     // DIALOGEX. It might lead to access violation/segmentation fault in
1060     // resource readers. For example,
1061     //   1 DIALOG 0, 0, 0, 65432
1062     //   STYLE 0xFFFF0001 {}
1063     // would be compiled to a DIALOGEX with 65432 controls.
1064     if ((UsedStyle >> 16) == DialogExMagic)
1065       return createError("16 higher bits of DIALOG resource style cannot be"
1066                          " equal to 0xFFFF");
1067 
1068     struct {
1069       ulittle32_t Style;
1070       ulittle32_t ExtStyle;
1071     } Prefix{ulittle32_t(UsedStyle),
1072              ulittle32_t(0)}; // As of now, we don't keep EXSTYLE.
1073 
1074     writeObject(Prefix);
1075   } else {
1076     struct {
1077       ulittle16_t Version;
1078       ulittle16_t Magic;
1079       ulittle32_t HelpID;
1080       ulittle32_t ExtStyle;
1081       ulittle32_t Style;
1082     } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
1083              ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)};
1084 
1085     writeObject(Prefix);
1086   }
1087 
1088   // Now, a common part. First, fixed-length fields.
1089   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
1090                                             "Number of dialog controls"));
1091   RETURN_IF_ERROR(
1092       checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
1093   RETURN_IF_ERROR(
1094       checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
1095   RETURN_IF_ERROR(
1096       checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
1097   RETURN_IF_ERROR(
1098       checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
1099   struct {
1100     ulittle16_t Count;
1101     ulittle16_t PosX;
1102     ulittle16_t PosY;
1103     ulittle16_t DialogWidth;
1104     ulittle16_t DialogHeight;
1105   } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
1106            ulittle16_t(Res->Y), ulittle16_t(Res->Width),
1107            ulittle16_t(Res->Height)};
1108   writeObject(Middle);
1109 
1110   // MENU field. As of now, we don't keep them in the state and can peacefully
1111   // think there is no menu attached to the dialog.
1112   writeInt<uint16_t>(0);
1113 
1114   // Window CLASS field. Not kept here.
1115   writeInt<uint16_t>(0);
1116 
1117   // Window title or a single word equal to 0.
1118   RETURN_IF_ERROR(writeCString(ObjectData.Caption));
1119 
1120   // If there *is* a window font declared, output its data.
1121   auto &Font = ObjectData.Font;
1122   if (Font) {
1123     writeInt<uint16_t>(Font->Size);
1124     // Additional description occurs only in DIALOGEX.
1125     if (Res->IsExtended) {
1126       writeInt<uint16_t>(Font->Weight);
1127       writeInt<uint8_t>(Font->IsItalic);
1128       writeInt<uint8_t>(Font->Charset);
1129     }
1130     RETURN_IF_ERROR(writeCString(Font->Typeface));
1131   }
1132 
1133   auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
1134     if (!Err)
1135       return Error::success();
1136     return joinErrors(createError("Error in " + Twine(Ctl.Type) +
1137                                   " control  (ID " + Twine(Ctl.ID) + "):"),
1138                       std::move(Err));
1139   };
1140 
1141   for (auto &Ctl : Res->Controls)
1142     RETURN_IF_ERROR(
1143         handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
1144 
1145   return Error::success();
1146 }
1147 
1148 // --- HTMLResource helpers. --- //
1149 
1150 Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
1151   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
1152 }
1153 
1154 // --- MenuResource helpers. --- //
1155 
1156 Error ResourceFileWriter::writeMenuDefinition(
1157     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
1158   assert(Def);
1159   const MenuDefinition *DefPtr = Def.get();
1160 
1161   if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
1162     writeInt<uint16_t>(Flags);
1163     RETURN_IF_ERROR(
1164         checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
1165     writeInt<uint16_t>(MenuItemPtr->Id);
1166     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
1167     return Error::success();
1168   }
1169 
1170   if (isa<MenuSeparator>(DefPtr)) {
1171     writeInt<uint16_t>(Flags);
1172     writeInt<uint32_t>(0);
1173     return Error::success();
1174   }
1175 
1176   auto *PopupPtr = cast<PopupItem>(DefPtr);
1177   writeInt<uint16_t>(Flags);
1178   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
1179   return writeMenuDefinitionList(PopupPtr->SubItems);
1180 }
1181 
1182 Error ResourceFileWriter::writeMenuDefinitionList(
1183     const MenuDefinitionList &List) {
1184   for (auto &Def : List.Definitions) {
1185     uint16_t Flags = Def->getResFlags();
1186     // Last element receives an additional 0x80 flag.
1187     const uint16_t LastElementFlag = 0x0080;
1188     if (&Def == &List.Definitions.back())
1189       Flags |= LastElementFlag;
1190 
1191     RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
1192   }
1193   return Error::success();
1194 }
1195 
1196 Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
1197   // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
1198   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
1199   writeInt<uint32_t>(0);
1200 
1201   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
1202 }
1203 
1204 // --- StringTableResource helpers. --- //
1205 
1206 class BundleResource : public RCResource {
1207 public:
1208   using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
1209   BundleType Bundle;
1210 
1211   BundleResource(const BundleType &StrBundle) : Bundle(StrBundle) {}
1212   IntOrString getResourceType() const override { return 6; }
1213 
1214   ResourceKind getKind() const override { return RkStringTableBundle; }
1215   static bool classof(const RCResource *Res) {
1216     return Res->getKind() == RkStringTableBundle;
1217   }
1218   Twine getResourceTypeName() const override { return "STRINGTABLE"; }
1219 };
1220 
1221 Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
1222   return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
1223 }
1224 
1225 Error ResourceFileWriter::insertStringIntoBundle(
1226     StringTableInfo::Bundle &Bundle, uint16_t StringID, StringRef String) {
1227   uint16_t StringLoc = StringID & 15;
1228   if (Bundle.Data[StringLoc])
1229     return createError("Multiple STRINGTABLE strings located under ID " +
1230                        Twine(StringID));
1231   Bundle.Data[StringLoc] = String;
1232   return Error::success();
1233 }
1234 
1235 Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
1236   auto *Res = cast<BundleResource>(Base);
1237   for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
1238     // The string format is a tiny bit different here. We
1239     // first output the size of the string, and then the string itself
1240     // (which is not null-terminated).
1241     bool IsLongString;
1242     SmallVector<UTF16, 128> Data;
1243     RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()),
1244                                   NullHandlingMethod::CutAtDoubleNull,
1245                                   IsLongString, Data, Params.CodePage));
1246     if (AppendNull && Res->Bundle.Data[ID])
1247       Data.push_back('\0');
1248     RETURN_IF_ERROR(
1249         checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
1250     writeInt<uint16_t>(Data.size());
1251     for (auto Char : Data)
1252       writeInt(Char);
1253   }
1254   return Error::success();
1255 }
1256 
1257 Error ResourceFileWriter::dumpAllStringTables() {
1258   for (auto Key : StringTableData.BundleList) {
1259     auto Iter = StringTableData.BundleData.find(Key);
1260     assert(Iter != StringTableData.BundleData.end());
1261 
1262     // For a moment, revert the context info to moment of bundle declaration.
1263     ContextKeeper RAII(this);
1264     ObjectData = Iter->second.DeclTimeInfo;
1265 
1266     BundleResource Res(Iter->second);
1267     // Bundle #(k+1) contains keys [16k, 16k + 15].
1268     Res.setName(Key.first + 1);
1269     RETURN_IF_ERROR(visitStringTableBundle(&Res));
1270   }
1271   return Error::success();
1272 }
1273 
1274 // --- UserDefinedResource helpers. --- //
1275 
1276 Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
1277   auto *Res = cast<UserDefinedResource>(Base);
1278 
1279   if (Res->IsFileResource)
1280     return appendFile(Res->FileLoc);
1281 
1282   for (auto &Elem : Res->Contents) {
1283     if (Elem.isInt()) {
1284       RETURN_IF_ERROR(
1285           checkRCInt(Elem.getInt(), "Number in user-defined resource"));
1286       writeRCInt(Elem.getInt());
1287       continue;
1288     }
1289 
1290     SmallVector<UTF16, 128> ProcessedString;
1291     bool IsLongString;
1292     RETURN_IF_ERROR(
1293         processString(Elem.getString(), NullHandlingMethod::UserResource,
1294                       IsLongString, ProcessedString, Params.CodePage));
1295 
1296     for (auto Ch : ProcessedString) {
1297       if (IsLongString) {
1298         writeInt(Ch);
1299         continue;
1300       }
1301 
1302       RETURN_IF_ERROR(checkNumberFits<uint8_t>(
1303           Ch, "Character in narrow string in user-defined resource"));
1304       writeInt<uint8_t>(Ch);
1305     }
1306   }
1307 
1308   return Error::success();
1309 }
1310 
1311 // --- VersionInfoResourceResource helpers. --- //
1312 
1313 Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
1314   // Output the header if the block has name.
1315   bool OutputHeader = Blk.Name != "";
1316   uint64_t LengthLoc;
1317 
1318   padStream(sizeof(uint32_t));
1319   if (OutputHeader) {
1320     LengthLoc = writeInt<uint16_t>(0);
1321     writeInt<uint16_t>(0);
1322     writeInt<uint16_t>(1); // true
1323     RETURN_IF_ERROR(writeCString(Blk.Name));
1324     padStream(sizeof(uint32_t));
1325   }
1326 
1327   for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
1328     VersionInfoStmt *ItemPtr = Item.get();
1329 
1330     if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
1331       RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
1332       continue;
1333     }
1334 
1335     auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
1336     RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
1337   }
1338 
1339   if (OutputHeader) {
1340     uint64_t CurLoc = tell();
1341     writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1342   }
1343 
1344   return Error::success();
1345 }
1346 
1347 Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
1348   // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
1349   // is a mapping from the key (string) to the value (a sequence of ints or
1350   // a sequence of strings).
1351   //
1352   // If integers are to be written: width of each integer written depends on
1353   // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
1354   // ValueLength defined in structure referenced below is then the total
1355   // number of bytes taken by these integers.
1356   //
1357   // If strings are to be written: characters are always WORDs.
1358   // Moreover, '\0' character is written after the last string, and between
1359   // every two strings separated by comma (if strings are not comma-separated,
1360   // they're simply concatenated). ValueLength is equal to the number of WORDs
1361   // written (that is, half of the bytes written).
1362   //
1363   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
1364   bool HasStrings = false, HasInts = false;
1365   for (auto &Item : Val.Values)
1366     (Item.isInt() ? HasInts : HasStrings) = true;
1367 
1368   assert((HasStrings || HasInts) && "VALUE must have at least one argument");
1369   if (HasStrings && HasInts)
1370     return createError(Twine("VALUE ") + Val.Key +
1371                        " cannot contain both strings and integers");
1372 
1373   padStream(sizeof(uint32_t));
1374   auto LengthLoc = writeInt<uint16_t>(0);
1375   auto ValLengthLoc = writeInt<uint16_t>(0);
1376   writeInt<uint16_t>(HasStrings);
1377   RETURN_IF_ERROR(writeCString(Val.Key));
1378   padStream(sizeof(uint32_t));
1379 
1380   auto DataLoc = tell();
1381   for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
1382     auto &Item = Val.Values[Id];
1383     if (Item.isInt()) {
1384       auto Value = Item.getInt();
1385       RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
1386       writeRCInt(Value);
1387       continue;
1388     }
1389 
1390     bool WriteTerminator =
1391         Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
1392     RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
1393   }
1394 
1395   auto CurLoc = tell();
1396   auto ValueLength = CurLoc - DataLoc;
1397   if (HasStrings) {
1398     assert(ValueLength % 2 == 0);
1399     ValueLength /= 2;
1400   }
1401   writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1402   writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
1403   return Error::success();
1404 }
1405 
1406 template <typename Ty>
1407 static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
1408                          const Ty &Default) {
1409   auto Iter = Map.find(Key);
1410   if (Iter != Map.end())
1411     return Iter->getValue();
1412   return Default;
1413 }
1414 
1415 Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
1416   auto *Res = cast<VersionInfoResource>(Base);
1417 
1418   const auto &FixedData = Res->FixedData;
1419 
1420   struct /* VS_FIXEDFILEINFO */ {
1421     ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
1422     ulittle32_t StructVersion = ulittle32_t(0x10000);
1423     // It's weird to have most-significant DWORD first on the little-endian
1424     // machines, but let it be this way.
1425     ulittle32_t FileVersionMS;
1426     ulittle32_t FileVersionLS;
1427     ulittle32_t ProductVersionMS;
1428     ulittle32_t ProductVersionLS;
1429     ulittle32_t FileFlagsMask;
1430     ulittle32_t FileFlags;
1431     ulittle32_t FileOS;
1432     ulittle32_t FileType;
1433     ulittle32_t FileSubtype;
1434     // MS implementation seems to always set these fields to 0.
1435     ulittle32_t FileDateMS = ulittle32_t(0);
1436     ulittle32_t FileDateLS = ulittle32_t(0);
1437   } FixedInfo;
1438 
1439   // First, VS_VERSIONINFO.
1440   auto LengthLoc = writeInt<uint16_t>(0);
1441   writeInt<uint16_t>(sizeof(FixedInfo));
1442   writeInt<uint16_t>(0);
1443   cantFail(writeCString("VS_VERSION_INFO"));
1444   padStream(sizeof(uint32_t));
1445 
1446   using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
1447   auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
1448     static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
1449     if (!FixedData.IsTypePresent[(int)Type])
1450       return DefaultOut;
1451     return FixedData.FixedInfo[(int)Type];
1452   };
1453 
1454   auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
1455   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1456       *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
1457   FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
1458   FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
1459 
1460   auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
1461   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1462       *std::max_element(ProdVer.begin(), ProdVer.end()),
1463       "PRODUCTVERSION fields"));
1464   FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
1465   FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
1466 
1467   FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
1468   FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
1469   FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
1470   FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
1471   FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
1472 
1473   writeObject(FixedInfo);
1474   padStream(sizeof(uint32_t));
1475 
1476   RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
1477 
1478   // FIXME: check overflow?
1479   writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
1480 
1481   return Error::success();
1482 }
1483 
1484 Expected<std::unique_ptr<MemoryBuffer>>
1485 ResourceFileWriter::loadFile(StringRef File) const {
1486   SmallString<128> Path;
1487   SmallString<128> Cwd;
1488   std::unique_ptr<MemoryBuffer> Result;
1489 
1490   // 1. The current working directory.
1491   sys::fs::current_path(Cwd);
1492   Path.assign(Cwd.begin(), Cwd.end());
1493   sys::path::append(Path, File);
1494   if (sys::fs::exists(Path))
1495     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1496 
1497   // 2. The directory of the input resource file, if it is different from the
1498   // current
1499   //    working directory.
1500   StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
1501   Path.assign(InputFileDir.begin(), InputFileDir.end());
1502   sys::path::append(Path, File);
1503   if (sys::fs::exists(Path))
1504     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1505 
1506   // 3. All of the include directories specified on the command line.
1507   for (StringRef ForceInclude : Params.Include) {
1508     Path.assign(ForceInclude.begin(), ForceInclude.end());
1509     sys::path::append(Path, File);
1510     if (sys::fs::exists(Path))
1511       return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1512   }
1513 
1514   if (auto Result =
1515           llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude))
1516     return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false));
1517 
1518   return make_error<StringError>("error : file not found : " + Twine(File),
1519                                  inconvertibleErrorCode());
1520 }
1521 
1522 } // namespace rc
1523 } // namespace llvm
1524