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