Home | History | Annotate | Line # | Download | only in llvm-rc
      1 //===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
      2 //
      3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
      4 // See https://llvm.org/LICENSE.txt for license information.
      5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
      6 //
      7 //===---------------------------------------------------------------------===//
      8 //
      9 // This implements the visitor serializing resources to a .res stream.
     10 //
     11 //===---------------------------------------------------------------------===//
     12 
     13 #include "ResourceFileWriter.h"
     14 #include "llvm/Object/WindowsResource.h"
     15 #include "llvm/Support/ConvertUTF.h"
     16 #include "llvm/Support/Endian.h"
     17 #include "llvm/Support/EndianStream.h"
     18 #include "llvm/Support/FileSystem.h"
     19 #include "llvm/Support/MemoryBuffer.h"
     20 #include "llvm/Support/Path.h"
     21 #include "llvm/Support/Process.h"
     22 
     23 using namespace llvm::support;
     24 
     25 // Take an expression returning llvm::Error and forward the error if it exists.
     26 #define RETURN_IF_ERROR(Expr)                                                  \
     27   if (auto Err = (Expr))                                                       \
     28     return Err;
     29 
     30 namespace llvm {
     31 namespace rc {
     32 
     33 // Class that employs RAII to save the current FileWriter object state
     34 // and revert to it as soon as we leave the scope. This is useful if resources
     35 // declare their own resource-local statements.
     36 class ContextKeeper {
     37   ResourceFileWriter *FileWriter;
     38   ResourceFileWriter::ObjectInfo SavedInfo;
     39 
     40 public:
     41   ContextKeeper(ResourceFileWriter *V)
     42       : FileWriter(V), SavedInfo(V->ObjectData) {}
     43   ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
     44 };
     45 
     46 static Error createError(const Twine &Message,
     47                          std::errc Type = std::errc::invalid_argument) {
     48   return make_error<StringError>(Message, std::make_error_code(Type));
     49 }
     50 
     51 static Error checkNumberFits(uint32_t Number, size_t MaxBits,
     52                              const Twine &FieldName) {
     53   assert(1 <= MaxBits && MaxBits <= 32);
     54   if (!(Number >> MaxBits))
     55     return Error::success();
     56   return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
     57                          Twine(MaxBits) + " bits.",
     58                      std::errc::value_too_large);
     59 }
     60 
     61 template <typename FitType>
     62 static Error checkNumberFits(uint32_t Number, const Twine &FieldName) {
     63   return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
     64 }
     65 
     66 // A similar function for signed integers.
     67 template <typename FitType>
     68 static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName,
     69                                    bool CanBeNegative) {
     70   int32_t SignedNum = Number;
     71   if (SignedNum < std::numeric_limits<FitType>::min() ||
     72       SignedNum > std::numeric_limits<FitType>::max())
     73     return createError(FieldName + " (" + Twine(SignedNum) +
     74                            ") does not fit in " + Twine(sizeof(FitType) * 8) +
     75                            "-bit signed integer type.",
     76                        std::errc::value_too_large);
     77 
     78   if (!CanBeNegative && SignedNum < 0)
     79     return createError(FieldName + " (" + Twine(SignedNum) +
     80                        ") cannot be negative.");
     81 
     82   return Error::success();
     83 }
     84 
     85 static Error checkRCInt(RCInt Number, const Twine &FieldName) {
     86   if (Number.isLong())
     87     return Error::success();
     88   return checkNumberFits<uint16_t>(Number, FieldName);
     89 }
     90 
     91 static Error checkIntOrString(IntOrString Value, const Twine &FieldName) {
     92   if (!Value.isInt())
     93     return Error::success();
     94   return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
     95 }
     96 
     97 static bool stripQuotes(StringRef &Str, bool &IsLongString) {
     98   if (!Str.contains('"'))
     99     return false;
    100 
    101   // Just take the contents of the string, checking if it's been marked long.
    102   IsLongString = Str.startswith_lower("L");
    103   if (IsLongString)
    104     Str = Str.drop_front();
    105 
    106   bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
    107   (void)StripSuccess;
    108   assert(StripSuccess && "Strings should be enclosed in quotes.");
    109   return true;
    110 }
    111 
    112 static UTF16 cp1252ToUnicode(unsigned char C) {
    113   static const UTF16 Map80[] = {
    114       0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021,
    115       0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f,
    116       0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014,
    117       0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178,
    118   };
    119   if (C >= 0x80 && C <= 0x9F)
    120     return Map80[C - 0x80];
    121   return C;
    122 }
    123 
    124 // Describes a way to handle '\0' characters when processing the string.
    125 // rc.exe tool sometimes behaves in a weird way in postprocessing.
    126 // If the string to be output is equivalent to a C-string (e.g. in MENU
    127 // titles), string is (predictably) truncated after first 0-byte.
    128 // When outputting a string table, the behavior is equivalent to appending
    129 // '\0\0' at the end of the string, and then stripping the string
    130 // before the first '\0\0' occurrence.
    131 // Finally, when handling strings in user-defined resources, 0-bytes
    132 // aren't stripped, nor do they terminate the string.
    133 
    134 enum class NullHandlingMethod {
    135   UserResource,   // Don't terminate string on '\0'.
    136   CutAtNull,      // Terminate string on '\0'.
    137   CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
    138 };
    139 
    140 // Parses an identifier or string and returns a processed version of it:
    141 //   * Strip the string boundary quotes.
    142 //   * Convert the input code page characters to UTF16.
    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 resource 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   IntWithNotMask CtlStyle(TypeInfo.Style);
    991   CtlStyle |= Ctl.Style.getValueOr(RCInt(0));
    992   uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
    993 
    994   // DIALOG(EX) item header prefix.
    995   if (!IsExtended) {
    996     struct {
    997       ulittle32_t Style;
    998       ulittle32_t ExtStyle;
    999     } Prefix{ulittle32_t(CtlStyle.getValue()), ulittle32_t(CtlExtStyle)};
   1000     writeObject(Prefix);
   1001   } else {
   1002     struct {
   1003       ulittle32_t HelpID;
   1004       ulittle32_t ExtStyle;
   1005       ulittle32_t Style;
   1006     } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
   1007              ulittle32_t(CtlStyle.getValue())};
   1008     writeObject(Prefix);
   1009   }
   1010 
   1011   // Common fixed-length part.
   1012   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
   1013       Ctl.X, "Dialog control x-coordinate", true));
   1014   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
   1015       Ctl.Y, "Dialog control y-coordinate", true));
   1016   RETURN_IF_ERROR(
   1017       checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
   1018   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
   1019       Ctl.Height, "Dialog control height", false));
   1020   struct {
   1021     ulittle16_t X;
   1022     ulittle16_t Y;
   1023     ulittle16_t Width;
   1024     ulittle16_t Height;
   1025   } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
   1026            ulittle16_t(Ctl.Height)};
   1027   writeObject(Middle);
   1028 
   1029   // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
   1030   if (!IsExtended) {
   1031     // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't
   1032     // want to refer to later.
   1033     if (Ctl.ID != static_cast<uint32_t>(-1))
   1034       RETURN_IF_ERROR(checkNumberFits<uint16_t>(
   1035           Ctl.ID, "Control ID in simple DIALOG resource"));
   1036     writeInt<uint16_t>(Ctl.ID);
   1037   } else {
   1038     writeInt<uint32_t>(Ctl.ID);
   1039   }
   1040 
   1041   // Window class - either 0xFFFF + 16-bit integer or a string.
   1042   RETURN_IF_ERROR(writeIntOrString(Ctl.Class));
   1043 
   1044   // Element caption/reference ID. ID is preceded by 0xFFFF.
   1045   RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
   1046   RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
   1047 
   1048   // # bytes of extra creation data count. Don't pass any.
   1049   writeInt<uint16_t>(0);
   1050 
   1051   return Error::success();
   1052 }
   1053 
   1054 Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
   1055   auto *Res = cast<DialogResource>(Base);
   1056 
   1057   // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
   1058   const uint32_t DefaultStyle = 0x80880000;
   1059   const uint32_t StyleFontFlag = 0x40;
   1060   const uint32_t StyleCaptionFlag = 0x00C00000;
   1061 
   1062   uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
   1063   if (ObjectData.Font)
   1064     UsedStyle |= StyleFontFlag;
   1065   else
   1066     UsedStyle &= ~StyleFontFlag;
   1067 
   1068   // Actually, in case of empty (but existent) caption, the examined field
   1069   // is equal to "\"\"". That's why empty captions are still noticed.
   1070   if (ObjectData.Caption != "")
   1071     UsedStyle |= StyleCaptionFlag;
   1072 
   1073   const uint16_t DialogExMagic = 0xFFFF;
   1074   uint32_t ExStyle = ObjectData.ExStyle.getValueOr(0);
   1075 
   1076   // Write DIALOG(EX) header prefix. These are pretty different.
   1077   if (!Res->IsExtended) {
   1078     // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
   1079     // In such a case, whole object (in .res file) is equivalent to a
   1080     // DIALOGEX. It might lead to access violation/segmentation fault in
   1081     // resource readers. For example,
   1082     //   1 DIALOG 0, 0, 0, 65432
   1083     //   STYLE 0xFFFF0001 {}
   1084     // would be compiled to a DIALOGEX with 65432 controls.
   1085     if ((UsedStyle >> 16) == DialogExMagic)
   1086       return createError("16 higher bits of DIALOG resource style cannot be"
   1087                          " equal to 0xFFFF");
   1088 
   1089     struct {
   1090       ulittle32_t Style;
   1091       ulittle32_t ExtStyle;
   1092     } Prefix{ulittle32_t(UsedStyle),
   1093              ulittle32_t(ExStyle)};
   1094 
   1095     writeObject(Prefix);
   1096   } else {
   1097     struct {
   1098       ulittle16_t Version;
   1099       ulittle16_t Magic;
   1100       ulittle32_t HelpID;
   1101       ulittle32_t ExtStyle;
   1102       ulittle32_t Style;
   1103     } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
   1104              ulittle32_t(Res->HelpID), ulittle32_t(ExStyle), ulittle32_t(UsedStyle)};
   1105 
   1106     writeObject(Prefix);
   1107   }
   1108 
   1109   // Now, a common part. First, fixed-length fields.
   1110   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
   1111                                             "Number of dialog controls"));
   1112   RETURN_IF_ERROR(
   1113       checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
   1114   RETURN_IF_ERROR(
   1115       checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
   1116   RETURN_IF_ERROR(
   1117       checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
   1118   RETURN_IF_ERROR(
   1119       checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
   1120   struct {
   1121     ulittle16_t Count;
   1122     ulittle16_t PosX;
   1123     ulittle16_t PosY;
   1124     ulittle16_t DialogWidth;
   1125     ulittle16_t DialogHeight;
   1126   } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
   1127            ulittle16_t(Res->Y), ulittle16_t(Res->Width),
   1128            ulittle16_t(Res->Height)};
   1129   writeObject(Middle);
   1130 
   1131   // MENU field. As of now, we don't keep them in the state and can peacefully
   1132   // think there is no menu attached to the dialog.
   1133   writeInt<uint16_t>(0);
   1134 
   1135   // Window CLASS field.
   1136   RETURN_IF_ERROR(writeIntOrString(ObjectData.Class));
   1137 
   1138   // Window title or a single word equal to 0.
   1139   RETURN_IF_ERROR(writeCString(ObjectData.Caption));
   1140 
   1141   // If there *is* a window font declared, output its data.
   1142   auto &Font = ObjectData.Font;
   1143   if (Font) {
   1144     writeInt<uint16_t>(Font->Size);
   1145     // Additional description occurs only in DIALOGEX.
   1146     if (Res->IsExtended) {
   1147       writeInt<uint16_t>(Font->Weight);
   1148       writeInt<uint8_t>(Font->IsItalic);
   1149       writeInt<uint8_t>(Font->Charset);
   1150     }
   1151     RETURN_IF_ERROR(writeCString(Font->Typeface));
   1152   }
   1153 
   1154   auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
   1155     if (!Err)
   1156       return Error::success();
   1157     return joinErrors(createError("Error in " + Twine(Ctl.Type) +
   1158                                   " control  (ID " + Twine(Ctl.ID) + "):"),
   1159                       std::move(Err));
   1160   };
   1161 
   1162   for (auto &Ctl : Res->Controls)
   1163     RETURN_IF_ERROR(
   1164         handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
   1165 
   1166   return Error::success();
   1167 }
   1168 
   1169 // --- HTMLResource helpers. --- //
   1170 
   1171 Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
   1172   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
   1173 }
   1174 
   1175 // --- MenuResource helpers. --- //
   1176 
   1177 Error ResourceFileWriter::writeMenuDefinition(
   1178     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
   1179   assert(Def);
   1180   const MenuDefinition *DefPtr = Def.get();
   1181 
   1182   if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
   1183     writeInt<uint16_t>(Flags);
   1184     // Some resource files use -1, i.e. UINT32_MAX, for empty menu items.
   1185     if (MenuItemPtr->Id != static_cast<uint32_t>(-1))
   1186       RETURN_IF_ERROR(
   1187           checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
   1188     writeInt<uint16_t>(MenuItemPtr->Id);
   1189     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
   1190     return Error::success();
   1191   }
   1192 
   1193   if (isa<MenuSeparator>(DefPtr)) {
   1194     writeInt<uint16_t>(Flags);
   1195     writeInt<uint32_t>(0);
   1196     return Error::success();
   1197   }
   1198 
   1199   auto *PopupPtr = cast<PopupItem>(DefPtr);
   1200   writeInt<uint16_t>(Flags);
   1201   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
   1202   return writeMenuDefinitionList(PopupPtr->SubItems);
   1203 }
   1204 
   1205 Error ResourceFileWriter::writeMenuDefinitionList(
   1206     const MenuDefinitionList &List) {
   1207   for (auto &Def : List.Definitions) {
   1208     uint16_t Flags = Def->getResFlags();
   1209     // Last element receives an additional 0x80 flag.
   1210     const uint16_t LastElementFlag = 0x0080;
   1211     if (&Def == &List.Definitions.back())
   1212       Flags |= LastElementFlag;
   1213 
   1214     RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
   1215   }
   1216   return Error::success();
   1217 }
   1218 
   1219 Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
   1220   // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
   1221   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
   1222   writeInt<uint32_t>(0);
   1223 
   1224   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
   1225 }
   1226 
   1227 // --- StringTableResource helpers. --- //
   1228 
   1229 class BundleResource : public RCResource {
   1230 public:
   1231   using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
   1232   BundleType Bundle;
   1233 
   1234   BundleResource(const BundleType &StrBundle)
   1235       : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {}
   1236   IntOrString getResourceType() const override { return 6; }
   1237 
   1238   ResourceKind getKind() const override { return RkStringTableBundle; }
   1239   static bool classof(const RCResource *Res) {
   1240     return Res->getKind() == RkStringTableBundle;
   1241   }
   1242   Twine getResourceTypeName() const override { return "STRINGTABLE"; }
   1243 };
   1244 
   1245 Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
   1246   return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
   1247 }
   1248 
   1249 Error ResourceFileWriter::insertStringIntoBundle(
   1250     StringTableInfo::Bundle &Bundle, uint16_t StringID,
   1251     const std::vector<StringRef> &String) {
   1252   uint16_t StringLoc = StringID & 15;
   1253   if (Bundle.Data[StringLoc])
   1254     return createError("Multiple STRINGTABLE strings located under ID " +
   1255                        Twine(StringID));
   1256   Bundle.Data[StringLoc] = String;
   1257   return Error::success();
   1258 }
   1259 
   1260 Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
   1261   auto *Res = cast<BundleResource>(Base);
   1262   for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
   1263     // The string format is a tiny bit different here. We
   1264     // first output the size of the string, and then the string itself
   1265     // (which is not null-terminated).
   1266     SmallVector<UTF16, 128> Data;
   1267     if (Res->Bundle.Data[ID]) {
   1268       bool IsLongString;
   1269       for (StringRef S : *Res->Bundle.Data[ID])
   1270         RETURN_IF_ERROR(processString(S, NullHandlingMethod::CutAtDoubleNull,
   1271                                       IsLongString, Data, Params.CodePage));
   1272       if (AppendNull)
   1273         Data.push_back('\0');
   1274     }
   1275     RETURN_IF_ERROR(
   1276         checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
   1277     writeInt<uint16_t>(Data.size());
   1278     for (auto Char : Data)
   1279       writeInt(Char);
   1280   }
   1281   return Error::success();
   1282 }
   1283 
   1284 Error ResourceFileWriter::dumpAllStringTables() {
   1285   for (auto Key : StringTableData.BundleList) {
   1286     auto Iter = StringTableData.BundleData.find(Key);
   1287     assert(Iter != StringTableData.BundleData.end());
   1288 
   1289     // For a moment, revert the context info to moment of bundle declaration.
   1290     ContextKeeper RAII(this);
   1291     ObjectData = Iter->second.DeclTimeInfo;
   1292 
   1293     BundleResource Res(Iter->second);
   1294     // Bundle #(k+1) contains keys [16k, 16k + 15].
   1295     Res.setName(Key.first + 1);
   1296     RETURN_IF_ERROR(visitStringTableBundle(&Res));
   1297   }
   1298   return Error::success();
   1299 }
   1300 
   1301 // --- UserDefinedResource helpers. --- //
   1302 
   1303 Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) {
   1304   auto *Res = cast<UserDefinedResource>(Base);
   1305 
   1306   if (Res->IsFileResource)
   1307     return appendFile(Res->FileLoc);
   1308 
   1309   for (auto &Elem : Res->Contents) {
   1310     if (Elem.isInt()) {
   1311       RETURN_IF_ERROR(
   1312           checkRCInt(Elem.getInt(), "Number in user-defined resource"));
   1313       writeRCInt(Elem.getInt());
   1314       continue;
   1315     }
   1316 
   1317     SmallVector<UTF16, 128> ProcessedString;
   1318     bool IsLongString;
   1319     RETURN_IF_ERROR(
   1320         processString(Elem.getString(), NullHandlingMethod::UserResource,
   1321                       IsLongString, ProcessedString, Params.CodePage));
   1322 
   1323     for (auto Ch : ProcessedString) {
   1324       if (IsLongString) {
   1325         writeInt(Ch);
   1326         continue;
   1327       }
   1328 
   1329       RETURN_IF_ERROR(checkNumberFits<uint8_t>(
   1330           Ch, "Character in narrow string in user-defined resource"));
   1331       writeInt<uint8_t>(Ch);
   1332     }
   1333   }
   1334 
   1335   return Error::success();
   1336 }
   1337 
   1338 // --- VersionInfoResourceResource helpers. --- //
   1339 
   1340 Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
   1341   // Output the header if the block has name.
   1342   bool OutputHeader = Blk.Name != "";
   1343   uint64_t LengthLoc;
   1344 
   1345   padStream(sizeof(uint32_t));
   1346   if (OutputHeader) {
   1347     LengthLoc = writeInt<uint16_t>(0);
   1348     writeInt<uint16_t>(0);
   1349     writeInt<uint16_t>(1); // true
   1350     RETURN_IF_ERROR(writeCString(Blk.Name));
   1351     padStream(sizeof(uint32_t));
   1352   }
   1353 
   1354   for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
   1355     VersionInfoStmt *ItemPtr = Item.get();
   1356 
   1357     if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
   1358       RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
   1359       continue;
   1360     }
   1361 
   1362     auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
   1363     RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
   1364   }
   1365 
   1366   if (OutputHeader) {
   1367     uint64_t CurLoc = tell();
   1368     writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
   1369   }
   1370 
   1371   return Error::success();
   1372 }
   1373 
   1374 Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
   1375   // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
   1376   // is a mapping from the key (string) to the value (a sequence of ints or
   1377   // a sequence of strings).
   1378   //
   1379   // If integers are to be written: width of each integer written depends on
   1380   // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
   1381   // ValueLength defined in structure referenced below is then the total
   1382   // number of bytes taken by these integers.
   1383   //
   1384   // If strings are to be written: characters are always WORDs.
   1385   // Moreover, '\0' character is written after the last string, and between
   1386   // every two strings separated by comma (if strings are not comma-separated,
   1387   // they're simply concatenated). ValueLength is equal to the number of WORDs
   1388   // written (that is, half of the bytes written).
   1389   //
   1390   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
   1391   bool HasStrings = false, HasInts = false;
   1392   for (auto &Item : Val.Values)
   1393     (Item.isInt() ? HasInts : HasStrings) = true;
   1394 
   1395   assert((HasStrings || HasInts) && "VALUE must have at least one argument");
   1396   if (HasStrings && HasInts)
   1397     return createError(Twine("VALUE ") + Val.Key +
   1398                        " cannot contain both strings and integers");
   1399 
   1400   padStream(sizeof(uint32_t));
   1401   auto LengthLoc = writeInt<uint16_t>(0);
   1402   auto ValLengthLoc = writeInt<uint16_t>(0);
   1403   writeInt<uint16_t>(HasStrings);
   1404   RETURN_IF_ERROR(writeCString(Val.Key));
   1405   padStream(sizeof(uint32_t));
   1406 
   1407   auto DataLoc = tell();
   1408   for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
   1409     auto &Item = Val.Values[Id];
   1410     if (Item.isInt()) {
   1411       auto Value = Item.getInt();
   1412       RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
   1413       writeRCInt(Value);
   1414       continue;
   1415     }
   1416 
   1417     bool WriteTerminator =
   1418         Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
   1419     RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
   1420   }
   1421 
   1422   auto CurLoc = tell();
   1423   auto ValueLength = CurLoc - DataLoc;
   1424   if (HasStrings) {
   1425     assert(ValueLength % 2 == 0);
   1426     ValueLength /= 2;
   1427   }
   1428   writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
   1429   writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
   1430   return Error::success();
   1431 }
   1432 
   1433 template <typename Ty>
   1434 static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
   1435                          const Ty &Default) {
   1436   auto Iter = Map.find(Key);
   1437   if (Iter != Map.end())
   1438     return Iter->getValue();
   1439   return Default;
   1440 }
   1441 
   1442 Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
   1443   auto *Res = cast<VersionInfoResource>(Base);
   1444 
   1445   const auto &FixedData = Res->FixedData;
   1446 
   1447   struct /* VS_FIXEDFILEINFO */ {
   1448     ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
   1449     ulittle32_t StructVersion = ulittle32_t(0x10000);
   1450     // It's weird to have most-significant DWORD first on the little-endian
   1451     // machines, but let it be this way.
   1452     ulittle32_t FileVersionMS;
   1453     ulittle32_t FileVersionLS;
   1454     ulittle32_t ProductVersionMS;
   1455     ulittle32_t ProductVersionLS;
   1456     ulittle32_t FileFlagsMask;
   1457     ulittle32_t FileFlags;
   1458     ulittle32_t FileOS;
   1459     ulittle32_t FileType;
   1460     ulittle32_t FileSubtype;
   1461     // MS implementation seems to always set these fields to 0.
   1462     ulittle32_t FileDateMS = ulittle32_t(0);
   1463     ulittle32_t FileDateLS = ulittle32_t(0);
   1464   } FixedInfo;
   1465 
   1466   // First, VS_VERSIONINFO.
   1467   auto LengthLoc = writeInt<uint16_t>(0);
   1468   writeInt<uint16_t>(sizeof(FixedInfo));
   1469   writeInt<uint16_t>(0);
   1470   cantFail(writeCString("VS_VERSION_INFO"));
   1471   padStream(sizeof(uint32_t));
   1472 
   1473   using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
   1474   auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
   1475     static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
   1476     if (!FixedData.IsTypePresent[(int)Type])
   1477       return DefaultOut;
   1478     return FixedData.FixedInfo[(int)Type];
   1479   };
   1480 
   1481   auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
   1482   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
   1483       *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
   1484   FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
   1485   FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
   1486 
   1487   auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
   1488   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
   1489       *std::max_element(ProdVer.begin(), ProdVer.end()),
   1490       "PRODUCTVERSION fields"));
   1491   FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
   1492   FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
   1493 
   1494   FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
   1495   FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
   1496   FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
   1497   FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
   1498   FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
   1499 
   1500   writeObject(FixedInfo);
   1501   padStream(sizeof(uint32_t));
   1502 
   1503   RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
   1504 
   1505   // FIXME: check overflow?
   1506   writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
   1507 
   1508   return Error::success();
   1509 }
   1510 
   1511 Expected<std::unique_ptr<MemoryBuffer>>
   1512 ResourceFileWriter::loadFile(StringRef File) const {
   1513   SmallString<128> Path;
   1514   SmallString<128> Cwd;
   1515   std::unique_ptr<MemoryBuffer> Result;
   1516 
   1517   // 0. The file path is absolute or has a root directory, so we shouldn't
   1518   // try to append it on top of other base directories. (An absolute path
   1519   // must have a root directory, but e.g. the path "\dir\file" on windows
   1520   // isn't considered absolute, but it does have a root directory. As long as
   1521   // sys::path::append doesn't handle appending an absolute path or a path
   1522   // starting with a root directory on top of a base, we must handle this
   1523   // case separately at the top. C++17's path::append handles that case
   1524   // properly though, so if using that to append paths below, this early
   1525   // exception case could be removed.)
   1526   if (sys::path::has_root_directory(File))
   1527     return errorOrToExpected(MemoryBuffer::getFile(
   1528         File, /*IsText=*/false, /*RequiresNullTerminator=*/false));
   1529 
   1530   // 1. The current working directory.
   1531   sys::fs::current_path(Cwd);
   1532   Path.assign(Cwd.begin(), Cwd.end());
   1533   sys::path::append(Path, File);
   1534   if (sys::fs::exists(Path))
   1535     return errorOrToExpected(MemoryBuffer::getFile(
   1536         Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
   1537 
   1538   // 2. The directory of the input resource file, if it is different from the
   1539   // current working directory.
   1540   StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath);
   1541   Path.assign(InputFileDir.begin(), InputFileDir.end());
   1542   sys::path::append(Path, File);
   1543   if (sys::fs::exists(Path))
   1544     return errorOrToExpected(MemoryBuffer::getFile(
   1545         Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
   1546 
   1547   // 3. All of the include directories specified on the command line.
   1548   for (StringRef ForceInclude : Params.Include) {
   1549     Path.assign(ForceInclude.begin(), ForceInclude.end());
   1550     sys::path::append(Path, File);
   1551     if (sys::fs::exists(Path))
   1552       return errorOrToExpected(MemoryBuffer::getFile(
   1553           Path, /*IsText=*/false, /*RequiresNullTerminator=*/false));
   1554   }
   1555 
   1556   if (!Params.NoInclude) {
   1557     if (auto Result = llvm::sys::Process::FindInEnvPath("INCLUDE", File))
   1558       return errorOrToExpected(MemoryBuffer::getFile(
   1559           *Result, /*IsText=*/false, /*RequiresNullTerminator=*/false));
   1560   }
   1561 
   1562   return make_error<StringError>("error : file not found : " + Twine(File),
   1563                                  inconvertibleErrorCode());
   1564 }
   1565 
   1566 } // namespace rc
   1567 } // namespace llvm
   1568