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::visitClassStmt(const ClassStmt *Stmt) {
462   ObjectData.Class = Stmt->Value;
463   return Error::success();
464 }
465 
466 Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
467   return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
468 }
469 
470 Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
471   return writeResource(Res, &ResourceFileWriter::writeMenuBody);
472 }
473 
474 Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
475   const auto *Res = cast<StringTableResource>(Base);
476 
477   ContextKeeper RAII(this);
478   RETURN_IF_ERROR(Res->applyStmts(this));
479 
480   for (auto &String : Res->Table) {
481     RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"));
482     uint16_t BundleID = String.first >> 4;
483     StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
484     auto &BundleData = StringTableData.BundleData;
485     auto Iter = BundleData.find(Key);
486 
487     if (Iter == BundleData.end()) {
488       // Need to create a bundle.
489       StringTableData.BundleList.push_back(Key);
490       auto EmplaceResult = BundleData.emplace(
491           Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags));
492       assert(EmplaceResult.second && "Could not create a bundle");
493       Iter = EmplaceResult.first;
494     }
495 
496     RETURN_IF_ERROR(
497         insertStringIntoBundle(Iter->second, String.first, String.second));
498   }
499 
500   return Error::success();
501 }
502 
503 Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) {
504   return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody);
505 }
506 
507 Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
508   return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
509 }
510 
511 Error ResourceFileWriter::visitCharacteristicsStmt(
512     const CharacteristicsStmt *Stmt) {
513   ObjectData.Characteristics = Stmt->Value;
514   return Error::success();
515 }
516 
517 Error ResourceFileWriter::visitExStyleStmt(const ExStyleStmt *Stmt) {
518   ObjectData.ExStyle = Stmt->Value;
519   return Error::success();
520 }
521 
522 Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
523   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
524   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
525   RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
526   ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
527                             Stmt->Charset};
528   ObjectData.Font.emplace(Font);
529   return Error::success();
530 }
531 
532 Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
533   RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
534   RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
535   ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
536   return Error::success();
537 }
538 
539 Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
540   ObjectData.Style = Stmt->Value;
541   return Error::success();
542 }
543 
544 Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
545   ObjectData.VersionInfo = Stmt->Value;
546   return Error::success();
547 }
548 
549 Error ResourceFileWriter::writeResource(
550     const RCResource *Res,
551     Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
552   // We don't know the sizes yet.
553   object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
554   uint64_t HeaderLoc = writeObject(HeaderPrefix);
555 
556   auto ResType = Res->getResourceType();
557   RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
558   RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
559   RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
560   RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
561 
562   // Apply the resource-local optional statements.
563   ContextKeeper RAII(this);
564   RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
565 
566   padStream(sizeof(uint32_t));
567   object::WinResHeaderSuffix HeaderSuffix{
568       ulittle32_t(0), // DataVersion; seems to always be 0
569       ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo),
570       ulittle32_t(ObjectData.VersionInfo),
571       ulittle32_t(ObjectData.Characteristics)};
572   writeObject(HeaderSuffix);
573 
574   uint64_t DataLoc = tell();
575   RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
576   // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
577 
578   // Update the sizes.
579   HeaderPrefix.DataSize = tell() - DataLoc;
580   HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
581   writeObjectAt(HeaderPrefix, HeaderLoc);
582   padStream(sizeof(uint32_t));
583 
584   return Error::success();
585 }
586 
587 // --- NullResource helpers. --- //
588 
589 Error ResourceFileWriter::writeNullBody(const RCResource *) {
590   return Error::success();
591 }
592 
593 // --- AcceleratorsResource helpers. --- //
594 
595 Error ResourceFileWriter::writeSingleAccelerator(
596     const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
597   using Accelerator = AcceleratorsResource::Accelerator;
598   using Opt = Accelerator::Options;
599 
600   struct AccelTableEntry {
601     ulittle16_t Flags;
602     ulittle16_t ANSICode;
603     ulittle16_t Id;
604     uint16_t Padding;
605   } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
606 
607   bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
608 
609   // Remove ASCII flags (which doesn't occur in .res files).
610   Entry.Flags = Obj.Flags & ~Opt::ASCII;
611 
612   if (IsLastItem)
613     Entry.Flags |= 0x80;
614 
615   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
616   Entry.Id = ulittle16_t(Obj.Id);
617 
618   auto createAccError = [&Obj](const char *Msg) {
619     return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
620   };
621 
622   if (IsASCII && IsVirtKey)
623     return createAccError("Accelerator can't be both ASCII and VIRTKEY");
624 
625   if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
626     return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
627                           " accelerators");
628 
629   if (Obj.Event.isInt()) {
630     if (!IsASCII && !IsVirtKey)
631       return createAccError(
632           "Accelerator with a numeric event must be either ASCII"
633           " or VIRTKEY");
634 
635     uint32_t EventVal = Obj.Event.getInt();
636     RETURN_IF_ERROR(
637         checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
638     Entry.ANSICode = ulittle16_t(EventVal);
639     writeObject(Entry);
640     return Error::success();
641   }
642 
643   StringRef Str = Obj.Event.getString();
644   bool IsWide;
645   stripQuotes(Str, IsWide);
646 
647   if (Str.size() == 0 || Str.size() > 2)
648     return createAccError(
649         "Accelerator string events should have length 1 or 2");
650 
651   if (Str[0] == '^') {
652     if (Str.size() == 1)
653       return createAccError("No character following '^' in accelerator event");
654     if (IsVirtKey)
655       return createAccError(
656           "VIRTKEY accelerator events can't be preceded by '^'");
657 
658     char Ch = Str[1];
659     if (Ch >= 'a' && Ch <= 'z')
660       Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
661     else if (Ch >= 'A' && Ch <= 'Z')
662       Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
663     else
664       return createAccError("Control character accelerator event should be"
665                             " alphabetic");
666 
667     writeObject(Entry);
668     return Error::success();
669   }
670 
671   if (Str.size() == 2)
672     return createAccError("Event string should be one-character, possibly"
673                           " preceded by '^'");
674 
675   uint8_t EventCh = Str[0];
676   // The original tool just warns in this situation. We chose to fail.
677   if (IsVirtKey && !isalnum(EventCh))
678     return createAccError("Non-alphanumeric characters cannot describe virtual"
679                           " keys");
680   if (EventCh > 0x7F)
681     return createAccError("Non-ASCII description of accelerator");
682 
683   if (IsVirtKey)
684     EventCh = toupper(EventCh);
685   Entry.ANSICode = ulittle16_t(EventCh);
686   writeObject(Entry);
687   return Error::success();
688 }
689 
690 Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
691   auto *Res = cast<AcceleratorsResource>(Base);
692   size_t AcceleratorId = 0;
693   for (auto &Acc : Res->Accelerators) {
694     ++AcceleratorId;
695     RETURN_IF_ERROR(
696         writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
697   }
698   return Error::success();
699 }
700 
701 // --- BitmapResource helpers. --- //
702 
703 Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) {
704   StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc;
705   bool IsLong;
706   stripQuotes(Filename, IsLong);
707 
708   auto File = loadFile(Filename);
709   if (!File)
710     return File.takeError();
711 
712   StringRef Buffer = (*File)->getBuffer();
713 
714   // Skip the 14 byte BITMAPFILEHEADER.
715   constexpr size_t BITMAPFILEHEADER_size = 14;
716   if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' ||
717       Buffer[1] != 'M')
718     return createError("Incorrect bitmap file.");
719 
720   *FS << Buffer.substr(BITMAPFILEHEADER_size);
721   return Error::success();
722 }
723 
724 // --- CursorResource and IconResource helpers. --- //
725 
726 // ICONRESDIR structure. Describes a single icon in resouce group.
727 //
728 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
729 struct IconResDir {
730   uint8_t Width;
731   uint8_t Height;
732   uint8_t ColorCount;
733   uint8_t Reserved;
734 };
735 
736 // CURSORDIR structure. Describes a single cursor in resource group.
737 //
738 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
739 struct CursorDir {
740   ulittle16_t Width;
741   ulittle16_t Height;
742 };
743 
744 // RESDIRENTRY structure, stripped from the last item. Stripping made
745 // for compatibility with RESDIR.
746 //
747 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
748 struct ResourceDirEntryStart {
749   union {
750     CursorDir Cursor; // Used in CURSOR resources.
751     IconResDir Icon;  // Used in .ico and .cur files, and ICON resources.
752   };
753   ulittle16_t Planes;   // HotspotX (.cur files but not CURSOR resource).
754   ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
755   ulittle32_t Size;
756   // ulittle32_t ImageOffset;  // Offset to image data (ICONDIRENTRY only).
757   // ulittle16_t IconID;       // Resource icon ID (RESDIR only).
758 };
759 
760 // BITMAPINFOHEADER structure. Describes basic information about the bitmap
761 // being read.
762 //
763 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
764 struct BitmapInfoHeader {
765   ulittle32_t Size;
766   ulittle32_t Width;
767   ulittle32_t Height;
768   ulittle16_t Planes;
769   ulittle16_t BitCount;
770   ulittle32_t Compression;
771   ulittle32_t SizeImage;
772   ulittle32_t XPelsPerMeter;
773   ulittle32_t YPelsPerMeter;
774   ulittle32_t ClrUsed;
775   ulittle32_t ClrImportant;
776 };
777 
778 // Group icon directory header. Called ICONDIR in .ico/.cur files and
779 // NEWHEADER in .res files.
780 //
781 // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
782 struct GroupIconDir {
783   ulittle16_t Reserved; // Always 0.
784   ulittle16_t ResType;  // 1 for icons, 2 for cursors.
785   ulittle16_t ResCount; // Number of items.
786 };
787 
788 enum class IconCursorGroupType { Icon, Cursor };
789 
790 class SingleIconCursorResource : public RCResource {
791 public:
792   IconCursorGroupType Type;
793   const ResourceDirEntryStart &Header;
794   ArrayRef<uint8_t> Image;
795 
796   SingleIconCursorResource(IconCursorGroupType ResourceType,
797                            const ResourceDirEntryStart &HeaderEntry,
798                            ArrayRef<uint8_t> ImageData, uint16_t Flags)
799       : RCResource(Flags), Type(ResourceType), Header(HeaderEntry),
800         Image(ImageData) {}
801 
802   Twine getResourceTypeName() const override { return "Icon/cursor image"; }
803   IntOrString getResourceType() const override {
804     return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
805   }
806   ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
807   static bool classof(const RCResource *Res) {
808     return Res->getKind() == RkSingleCursorOrIconRes;
809   }
810 };
811 
812 class IconCursorGroupResource : public RCResource {
813 public:
814   IconCursorGroupType Type;
815   GroupIconDir Header;
816   std::vector<ResourceDirEntryStart> ItemEntries;
817 
818   IconCursorGroupResource(IconCursorGroupType ResourceType,
819                           const GroupIconDir &HeaderData,
820                           std::vector<ResourceDirEntryStart> &&Entries)
821       : Type(ResourceType), Header(HeaderData),
822         ItemEntries(std::move(Entries)) {}
823 
824   Twine getResourceTypeName() const override { return "Icon/cursor group"; }
825   IntOrString getResourceType() const override {
826     return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
827   }
828   ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
829   static bool classof(const RCResource *Res) {
830     return Res->getKind() == RkCursorOrIconGroupRes;
831   }
832 };
833 
834 Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
835   auto *Res = cast<SingleIconCursorResource>(Base);
836   if (Res->Type == IconCursorGroupType::Cursor) {
837     // In case of cursors, two WORDS are appended to the beginning
838     // of the resource: HotspotX (Planes in RESDIRENTRY),
839     // and HotspotY (BitCount).
840     //
841     // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
842     //  (Remarks section).
843     writeObject(Res->Header.Planes);
844     writeObject(Res->Header.BitCount);
845   }
846 
847   writeObject(Res->Image);
848   return Error::success();
849 }
850 
851 Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
852   auto *Res = cast<IconCursorGroupResource>(Base);
853   writeObject(Res->Header);
854   for (auto Item : Res->ItemEntries) {
855     writeObject(Item);
856     writeInt(IconCursorID++);
857   }
858   return Error::success();
859 }
860 
861 Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
862   return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
863 }
864 
865 Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
866   return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
867 }
868 
869 Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
870   IconCursorGroupType Type;
871   StringRef FileStr;
872   IntOrString ResName = Base->ResName;
873 
874   if (auto *IconRes = dyn_cast<IconResource>(Base)) {
875     FileStr = IconRes->IconLoc;
876     Type = IconCursorGroupType::Icon;
877   } else {
878     auto *CursorRes = dyn_cast<CursorResource>(Base);
879     FileStr = CursorRes->CursorLoc;
880     Type = IconCursorGroupType::Cursor;
881   }
882 
883   bool IsLong;
884   stripQuotes(FileStr, IsLong);
885   auto File = loadFile(FileStr);
886 
887   if (!File)
888     return File.takeError();
889 
890   BinaryStreamReader Reader((*File)->getBuffer(), support::little);
891 
892   // Read the file headers.
893   //   - At the beginning, ICONDIR/NEWHEADER header.
894   //   - Then, a number of RESDIR headers follow. These contain offsets
895   //       to data.
896   const GroupIconDir *Header;
897 
898   RETURN_IF_ERROR(Reader.readObject(Header));
899   if (Header->Reserved != 0)
900     return createError("Incorrect icon/cursor Reserved field; should be 0.");
901   uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
902   if (Header->ResType != NeededType)
903     return createError("Incorrect icon/cursor ResType field; should be " +
904                        Twine(NeededType) + ".");
905 
906   uint16_t NumItems = Header->ResCount;
907 
908   // Read single ico/cur headers.
909   std::vector<ResourceDirEntryStart> ItemEntries;
910   ItemEntries.reserve(NumItems);
911   std::vector<uint32_t> ItemOffsets(NumItems);
912   for (size_t ID = 0; ID < NumItems; ++ID) {
913     const ResourceDirEntryStart *Object;
914     RETURN_IF_ERROR(Reader.readObject(Object));
915     ItemEntries.push_back(*Object);
916     RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
917   }
918 
919   // Now write each icon/cursors one by one. At first, all the contents
920   // without ICO/CUR header. This is described by SingleIconCursorResource.
921   for (size_t ID = 0; ID < NumItems; ++ID) {
922     // Load the fragment of file.
923     Reader.setOffset(ItemOffsets[ID]);
924     ArrayRef<uint8_t> Image;
925     RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
926     SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image,
927                                        Base->MemoryFlags);
928     SingleRes.setName(IconCursorID + ID);
929     RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
930   }
931 
932   // Now, write all the headers concatenated into a separate resource.
933   for (size_t ID = 0; ID < NumItems; ++ID) {
934     // We need to rewrite the cursor headers, and fetch actual values
935     // for Planes/BitCount.
936     const auto &OldHeader = ItemEntries[ID];
937     ResourceDirEntryStart NewHeader = OldHeader;
938 
939     if (Type == IconCursorGroupType::Cursor) {
940       NewHeader.Cursor.Width = OldHeader.Icon.Width;
941       // Each cursor in fact stores two bitmaps, one under another.
942       // Height provided in cursor definition describes the height of the
943       // cursor, whereas the value existing in resource definition describes
944       // the height of the bitmap. Therefore, we need to double this height.
945       NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
946 
947       // Two WORDs were written at the beginning of the resource (hotspot
948       // location). This is reflected in Size field.
949       NewHeader.Size += 2 * sizeof(uint16_t);
950     }
951 
952     // Now, we actually need to read the bitmap header to find
953     // the number of planes and the number of bits per pixel.
954     Reader.setOffset(ItemOffsets[ID]);
955     const BitmapInfoHeader *BMPHeader;
956     RETURN_IF_ERROR(Reader.readObject(BMPHeader));
957     if (BMPHeader->Size == sizeof(BitmapInfoHeader)) {
958       NewHeader.Planes = BMPHeader->Planes;
959       NewHeader.BitCount = BMPHeader->BitCount;
960     } else {
961       // A PNG .ico file.
962       // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473
963       // "The image must be in 32bpp"
964       NewHeader.Planes = 1;
965       NewHeader.BitCount = 32;
966     }
967 
968     ItemEntries[ID] = NewHeader;
969   }
970 
971   IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
972   HeaderRes.setName(ResName);
973   if (Base->MemoryFlags & MfPreload) {
974     HeaderRes.MemoryFlags |= MfPreload;
975     HeaderRes.MemoryFlags &= ~MfPure;
976   }
977   RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
978 
979   return Error::success();
980 }
981 
982 // --- DialogResource helpers. --- //
983 
984 Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
985                                                    bool IsExtended) {
986   // Each control should be aligned to DWORD.
987   padStream(sizeof(uint32_t));
988 
989   auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
990   uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0);
991   uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
992 
993   // DIALOG(EX) item header prefix.
994   if (!IsExtended) {
995     struct {
996       ulittle32_t Style;
997       ulittle32_t ExtStyle;
998     } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)};
999     writeObject(Prefix);
1000   } else {
1001     struct {
1002       ulittle32_t HelpID;
1003       ulittle32_t ExtStyle;
1004       ulittle32_t Style;
1005     } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
1006              ulittle32_t(CtlStyle)};
1007     writeObject(Prefix);
1008   }
1009 
1010   // Common fixed-length part.
1011   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
1012       Ctl.X, "Dialog control x-coordinate", true));
1013   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
1014       Ctl.Y, "Dialog control y-coordinate", true));
1015   RETURN_IF_ERROR(
1016       checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
1017   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
1018       Ctl.Height, "Dialog control height", false));
1019   struct {
1020     ulittle16_t X;
1021     ulittle16_t Y;
1022     ulittle16_t Width;
1023     ulittle16_t Height;
1024   } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
1025            ulittle16_t(Ctl.Height)};
1026   writeObject(Middle);
1027 
1028   // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
1029   if (!IsExtended) {
1030     // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
1031     // want to refer to later.
1032     if (Ctl.ID != static_cast<uint32_t>(-1))
1033       RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1034           Ctl.ID, "Control ID in simple DIALOG resource"));
1035     writeInt<uint16_t>(Ctl.ID);
1036   } else {
1037     writeInt<uint32_t>(Ctl.ID);
1038   }
1039 
1040   // Window class - either 0xFFFF + 16-bit integer or a string.
1041   RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
1042 
1043   // Element caption/reference ID. ID is preceded by 0xFFFF.
1044   RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
1045   RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
1046 
1047   // # bytes of extra creation data count. Don't pass any.
1048   writeInt<uint16_t>(0);
1049 
1050   return Error::success();
1051 }
1052 
1053 Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
1054   auto *Res = cast<DialogResource>(Base);
1055 
1056   // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
1057   const uint32_t DefaultStyle = 0x80880000;
1058   const uint32_t StyleFontFlag = 0x40;
1059   const uint32_t StyleCaptionFlag = 0x00C00000;
1060 
1061   uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
1062   if (ObjectData.Font)
1063     UsedStyle |= StyleFontFlag;
1064   else
1065     UsedStyle &= ~StyleFontFlag;
1066 
1067   // Actually, in case of empty (but existent) caption, the examined field
1068   // is equal to "\"\"". That's why empty captions are still noticed.
1069   if (ObjectData.Caption != "")
1070     UsedStyle |= StyleCaptionFlag;
1071 
1072   const uint16_t DialogExMagic = 0xFFFF;
1073   uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0);
1074 
1075   // Write DIALOG(EX) header prefix. These are pretty different.
1076   if (!Res->IsExtended) {
1077     // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
1078     // In such a case, whole object (in .res file) is equivalent to a
1079     // DIALOGEX. It might lead to access violation/segmentation fault in
1080     // resource readers. For example,
1081     //   1 DIALOG 0, 0, 0, 65432
1082     //   STYLE 0xFFFF0001 {}
1083     // would be compiled to a DIALOGEX with 65432 controls.
1084     if ((UsedStyle >> 16) == DialogExMagic)
1085       return createError("16 higher bits of DIALOG resource style cannot be"
1086                          " equal to 0xFFFF");
1087 
1088     struct {
1089       ulittle32_t Style;
1090       ulittle32_t ExtStyle;
1091     } Prefix{ulittle32_t(UsedStyle),
1092              ulittle32_t(ExStyle)};
1093 
1094     writeObject(Prefix);
1095   } else {
1096     struct {
1097       ulittle16_t Version;
1098       ulittle16_t Magic;
1099       ulittle32_t HelpID;
1100       ulittle32_t ExtStyle;
1101       ulittle32_t Style;
1102     } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
1103              ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)};
1104 
1105     writeObject(Prefix);
1106   }
1107 
1108   // Now, a common part. First, fixed-length fields.
1109   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
1110                                             "Number of dialog controls"));
1111   RETURN_IF_ERROR(
1112       checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
1113   RETURN_IF_ERROR(
1114       checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
1115   RETURN_IF_ERROR(
1116       checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
1117   RETURN_IF_ERROR(
1118       checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
1119   struct {
1120     ulittle16_t Count;
1121     ulittle16_t PosX;
1122     ulittle16_t PosY;
1123     ulittle16_t DialogWidth;
1124     ulittle16_t DialogHeight;
1125   } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
1126            ulittle16_t(Res->Y), ulittle16_t(Res->Width),
1127            ulittle16_t(Res->Height)};
1128   writeObject(Middle);
1129 
1130   // MENU field. As of now, we don't keep them in the state and can peacefully
1131   // think there is no menu attached to the dialog.
1132   writeInt<uint16_t>(0);
1133 
1134   // Window CLASS field.
1135   RETURN_IF_ERROR(writeIntOrString(ObjectData.Class));
1136 
1137   // Window title or a single word equal to 0.
1138   RETURN_IF_ERROR(writeCString(ObjectData.Caption));
1139 
1140   // If there *is* a window font declared, output its data.
1141   auto &Font = ObjectData.Font;
1142   if (Font) {
1143     writeInt<uint16_t>(Font->Size);
1144     // Additional description occurs only in DIALOGEX.
1145     if (Res->IsExtended) {
1146       writeInt<uint16_t>(Font->Weight);
1147       writeInt<uint8_t>(Font->IsItalic);
1148       writeInt<uint8_t>(Font->Charset);
1149     }
1150     RETURN_IF_ERROR(writeCString(Font->Typeface));
1151   }
1152 
1153   auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
1154     if (!Err)
1155       return Error::success();
1156     return joinErrors(createError("Error in " + Twine(Ctl.Type) +
1157                                   " control  (ID " + Twine(Ctl.ID) + "):"),
1158                       std::move(Err));
1159   };
1160 
1161   for (auto &Ctl : Res->Controls)
1162     RETURN_IF_ERROR(
1163         handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
1164 
1165   return Error::success();
1166 }
1167 
1168 // --- HTMLResource helpers. --- //
1169 
1170 Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
1171   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
1172 }
1173 
1174 // --- MenuResource helpers. --- //
1175 
1176 Error ResourceFileWriter::writeMenuDefinition(
1177     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
1178   assert(Def);
1179   const MenuDefinition *DefPtr = Def.get();
1180 
1181   if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
1182     writeInt<uint16_t>(Flags);
1183     RETURN_IF_ERROR(
1184         checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
1185     writeInt<uint16_t>(MenuItemPtr->Id);
1186     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
1187     return Error::success();
1188   }
1189 
1190   if (isa<MenuSeparator>(DefPtr)) {
1191     writeInt<uint16_t>(Flags);
1192     writeInt<uint32_t>(0);
1193     return Error::success();
1194   }
1195 
1196   auto *PopupPtr = cast<PopupItem>(DefPtr);
1197   writeInt<uint16_t>(Flags);
1198   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
1199   return writeMenuDefinitionList(PopupPtr->SubItems);
1200 }
1201 
1202 Error ResourceFileWriter::writeMenuDefinitionList(
1203     const MenuDefinitionList &List) {
1204   for (auto &Def : List.Definitions) {
1205     uint16_t Flags = Def->getResFlags();
1206     // Last element receives an additional 0x80 flag.
1207     const uint16_t LastElementFlag = 0x0080;
1208     if (&Def == &List.Definitions.back())
1209       Flags |= LastElementFlag;
1210 
1211     RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
1212   }
1213   return Error::success();
1214 }
1215 
1216 Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
1217   // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
1218   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
1219   writeInt<uint32_t>(0);
1220 
1221   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
1222 }
1223 
1224 // --- StringTableResource helpers. --- //
1225 
1226 class BundleResource : public RCResource {
1227 public:
1228   using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
1229   BundleType Bundle;
1230 
1231   BundleResource(const BundleType &StrBundle)
1232       : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
1233   IntOrString getResourceType() const override { return 6; }
1234 
1235   ResourceKind getKind() const override { return RkStringTableBundle; }
1236   static bool classof(const RCResource *Res) {
1237     return Res->getKind() == RkStringTableBundle;
1238   }
1239   Twine getResourceTypeName() const override { return "STRINGTABLE"; }
1240 };
1241 
1242 Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
1243   return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
1244 }
1245 
1246 Error ResourceFileWriter::insertStringIntoBundle(
1247     StringTableInfo::Bundle &Bundle, uint16_t StringID, StringRef String) {
1248   uint16_t StringLoc = StringID & 15;
1249   if (Bundle.Data[StringLoc])
1250     return createError("Multiple STRINGTABLE strings located under ID " +
1251                        Twine(StringID));
1252   Bundle.Data[StringLoc] = String;
1253   return Error::success();
1254 }
1255 
1256 Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
1257   auto *Res = cast<BundleResource>(Base);
1258   for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
1259     // The string format is a tiny bit different here. We
1260     // first output the size of the string, and then the string itself
1261     // (which is not null-terminated).
1262     bool IsLongString;
1263     SmallVector<UTF16, 128> Data;
1264     RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()),
1265                                   NullHandlingMethod::CutAtDoubleNull,
1266                                   IsLongString, Data, Params.CodePage));
1267     if (AppendNull && Res->Bundle.Data[ID])
1268       Data.push_back('\0');
1269     RETURN_IF_ERROR(
1270         checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
1271     writeInt<uint16_t>(Data.size());
1272     for (auto Char : Data)
1273       writeInt(Char);
1274   }
1275   return Error::success();
1276 }
1277 
1278 Error ResourceFileWriter::dumpAllStringTables() {
1279   for (auto Key : StringTableData.BundleList) {
1280     auto Iter = StringTableData.BundleData.find(Key);
1281     assert(Iter != StringTableData.BundleData.end());
1282 
1283     // For a moment, revert the context info to moment of bundle declaration.
1284     ContextKeeper RAII(this);
1285     ObjectData = Iter->second.DeclTimeInfo;
1286 
1287     BundleResource Res(Iter->second);
1288     // Bundle #(k+1) contains keys [16k, 16k + 15].
1289     Res.setName(Key.first + 1);
1290     RETURN_IF_ERROR(visitStringTableBundle(&Res));
1291   }
1292   return Error::success();
1293 }
1294 
1295 // --- UserDefinedResource helpers. --- //
1296 
1297 Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
1298   auto *Res = cast<UserDefinedResource>(Base);
1299 
1300   if (Res->IsFileResource)
1301     return appendFile(Res->FileLoc);
1302 
1303   for (auto &Elem : Res->Contents) {
1304     if (Elem.isInt()) {
1305       RETURN_IF_ERROR(
1306           checkRCInt(Elem.getInt(), "Number in user-defined resource"));
1307       writeRCInt(Elem.getInt());
1308       continue;
1309     }
1310 
1311     SmallVector<UTF16, 128> ProcessedString;
1312     bool IsLongString;
1313     RETURN_IF_ERROR(
1314         processString(Elem.getString(), NullHandlingMethod::UserResource,
1315                       IsLongString, ProcessedString, Params.CodePage));
1316 
1317     for (auto Ch : ProcessedString) {
1318       if (IsLongString) {
1319         writeInt(Ch);
1320         continue;
1321       }
1322 
1323       RETURN_IF_ERROR(checkNumberFits<uint8_t>(
1324           Ch, "Character in narrow string in user-defined resource"));
1325       writeInt<uint8_t>(Ch);
1326     }
1327   }
1328 
1329   return Error::success();
1330 }
1331 
1332 // --- VersionInfoResourceResource helpers. --- //
1333 
1334 Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
1335   // Output the header if the block has name.
1336   bool OutputHeader = Blk.Name != "";
1337   uint64_t LengthLoc;
1338 
1339   padStream(sizeof(uint32_t));
1340   if (OutputHeader) {
1341     LengthLoc = writeInt<uint16_t>(0);
1342     writeInt<uint16_t>(0);
1343     writeInt<uint16_t>(1); // true
1344     RETURN_IF_ERROR(writeCString(Blk.Name));
1345     padStream(sizeof(uint32_t));
1346   }
1347 
1348   for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
1349     VersionInfoStmt *ItemPtr = Item.get();
1350 
1351     if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
1352       RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
1353       continue;
1354     }
1355 
1356     auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
1357     RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
1358   }
1359 
1360   if (OutputHeader) {
1361     uint64_t CurLoc = tell();
1362     writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1363   }
1364 
1365   return Error::success();
1366 }
1367 
1368 Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
1369   // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
1370   // is a mapping from the key (string) to the value (a sequence of ints or
1371   // a sequence of strings).
1372   //
1373   // If integers are to be written: width of each integer written depends on
1374   // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
1375   // ValueLength defined in structure referenced below is then the total
1376   // number of bytes taken by these integers.
1377   //
1378   // If strings are to be written: characters are always WORDs.
1379   // Moreover, '\0' character is written after the last string, and between
1380   // every two strings separated by comma (if strings are not comma-separated,
1381   // they're simply concatenated). ValueLength is equal to the number of WORDs
1382   // written (that is, half of the bytes written).
1383   //
1384   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
1385   bool HasStrings = false, HasInts = false;
1386   for (auto &Item : Val.Values)
1387     (Item.isInt() ? HasInts : HasStrings) = true;
1388 
1389   assert((HasStrings || HasInts) && "VALUE must have at least one argument");
1390   if (HasStrings && HasInts)
1391     return createError(Twine("VALUE ") + Val.Key +
1392                        " cannot contain both strings and integers");
1393 
1394   padStream(sizeof(uint32_t));
1395   auto LengthLoc = writeInt<uint16_t>(0);
1396   auto ValLengthLoc = writeInt<uint16_t>(0);
1397   writeInt<uint16_t>(HasStrings);
1398   RETURN_IF_ERROR(writeCString(Val.Key));
1399   padStream(sizeof(uint32_t));
1400 
1401   auto DataLoc = tell();
1402   for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
1403     auto &Item = Val.Values[Id];
1404     if (Item.isInt()) {
1405       auto Value = Item.getInt();
1406       RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
1407       writeRCInt(Value);
1408       continue;
1409     }
1410 
1411     bool WriteTerminator =
1412         Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
1413     RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
1414   }
1415 
1416   auto CurLoc = tell();
1417   auto ValueLength = CurLoc - DataLoc;
1418   if (HasStrings) {
1419     assert(ValueLength % 2 == 0);
1420     ValueLength /= 2;
1421   }
1422   writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
1423   writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
1424   return Error::success();
1425 }
1426 
1427 template <typename Ty>
1428 static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
1429                          const Ty &Default) {
1430   auto Iter = Map.find(Key);
1431   if (Iter != Map.end())
1432     return Iter->getValue();
1433   return Default;
1434 }
1435 
1436 Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
1437   auto *Res = cast<VersionInfoResource>(Base);
1438 
1439   const auto &FixedData = Res->FixedData;
1440 
1441   struct /* VS_FIXEDFILEINFO */ {
1442     ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
1443     ulittle32_t StructVersion = ulittle32_t(0x10000);
1444     // It's weird to have most-significant DWORD first on the little-endian
1445     // machines, but let it be this way.
1446     ulittle32_t FileVersionMS;
1447     ulittle32_t FileVersionLS;
1448     ulittle32_t ProductVersionMS;
1449     ulittle32_t ProductVersionLS;
1450     ulittle32_t FileFlagsMask;
1451     ulittle32_t FileFlags;
1452     ulittle32_t FileOS;
1453     ulittle32_t FileType;
1454     ulittle32_t FileSubtype;
1455     // MS implementation seems to always set these fields to 0.
1456     ulittle32_t FileDateMS = ulittle32_t(0);
1457     ulittle32_t FileDateLS = ulittle32_t(0);
1458   } FixedInfo;
1459 
1460   // First, VS_VERSIONINFO.
1461   auto LengthLoc = writeInt<uint16_t>(0);
1462   writeInt<uint16_t>(sizeof(FixedInfo));
1463   writeInt<uint16_t>(0);
1464   cantFail(writeCString("VS_VERSION_INFO"));
1465   padStream(sizeof(uint32_t));
1466 
1467   using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
1468   auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
1469     static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
1470     if (!FixedData.IsTypePresent[(int)Type])
1471       return DefaultOut;
1472     return FixedData.FixedInfo[(int)Type];
1473   };
1474 
1475   auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
1476   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1477       *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
1478   FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
1479   FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
1480 
1481   auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
1482   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
1483       *std::max_element(ProdVer.begin(), ProdVer.end()),
1484       "PRODUCTVERSION fields"));
1485   FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
1486   FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
1487 
1488   FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
1489   FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
1490   FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
1491   FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
1492   FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
1493 
1494   writeObject(FixedInfo);
1495   padStream(sizeof(uint32_t));
1496 
1497   RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
1498 
1499   // FIXME: check overflow?
1500   writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
1501 
1502   return Error::success();
1503 }
1504 
1505 Expected<std::unique_ptr<MemoryBuffer>>
1506 ResourceFileWriter::loadFile(StringRef File) const {
1507   SmallString<128> Path;
1508   SmallString<128> Cwd;
1509   std::unique_ptr<MemoryBuffer> Result;
1510 
1511   // 0. The file path is absolute and the file exists.
1512   if (sys::path::is_absolute(File))
1513     return errorOrToExpected(MemoryBuffer::getFile(File, -1, false));
1514 
1515   // 1. The current working directory.
1516   sys::fs::current_path(Cwd);
1517   Path.assign(Cwd.begin(), Cwd.end());
1518   sys::path::append(Path, File);
1519   if (sys::fs::exists(Path))
1520     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1521 
1522   // 2. The directory of the input resource file, if it is different from the
1523   // current working directory.
1524   StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
1525   Path.assign(InputFileDir.begin(), InputFileDir.end());
1526   sys::path::append(Path, File);
1527   if (sys::fs::exists(Path))
1528     return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1529 
1530   // 3. All of the include directories specified on the command line.
1531   for (StringRef ForceInclude : Params.Include) {
1532     Path.assign(ForceInclude.begin(), ForceInclude.end());
1533     sys::path::append(Path, File);
1534     if (sys::fs::exists(Path))
1535       return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false));
1536   }
1537 
1538   if (auto Result =
1539           llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude))
1540     return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false));
1541 
1542   return make_error<StringError>("error : file not found : " + Twine(File),
1543                                  inconvertibleErrorCode());
1544 }
1545 
1546 } // namespace rc
1547 } // namespace llvm
1548