18f19343aSMarek Sokolowski //===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===//
28f19343aSMarek Sokolowski //
38f19343aSMarek Sokolowski //                     The LLVM Compiler Infrastructure
48f19343aSMarek Sokolowski //
58f19343aSMarek Sokolowski // This file is distributed under the University of Illinois Open Source
68f19343aSMarek Sokolowski // License. See LICENSE.TXT for details.
78f19343aSMarek Sokolowski //
88f19343aSMarek Sokolowski //===---------------------------------------------------------------------===//
98f19343aSMarek Sokolowski //
108f19343aSMarek Sokolowski // This implements the visitor serializing resources to a .res stream.
118f19343aSMarek Sokolowski //
128f19343aSMarek Sokolowski //===---------------------------------------------------------------------===//
138f19343aSMarek Sokolowski 
148f19343aSMarek Sokolowski #include "ResourceFileWriter.h"
158f19343aSMarek Sokolowski 
168f19343aSMarek Sokolowski #include "llvm/Object/WindowsResource.h"
178f19343aSMarek Sokolowski #include "llvm/Support/ConvertUTF.h"
188f19343aSMarek Sokolowski #include "llvm/Support/Endian.h"
198f19343aSMarek Sokolowski #include "llvm/Support/EndianStream.h"
208f19343aSMarek Sokolowski 
218f19343aSMarek Sokolowski using namespace llvm::support;
228f19343aSMarek Sokolowski 
238f19343aSMarek Sokolowski // Take an expression returning llvm::Error and forward the error if it exists.
248f19343aSMarek Sokolowski #define RETURN_IF_ERROR(Expr)                                                  \
258f19343aSMarek Sokolowski   if (auto Err = (Expr))                                                       \
268f19343aSMarek Sokolowski     return Err;
278f19343aSMarek Sokolowski 
288f19343aSMarek Sokolowski namespace llvm {
298f19343aSMarek Sokolowski namespace rc {
308f19343aSMarek Sokolowski 
3107bc04ffSZachary Turner // Class that employs RAII to save the current FileWriter object state
3222fccd64SMarek Sokolowski // and revert to it as soon as we leave the scope. This is useful if resources
3322fccd64SMarek Sokolowski // declare their own resource-local statements.
3422fccd64SMarek Sokolowski class ContextKeeper {
3522fccd64SMarek Sokolowski   ResourceFileWriter *FileWriter;
3622fccd64SMarek Sokolowski   ResourceFileWriter::ObjectInfo SavedInfo;
3722fccd64SMarek Sokolowski 
3822fccd64SMarek Sokolowski public:
3922fccd64SMarek Sokolowski   ContextKeeper(ResourceFileWriter *V)
4022fccd64SMarek Sokolowski       : FileWriter(V), SavedInfo(V->ObjectData) {}
4122fccd64SMarek Sokolowski   ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; }
4222fccd64SMarek Sokolowski };
4322fccd64SMarek Sokolowski 
448f19343aSMarek Sokolowski static Error createError(Twine Message,
458f19343aSMarek Sokolowski                          std::errc Type = std::errc::invalid_argument) {
468f19343aSMarek Sokolowski   return make_error<StringError>(Message, std::make_error_code(Type));
478f19343aSMarek Sokolowski }
488f19343aSMarek Sokolowski 
498f19343aSMarek Sokolowski static Error checkNumberFits(uint32_t Number, size_t MaxBits, Twine FieldName) {
508f19343aSMarek Sokolowski   assert(1 <= MaxBits && MaxBits <= 32);
518f19343aSMarek Sokolowski   if (!(Number >> MaxBits))
528f19343aSMarek Sokolowski     return Error::success();
538f19343aSMarek Sokolowski   return createError(FieldName + " (" + Twine(Number) + ") does not fit in " +
548f19343aSMarek Sokolowski                          Twine(MaxBits) + " bits.",
558f19343aSMarek Sokolowski                      std::errc::value_too_large);
568f19343aSMarek Sokolowski }
578f19343aSMarek Sokolowski 
588f19343aSMarek Sokolowski template <typename FitType>
598f19343aSMarek Sokolowski static Error checkNumberFits(uint32_t Number, Twine FieldName) {
608f19343aSMarek Sokolowski   return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
618f19343aSMarek Sokolowski }
628f19343aSMarek Sokolowski 
637f7745c0SMarek Sokolowski // A similar function for signed integers.
647f7745c0SMarek Sokolowski template <typename FitType>
657f7745c0SMarek Sokolowski static Error checkSignedNumberFits(uint32_t Number, Twine FieldName,
667f7745c0SMarek Sokolowski                                    bool CanBeNegative) {
677f7745c0SMarek Sokolowski   int32_t SignedNum = Number;
687f7745c0SMarek Sokolowski   if (SignedNum < std::numeric_limits<FitType>::min() ||
697f7745c0SMarek Sokolowski       SignedNum > std::numeric_limits<FitType>::max())
707f7745c0SMarek Sokolowski     return createError(FieldName + " (" + Twine(SignedNum) +
717f7745c0SMarek Sokolowski                            ") does not fit in " + Twine(sizeof(FitType) * 8) +
727f7745c0SMarek Sokolowski                            "-bit signed integer type.",
737f7745c0SMarek Sokolowski                        std::errc::value_too_large);
747f7745c0SMarek Sokolowski 
757f7745c0SMarek Sokolowski   if (!CanBeNegative && SignedNum < 0)
767f7745c0SMarek Sokolowski     return createError(FieldName + " (" + Twine(SignedNum) +
777f7745c0SMarek Sokolowski                        ") cannot be negative.");
787f7745c0SMarek Sokolowski 
797f7745c0SMarek Sokolowski   return Error::success();
807f7745c0SMarek Sokolowski }
817f7745c0SMarek Sokolowski 
8207bc04ffSZachary Turner static Error checkRCInt(RCInt Number, Twine FieldName) {
8307bc04ffSZachary Turner   if (Number.isLong())
8407bc04ffSZachary Turner     return Error::success();
8507bc04ffSZachary Turner   return checkNumberFits<uint16_t>(Number, FieldName);
8607bc04ffSZachary Turner }
8707bc04ffSZachary Turner 
888f19343aSMarek Sokolowski static Error checkIntOrString(IntOrString Value, Twine FieldName) {
898f19343aSMarek Sokolowski   if (!Value.isInt())
908f19343aSMarek Sokolowski     return Error::success();
918f19343aSMarek Sokolowski   return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
928f19343aSMarek Sokolowski }
938f19343aSMarek Sokolowski 
948f19343aSMarek Sokolowski static bool stripQuotes(StringRef &Str, bool &IsLongString) {
958f19343aSMarek Sokolowski   if (!Str.contains('"'))
968f19343aSMarek Sokolowski     return false;
978f19343aSMarek Sokolowski 
988f19343aSMarek Sokolowski   // Just take the contents of the string, checking if it's been marked long.
998f19343aSMarek Sokolowski   IsLongString = Str.startswith_lower("L");
1008f19343aSMarek Sokolowski   if (IsLongString)
1018f19343aSMarek Sokolowski     Str = Str.drop_front();
1028f19343aSMarek Sokolowski 
1038f19343aSMarek Sokolowski   bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
1048f19343aSMarek Sokolowski   (void)StripSuccess;
1058f19343aSMarek Sokolowski   assert(StripSuccess && "Strings should be enclosed in quotes.");
1068f19343aSMarek Sokolowski   return true;
1078f19343aSMarek Sokolowski }
1088f19343aSMarek Sokolowski 
1098f19343aSMarek Sokolowski // Describes a way to handle '\0' characters when processing the string.
1108f19343aSMarek Sokolowski // rc.exe tool sometimes behaves in a weird way in postprocessing.
1118f19343aSMarek Sokolowski // If the string to be output is equivalent to a C-string (e.g. in MENU
1128f19343aSMarek Sokolowski // titles), string is (predictably) truncated after first 0-byte.
1138f19343aSMarek Sokolowski // When outputting a string table, the behavior is equivalent to appending
1148f19343aSMarek Sokolowski // '\0\0' at the end of the string, and then stripping the string
1158f19343aSMarek Sokolowski // before the first '\0\0' occurrence.
1168f19343aSMarek Sokolowski // Finally, when handling strings in user-defined resources, 0-bytes
1178f19343aSMarek Sokolowski // aren't stripped, nor do they terminate the string.
1188f19343aSMarek Sokolowski 
1198f19343aSMarek Sokolowski enum class NullHandlingMethod {
1208f19343aSMarek Sokolowski   UserResource,   // Don't terminate string on '\0'.
1218f19343aSMarek Sokolowski   CutAtNull,      // Terminate string on '\0'.
1228f19343aSMarek Sokolowski   CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
1238f19343aSMarek Sokolowski };
1248f19343aSMarek Sokolowski 
1258f19343aSMarek Sokolowski // Parses an identifier or string and returns a processed version of it.
1268f19343aSMarek Sokolowski // For now, it only strips the string boundaries, but TODO:
1278f19343aSMarek Sokolowski //   * Squash "" to a single ".
1288f19343aSMarek Sokolowski //   * Replace the escape sequences with their processed version.
1298f19343aSMarek Sokolowski // For identifiers, this is no-op.
1308f19343aSMarek Sokolowski static Error processString(StringRef Str, NullHandlingMethod NullHandler,
1318f19343aSMarek Sokolowski                            bool &IsLongString, SmallVectorImpl<UTF16> &Result) {
1328f19343aSMarek Sokolowski   bool IsString = stripQuotes(Str, IsLongString);
1338f19343aSMarek Sokolowski   convertUTF8ToUTF16String(Str, Result);
1348f19343aSMarek Sokolowski 
1358f19343aSMarek Sokolowski   if (!IsString) {
1368f19343aSMarek Sokolowski     // It's an identifier if it's not a string. Make all characters uppercase.
1378f19343aSMarek Sokolowski     for (UTF16 &Ch : Result) {
1388f19343aSMarek Sokolowski       assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII");
1398f19343aSMarek Sokolowski       Ch = toupper(Ch);
1408f19343aSMarek Sokolowski     }
1418f19343aSMarek Sokolowski     return Error::success();
1428f19343aSMarek Sokolowski   }
1438f19343aSMarek Sokolowski 
144*da366693SZachary Turner   switch (NullHandler) {
145*da366693SZachary Turner   case NullHandlingMethod::CutAtNull:
1468f19343aSMarek Sokolowski     for (size_t Pos = 0; Pos < Result.size(); ++Pos)
1478f19343aSMarek Sokolowski       if (Result[Pos] == '\0')
1488f19343aSMarek Sokolowski         Result.resize(Pos);
149*da366693SZachary Turner     break;
150*da366693SZachary Turner 
151*da366693SZachary Turner   case NullHandlingMethod::CutAtDoubleNull:
152*da366693SZachary Turner     for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos)
153*da366693SZachary Turner       if (Result[Pos] == '\0' && Result[Pos + 1] == '\0')
154*da366693SZachary Turner         Result.resize(Pos);
155*da366693SZachary Turner     if (Result.size() > 0 && Result.back() == '\0')
156*da366693SZachary Turner       Result.pop_back();
157*da366693SZachary Turner     break;
158*da366693SZachary Turner 
159*da366693SZachary Turner   case NullHandlingMethod::UserResource:
160*da366693SZachary Turner     break;
161*da366693SZachary Turner   }
1628f19343aSMarek Sokolowski 
1638f19343aSMarek Sokolowski   return Error::success();
1648f19343aSMarek Sokolowski }
1658f19343aSMarek Sokolowski 
1668f19343aSMarek Sokolowski uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
1678f19343aSMarek Sokolowski   uint64_t Result = tell();
1688f19343aSMarek Sokolowski   FS->write((const char *)Data.begin(), Data.size());
1698f19343aSMarek Sokolowski   return Result;
1708f19343aSMarek Sokolowski }
1718f19343aSMarek Sokolowski 
1728f19343aSMarek Sokolowski Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
1738f19343aSMarek Sokolowski   SmallVector<UTF16, 128> ProcessedString;
1748f19343aSMarek Sokolowski   bool IsLongString;
1758f19343aSMarek Sokolowski   RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
1768f19343aSMarek Sokolowski                                 IsLongString, ProcessedString));
1778f19343aSMarek Sokolowski   for (auto Ch : ProcessedString)
1788f19343aSMarek Sokolowski     writeInt<uint16_t>(Ch);
1798f19343aSMarek Sokolowski   if (WriteTerminator)
1808f19343aSMarek Sokolowski     writeInt<uint16_t>(0);
1818f19343aSMarek Sokolowski   return Error::success();
1828f19343aSMarek Sokolowski }
1838f19343aSMarek Sokolowski 
1848f19343aSMarek Sokolowski Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
1858f19343aSMarek Sokolowski   return writeIntOrString(Ident);
1868f19343aSMarek Sokolowski }
1878f19343aSMarek Sokolowski 
1888f19343aSMarek Sokolowski Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
1898f19343aSMarek Sokolowski   if (!Value.isInt())
1908f19343aSMarek Sokolowski     return writeCString(Value.getString());
1918f19343aSMarek Sokolowski 
1928f19343aSMarek Sokolowski   writeInt<uint16_t>(0xFFFF);
1938f19343aSMarek Sokolowski   writeInt<uint16_t>(Value.getInt());
1948f19343aSMarek Sokolowski   return Error::success();
1958f19343aSMarek Sokolowski }
1968f19343aSMarek Sokolowski 
19707bc04ffSZachary Turner void ResourceFileWriter::writeRCInt(RCInt Value) {
19807bc04ffSZachary Turner   if (Value.isLong())
19907bc04ffSZachary Turner     writeObject((uint32_t)Value);
20007bc04ffSZachary Turner   else
20107bc04ffSZachary Turner     writeObject((uint16_t)Value);
20207bc04ffSZachary Turner }
20307bc04ffSZachary Turner 
2048f19343aSMarek Sokolowski Error ResourceFileWriter::appendFile(StringRef Filename) {
2058f19343aSMarek Sokolowski   bool IsLong;
2068f19343aSMarek Sokolowski   stripQuotes(Filename, IsLong);
2078f19343aSMarek Sokolowski 
2088f19343aSMarek Sokolowski   // Filename path should be relative to the current working directory.
2098f19343aSMarek Sokolowski   // FIXME: docs say so, but reality is more complicated, script
2108f19343aSMarek Sokolowski   // location and include paths must be taken into account.
2118f19343aSMarek Sokolowski   ErrorOr<std::unique_ptr<MemoryBuffer>> File =
2128f19343aSMarek Sokolowski       MemoryBuffer::getFile(Filename, -1, false);
2138f19343aSMarek Sokolowski   if (!File)
2148f19343aSMarek Sokolowski     return make_error<StringError>("Error opening file '" + Filename +
2158f19343aSMarek Sokolowski                                        "': " + File.getError().message(),
2168f19343aSMarek Sokolowski                                    File.getError());
2178f19343aSMarek Sokolowski   *FS << (*File)->getBuffer();
2188f19343aSMarek Sokolowski   return Error::success();
2198f19343aSMarek Sokolowski }
2208f19343aSMarek Sokolowski 
2218f19343aSMarek Sokolowski void ResourceFileWriter::padStream(uint64_t Length) {
2228f19343aSMarek Sokolowski   assert(Length > 0);
2238f19343aSMarek Sokolowski   uint64_t Location = tell();
2248f19343aSMarek Sokolowski   Location %= Length;
2258f19343aSMarek Sokolowski   uint64_t Pad = (Length - Location) % Length;
2268f19343aSMarek Sokolowski   for (uint64_t i = 0; i < Pad; ++i)
2278f19343aSMarek Sokolowski     writeInt<uint8_t>(0);
2288f19343aSMarek Sokolowski }
2298f19343aSMarek Sokolowski 
2308f19343aSMarek Sokolowski Error ResourceFileWriter::handleError(Error &&Err, const RCResource *Res) {
2318f19343aSMarek Sokolowski   if (Err)
2328f19343aSMarek Sokolowski     return joinErrors(createError("Error in " + Res->getResourceTypeName() +
2338f19343aSMarek Sokolowski                                   " statement (ID " + Twine(Res->ResName) +
2348f19343aSMarek Sokolowski                                   "): "),
2358f19343aSMarek Sokolowski                       std::move(Err));
2368f19343aSMarek Sokolowski   return Error::success();
2378f19343aSMarek Sokolowski }
2388f19343aSMarek Sokolowski 
2398f19343aSMarek Sokolowski Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
2408f19343aSMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeNullBody);
2418f19343aSMarek Sokolowski }
2428f19343aSMarek Sokolowski 
24322fccd64SMarek Sokolowski Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
24422fccd64SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
24522fccd64SMarek Sokolowski }
24622fccd64SMarek Sokolowski 
247c3ab013aSZachary Turner Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
248c3ab013aSZachary Turner   return handleError(visitIconOrCursorResource(Res), Res);
249c3ab013aSZachary Turner }
250c3ab013aSZachary Turner 
2517f7745c0SMarek Sokolowski Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
2527f7745c0SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeDialogBody);
2537f7745c0SMarek Sokolowski }
2547f7745c0SMarek Sokolowski 
255c3ab013aSZachary Turner Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
256c3ab013aSZachary Turner   return handleError(visitIconOrCursorResource(Res), Res);
257c3ab013aSZachary Turner }
258c3ab013aSZachary Turner 
259420090afSZachary Turner Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
260420090afSZachary Turner   ObjectData.Caption = Stmt->Value;
261420090afSZachary Turner   return Error::success();
262420090afSZachary Turner }
263420090afSZachary Turner 
2648f19343aSMarek Sokolowski Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
2658f19343aSMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
2668f19343aSMarek Sokolowski }
2678f19343aSMarek Sokolowski 
26842f494d6SMarek Sokolowski Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
26942f494d6SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeMenuBody);
27042f494d6SMarek Sokolowski }
27142f494d6SMarek Sokolowski 
272*da366693SZachary Turner Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
273*da366693SZachary Turner   const auto *Res = cast<StringTableResource>(Base);
274*da366693SZachary Turner 
275*da366693SZachary Turner   ContextKeeper RAII(this);
276*da366693SZachary Turner   RETURN_IF_ERROR(Res->applyStmts(this));
277*da366693SZachary Turner 
278*da366693SZachary Turner   for (auto &String : Res->Table) {
279*da366693SZachary Turner     RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID"));
280*da366693SZachary Turner     uint16_t BundleID = String.first >> 4;
281*da366693SZachary Turner     StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo);
282*da366693SZachary Turner     auto &BundleData = StringTableData.BundleData;
283*da366693SZachary Turner     auto Iter = BundleData.find(Key);
284*da366693SZachary Turner 
285*da366693SZachary Turner     if (Iter == BundleData.end()) {
286*da366693SZachary Turner       // Need to create a bundle.
287*da366693SZachary Turner       StringTableData.BundleList.push_back(Key);
288*da366693SZachary Turner       auto EmplaceResult =
289*da366693SZachary Turner           BundleData.emplace(Key, StringTableInfo::Bundle(ObjectData));
290*da366693SZachary Turner       assert(EmplaceResult.second && "Could not create a bundle");
291*da366693SZachary Turner       Iter = EmplaceResult.first;
292*da366693SZachary Turner     }
293*da366693SZachary Turner 
294*da366693SZachary Turner     RETURN_IF_ERROR(
295*da366693SZachary Turner         insertStringIntoBundle(Iter->second, String.first, String.second));
296*da366693SZachary Turner   }
297*da366693SZachary Turner 
298*da366693SZachary Turner   return Error::success();
299*da366693SZachary Turner }
300*da366693SZachary Turner 
30107bc04ffSZachary Turner Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) {
30207bc04ffSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody);
30307bc04ffSZachary Turner }
30407bc04ffSZachary Turner 
30522fccd64SMarek Sokolowski Error ResourceFileWriter::visitCharacteristicsStmt(
30622fccd64SMarek Sokolowski     const CharacteristicsStmt *Stmt) {
30722fccd64SMarek Sokolowski   ObjectData.Characteristics = Stmt->Value;
30822fccd64SMarek Sokolowski   return Error::success();
30922fccd64SMarek Sokolowski }
31022fccd64SMarek Sokolowski 
311420090afSZachary Turner Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
312420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
313420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
314420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
315420090afSZachary Turner   ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
316420090afSZachary Turner                             Stmt->Charset};
317420090afSZachary Turner   ObjectData.Font.emplace(Font);
318420090afSZachary Turner   return Error::success();
319420090afSZachary Turner }
320420090afSZachary Turner 
3218f19343aSMarek Sokolowski Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
3228f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
3238f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
3248f19343aSMarek Sokolowski   ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
3258f19343aSMarek Sokolowski   return Error::success();
3268f19343aSMarek Sokolowski }
3278f19343aSMarek Sokolowski 
328420090afSZachary Turner Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
329420090afSZachary Turner   ObjectData.Style = Stmt->Value;
330420090afSZachary Turner   return Error::success();
331420090afSZachary Turner }
332420090afSZachary Turner 
33322fccd64SMarek Sokolowski Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
33422fccd64SMarek Sokolowski   ObjectData.VersionInfo = Stmt->Value;
33522fccd64SMarek Sokolowski   return Error::success();
33622fccd64SMarek Sokolowski }
33722fccd64SMarek Sokolowski 
3388f19343aSMarek Sokolowski Error ResourceFileWriter::writeResource(
3398f19343aSMarek Sokolowski     const RCResource *Res,
3408f19343aSMarek Sokolowski     Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
3418f19343aSMarek Sokolowski   // We don't know the sizes yet.
3428f19343aSMarek Sokolowski   object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
3438f19343aSMarek Sokolowski   uint64_t HeaderLoc = writeObject(HeaderPrefix);
3448f19343aSMarek Sokolowski 
3458f19343aSMarek Sokolowski   auto ResType = Res->getResourceType();
3468f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
3478f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
3488f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
3498f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
3508f19343aSMarek Sokolowski 
35122fccd64SMarek Sokolowski   // Apply the resource-local optional statements.
35222fccd64SMarek Sokolowski   ContextKeeper RAII(this);
35322fccd64SMarek Sokolowski   RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
35422fccd64SMarek Sokolowski 
3558f19343aSMarek Sokolowski   padStream(sizeof(uint32_t));
3568f19343aSMarek Sokolowski   object::WinResHeaderSuffix HeaderSuffix{
3578f19343aSMarek Sokolowski       ulittle32_t(0), // DataVersion; seems to always be 0
3588f19343aSMarek Sokolowski       ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo),
35922fccd64SMarek Sokolowski       ulittle32_t(ObjectData.VersionInfo),
36022fccd64SMarek Sokolowski       ulittle32_t(ObjectData.Characteristics)};
3618f19343aSMarek Sokolowski   writeObject(HeaderSuffix);
3628f19343aSMarek Sokolowski 
3638f19343aSMarek Sokolowski   uint64_t DataLoc = tell();
3648f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
3658f19343aSMarek Sokolowski   // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
3668f19343aSMarek Sokolowski 
3678f19343aSMarek Sokolowski   // Update the sizes.
3688f19343aSMarek Sokolowski   HeaderPrefix.DataSize = tell() - DataLoc;
3698f19343aSMarek Sokolowski   HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
3708f19343aSMarek Sokolowski   writeObjectAt(HeaderPrefix, HeaderLoc);
3718f19343aSMarek Sokolowski   padStream(sizeof(uint32_t));
3728f19343aSMarek Sokolowski 
3738f19343aSMarek Sokolowski   return Error::success();
3748f19343aSMarek Sokolowski }
3758f19343aSMarek Sokolowski 
37622fccd64SMarek Sokolowski // --- NullResource helpers. --- //
37722fccd64SMarek Sokolowski 
3788f19343aSMarek Sokolowski Error ResourceFileWriter::writeNullBody(const RCResource *) {
3798f19343aSMarek Sokolowski   return Error::success();
3808f19343aSMarek Sokolowski }
3818f19343aSMarek Sokolowski 
38222fccd64SMarek Sokolowski // --- AcceleratorsResource helpers. --- //
38322fccd64SMarek Sokolowski 
38422fccd64SMarek Sokolowski Error ResourceFileWriter::writeSingleAccelerator(
38522fccd64SMarek Sokolowski     const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
38622fccd64SMarek Sokolowski   using Accelerator = AcceleratorsResource::Accelerator;
38722fccd64SMarek Sokolowski   using Opt = Accelerator::Options;
38822fccd64SMarek Sokolowski 
38922fccd64SMarek Sokolowski   struct AccelTableEntry {
39022fccd64SMarek Sokolowski     ulittle16_t Flags;
39122fccd64SMarek Sokolowski     ulittle16_t ANSICode;
39222fccd64SMarek Sokolowski     ulittle16_t Id;
39322fccd64SMarek Sokolowski     uint16_t Padding;
39422fccd64SMarek Sokolowski   } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
39522fccd64SMarek Sokolowski 
39622fccd64SMarek Sokolowski   bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
39722fccd64SMarek Sokolowski 
39822fccd64SMarek Sokolowski   // Remove ASCII flags (which doesn't occur in .res files).
39922fccd64SMarek Sokolowski   Entry.Flags = Obj.Flags & ~Opt::ASCII;
40022fccd64SMarek Sokolowski 
40122fccd64SMarek Sokolowski   if (IsLastItem)
40222fccd64SMarek Sokolowski     Entry.Flags |= 0x80;
40322fccd64SMarek Sokolowski 
40422fccd64SMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
40522fccd64SMarek Sokolowski   Entry.Id = ulittle16_t(Obj.Id);
40622fccd64SMarek Sokolowski 
40722fccd64SMarek Sokolowski   auto createAccError = [&Obj](const char *Msg) {
40822fccd64SMarek Sokolowski     return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
40922fccd64SMarek Sokolowski   };
41022fccd64SMarek Sokolowski 
41122fccd64SMarek Sokolowski   if (IsASCII && IsVirtKey)
41222fccd64SMarek Sokolowski     return createAccError("Accelerator can't be both ASCII and VIRTKEY");
41322fccd64SMarek Sokolowski 
41422fccd64SMarek Sokolowski   if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
41522fccd64SMarek Sokolowski     return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
41622fccd64SMarek Sokolowski                           " accelerators");
41722fccd64SMarek Sokolowski 
41822fccd64SMarek Sokolowski   if (Obj.Event.isInt()) {
41922fccd64SMarek Sokolowski     if (!IsASCII && !IsVirtKey)
42022fccd64SMarek Sokolowski       return createAccError(
42122fccd64SMarek Sokolowski           "Accelerator with a numeric event must be either ASCII"
42222fccd64SMarek Sokolowski           " or VIRTKEY");
42322fccd64SMarek Sokolowski 
42422fccd64SMarek Sokolowski     uint32_t EventVal = Obj.Event.getInt();
42522fccd64SMarek Sokolowski     RETURN_IF_ERROR(
42622fccd64SMarek Sokolowski         checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
42722fccd64SMarek Sokolowski     Entry.ANSICode = ulittle16_t(EventVal);
42822fccd64SMarek Sokolowski     writeObject(Entry);
42922fccd64SMarek Sokolowski     return Error::success();
43022fccd64SMarek Sokolowski   }
43122fccd64SMarek Sokolowski 
43222fccd64SMarek Sokolowski   StringRef Str = Obj.Event.getString();
43322fccd64SMarek Sokolowski   bool IsWide;
43422fccd64SMarek Sokolowski   stripQuotes(Str, IsWide);
43522fccd64SMarek Sokolowski 
43622fccd64SMarek Sokolowski   if (Str.size() == 0 || Str.size() > 2)
43722fccd64SMarek Sokolowski     return createAccError(
43822fccd64SMarek Sokolowski         "Accelerator string events should have length 1 or 2");
43922fccd64SMarek Sokolowski 
44022fccd64SMarek Sokolowski   if (Str[0] == '^') {
44122fccd64SMarek Sokolowski     if (Str.size() == 1)
44222fccd64SMarek Sokolowski       return createAccError("No character following '^' in accelerator event");
44322fccd64SMarek Sokolowski     if (IsVirtKey)
44422fccd64SMarek Sokolowski       return createAccError(
44522fccd64SMarek Sokolowski           "VIRTKEY accelerator events can't be preceded by '^'");
44622fccd64SMarek Sokolowski 
44722fccd64SMarek Sokolowski     char Ch = Str[1];
44822fccd64SMarek Sokolowski     if (Ch >= 'a' && Ch <= 'z')
44922fccd64SMarek Sokolowski       Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
45022fccd64SMarek Sokolowski     else if (Ch >= 'A' && Ch <= 'Z')
45122fccd64SMarek Sokolowski       Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
45222fccd64SMarek Sokolowski     else
45322fccd64SMarek Sokolowski       return createAccError("Control character accelerator event should be"
45422fccd64SMarek Sokolowski                             " alphabetic");
45522fccd64SMarek Sokolowski 
45622fccd64SMarek Sokolowski     writeObject(Entry);
45722fccd64SMarek Sokolowski     return Error::success();
45822fccd64SMarek Sokolowski   }
45922fccd64SMarek Sokolowski 
46022fccd64SMarek Sokolowski   if (Str.size() == 2)
46122fccd64SMarek Sokolowski     return createAccError("Event string should be one-character, possibly"
46222fccd64SMarek Sokolowski                           " preceded by '^'");
46322fccd64SMarek Sokolowski 
46422fccd64SMarek Sokolowski   uint8_t EventCh = Str[0];
46522fccd64SMarek Sokolowski   // The original tool just warns in this situation. We chose to fail.
46622fccd64SMarek Sokolowski   if (IsVirtKey && !isalnum(EventCh))
46722fccd64SMarek Sokolowski     return createAccError("Non-alphanumeric characters cannot describe virtual"
46822fccd64SMarek Sokolowski                           " keys");
46922fccd64SMarek Sokolowski   if (EventCh > 0x7F)
47022fccd64SMarek Sokolowski     return createAccError("Non-ASCII description of accelerator");
47122fccd64SMarek Sokolowski 
47222fccd64SMarek Sokolowski   if (IsVirtKey)
47322fccd64SMarek Sokolowski     EventCh = toupper(EventCh);
47422fccd64SMarek Sokolowski   Entry.ANSICode = ulittle16_t(EventCh);
47522fccd64SMarek Sokolowski   writeObject(Entry);
47622fccd64SMarek Sokolowski   return Error::success();
47722fccd64SMarek Sokolowski }
47822fccd64SMarek Sokolowski 
47922fccd64SMarek Sokolowski Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
48022fccd64SMarek Sokolowski   auto *Res = cast<AcceleratorsResource>(Base);
48122fccd64SMarek Sokolowski   size_t AcceleratorId = 0;
48222fccd64SMarek Sokolowski   for (auto &Acc : Res->Accelerators) {
48322fccd64SMarek Sokolowski     ++AcceleratorId;
48422fccd64SMarek Sokolowski     RETURN_IF_ERROR(
48522fccd64SMarek Sokolowski         writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
48622fccd64SMarek Sokolowski   }
48722fccd64SMarek Sokolowski   return Error::success();
48822fccd64SMarek Sokolowski }
48922fccd64SMarek Sokolowski 
490c3ab013aSZachary Turner // --- CursorResource and IconResource helpers. --- //
491c3ab013aSZachary Turner 
492c3ab013aSZachary Turner // ICONRESDIR structure. Describes a single icon in resouce group.
493c3ab013aSZachary Turner //
494c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
495c3ab013aSZachary Turner struct IconResDir {
496c3ab013aSZachary Turner   uint8_t Width;
497c3ab013aSZachary Turner   uint8_t Height;
498c3ab013aSZachary Turner   uint8_t ColorCount;
499c3ab013aSZachary Turner   uint8_t Reserved;
500c3ab013aSZachary Turner };
501c3ab013aSZachary Turner 
502c3ab013aSZachary Turner // CURSORDIR structure. Describes a single cursor in resource group.
503c3ab013aSZachary Turner //
504c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
505c3ab013aSZachary Turner struct CursorDir {
506c3ab013aSZachary Turner   ulittle16_t Width;
507c3ab013aSZachary Turner   ulittle16_t Height;
508c3ab013aSZachary Turner };
509c3ab013aSZachary Turner 
510c3ab013aSZachary Turner // RESDIRENTRY structure, stripped from the last item. Stripping made
511c3ab013aSZachary Turner // for compatibility with RESDIR.
512c3ab013aSZachary Turner //
513c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
514c3ab013aSZachary Turner struct ResourceDirEntryStart {
515c3ab013aSZachary Turner   union {
516c3ab013aSZachary Turner     CursorDir Cursor; // Used in CURSOR resources.
517c3ab013aSZachary Turner     IconResDir Icon;  // Used in .ico and .cur files, and ICON resources.
518c3ab013aSZachary Turner   };
519c3ab013aSZachary Turner   ulittle16_t Planes;   // HotspotX (.cur files but not CURSOR resource).
520c3ab013aSZachary Turner   ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
521c3ab013aSZachary Turner   ulittle32_t Size;
522c3ab013aSZachary Turner   // ulittle32_t ImageOffset;  // Offset to image data (ICONDIRENTRY only).
523c3ab013aSZachary Turner   // ulittle16_t IconID;       // Resource icon ID (RESDIR only).
524c3ab013aSZachary Turner };
525c3ab013aSZachary Turner 
526c3ab013aSZachary Turner // BITMAPINFOHEADER structure. Describes basic information about the bitmap
527c3ab013aSZachary Turner // being read.
528c3ab013aSZachary Turner //
529c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
530c3ab013aSZachary Turner struct BitmapInfoHeader {
531c3ab013aSZachary Turner   ulittle32_t Size;
532c3ab013aSZachary Turner   ulittle32_t Width;
533c3ab013aSZachary Turner   ulittle32_t Height;
534c3ab013aSZachary Turner   ulittle16_t Planes;
535c3ab013aSZachary Turner   ulittle16_t BitCount;
536c3ab013aSZachary Turner   ulittle32_t Compression;
537c3ab013aSZachary Turner   ulittle32_t SizeImage;
538c3ab013aSZachary Turner   ulittle32_t XPelsPerMeter;
539c3ab013aSZachary Turner   ulittle32_t YPelsPerMeter;
540c3ab013aSZachary Turner   ulittle32_t ClrUsed;
541c3ab013aSZachary Turner   ulittle32_t ClrImportant;
542c3ab013aSZachary Turner };
543c3ab013aSZachary Turner 
544c3ab013aSZachary Turner // Group icon directory header. Called ICONDIR in .ico/.cur files and
545c3ab013aSZachary Turner // NEWHEADER in .res files.
546c3ab013aSZachary Turner //
547c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
548c3ab013aSZachary Turner struct GroupIconDir {
549c3ab013aSZachary Turner   ulittle16_t Reserved; // Always 0.
550c3ab013aSZachary Turner   ulittle16_t ResType;  // 1 for icons, 2 for cursors.
551c3ab013aSZachary Turner   ulittle16_t ResCount; // Number of items.
552c3ab013aSZachary Turner };
553c3ab013aSZachary Turner 
554c3ab013aSZachary Turner enum class IconCursorGroupType { Icon, Cursor };
555c3ab013aSZachary Turner 
556c3ab013aSZachary Turner class SingleIconCursorResource : public RCResource {
557c3ab013aSZachary Turner public:
558c3ab013aSZachary Turner   IconCursorGroupType Type;
559c3ab013aSZachary Turner   const ResourceDirEntryStart &Header;
560c3ab013aSZachary Turner   ArrayRef<uint8_t> Image;
561c3ab013aSZachary Turner 
562c3ab013aSZachary Turner   SingleIconCursorResource(IconCursorGroupType ResourceType,
563c3ab013aSZachary Turner                            const ResourceDirEntryStart &HeaderEntry,
564c3ab013aSZachary Turner                            ArrayRef<uint8_t> ImageData)
565c3ab013aSZachary Turner       : Type(ResourceType), Header(HeaderEntry), Image(ImageData) {}
566c3ab013aSZachary Turner 
567c3ab013aSZachary Turner   Twine getResourceTypeName() const override { return "Icon/cursor image"; }
568c3ab013aSZachary Turner   IntOrString getResourceType() const override {
569c3ab013aSZachary Turner     return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
570c3ab013aSZachary Turner   }
571c3ab013aSZachary Turner   uint16_t getMemoryFlags() const override {
572c3ab013aSZachary Turner     return MfDiscardable | MfMoveable;
573c3ab013aSZachary Turner   }
574c3ab013aSZachary Turner   ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
575c3ab013aSZachary Turner   static bool classof(const RCResource *Res) {
576c3ab013aSZachary Turner     return Res->getKind() == RkSingleCursorOrIconRes;
577c3ab013aSZachary Turner   }
578c3ab013aSZachary Turner };
579c3ab013aSZachary Turner 
580c3ab013aSZachary Turner class IconCursorGroupResource : public RCResource {
581c3ab013aSZachary Turner public:
582c3ab013aSZachary Turner   IconCursorGroupType Type;
583c3ab013aSZachary Turner   GroupIconDir Header;
584c3ab013aSZachary Turner   std::vector<ResourceDirEntryStart> ItemEntries;
585c3ab013aSZachary Turner 
586c3ab013aSZachary Turner   IconCursorGroupResource(IconCursorGroupType ResourceType,
587c3ab013aSZachary Turner                           const GroupIconDir &HeaderData,
588c3ab013aSZachary Turner                           std::vector<ResourceDirEntryStart> &&Entries)
589c3ab013aSZachary Turner       : Type(ResourceType), Header(HeaderData),
590c3ab013aSZachary Turner         ItemEntries(std::move(Entries)) {}
591c3ab013aSZachary Turner 
592c3ab013aSZachary Turner   Twine getResourceTypeName() const override { return "Icon/cursor group"; }
593c3ab013aSZachary Turner   IntOrString getResourceType() const override {
594c3ab013aSZachary Turner     return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
595c3ab013aSZachary Turner   }
596c3ab013aSZachary Turner   ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
597c3ab013aSZachary Turner   static bool classof(const RCResource *Res) {
598c3ab013aSZachary Turner     return Res->getKind() == RkCursorOrIconGroupRes;
599c3ab013aSZachary Turner   }
600c3ab013aSZachary Turner };
601c3ab013aSZachary Turner 
602c3ab013aSZachary Turner Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
603c3ab013aSZachary Turner   auto *Res = cast<SingleIconCursorResource>(Base);
604c3ab013aSZachary Turner   if (Res->Type == IconCursorGroupType::Cursor) {
605c3ab013aSZachary Turner     // In case of cursors, two WORDS are appended to the beginning
606c3ab013aSZachary Turner     // of the resource: HotspotX (Planes in RESDIRENTRY),
607c3ab013aSZachary Turner     // and HotspotY (BitCount).
608c3ab013aSZachary Turner     //
609c3ab013aSZachary Turner     // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
610c3ab013aSZachary Turner     //  (Remarks section).
611c3ab013aSZachary Turner     writeObject(Res->Header.Planes);
612c3ab013aSZachary Turner     writeObject(Res->Header.BitCount);
613c3ab013aSZachary Turner   }
614c3ab013aSZachary Turner 
615c3ab013aSZachary Turner   writeObject(Res->Image);
616c3ab013aSZachary Turner   return Error::success();
617c3ab013aSZachary Turner }
618c3ab013aSZachary Turner 
619c3ab013aSZachary Turner Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
620c3ab013aSZachary Turner   auto *Res = cast<IconCursorGroupResource>(Base);
621c3ab013aSZachary Turner   writeObject(Res->Header);
622c3ab013aSZachary Turner   for (auto Item : Res->ItemEntries) {
623c3ab013aSZachary Turner     writeObject(Item);
624c3ab013aSZachary Turner     writeObject(ulittle16_t(IconCursorID++));
625c3ab013aSZachary Turner   }
626c3ab013aSZachary Turner   return Error::success();
627c3ab013aSZachary Turner }
628c3ab013aSZachary Turner 
629c3ab013aSZachary Turner Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
630c3ab013aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
631c3ab013aSZachary Turner }
632c3ab013aSZachary Turner 
633c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
634c3ab013aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
635c3ab013aSZachary Turner }
636c3ab013aSZachary Turner 
637c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
638c3ab013aSZachary Turner   IconCursorGroupType Type;
639c3ab013aSZachary Turner   StringRef FileStr;
640c3ab013aSZachary Turner   IntOrString ResName = Base->ResName;
641c3ab013aSZachary Turner 
642c3ab013aSZachary Turner   if (auto *IconRes = dyn_cast<IconResource>(Base)) {
643c3ab013aSZachary Turner     FileStr = IconRes->IconLoc;
644c3ab013aSZachary Turner     Type = IconCursorGroupType::Icon;
645c3ab013aSZachary Turner   } else {
646c3ab013aSZachary Turner     auto *CursorRes = dyn_cast<CursorResource>(Base);
647c3ab013aSZachary Turner     FileStr = CursorRes->CursorLoc;
648c3ab013aSZachary Turner     Type = IconCursorGroupType::Cursor;
649c3ab013aSZachary Turner   }
650c3ab013aSZachary Turner 
651c3ab013aSZachary Turner   bool IsLong;
652c3ab013aSZachary Turner   stripQuotes(FileStr, IsLong);
653c3ab013aSZachary Turner   ErrorOr<std::unique_ptr<MemoryBuffer>> File =
654c3ab013aSZachary Turner       MemoryBuffer::getFile(FileStr, -1, false);
655c3ab013aSZachary Turner 
656c3ab013aSZachary Turner   if (!File)
657c3ab013aSZachary Turner     return make_error<StringError>(
658c3ab013aSZachary Turner         "Error opening " +
659c3ab013aSZachary Turner             Twine(Type == IconCursorGroupType::Icon ? "icon" : "cursor") +
660c3ab013aSZachary Turner             " '" + FileStr + "': " + File.getError().message(),
661c3ab013aSZachary Turner         File.getError());
662c3ab013aSZachary Turner 
663c3ab013aSZachary Turner   BinaryStreamReader Reader((*File)->getBuffer(), support::little);
664c3ab013aSZachary Turner 
665c3ab013aSZachary Turner   // Read the file headers.
666c3ab013aSZachary Turner   //   - At the beginning, ICONDIR/NEWHEADER header.
667c3ab013aSZachary Turner   //   - Then, a number of RESDIR headers follow. These contain offsets
668c3ab013aSZachary Turner   //       to data.
669c3ab013aSZachary Turner   const GroupIconDir *Header;
670c3ab013aSZachary Turner 
671c3ab013aSZachary Turner   RETURN_IF_ERROR(Reader.readObject(Header));
672c3ab013aSZachary Turner   if (Header->Reserved != 0)
673c3ab013aSZachary Turner     return createError("Incorrect icon/cursor Reserved field; should be 0.");
674c3ab013aSZachary Turner   uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
675c3ab013aSZachary Turner   if (Header->ResType != NeededType)
676c3ab013aSZachary Turner     return createError("Incorrect icon/cursor ResType field; should be " +
677c3ab013aSZachary Turner                        Twine(NeededType) + ".");
678c3ab013aSZachary Turner 
679c3ab013aSZachary Turner   uint16_t NumItems = Header->ResCount;
680c3ab013aSZachary Turner 
681c3ab013aSZachary Turner   // Read single ico/cur headers.
682c3ab013aSZachary Turner   std::vector<ResourceDirEntryStart> ItemEntries;
683c3ab013aSZachary Turner   ItemEntries.reserve(NumItems);
684c3ab013aSZachary Turner   std::vector<uint32_t> ItemOffsets(NumItems);
685c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
686c3ab013aSZachary Turner     const ResourceDirEntryStart *Object;
687c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readObject(Object));
688c3ab013aSZachary Turner     ItemEntries.push_back(*Object);
689c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
690c3ab013aSZachary Turner   }
691c3ab013aSZachary Turner 
692c3ab013aSZachary Turner   // Now write each icon/cursors one by one. At first, all the contents
693c3ab013aSZachary Turner   // without ICO/CUR header. This is described by SingleIconCursorResource.
694c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
695c3ab013aSZachary Turner     // Load the fragment of file.
696c3ab013aSZachary Turner     Reader.setOffset(ItemOffsets[ID]);
697c3ab013aSZachary Turner     ArrayRef<uint8_t> Image;
698c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
699c3ab013aSZachary Turner     SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image);
700c3ab013aSZachary Turner     SingleRes.setName(IconCursorID + ID);
701c3ab013aSZachary Turner     RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
702c3ab013aSZachary Turner   }
703c3ab013aSZachary Turner 
704c3ab013aSZachary Turner   // Now, write all the headers concatenated into a separate resource.
705c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
706c3ab013aSZachary Turner     if (Type == IconCursorGroupType::Icon) {
707c3ab013aSZachary Turner       // rc.exe seems to always set NumPlanes to 1. No idea why it happens.
708c3ab013aSZachary Turner       ItemEntries[ID].Planes = 1;
709c3ab013aSZachary Turner       continue;
710c3ab013aSZachary Turner     }
711c3ab013aSZachary Turner 
712c3ab013aSZachary Turner     // We need to rewrite the cursor headers.
713c3ab013aSZachary Turner     const auto &OldHeader = ItemEntries[ID];
714c3ab013aSZachary Turner     ResourceDirEntryStart NewHeader;
715c3ab013aSZachary Turner     NewHeader.Cursor.Width = OldHeader.Icon.Width;
716c3ab013aSZachary Turner     // Each cursor in fact stores two bitmaps, one under another.
717c3ab013aSZachary Turner     // Height provided in cursor definition describes the height of the
718c3ab013aSZachary Turner     // cursor, whereas the value existing in resource definition describes
719c3ab013aSZachary Turner     // the height of the bitmap. Therefore, we need to double this height.
720c3ab013aSZachary Turner     NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
721c3ab013aSZachary Turner 
722c3ab013aSZachary Turner     // Now, we actually need to read the bitmap header to find
723c3ab013aSZachary Turner     // the number of planes and the number of bits per pixel.
724c3ab013aSZachary Turner     Reader.setOffset(ItemOffsets[ID]);
725c3ab013aSZachary Turner     const BitmapInfoHeader *BMPHeader;
726c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readObject(BMPHeader));
727c3ab013aSZachary Turner     NewHeader.Planes = BMPHeader->Planes;
728c3ab013aSZachary Turner     NewHeader.BitCount = BMPHeader->BitCount;
729c3ab013aSZachary Turner 
730c3ab013aSZachary Turner     // Two WORDs were written at the beginning of the resource (hotspot
731c3ab013aSZachary Turner     // location). This is reflected in Size field.
732c3ab013aSZachary Turner     NewHeader.Size = OldHeader.Size + 2 * sizeof(uint16_t);
733c3ab013aSZachary Turner 
734c3ab013aSZachary Turner     ItemEntries[ID] = NewHeader;
735c3ab013aSZachary Turner   }
736c3ab013aSZachary Turner 
737c3ab013aSZachary Turner   IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
738c3ab013aSZachary Turner   HeaderRes.setName(ResName);
739c3ab013aSZachary Turner   RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
740c3ab013aSZachary Turner 
741c3ab013aSZachary Turner   return Error::success();
742c3ab013aSZachary Turner }
743c3ab013aSZachary Turner 
7447f7745c0SMarek Sokolowski // --- DialogResource helpers. --- //
74522fccd64SMarek Sokolowski 
7467f7745c0SMarek Sokolowski Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
7477f7745c0SMarek Sokolowski                                                    bool IsExtended) {
7487f7745c0SMarek Sokolowski   // Each control should be aligned to DWORD.
7497f7745c0SMarek Sokolowski   padStream(sizeof(uint32_t));
7507f7745c0SMarek Sokolowski 
7517f7745c0SMarek Sokolowski   auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
7527f7745c0SMarek Sokolowski   uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0);
7537f7745c0SMarek Sokolowski   uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
7547f7745c0SMarek Sokolowski 
7557f7745c0SMarek Sokolowski   // DIALOG(EX) item header prefix.
7567f7745c0SMarek Sokolowski   if (!IsExtended) {
7577f7745c0SMarek Sokolowski     struct {
7587f7745c0SMarek Sokolowski       ulittle32_t Style;
7597f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
7607f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)};
7617f7745c0SMarek Sokolowski     writeObject(Prefix);
7627f7745c0SMarek Sokolowski   } else {
7637f7745c0SMarek Sokolowski     struct {
7647f7745c0SMarek Sokolowski       ulittle32_t HelpID;
7657f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
7667f7745c0SMarek Sokolowski       ulittle32_t Style;
7677f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
7687f7745c0SMarek Sokolowski              ulittle32_t(CtlStyle)};
7697f7745c0SMarek Sokolowski     writeObject(Prefix);
7707f7745c0SMarek Sokolowski   }
7717f7745c0SMarek Sokolowski 
7727f7745c0SMarek Sokolowski   // Common fixed-length part.
7737f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
7747f7745c0SMarek Sokolowski       Ctl.X, "Dialog control x-coordinate", true));
7757f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
7767f7745c0SMarek Sokolowski       Ctl.Y, "Dialog control y-coordinate", true));
7777f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
7787f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
7797f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
7807f7745c0SMarek Sokolowski       Ctl.Height, "Dialog control height", false));
7817f7745c0SMarek Sokolowski   struct {
7827f7745c0SMarek Sokolowski     ulittle16_t X;
7837f7745c0SMarek Sokolowski     ulittle16_t Y;
7847f7745c0SMarek Sokolowski     ulittle16_t Width;
7857f7745c0SMarek Sokolowski     ulittle16_t Height;
7867f7745c0SMarek Sokolowski   } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
7877f7745c0SMarek Sokolowski            ulittle16_t(Ctl.Height)};
7887f7745c0SMarek Sokolowski   writeObject(Middle);
7897f7745c0SMarek Sokolowski 
7907f7745c0SMarek Sokolowski   // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
7917f7745c0SMarek Sokolowski   if (!IsExtended) {
7927f7745c0SMarek Sokolowski     RETURN_IF_ERROR(checkNumberFits<uint16_t>(
7937f7745c0SMarek Sokolowski         Ctl.ID, "Control ID in simple DIALOG resource"));
7947f7745c0SMarek Sokolowski     writeInt<uint16_t>(Ctl.ID);
7957f7745c0SMarek Sokolowski   } else {
7967f7745c0SMarek Sokolowski     writeInt<uint32_t>(Ctl.ID);
7977f7745c0SMarek Sokolowski   }
7987f7745c0SMarek Sokolowski 
7997f7745c0SMarek Sokolowski   // Window class - either 0xFFFF + 16-bit integer or a string.
8007f7745c0SMarek Sokolowski   RETURN_IF_ERROR(writeIntOrString(IntOrString(TypeInfo.CtlClass)));
8017f7745c0SMarek Sokolowski 
8027f7745c0SMarek Sokolowski   // Element caption/reference ID. ID is preceded by 0xFFFF.
8037f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
8047f7745c0SMarek Sokolowski   RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
8057f7745c0SMarek Sokolowski 
8067f7745c0SMarek Sokolowski   // # bytes of extra creation data count. Don't pass any.
8077f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
8087f7745c0SMarek Sokolowski 
8097f7745c0SMarek Sokolowski   return Error::success();
8107f7745c0SMarek Sokolowski }
8117f7745c0SMarek Sokolowski 
8127f7745c0SMarek Sokolowski Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
8137f7745c0SMarek Sokolowski   auto *Res = cast<DialogResource>(Base);
8147f7745c0SMarek Sokolowski 
8157f7745c0SMarek Sokolowski   // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
816420090afSZachary Turner   const uint32_t DefaultStyle = 0x80880000;
817420090afSZachary Turner   const uint32_t StyleFontFlag = 0x40;
818420090afSZachary Turner   const uint32_t StyleCaptionFlag = 0x00C00000;
819420090afSZachary Turner 
820420090afSZachary Turner   uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
821420090afSZachary Turner   if (ObjectData.Font)
822420090afSZachary Turner     UsedStyle |= StyleFontFlag;
823420090afSZachary Turner   else
824420090afSZachary Turner     UsedStyle &= ~StyleFontFlag;
825420090afSZachary Turner 
826420090afSZachary Turner   // Actually, in case of empty (but existent) caption, the examined field
827420090afSZachary Turner   // is equal to "\"\"". That's why empty captions are still noticed.
828420090afSZachary Turner   if (ObjectData.Caption != "")
829420090afSZachary Turner     UsedStyle |= StyleCaptionFlag;
830420090afSZachary Turner 
831420090afSZachary Turner   const uint16_t DialogExMagic = 0xFFFF;
8327f7745c0SMarek Sokolowski 
8337f7745c0SMarek Sokolowski   // Write DIALOG(EX) header prefix. These are pretty different.
8347f7745c0SMarek Sokolowski   if (!Res->IsExtended) {
835420090afSZachary Turner     // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
836420090afSZachary Turner     // In such a case, whole object (in .res file) is equivalent to a
837420090afSZachary Turner     // DIALOGEX. It might lead to access violation/segmentation fault in
838420090afSZachary Turner     // resource readers. For example,
839420090afSZachary Turner     //   1 DIALOG 0, 0, 0, 65432
840420090afSZachary Turner     //   STYLE 0xFFFF0001 {}
841420090afSZachary Turner     // would be compiled to a DIALOGEX with 65432 controls.
842420090afSZachary Turner     if ((UsedStyle >> 16) == DialogExMagic)
843420090afSZachary Turner       return createError("16 higher bits of DIALOG resource style cannot be"
844420090afSZachary Turner                          " equal to 0xFFFF");
845420090afSZachary Turner 
8467f7745c0SMarek Sokolowski     struct {
8477f7745c0SMarek Sokolowski       ulittle32_t Style;
8487f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
8497f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(UsedStyle),
8507f7745c0SMarek Sokolowski              ulittle32_t(0)}; // As of now, we don't keep EXSTYLE.
8517f7745c0SMarek Sokolowski 
8527f7745c0SMarek Sokolowski     writeObject(Prefix);
8537f7745c0SMarek Sokolowski   } else {
8547f7745c0SMarek Sokolowski     struct {
8557f7745c0SMarek Sokolowski       ulittle16_t Version;
8567f7745c0SMarek Sokolowski       ulittle16_t Magic;
8577f7745c0SMarek Sokolowski       ulittle32_t HelpID;
8587f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
8597f7745c0SMarek Sokolowski       ulittle32_t Style;
8607f7745c0SMarek Sokolowski     } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
8617f7745c0SMarek Sokolowski              ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)};
8627f7745c0SMarek Sokolowski 
8637f7745c0SMarek Sokolowski     writeObject(Prefix);
8647f7745c0SMarek Sokolowski   }
8657f7745c0SMarek Sokolowski 
8667f7745c0SMarek Sokolowski   // Now, a common part. First, fixed-length fields.
8677f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
8687f7745c0SMarek Sokolowski                                             "Number of dialog controls"));
8697f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
8707f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
8717f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
8727f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
8737f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
8747f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
8757f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
8767f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
8777f7745c0SMarek Sokolowski   struct {
8787f7745c0SMarek Sokolowski     ulittle16_t Count;
8797f7745c0SMarek Sokolowski     ulittle16_t PosX;
8807f7745c0SMarek Sokolowski     ulittle16_t PosY;
8817f7745c0SMarek Sokolowski     ulittle16_t DialogWidth;
8827f7745c0SMarek Sokolowski     ulittle16_t DialogHeight;
8837f7745c0SMarek Sokolowski   } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
8847f7745c0SMarek Sokolowski            ulittle16_t(Res->Y), ulittle16_t(Res->Width),
8857f7745c0SMarek Sokolowski            ulittle16_t(Res->Height)};
8867f7745c0SMarek Sokolowski   writeObject(Middle);
8877f7745c0SMarek Sokolowski 
8887f7745c0SMarek Sokolowski   // MENU field. As of now, we don't keep them in the state and can peacefully
8897f7745c0SMarek Sokolowski   // think there is no menu attached to the dialog.
8907f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
8917f7745c0SMarek Sokolowski 
8927f7745c0SMarek Sokolowski   // Window CLASS field. Not kept here.
8937f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
8947f7745c0SMarek Sokolowski 
895420090afSZachary Turner   // Window title or a single word equal to 0.
896420090afSZachary Turner   RETURN_IF_ERROR(writeCString(ObjectData.Caption));
897420090afSZachary Turner 
898420090afSZachary Turner   // If there *is* a window font declared, output its data.
899420090afSZachary Turner   auto &Font = ObjectData.Font;
900420090afSZachary Turner   if (Font) {
901420090afSZachary Turner     writeInt<uint16_t>(Font->Size);
902420090afSZachary Turner     // Additional description occurs only in DIALOGEX.
903420090afSZachary Turner     if (Res->IsExtended) {
904420090afSZachary Turner       writeInt<uint16_t>(Font->Weight);
905420090afSZachary Turner       writeInt<uint8_t>(Font->IsItalic);
906420090afSZachary Turner       writeInt<uint8_t>(Font->Charset);
907420090afSZachary Turner     }
908420090afSZachary Turner     RETURN_IF_ERROR(writeCString(Font->Typeface));
909420090afSZachary Turner   }
9107f7745c0SMarek Sokolowski 
9117f7745c0SMarek Sokolowski   auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
9127f7745c0SMarek Sokolowski     if (!Err)
9137f7745c0SMarek Sokolowski       return Error::success();
9147f7745c0SMarek Sokolowski     return joinErrors(createError("Error in " + Twine(Ctl.Type) +
9157f7745c0SMarek Sokolowski                                   " control  (ID " + Twine(Ctl.ID) + "):"),
9167f7745c0SMarek Sokolowski                       std::move(Err));
9177f7745c0SMarek Sokolowski   };
9187f7745c0SMarek Sokolowski 
9197f7745c0SMarek Sokolowski   for (auto &Ctl : Res->Controls)
9207f7745c0SMarek Sokolowski     RETURN_IF_ERROR(
9217f7745c0SMarek Sokolowski         handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
9227f7745c0SMarek Sokolowski 
9237f7745c0SMarek Sokolowski   return Error::success();
9247f7745c0SMarek Sokolowski }
9257f7745c0SMarek Sokolowski 
9267f7745c0SMarek Sokolowski // --- HTMLResource helpers. --- //
92722fccd64SMarek Sokolowski 
9288f19343aSMarek Sokolowski Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
9298f19343aSMarek Sokolowski   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
9308f19343aSMarek Sokolowski }
9318f19343aSMarek Sokolowski 
93242f494d6SMarek Sokolowski // --- MenuResource helpers. --- //
93342f494d6SMarek Sokolowski 
93442f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinition(
93542f494d6SMarek Sokolowski     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
93642f494d6SMarek Sokolowski   assert(Def);
93742f494d6SMarek Sokolowski   const MenuDefinition *DefPtr = Def.get();
93842f494d6SMarek Sokolowski 
93942f494d6SMarek Sokolowski   if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
94042f494d6SMarek Sokolowski     writeInt<uint16_t>(Flags);
94142f494d6SMarek Sokolowski     RETURN_IF_ERROR(
94242f494d6SMarek Sokolowski         checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
94342f494d6SMarek Sokolowski     writeInt<uint16_t>(MenuItemPtr->Id);
94442f494d6SMarek Sokolowski     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
94542f494d6SMarek Sokolowski     return Error::success();
94642f494d6SMarek Sokolowski   }
94742f494d6SMarek Sokolowski 
94842f494d6SMarek Sokolowski   if (isa<MenuSeparator>(DefPtr)) {
94942f494d6SMarek Sokolowski     writeInt<uint16_t>(Flags);
95042f494d6SMarek Sokolowski     writeInt<uint32_t>(0);
95142f494d6SMarek Sokolowski     return Error::success();
95242f494d6SMarek Sokolowski   }
95342f494d6SMarek Sokolowski 
95442f494d6SMarek Sokolowski   auto *PopupPtr = cast<PopupItem>(DefPtr);
95542f494d6SMarek Sokolowski   writeInt<uint16_t>(Flags);
95642f494d6SMarek Sokolowski   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
95742f494d6SMarek Sokolowski   return writeMenuDefinitionList(PopupPtr->SubItems);
95842f494d6SMarek Sokolowski }
95942f494d6SMarek Sokolowski 
96042f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinitionList(
96142f494d6SMarek Sokolowski     const MenuDefinitionList &List) {
96242f494d6SMarek Sokolowski   for (auto &Def : List.Definitions) {
96342f494d6SMarek Sokolowski     uint16_t Flags = Def->getResFlags();
96442f494d6SMarek Sokolowski     // Last element receives an additional 0x80 flag.
96542f494d6SMarek Sokolowski     const uint16_t LastElementFlag = 0x0080;
96642f494d6SMarek Sokolowski     if (&Def == &List.Definitions.back())
96742f494d6SMarek Sokolowski       Flags |= LastElementFlag;
96842f494d6SMarek Sokolowski 
96942f494d6SMarek Sokolowski     RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
97042f494d6SMarek Sokolowski   }
97142f494d6SMarek Sokolowski   return Error::success();
97242f494d6SMarek Sokolowski }
97342f494d6SMarek Sokolowski 
97442f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
97542f494d6SMarek Sokolowski   // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
97642f494d6SMarek Sokolowski   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
97742f494d6SMarek Sokolowski   writeObject<uint32_t>(0);
97842f494d6SMarek Sokolowski 
97942f494d6SMarek Sokolowski   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
98042f494d6SMarek Sokolowski }
98142f494d6SMarek Sokolowski 
982*da366693SZachary Turner // --- StringTableResource helpers. --- //
983*da366693SZachary Turner 
984*da366693SZachary Turner class BundleResource : public RCResource {
985*da366693SZachary Turner public:
986*da366693SZachary Turner   using BundleType = ResourceFileWriter::StringTableInfo::Bundle;
987*da366693SZachary Turner   BundleType Bundle;
988*da366693SZachary Turner 
989*da366693SZachary Turner   BundleResource(const BundleType &StrBundle) : Bundle(StrBundle) {}
990*da366693SZachary Turner   IntOrString getResourceType() const override { return 6; }
991*da366693SZachary Turner 
992*da366693SZachary Turner   ResourceKind getKind() const override { return RkStringTableBundle; }
993*da366693SZachary Turner   static bool classof(const RCResource *Res) {
994*da366693SZachary Turner     return Res->getKind() == RkStringTableBundle;
995*da366693SZachary Turner   }
996*da366693SZachary Turner };
997*da366693SZachary Turner 
998*da366693SZachary Turner Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) {
999*da366693SZachary Turner   return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody);
1000*da366693SZachary Turner }
1001*da366693SZachary Turner 
1002*da366693SZachary Turner Error ResourceFileWriter::insertStringIntoBundle(
1003*da366693SZachary Turner     StringTableInfo::Bundle &Bundle, uint16_t StringID, StringRef String) {
1004*da366693SZachary Turner   uint16_t StringLoc = StringID & 15;
1005*da366693SZachary Turner   if (Bundle.Data[StringLoc])
1006*da366693SZachary Turner     return createError("Multiple STRINGTABLE strings located under ID " +
1007*da366693SZachary Turner                        Twine(StringID));
1008*da366693SZachary Turner   Bundle.Data[StringLoc] = String;
1009*da366693SZachary Turner   return Error::success();
1010*da366693SZachary Turner }
1011*da366693SZachary Turner 
1012*da366693SZachary Turner Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) {
1013*da366693SZachary Turner   auto *Res = cast<BundleResource>(Base);
1014*da366693SZachary Turner   for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) {
1015*da366693SZachary Turner     // The string format is a tiny bit different here. We
1016*da366693SZachary Turner     // first output the size of the string, and then the string itself
1017*da366693SZachary Turner     // (which is not null-terminated).
1018*da366693SZachary Turner     bool IsLongString;
1019*da366693SZachary Turner     SmallVector<UTF16, 128> Data;
1020*da366693SZachary Turner     RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()),
1021*da366693SZachary Turner                                   NullHandlingMethod::CutAtDoubleNull,
1022*da366693SZachary Turner                                   IsLongString, Data));
1023*da366693SZachary Turner     if (AppendNull && Res->Bundle.Data[ID])
1024*da366693SZachary Turner       Data.push_back('\0');
1025*da366693SZachary Turner     RETURN_IF_ERROR(
1026*da366693SZachary Turner         checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size"));
1027*da366693SZachary Turner     writeObject(ulittle16_t(Data.size()));
1028*da366693SZachary Turner     for (auto Char : Data)
1029*da366693SZachary Turner       writeObject(ulittle16_t(Char));
1030*da366693SZachary Turner   }
1031*da366693SZachary Turner   return Error::success();
1032*da366693SZachary Turner }
1033*da366693SZachary Turner 
1034*da366693SZachary Turner Error ResourceFileWriter::dumpAllStringTables() {
1035*da366693SZachary Turner   for (auto Key : StringTableData.BundleList) {
1036*da366693SZachary Turner     auto Iter = StringTableData.BundleData.find(Key);
1037*da366693SZachary Turner     assert(Iter != StringTableData.BundleData.end());
1038*da366693SZachary Turner 
1039*da366693SZachary Turner     // For a moment, revert the context info to moment of bundle declaration.
1040*da366693SZachary Turner     ContextKeeper RAII(this);
1041*da366693SZachary Turner     ObjectData = Iter->second.DeclTimeInfo;
1042*da366693SZachary Turner 
1043*da366693SZachary Turner     BundleResource Res(Iter->second);
1044*da366693SZachary Turner     // Bundle #(k+1) contains keys [16k, 16k + 15].
1045*da366693SZachary Turner     Res.setName(Key.first + 1);
1046*da366693SZachary Turner     RETURN_IF_ERROR(visitStringTableBundle(&Res));
1047*da366693SZachary Turner   }
1048*da366693SZachary Turner   return Error::success();
1049*da366693SZachary Turner }
1050*da366693SZachary Turner 
105107bc04ffSZachary Turner // --- VersionInfoResourceResource helpers. --- //
105207bc04ffSZachary Turner 
105307bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) {
105407bc04ffSZachary Turner   // Output the header if the block has name.
105507bc04ffSZachary Turner   bool OutputHeader = Blk.Name != "";
105607bc04ffSZachary Turner   uint64_t LengthLoc;
105707bc04ffSZachary Turner 
105807bc04ffSZachary Turner   if (OutputHeader) {
105907bc04ffSZachary Turner     LengthLoc = writeObject<uint16_t>(0);
106007bc04ffSZachary Turner     writeObject<uint16_t>(0);
106107bc04ffSZachary Turner     writeObject<uint16_t>(true);
106207bc04ffSZachary Turner     RETURN_IF_ERROR(writeCString(Blk.Name));
106307bc04ffSZachary Turner     padStream(sizeof(uint32_t));
106407bc04ffSZachary Turner   }
106507bc04ffSZachary Turner 
106607bc04ffSZachary Turner   for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) {
106707bc04ffSZachary Turner     VersionInfoStmt *ItemPtr = Item.get();
106807bc04ffSZachary Turner 
106907bc04ffSZachary Turner     if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) {
107007bc04ffSZachary Turner       RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr));
107107bc04ffSZachary Turner       continue;
107207bc04ffSZachary Turner     }
107307bc04ffSZachary Turner 
107407bc04ffSZachary Turner     auto *ValuePtr = cast<VersionInfoValue>(ItemPtr);
107507bc04ffSZachary Turner     RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr));
107607bc04ffSZachary Turner   }
107707bc04ffSZachary Turner 
107807bc04ffSZachary Turner   if (OutputHeader) {
107907bc04ffSZachary Turner     uint64_t CurLoc = tell();
108007bc04ffSZachary Turner     writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
108107bc04ffSZachary Turner   }
108207bc04ffSZachary Turner 
108307bc04ffSZachary Turner   padStream(sizeof(uint32_t));
108407bc04ffSZachary Turner   return Error::success();
108507bc04ffSZachary Turner }
108607bc04ffSZachary Turner 
108707bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) {
108807bc04ffSZachary Turner   // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE
108907bc04ffSZachary Turner   // is a mapping from the key (string) to the value (a sequence of ints or
109007bc04ffSZachary Turner   // a sequence of strings).
109107bc04ffSZachary Turner   //
109207bc04ffSZachary Turner   // If integers are to be written: width of each integer written depends on
109307bc04ffSZachary Turner   // whether it's been declared 'long' (it's DWORD then) or not (it's WORD).
109407bc04ffSZachary Turner   // ValueLength defined in structure referenced below is then the total
109507bc04ffSZachary Turner   // number of bytes taken by these integers.
109607bc04ffSZachary Turner   //
109707bc04ffSZachary Turner   // If strings are to be written: characters are always WORDs.
109807bc04ffSZachary Turner   // Moreover, '\0' character is written after the last string, and between
109907bc04ffSZachary Turner   // every two strings separated by comma (if strings are not comma-separated,
110007bc04ffSZachary Turner   // they're simply concatenated). ValueLength is equal to the number of WORDs
110107bc04ffSZachary Turner   // written (that is, half of the bytes written).
110207bc04ffSZachary Turner   //
110307bc04ffSZachary Turner   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx
110407bc04ffSZachary Turner   bool HasStrings = false, HasInts = false;
110507bc04ffSZachary Turner   for (auto &Item : Val.Values)
110607bc04ffSZachary Turner     (Item.isInt() ? HasInts : HasStrings) = true;
110707bc04ffSZachary Turner 
110807bc04ffSZachary Turner   assert((HasStrings || HasInts) && "VALUE must have at least one argument");
110907bc04ffSZachary Turner   if (HasStrings && HasInts)
111007bc04ffSZachary Turner     return createError(Twine("VALUE ") + Val.Key +
111107bc04ffSZachary Turner                        " cannot contain both strings and integers");
111207bc04ffSZachary Turner 
111307bc04ffSZachary Turner   auto LengthLoc = writeObject<uint16_t>(0);
111407bc04ffSZachary Turner   auto ValLengthLoc = writeObject<uint16_t>(0);
111507bc04ffSZachary Turner   writeObject<uint16_t>(HasStrings);
111607bc04ffSZachary Turner   RETURN_IF_ERROR(writeCString(Val.Key));
111707bc04ffSZachary Turner   padStream(sizeof(uint32_t));
111807bc04ffSZachary Turner 
111907bc04ffSZachary Turner   auto DataLoc = tell();
112007bc04ffSZachary Turner   for (size_t Id = 0; Id < Val.Values.size(); ++Id) {
112107bc04ffSZachary Turner     auto &Item = Val.Values[Id];
112207bc04ffSZachary Turner     if (Item.isInt()) {
112307bc04ffSZachary Turner       auto Value = Item.getInt();
112407bc04ffSZachary Turner       RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value"));
112507bc04ffSZachary Turner       writeRCInt(Value);
112607bc04ffSZachary Turner       continue;
112707bc04ffSZachary Turner     }
112807bc04ffSZachary Turner 
112907bc04ffSZachary Turner     bool WriteTerminator =
113007bc04ffSZachary Turner         Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1];
113107bc04ffSZachary Turner     RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator));
113207bc04ffSZachary Turner   }
113307bc04ffSZachary Turner 
113407bc04ffSZachary Turner   auto CurLoc = tell();
113507bc04ffSZachary Turner   auto ValueLength = CurLoc - DataLoc;
113607bc04ffSZachary Turner   if (HasStrings) {
113707bc04ffSZachary Turner     assert(ValueLength % 2 == 0);
113807bc04ffSZachary Turner     ValueLength /= 2;
113907bc04ffSZachary Turner   }
114007bc04ffSZachary Turner   writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc);
114107bc04ffSZachary Turner   writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc);
114207bc04ffSZachary Turner   padStream(sizeof(uint32_t));
114307bc04ffSZachary Turner   return Error::success();
114407bc04ffSZachary Turner }
114507bc04ffSZachary Turner 
114607bc04ffSZachary Turner template <typename Ty>
114707bc04ffSZachary Turner static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key,
114807bc04ffSZachary Turner                          const Ty &Default) {
114907bc04ffSZachary Turner   auto Iter = Map.find(Key);
115007bc04ffSZachary Turner   if (Iter != Map.end())
115107bc04ffSZachary Turner     return Iter->getValue();
115207bc04ffSZachary Turner   return Default;
115307bc04ffSZachary Turner }
115407bc04ffSZachary Turner 
115507bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) {
115607bc04ffSZachary Turner   auto *Res = cast<VersionInfoResource>(Base);
115707bc04ffSZachary Turner 
115807bc04ffSZachary Turner   const auto &FixedData = Res->FixedData;
115907bc04ffSZachary Turner 
116007bc04ffSZachary Turner   struct /* VS_FIXEDFILEINFO */ {
116107bc04ffSZachary Turner     ulittle32_t Signature = ulittle32_t(0xFEEF04BD);
116207bc04ffSZachary Turner     ulittle32_t StructVersion = ulittle32_t(0x10000);
116307bc04ffSZachary Turner     // It's weird to have most-significant DWORD first on the little-endian
116407bc04ffSZachary Turner     // machines, but let it be this way.
116507bc04ffSZachary Turner     ulittle32_t FileVersionMS;
116607bc04ffSZachary Turner     ulittle32_t FileVersionLS;
116707bc04ffSZachary Turner     ulittle32_t ProductVersionMS;
116807bc04ffSZachary Turner     ulittle32_t ProductVersionLS;
116907bc04ffSZachary Turner     ulittle32_t FileFlagsMask;
117007bc04ffSZachary Turner     ulittle32_t FileFlags;
117107bc04ffSZachary Turner     ulittle32_t FileOS;
117207bc04ffSZachary Turner     ulittle32_t FileType;
117307bc04ffSZachary Turner     ulittle32_t FileSubtype;
117407bc04ffSZachary Turner     // MS implementation seems to always set these fields to 0.
117507bc04ffSZachary Turner     ulittle32_t FileDateMS = ulittle32_t(0);
117607bc04ffSZachary Turner     ulittle32_t FileDateLS = ulittle32_t(0);
117707bc04ffSZachary Turner   } FixedInfo;
117807bc04ffSZachary Turner 
117907bc04ffSZachary Turner   // First, VS_VERSIONINFO.
118007bc04ffSZachary Turner   auto LengthLoc = writeObject<uint16_t>(0);
118107bc04ffSZachary Turner   writeObject(ulittle16_t(sizeof(FixedInfo)));
118207bc04ffSZachary Turner   writeObject(ulittle16_t(0));
118307bc04ffSZachary Turner   cantFail(writeCString("VS_VERSION_INFO"));
118407bc04ffSZachary Turner   padStream(sizeof(uint32_t));
118507bc04ffSZachary Turner 
118607bc04ffSZachary Turner   using VersionInfoFixed = VersionInfoResource::VersionInfoFixed;
118707bc04ffSZachary Turner   auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) {
118807bc04ffSZachary Turner     static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0};
118907bc04ffSZachary Turner     if (!FixedData.IsTypePresent[(int)Type])
119007bc04ffSZachary Turner       return DefaultOut;
119107bc04ffSZachary Turner     return FixedData.FixedInfo[(int)Type];
119207bc04ffSZachary Turner   };
119307bc04ffSZachary Turner 
119407bc04ffSZachary Turner   auto FileVer = GetField(VersionInfoFixed::FtFileVersion);
119507bc04ffSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
119607bc04ffSZachary Turner       *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields"));
119707bc04ffSZachary Turner   FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1];
119807bc04ffSZachary Turner   FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3];
119907bc04ffSZachary Turner 
120007bc04ffSZachary Turner   auto ProdVer = GetField(VersionInfoFixed::FtProductVersion);
120107bc04ffSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(
120207bc04ffSZachary Turner       *std::max_element(ProdVer.begin(), ProdVer.end()),
120307bc04ffSZachary Turner       "PRODUCTVERSION fields"));
120407bc04ffSZachary Turner   FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1];
120507bc04ffSZachary Turner   FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3];
120607bc04ffSZachary Turner 
120707bc04ffSZachary Turner   FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0];
120807bc04ffSZachary Turner   FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0];
120907bc04ffSZachary Turner   FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0];
121007bc04ffSZachary Turner   FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0];
121107bc04ffSZachary Turner   FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0];
121207bc04ffSZachary Turner 
121307bc04ffSZachary Turner   writeObject(FixedInfo);
121407bc04ffSZachary Turner   padStream(sizeof(uint32_t));
121507bc04ffSZachary Turner 
121607bc04ffSZachary Turner   RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock));
121707bc04ffSZachary Turner 
121807bc04ffSZachary Turner   // FIXME: check overflow?
121907bc04ffSZachary Turner   writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc);
122007bc04ffSZachary Turner 
122107bc04ffSZachary Turner   return Error::success();
122207bc04ffSZachary Turner }
122307bc04ffSZachary Turner 
12248f19343aSMarek Sokolowski } // namespace rc
12258f19343aSMarek Sokolowski } // namespace llvm
1226