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 
3122fccd64SMarek Sokolowski // Class that employs RAII to save the current serializator 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 
828f19343aSMarek Sokolowski static Error checkIntOrString(IntOrString Value, Twine FieldName) {
838f19343aSMarek Sokolowski   if (!Value.isInt())
848f19343aSMarek Sokolowski     return Error::success();
858f19343aSMarek Sokolowski   return checkNumberFits<uint16_t>(Value.getInt(), FieldName);
868f19343aSMarek Sokolowski }
878f19343aSMarek Sokolowski 
888f19343aSMarek Sokolowski static bool stripQuotes(StringRef &Str, bool &IsLongString) {
898f19343aSMarek Sokolowski   if (!Str.contains('"'))
908f19343aSMarek Sokolowski     return false;
918f19343aSMarek Sokolowski 
928f19343aSMarek Sokolowski   // Just take the contents of the string, checking if it's been marked long.
938f19343aSMarek Sokolowski   IsLongString = Str.startswith_lower("L");
948f19343aSMarek Sokolowski   if (IsLongString)
958f19343aSMarek Sokolowski     Str = Str.drop_front();
968f19343aSMarek Sokolowski 
978f19343aSMarek Sokolowski   bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\"");
988f19343aSMarek Sokolowski   (void)StripSuccess;
998f19343aSMarek Sokolowski   assert(StripSuccess && "Strings should be enclosed in quotes.");
1008f19343aSMarek Sokolowski   return true;
1018f19343aSMarek Sokolowski }
1028f19343aSMarek Sokolowski 
1038f19343aSMarek Sokolowski // Describes a way to handle '\0' characters when processing the string.
1048f19343aSMarek Sokolowski // rc.exe tool sometimes behaves in a weird way in postprocessing.
1058f19343aSMarek Sokolowski // If the string to be output is equivalent to a C-string (e.g. in MENU
1068f19343aSMarek Sokolowski // titles), string is (predictably) truncated after first 0-byte.
1078f19343aSMarek Sokolowski // When outputting a string table, the behavior is equivalent to appending
1088f19343aSMarek Sokolowski // '\0\0' at the end of the string, and then stripping the string
1098f19343aSMarek Sokolowski // before the first '\0\0' occurrence.
1108f19343aSMarek Sokolowski // Finally, when handling strings in user-defined resources, 0-bytes
1118f19343aSMarek Sokolowski // aren't stripped, nor do they terminate the string.
1128f19343aSMarek Sokolowski 
1138f19343aSMarek Sokolowski enum class NullHandlingMethod {
1148f19343aSMarek Sokolowski   UserResource,   // Don't terminate string on '\0'.
1158f19343aSMarek Sokolowski   CutAtNull,      // Terminate string on '\0'.
1168f19343aSMarek Sokolowski   CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'.
1178f19343aSMarek Sokolowski };
1188f19343aSMarek Sokolowski 
1198f19343aSMarek Sokolowski // Parses an identifier or string and returns a processed version of it.
1208f19343aSMarek Sokolowski // For now, it only strips the string boundaries, but TODO:
1218f19343aSMarek Sokolowski //   * Squash "" to a single ".
1228f19343aSMarek Sokolowski //   * Replace the escape sequences with their processed version.
1238f19343aSMarek Sokolowski // For identifiers, this is no-op.
1248f19343aSMarek Sokolowski static Error processString(StringRef Str, NullHandlingMethod NullHandler,
1258f19343aSMarek Sokolowski                            bool &IsLongString, SmallVectorImpl<UTF16> &Result) {
1268f19343aSMarek Sokolowski   assert(NullHandler == NullHandlingMethod::CutAtNull);
1278f19343aSMarek Sokolowski 
1288f19343aSMarek Sokolowski   bool IsString = stripQuotes(Str, IsLongString);
1298f19343aSMarek Sokolowski   convertUTF8ToUTF16String(Str, Result);
1308f19343aSMarek Sokolowski 
1318f19343aSMarek Sokolowski   if (!IsString) {
1328f19343aSMarek Sokolowski     // It's an identifier if it's not a string. Make all characters uppercase.
1338f19343aSMarek Sokolowski     for (UTF16 &Ch : Result) {
1348f19343aSMarek Sokolowski       assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII");
1358f19343aSMarek Sokolowski       Ch = toupper(Ch);
1368f19343aSMarek Sokolowski     }
1378f19343aSMarek Sokolowski     return Error::success();
1388f19343aSMarek Sokolowski   }
1398f19343aSMarek Sokolowski 
1408f19343aSMarek Sokolowski   // We don't process the string contents. Only cut at '\0'.
1418f19343aSMarek Sokolowski 
1428f19343aSMarek Sokolowski   for (size_t Pos = 0; Pos < Result.size(); ++Pos)
1438f19343aSMarek Sokolowski     if (Result[Pos] == '\0')
1448f19343aSMarek Sokolowski       Result.resize(Pos);
1458f19343aSMarek Sokolowski 
1468f19343aSMarek Sokolowski   return Error::success();
1478f19343aSMarek Sokolowski }
1488f19343aSMarek Sokolowski 
1498f19343aSMarek Sokolowski uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) {
1508f19343aSMarek Sokolowski   uint64_t Result = tell();
1518f19343aSMarek Sokolowski   FS->write((const char *)Data.begin(), Data.size());
1528f19343aSMarek Sokolowski   return Result;
1538f19343aSMarek Sokolowski }
1548f19343aSMarek Sokolowski 
1558f19343aSMarek Sokolowski Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) {
1568f19343aSMarek Sokolowski   SmallVector<UTF16, 128> ProcessedString;
1578f19343aSMarek Sokolowski   bool IsLongString;
1588f19343aSMarek Sokolowski   RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull,
1598f19343aSMarek Sokolowski                                 IsLongString, ProcessedString));
1608f19343aSMarek Sokolowski   for (auto Ch : ProcessedString)
1618f19343aSMarek Sokolowski     writeInt<uint16_t>(Ch);
1628f19343aSMarek Sokolowski   if (WriteTerminator)
1638f19343aSMarek Sokolowski     writeInt<uint16_t>(0);
1648f19343aSMarek Sokolowski   return Error::success();
1658f19343aSMarek Sokolowski }
1668f19343aSMarek Sokolowski 
1678f19343aSMarek Sokolowski Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) {
1688f19343aSMarek Sokolowski   return writeIntOrString(Ident);
1698f19343aSMarek Sokolowski }
1708f19343aSMarek Sokolowski 
1718f19343aSMarek Sokolowski Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) {
1728f19343aSMarek Sokolowski   if (!Value.isInt())
1738f19343aSMarek Sokolowski     return writeCString(Value.getString());
1748f19343aSMarek Sokolowski 
1758f19343aSMarek Sokolowski   writeInt<uint16_t>(0xFFFF);
1768f19343aSMarek Sokolowski   writeInt<uint16_t>(Value.getInt());
1778f19343aSMarek Sokolowski   return Error::success();
1788f19343aSMarek Sokolowski }
1798f19343aSMarek Sokolowski 
1808f19343aSMarek Sokolowski Error ResourceFileWriter::appendFile(StringRef Filename) {
1818f19343aSMarek Sokolowski   bool IsLong;
1828f19343aSMarek Sokolowski   stripQuotes(Filename, IsLong);
1838f19343aSMarek Sokolowski 
1848f19343aSMarek Sokolowski   // Filename path should be relative to the current working directory.
1858f19343aSMarek Sokolowski   // FIXME: docs say so, but reality is more complicated, script
1868f19343aSMarek Sokolowski   // location and include paths must be taken into account.
1878f19343aSMarek Sokolowski   ErrorOr<std::unique_ptr<MemoryBuffer>> File =
1888f19343aSMarek Sokolowski       MemoryBuffer::getFile(Filename, -1, false);
1898f19343aSMarek Sokolowski   if (!File)
1908f19343aSMarek Sokolowski     return make_error<StringError>("Error opening file '" + Filename +
1918f19343aSMarek Sokolowski                                        "': " + File.getError().message(),
1928f19343aSMarek Sokolowski                                    File.getError());
1938f19343aSMarek Sokolowski   *FS << (*File)->getBuffer();
1948f19343aSMarek Sokolowski   return Error::success();
1958f19343aSMarek Sokolowski }
1968f19343aSMarek Sokolowski 
1978f19343aSMarek Sokolowski void ResourceFileWriter::padStream(uint64_t Length) {
1988f19343aSMarek Sokolowski   assert(Length > 0);
1998f19343aSMarek Sokolowski   uint64_t Location = tell();
2008f19343aSMarek Sokolowski   Location %= Length;
2018f19343aSMarek Sokolowski   uint64_t Pad = (Length - Location) % Length;
2028f19343aSMarek Sokolowski   for (uint64_t i = 0; i < Pad; ++i)
2038f19343aSMarek Sokolowski     writeInt<uint8_t>(0);
2048f19343aSMarek Sokolowski }
2058f19343aSMarek Sokolowski 
2068f19343aSMarek Sokolowski Error ResourceFileWriter::handleError(Error &&Err, const RCResource *Res) {
2078f19343aSMarek Sokolowski   if (Err)
2088f19343aSMarek Sokolowski     return joinErrors(createError("Error in " + Res->getResourceTypeName() +
2098f19343aSMarek Sokolowski                                   " statement (ID " + Twine(Res->ResName) +
2108f19343aSMarek Sokolowski                                   "): "),
2118f19343aSMarek Sokolowski                       std::move(Err));
2128f19343aSMarek Sokolowski   return Error::success();
2138f19343aSMarek Sokolowski }
2148f19343aSMarek Sokolowski 
2158f19343aSMarek Sokolowski Error ResourceFileWriter::visitNullResource(const RCResource *Res) {
2168f19343aSMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeNullBody);
2178f19343aSMarek Sokolowski }
2188f19343aSMarek Sokolowski 
21922fccd64SMarek Sokolowski Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
22022fccd64SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
22122fccd64SMarek Sokolowski }
22222fccd64SMarek Sokolowski 
223*c3ab013aSZachary Turner Error ResourceFileWriter::visitCursorResource(const RCResource *Res) {
224*c3ab013aSZachary Turner   return handleError(visitIconOrCursorResource(Res), Res);
225*c3ab013aSZachary Turner }
226*c3ab013aSZachary Turner 
2277f7745c0SMarek Sokolowski Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
2287f7745c0SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeDialogBody);
2297f7745c0SMarek Sokolowski }
2307f7745c0SMarek Sokolowski 
231*c3ab013aSZachary Turner Error ResourceFileWriter::visitIconResource(const RCResource *Res) {
232*c3ab013aSZachary Turner   return handleError(visitIconOrCursorResource(Res), Res);
233*c3ab013aSZachary Turner }
234*c3ab013aSZachary Turner 
235420090afSZachary Turner Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) {
236420090afSZachary Turner   ObjectData.Caption = Stmt->Value;
237420090afSZachary Turner   return Error::success();
238420090afSZachary Turner }
239420090afSZachary Turner 
2408f19343aSMarek Sokolowski Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
2418f19343aSMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
2428f19343aSMarek Sokolowski }
2438f19343aSMarek Sokolowski 
24442f494d6SMarek Sokolowski Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
24542f494d6SMarek Sokolowski   return writeResource(Res, &ResourceFileWriter::writeMenuBody);
24642f494d6SMarek Sokolowski }
24742f494d6SMarek Sokolowski 
24822fccd64SMarek Sokolowski Error ResourceFileWriter::visitCharacteristicsStmt(
24922fccd64SMarek Sokolowski     const CharacteristicsStmt *Stmt) {
25022fccd64SMarek Sokolowski   ObjectData.Characteristics = Stmt->Value;
25122fccd64SMarek Sokolowski   return Error::success();
25222fccd64SMarek Sokolowski }
25322fccd64SMarek Sokolowski 
254420090afSZachary Turner Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) {
255420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size"));
256420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight"));
257420090afSZachary Turner   RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset"));
258420090afSZachary Turner   ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic,
259420090afSZachary Turner                             Stmt->Charset};
260420090afSZachary Turner   ObjectData.Font.emplace(Font);
261420090afSZachary Turner   return Error::success();
262420090afSZachary Turner }
263420090afSZachary Turner 
2648f19343aSMarek Sokolowski Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) {
2658f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID"));
2668f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID"));
2678f19343aSMarek Sokolowski   ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10);
2688f19343aSMarek Sokolowski   return Error::success();
2698f19343aSMarek Sokolowski }
2708f19343aSMarek Sokolowski 
271420090afSZachary Turner Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) {
272420090afSZachary Turner   ObjectData.Style = Stmt->Value;
273420090afSZachary Turner   return Error::success();
274420090afSZachary Turner }
275420090afSZachary Turner 
27622fccd64SMarek Sokolowski Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) {
27722fccd64SMarek Sokolowski   ObjectData.VersionInfo = Stmt->Value;
27822fccd64SMarek Sokolowski   return Error::success();
27922fccd64SMarek Sokolowski }
28022fccd64SMarek Sokolowski 
2818f19343aSMarek Sokolowski Error ResourceFileWriter::writeResource(
2828f19343aSMarek Sokolowski     const RCResource *Res,
2838f19343aSMarek Sokolowski     Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) {
2848f19343aSMarek Sokolowski   // We don't know the sizes yet.
2858f19343aSMarek Sokolowski   object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)};
2868f19343aSMarek Sokolowski   uint64_t HeaderLoc = writeObject(HeaderPrefix);
2878f19343aSMarek Sokolowski 
2888f19343aSMarek Sokolowski   auto ResType = Res->getResourceType();
2898f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type"));
2908f19343aSMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID"));
2918f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res));
2928f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res));
2938f19343aSMarek Sokolowski 
29422fccd64SMarek Sokolowski   // Apply the resource-local optional statements.
29522fccd64SMarek Sokolowski   ContextKeeper RAII(this);
29622fccd64SMarek Sokolowski   RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res));
29722fccd64SMarek Sokolowski 
2988f19343aSMarek Sokolowski   padStream(sizeof(uint32_t));
2998f19343aSMarek Sokolowski   object::WinResHeaderSuffix HeaderSuffix{
3008f19343aSMarek Sokolowski       ulittle32_t(0), // DataVersion; seems to always be 0
3018f19343aSMarek Sokolowski       ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo),
30222fccd64SMarek Sokolowski       ulittle32_t(ObjectData.VersionInfo),
30322fccd64SMarek Sokolowski       ulittle32_t(ObjectData.Characteristics)};
3048f19343aSMarek Sokolowski   writeObject(HeaderSuffix);
3058f19343aSMarek Sokolowski 
3068f19343aSMarek Sokolowski   uint64_t DataLoc = tell();
3078f19343aSMarek Sokolowski   RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res));
3088f19343aSMarek Sokolowski   // RETURN_IF_ERROR(handleError(dumpResource(Ctx)));
3098f19343aSMarek Sokolowski 
3108f19343aSMarek Sokolowski   // Update the sizes.
3118f19343aSMarek Sokolowski   HeaderPrefix.DataSize = tell() - DataLoc;
3128f19343aSMarek Sokolowski   HeaderPrefix.HeaderSize = DataLoc - HeaderLoc;
3138f19343aSMarek Sokolowski   writeObjectAt(HeaderPrefix, HeaderLoc);
3148f19343aSMarek Sokolowski   padStream(sizeof(uint32_t));
3158f19343aSMarek Sokolowski 
3168f19343aSMarek Sokolowski   return Error::success();
3178f19343aSMarek Sokolowski }
3188f19343aSMarek Sokolowski 
31922fccd64SMarek Sokolowski // --- NullResource helpers. --- //
32022fccd64SMarek Sokolowski 
3218f19343aSMarek Sokolowski Error ResourceFileWriter::writeNullBody(const RCResource *) {
3228f19343aSMarek Sokolowski   return Error::success();
3238f19343aSMarek Sokolowski }
3248f19343aSMarek Sokolowski 
32522fccd64SMarek Sokolowski // --- AcceleratorsResource helpers. --- //
32622fccd64SMarek Sokolowski 
32722fccd64SMarek Sokolowski Error ResourceFileWriter::writeSingleAccelerator(
32822fccd64SMarek Sokolowski     const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) {
32922fccd64SMarek Sokolowski   using Accelerator = AcceleratorsResource::Accelerator;
33022fccd64SMarek Sokolowski   using Opt = Accelerator::Options;
33122fccd64SMarek Sokolowski 
33222fccd64SMarek Sokolowski   struct AccelTableEntry {
33322fccd64SMarek Sokolowski     ulittle16_t Flags;
33422fccd64SMarek Sokolowski     ulittle16_t ANSICode;
33522fccd64SMarek Sokolowski     ulittle16_t Id;
33622fccd64SMarek Sokolowski     uint16_t Padding;
33722fccd64SMarek Sokolowski   } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0};
33822fccd64SMarek Sokolowski 
33922fccd64SMarek Sokolowski   bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY;
34022fccd64SMarek Sokolowski 
34122fccd64SMarek Sokolowski   // Remove ASCII flags (which doesn't occur in .res files).
34222fccd64SMarek Sokolowski   Entry.Flags = Obj.Flags & ~Opt::ASCII;
34322fccd64SMarek Sokolowski 
34422fccd64SMarek Sokolowski   if (IsLastItem)
34522fccd64SMarek Sokolowski     Entry.Flags |= 0x80;
34622fccd64SMarek Sokolowski 
34722fccd64SMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID"));
34822fccd64SMarek Sokolowski   Entry.Id = ulittle16_t(Obj.Id);
34922fccd64SMarek Sokolowski 
35022fccd64SMarek Sokolowski   auto createAccError = [&Obj](const char *Msg) {
35122fccd64SMarek Sokolowski     return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg);
35222fccd64SMarek Sokolowski   };
35322fccd64SMarek Sokolowski 
35422fccd64SMarek Sokolowski   if (IsASCII && IsVirtKey)
35522fccd64SMarek Sokolowski     return createAccError("Accelerator can't be both ASCII and VIRTKEY");
35622fccd64SMarek Sokolowski 
35722fccd64SMarek Sokolowski   if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL)))
35822fccd64SMarek Sokolowski     return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY"
35922fccd64SMarek Sokolowski                           " accelerators");
36022fccd64SMarek Sokolowski 
36122fccd64SMarek Sokolowski   if (Obj.Event.isInt()) {
36222fccd64SMarek Sokolowski     if (!IsASCII && !IsVirtKey)
36322fccd64SMarek Sokolowski       return createAccError(
36422fccd64SMarek Sokolowski           "Accelerator with a numeric event must be either ASCII"
36522fccd64SMarek Sokolowski           " or VIRTKEY");
36622fccd64SMarek Sokolowski 
36722fccd64SMarek Sokolowski     uint32_t EventVal = Obj.Event.getInt();
36822fccd64SMarek Sokolowski     RETURN_IF_ERROR(
36922fccd64SMarek Sokolowski         checkNumberFits<uint16_t>(EventVal, "Numeric event key ID"));
37022fccd64SMarek Sokolowski     Entry.ANSICode = ulittle16_t(EventVal);
37122fccd64SMarek Sokolowski     writeObject(Entry);
37222fccd64SMarek Sokolowski     return Error::success();
37322fccd64SMarek Sokolowski   }
37422fccd64SMarek Sokolowski 
37522fccd64SMarek Sokolowski   StringRef Str = Obj.Event.getString();
37622fccd64SMarek Sokolowski   bool IsWide;
37722fccd64SMarek Sokolowski   stripQuotes(Str, IsWide);
37822fccd64SMarek Sokolowski 
37922fccd64SMarek Sokolowski   if (Str.size() == 0 || Str.size() > 2)
38022fccd64SMarek Sokolowski     return createAccError(
38122fccd64SMarek Sokolowski         "Accelerator string events should have length 1 or 2");
38222fccd64SMarek Sokolowski 
38322fccd64SMarek Sokolowski   if (Str[0] == '^') {
38422fccd64SMarek Sokolowski     if (Str.size() == 1)
38522fccd64SMarek Sokolowski       return createAccError("No character following '^' in accelerator event");
38622fccd64SMarek Sokolowski     if (IsVirtKey)
38722fccd64SMarek Sokolowski       return createAccError(
38822fccd64SMarek Sokolowski           "VIRTKEY accelerator events can't be preceded by '^'");
38922fccd64SMarek Sokolowski 
39022fccd64SMarek Sokolowski     char Ch = Str[1];
39122fccd64SMarek Sokolowski     if (Ch >= 'a' && Ch <= 'z')
39222fccd64SMarek Sokolowski       Entry.ANSICode = ulittle16_t(Ch - 'a' + 1);
39322fccd64SMarek Sokolowski     else if (Ch >= 'A' && Ch <= 'Z')
39422fccd64SMarek Sokolowski       Entry.ANSICode = ulittle16_t(Ch - 'A' + 1);
39522fccd64SMarek Sokolowski     else
39622fccd64SMarek Sokolowski       return createAccError("Control character accelerator event should be"
39722fccd64SMarek Sokolowski                             " alphabetic");
39822fccd64SMarek Sokolowski 
39922fccd64SMarek Sokolowski     writeObject(Entry);
40022fccd64SMarek Sokolowski     return Error::success();
40122fccd64SMarek Sokolowski   }
40222fccd64SMarek Sokolowski 
40322fccd64SMarek Sokolowski   if (Str.size() == 2)
40422fccd64SMarek Sokolowski     return createAccError("Event string should be one-character, possibly"
40522fccd64SMarek Sokolowski                           " preceded by '^'");
40622fccd64SMarek Sokolowski 
40722fccd64SMarek Sokolowski   uint8_t EventCh = Str[0];
40822fccd64SMarek Sokolowski   // The original tool just warns in this situation. We chose to fail.
40922fccd64SMarek Sokolowski   if (IsVirtKey && !isalnum(EventCh))
41022fccd64SMarek Sokolowski     return createAccError("Non-alphanumeric characters cannot describe virtual"
41122fccd64SMarek Sokolowski                           " keys");
41222fccd64SMarek Sokolowski   if (EventCh > 0x7F)
41322fccd64SMarek Sokolowski     return createAccError("Non-ASCII description of accelerator");
41422fccd64SMarek Sokolowski 
41522fccd64SMarek Sokolowski   if (IsVirtKey)
41622fccd64SMarek Sokolowski     EventCh = toupper(EventCh);
41722fccd64SMarek Sokolowski   Entry.ANSICode = ulittle16_t(EventCh);
41822fccd64SMarek Sokolowski   writeObject(Entry);
41922fccd64SMarek Sokolowski   return Error::success();
42022fccd64SMarek Sokolowski }
42122fccd64SMarek Sokolowski 
42222fccd64SMarek Sokolowski Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
42322fccd64SMarek Sokolowski   auto *Res = cast<AcceleratorsResource>(Base);
42422fccd64SMarek Sokolowski   size_t AcceleratorId = 0;
42522fccd64SMarek Sokolowski   for (auto &Acc : Res->Accelerators) {
42622fccd64SMarek Sokolowski     ++AcceleratorId;
42722fccd64SMarek Sokolowski     RETURN_IF_ERROR(
42822fccd64SMarek Sokolowski         writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size()));
42922fccd64SMarek Sokolowski   }
43022fccd64SMarek Sokolowski   return Error::success();
43122fccd64SMarek Sokolowski }
43222fccd64SMarek Sokolowski 
433*c3ab013aSZachary Turner // --- CursorResource and IconResource helpers. --- //
434*c3ab013aSZachary Turner 
435*c3ab013aSZachary Turner // ICONRESDIR structure. Describes a single icon in resouce group.
436*c3ab013aSZachary Turner //
437*c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx
438*c3ab013aSZachary Turner struct IconResDir {
439*c3ab013aSZachary Turner   uint8_t Width;
440*c3ab013aSZachary Turner   uint8_t Height;
441*c3ab013aSZachary Turner   uint8_t ColorCount;
442*c3ab013aSZachary Turner   uint8_t Reserved;
443*c3ab013aSZachary Turner };
444*c3ab013aSZachary Turner 
445*c3ab013aSZachary Turner // CURSORDIR structure. Describes a single cursor in resource group.
446*c3ab013aSZachary Turner //
447*c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx
448*c3ab013aSZachary Turner struct CursorDir {
449*c3ab013aSZachary Turner   ulittle16_t Width;
450*c3ab013aSZachary Turner   ulittle16_t Height;
451*c3ab013aSZachary Turner };
452*c3ab013aSZachary Turner 
453*c3ab013aSZachary Turner // RESDIRENTRY structure, stripped from the last item. Stripping made
454*c3ab013aSZachary Turner // for compatibility with RESDIR.
455*c3ab013aSZachary Turner //
456*c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx
457*c3ab013aSZachary Turner struct ResourceDirEntryStart {
458*c3ab013aSZachary Turner   union {
459*c3ab013aSZachary Turner     CursorDir Cursor; // Used in CURSOR resources.
460*c3ab013aSZachary Turner     IconResDir Icon;  // Used in .ico and .cur files, and ICON resources.
461*c3ab013aSZachary Turner   };
462*c3ab013aSZachary Turner   ulittle16_t Planes;   // HotspotX (.cur files but not CURSOR resource).
463*c3ab013aSZachary Turner   ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource).
464*c3ab013aSZachary Turner   ulittle32_t Size;
465*c3ab013aSZachary Turner   // ulittle32_t ImageOffset;  // Offset to image data (ICONDIRENTRY only).
466*c3ab013aSZachary Turner   // ulittle16_t IconID;       // Resource icon ID (RESDIR only).
467*c3ab013aSZachary Turner };
468*c3ab013aSZachary Turner 
469*c3ab013aSZachary Turner // BITMAPINFOHEADER structure. Describes basic information about the bitmap
470*c3ab013aSZachary Turner // being read.
471*c3ab013aSZachary Turner //
472*c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx
473*c3ab013aSZachary Turner struct BitmapInfoHeader {
474*c3ab013aSZachary Turner   ulittle32_t Size;
475*c3ab013aSZachary Turner   ulittle32_t Width;
476*c3ab013aSZachary Turner   ulittle32_t Height;
477*c3ab013aSZachary Turner   ulittle16_t Planes;
478*c3ab013aSZachary Turner   ulittle16_t BitCount;
479*c3ab013aSZachary Turner   ulittle32_t Compression;
480*c3ab013aSZachary Turner   ulittle32_t SizeImage;
481*c3ab013aSZachary Turner   ulittle32_t XPelsPerMeter;
482*c3ab013aSZachary Turner   ulittle32_t YPelsPerMeter;
483*c3ab013aSZachary Turner   ulittle32_t ClrUsed;
484*c3ab013aSZachary Turner   ulittle32_t ClrImportant;
485*c3ab013aSZachary Turner };
486*c3ab013aSZachary Turner 
487*c3ab013aSZachary Turner // Group icon directory header. Called ICONDIR in .ico/.cur files and
488*c3ab013aSZachary Turner // NEWHEADER in .res files.
489*c3ab013aSZachary Turner //
490*c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx
491*c3ab013aSZachary Turner struct GroupIconDir {
492*c3ab013aSZachary Turner   ulittle16_t Reserved; // Always 0.
493*c3ab013aSZachary Turner   ulittle16_t ResType;  // 1 for icons, 2 for cursors.
494*c3ab013aSZachary Turner   ulittle16_t ResCount; // Number of items.
495*c3ab013aSZachary Turner };
496*c3ab013aSZachary Turner 
497*c3ab013aSZachary Turner enum class IconCursorGroupType { Icon, Cursor };
498*c3ab013aSZachary Turner 
499*c3ab013aSZachary Turner class SingleIconCursorResource : public RCResource {
500*c3ab013aSZachary Turner public:
501*c3ab013aSZachary Turner   IconCursorGroupType Type;
502*c3ab013aSZachary Turner   const ResourceDirEntryStart &Header;
503*c3ab013aSZachary Turner   ArrayRef<uint8_t> Image;
504*c3ab013aSZachary Turner 
505*c3ab013aSZachary Turner   SingleIconCursorResource(IconCursorGroupType ResourceType,
506*c3ab013aSZachary Turner                            const ResourceDirEntryStart &HeaderEntry,
507*c3ab013aSZachary Turner                            ArrayRef<uint8_t> ImageData)
508*c3ab013aSZachary Turner       : Type(ResourceType), Header(HeaderEntry), Image(ImageData) {}
509*c3ab013aSZachary Turner 
510*c3ab013aSZachary Turner   Twine getResourceTypeName() const override { return "Icon/cursor image"; }
511*c3ab013aSZachary Turner   IntOrString getResourceType() const override {
512*c3ab013aSZachary Turner     return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor;
513*c3ab013aSZachary Turner   }
514*c3ab013aSZachary Turner   uint16_t getMemoryFlags() const override {
515*c3ab013aSZachary Turner     return MfDiscardable | MfMoveable;
516*c3ab013aSZachary Turner   }
517*c3ab013aSZachary Turner   ResourceKind getKind() const override { return RkSingleCursorOrIconRes; }
518*c3ab013aSZachary Turner   static bool classof(const RCResource *Res) {
519*c3ab013aSZachary Turner     return Res->getKind() == RkSingleCursorOrIconRes;
520*c3ab013aSZachary Turner   }
521*c3ab013aSZachary Turner };
522*c3ab013aSZachary Turner 
523*c3ab013aSZachary Turner class IconCursorGroupResource : public RCResource {
524*c3ab013aSZachary Turner public:
525*c3ab013aSZachary Turner   IconCursorGroupType Type;
526*c3ab013aSZachary Turner   GroupIconDir Header;
527*c3ab013aSZachary Turner   std::vector<ResourceDirEntryStart> ItemEntries;
528*c3ab013aSZachary Turner 
529*c3ab013aSZachary Turner   IconCursorGroupResource(IconCursorGroupType ResourceType,
530*c3ab013aSZachary Turner                           const GroupIconDir &HeaderData,
531*c3ab013aSZachary Turner                           std::vector<ResourceDirEntryStart> &&Entries)
532*c3ab013aSZachary Turner       : Type(ResourceType), Header(HeaderData),
533*c3ab013aSZachary Turner         ItemEntries(std::move(Entries)) {}
534*c3ab013aSZachary Turner 
535*c3ab013aSZachary Turner   Twine getResourceTypeName() const override { return "Icon/cursor group"; }
536*c3ab013aSZachary Turner   IntOrString getResourceType() const override {
537*c3ab013aSZachary Turner     return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup;
538*c3ab013aSZachary Turner   }
539*c3ab013aSZachary Turner   ResourceKind getKind() const override { return RkCursorOrIconGroupRes; }
540*c3ab013aSZachary Turner   static bool classof(const RCResource *Res) {
541*c3ab013aSZachary Turner     return Res->getKind() == RkCursorOrIconGroupRes;
542*c3ab013aSZachary Turner   }
543*c3ab013aSZachary Turner };
544*c3ab013aSZachary Turner 
545*c3ab013aSZachary Turner Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) {
546*c3ab013aSZachary Turner   auto *Res = cast<SingleIconCursorResource>(Base);
547*c3ab013aSZachary Turner   if (Res->Type == IconCursorGroupType::Cursor) {
548*c3ab013aSZachary Turner     // In case of cursors, two WORDS are appended to the beginning
549*c3ab013aSZachary Turner     // of the resource: HotspotX (Planes in RESDIRENTRY),
550*c3ab013aSZachary Turner     // and HotspotY (BitCount).
551*c3ab013aSZachary Turner     //
552*c3ab013aSZachary Turner     // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx
553*c3ab013aSZachary Turner     //  (Remarks section).
554*c3ab013aSZachary Turner     writeObject(Res->Header.Planes);
555*c3ab013aSZachary Turner     writeObject(Res->Header.BitCount);
556*c3ab013aSZachary Turner   }
557*c3ab013aSZachary Turner 
558*c3ab013aSZachary Turner   writeObject(Res->Image);
559*c3ab013aSZachary Turner   return Error::success();
560*c3ab013aSZachary Turner }
561*c3ab013aSZachary Turner 
562*c3ab013aSZachary Turner Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) {
563*c3ab013aSZachary Turner   auto *Res = cast<IconCursorGroupResource>(Base);
564*c3ab013aSZachary Turner   writeObject(Res->Header);
565*c3ab013aSZachary Turner   for (auto Item : Res->ItemEntries) {
566*c3ab013aSZachary Turner     writeObject(Item);
567*c3ab013aSZachary Turner     writeObject(ulittle16_t(IconCursorID++));
568*c3ab013aSZachary Turner   }
569*c3ab013aSZachary Turner   return Error::success();
570*c3ab013aSZachary Turner }
571*c3ab013aSZachary Turner 
572*c3ab013aSZachary Turner Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) {
573*c3ab013aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody);
574*c3ab013aSZachary Turner }
575*c3ab013aSZachary Turner 
576*c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) {
577*c3ab013aSZachary Turner   return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody);
578*c3ab013aSZachary Turner }
579*c3ab013aSZachary Turner 
580*c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) {
581*c3ab013aSZachary Turner   IconCursorGroupType Type;
582*c3ab013aSZachary Turner   StringRef FileStr;
583*c3ab013aSZachary Turner   IntOrString ResName = Base->ResName;
584*c3ab013aSZachary Turner 
585*c3ab013aSZachary Turner   if (auto *IconRes = dyn_cast<IconResource>(Base)) {
586*c3ab013aSZachary Turner     FileStr = IconRes->IconLoc;
587*c3ab013aSZachary Turner     Type = IconCursorGroupType::Icon;
588*c3ab013aSZachary Turner   } else {
589*c3ab013aSZachary Turner     auto *CursorRes = dyn_cast<CursorResource>(Base);
590*c3ab013aSZachary Turner     FileStr = CursorRes->CursorLoc;
591*c3ab013aSZachary Turner     Type = IconCursorGroupType::Cursor;
592*c3ab013aSZachary Turner   }
593*c3ab013aSZachary Turner 
594*c3ab013aSZachary Turner   bool IsLong;
595*c3ab013aSZachary Turner   stripQuotes(FileStr, IsLong);
596*c3ab013aSZachary Turner   ErrorOr<std::unique_ptr<MemoryBuffer>> File =
597*c3ab013aSZachary Turner       MemoryBuffer::getFile(FileStr, -1, false);
598*c3ab013aSZachary Turner 
599*c3ab013aSZachary Turner   if (!File)
600*c3ab013aSZachary Turner     return make_error<StringError>(
601*c3ab013aSZachary Turner         "Error opening " +
602*c3ab013aSZachary Turner             Twine(Type == IconCursorGroupType::Icon ? "icon" : "cursor") +
603*c3ab013aSZachary Turner             " '" + FileStr + "': " + File.getError().message(),
604*c3ab013aSZachary Turner         File.getError());
605*c3ab013aSZachary Turner 
606*c3ab013aSZachary Turner   BinaryStreamReader Reader((*File)->getBuffer(), support::little);
607*c3ab013aSZachary Turner 
608*c3ab013aSZachary Turner   // Read the file headers.
609*c3ab013aSZachary Turner   //   - At the beginning, ICONDIR/NEWHEADER header.
610*c3ab013aSZachary Turner   //   - Then, a number of RESDIR headers follow. These contain offsets
611*c3ab013aSZachary Turner   //       to data.
612*c3ab013aSZachary Turner   const GroupIconDir *Header;
613*c3ab013aSZachary Turner 
614*c3ab013aSZachary Turner   RETURN_IF_ERROR(Reader.readObject(Header));
615*c3ab013aSZachary Turner   if (Header->Reserved != 0)
616*c3ab013aSZachary Turner     return createError("Incorrect icon/cursor Reserved field; should be 0.");
617*c3ab013aSZachary Turner   uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2;
618*c3ab013aSZachary Turner   if (Header->ResType != NeededType)
619*c3ab013aSZachary Turner     return createError("Incorrect icon/cursor ResType field; should be " +
620*c3ab013aSZachary Turner                        Twine(NeededType) + ".");
621*c3ab013aSZachary Turner 
622*c3ab013aSZachary Turner   uint16_t NumItems = Header->ResCount;
623*c3ab013aSZachary Turner 
624*c3ab013aSZachary Turner   // Read single ico/cur headers.
625*c3ab013aSZachary Turner   std::vector<ResourceDirEntryStart> ItemEntries;
626*c3ab013aSZachary Turner   ItemEntries.reserve(NumItems);
627*c3ab013aSZachary Turner   std::vector<uint32_t> ItemOffsets(NumItems);
628*c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
629*c3ab013aSZachary Turner     const ResourceDirEntryStart *Object;
630*c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readObject(Object));
631*c3ab013aSZachary Turner     ItemEntries.push_back(*Object);
632*c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID]));
633*c3ab013aSZachary Turner   }
634*c3ab013aSZachary Turner 
635*c3ab013aSZachary Turner   // Now write each icon/cursors one by one. At first, all the contents
636*c3ab013aSZachary Turner   // without ICO/CUR header. This is described by SingleIconCursorResource.
637*c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
638*c3ab013aSZachary Turner     // Load the fragment of file.
639*c3ab013aSZachary Turner     Reader.setOffset(ItemOffsets[ID]);
640*c3ab013aSZachary Turner     ArrayRef<uint8_t> Image;
641*c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size));
642*c3ab013aSZachary Turner     SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image);
643*c3ab013aSZachary Turner     SingleRes.setName(IconCursorID + ID);
644*c3ab013aSZachary Turner     RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes));
645*c3ab013aSZachary Turner   }
646*c3ab013aSZachary Turner 
647*c3ab013aSZachary Turner   // Now, write all the headers concatenated into a separate resource.
648*c3ab013aSZachary Turner   for (size_t ID = 0; ID < NumItems; ++ID) {
649*c3ab013aSZachary Turner     if (Type == IconCursorGroupType::Icon) {
650*c3ab013aSZachary Turner       // rc.exe seems to always set NumPlanes to 1. No idea why it happens.
651*c3ab013aSZachary Turner       ItemEntries[ID].Planes = 1;
652*c3ab013aSZachary Turner       continue;
653*c3ab013aSZachary Turner     }
654*c3ab013aSZachary Turner 
655*c3ab013aSZachary Turner     // We need to rewrite the cursor headers.
656*c3ab013aSZachary Turner     const auto &OldHeader = ItemEntries[ID];
657*c3ab013aSZachary Turner     ResourceDirEntryStart NewHeader;
658*c3ab013aSZachary Turner     NewHeader.Cursor.Width = OldHeader.Icon.Width;
659*c3ab013aSZachary Turner     // Each cursor in fact stores two bitmaps, one under another.
660*c3ab013aSZachary Turner     // Height provided in cursor definition describes the height of the
661*c3ab013aSZachary Turner     // cursor, whereas the value existing in resource definition describes
662*c3ab013aSZachary Turner     // the height of the bitmap. Therefore, we need to double this height.
663*c3ab013aSZachary Turner     NewHeader.Cursor.Height = OldHeader.Icon.Height * 2;
664*c3ab013aSZachary Turner 
665*c3ab013aSZachary Turner     // Now, we actually need to read the bitmap header to find
666*c3ab013aSZachary Turner     // the number of planes and the number of bits per pixel.
667*c3ab013aSZachary Turner     Reader.setOffset(ItemOffsets[ID]);
668*c3ab013aSZachary Turner     const BitmapInfoHeader *BMPHeader;
669*c3ab013aSZachary Turner     RETURN_IF_ERROR(Reader.readObject(BMPHeader));
670*c3ab013aSZachary Turner     NewHeader.Planes = BMPHeader->Planes;
671*c3ab013aSZachary Turner     NewHeader.BitCount = BMPHeader->BitCount;
672*c3ab013aSZachary Turner 
673*c3ab013aSZachary Turner     // Two WORDs were written at the beginning of the resource (hotspot
674*c3ab013aSZachary Turner     // location). This is reflected in Size field.
675*c3ab013aSZachary Turner     NewHeader.Size = OldHeader.Size + 2 * sizeof(uint16_t);
676*c3ab013aSZachary Turner 
677*c3ab013aSZachary Turner     ItemEntries[ID] = NewHeader;
678*c3ab013aSZachary Turner   }
679*c3ab013aSZachary Turner 
680*c3ab013aSZachary Turner   IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries));
681*c3ab013aSZachary Turner   HeaderRes.setName(ResName);
682*c3ab013aSZachary Turner   RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes));
683*c3ab013aSZachary Turner 
684*c3ab013aSZachary Turner   return Error::success();
685*c3ab013aSZachary Turner }
686*c3ab013aSZachary Turner 
6877f7745c0SMarek Sokolowski // --- DialogResource helpers. --- //
68822fccd64SMarek Sokolowski 
6897f7745c0SMarek Sokolowski Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
6907f7745c0SMarek Sokolowski                                                    bool IsExtended) {
6917f7745c0SMarek Sokolowski   // Each control should be aligned to DWORD.
6927f7745c0SMarek Sokolowski   padStream(sizeof(uint32_t));
6937f7745c0SMarek Sokolowski 
6947f7745c0SMarek Sokolowski   auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
6957f7745c0SMarek Sokolowski   uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0);
6967f7745c0SMarek Sokolowski   uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
6977f7745c0SMarek Sokolowski 
6987f7745c0SMarek Sokolowski   // DIALOG(EX) item header prefix.
6997f7745c0SMarek Sokolowski   if (!IsExtended) {
7007f7745c0SMarek Sokolowski     struct {
7017f7745c0SMarek Sokolowski       ulittle32_t Style;
7027f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
7037f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)};
7047f7745c0SMarek Sokolowski     writeObject(Prefix);
7057f7745c0SMarek Sokolowski   } else {
7067f7745c0SMarek Sokolowski     struct {
7077f7745c0SMarek Sokolowski       ulittle32_t HelpID;
7087f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
7097f7745c0SMarek Sokolowski       ulittle32_t Style;
7107f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
7117f7745c0SMarek Sokolowski              ulittle32_t(CtlStyle)};
7127f7745c0SMarek Sokolowski     writeObject(Prefix);
7137f7745c0SMarek Sokolowski   }
7147f7745c0SMarek Sokolowski 
7157f7745c0SMarek Sokolowski   // Common fixed-length part.
7167f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
7177f7745c0SMarek Sokolowski       Ctl.X, "Dialog control x-coordinate", true));
7187f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
7197f7745c0SMarek Sokolowski       Ctl.Y, "Dialog control y-coordinate", true));
7207f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
7217f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
7227f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
7237f7745c0SMarek Sokolowski       Ctl.Height, "Dialog control height", false));
7247f7745c0SMarek Sokolowski   struct {
7257f7745c0SMarek Sokolowski     ulittle16_t X;
7267f7745c0SMarek Sokolowski     ulittle16_t Y;
7277f7745c0SMarek Sokolowski     ulittle16_t Width;
7287f7745c0SMarek Sokolowski     ulittle16_t Height;
7297f7745c0SMarek Sokolowski   } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
7307f7745c0SMarek Sokolowski            ulittle16_t(Ctl.Height)};
7317f7745c0SMarek Sokolowski   writeObject(Middle);
7327f7745c0SMarek Sokolowski 
7337f7745c0SMarek Sokolowski   // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
7347f7745c0SMarek Sokolowski   if (!IsExtended) {
7357f7745c0SMarek Sokolowski     RETURN_IF_ERROR(checkNumberFits<uint16_t>(
7367f7745c0SMarek Sokolowski         Ctl.ID, "Control ID in simple DIALOG resource"));
7377f7745c0SMarek Sokolowski     writeInt<uint16_t>(Ctl.ID);
7387f7745c0SMarek Sokolowski   } else {
7397f7745c0SMarek Sokolowski     writeInt<uint32_t>(Ctl.ID);
7407f7745c0SMarek Sokolowski   }
7417f7745c0SMarek Sokolowski 
7427f7745c0SMarek Sokolowski   // Window class - either 0xFFFF + 16-bit integer or a string.
7437f7745c0SMarek Sokolowski   RETURN_IF_ERROR(writeIntOrString(IntOrString(TypeInfo.CtlClass)));
7447f7745c0SMarek Sokolowski 
7457f7745c0SMarek Sokolowski   // Element caption/reference ID. ID is preceded by 0xFFFF.
7467f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
7477f7745c0SMarek Sokolowski   RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
7487f7745c0SMarek Sokolowski 
7497f7745c0SMarek Sokolowski   // # bytes of extra creation data count. Don't pass any.
7507f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
7517f7745c0SMarek Sokolowski 
7527f7745c0SMarek Sokolowski   return Error::success();
7537f7745c0SMarek Sokolowski }
7547f7745c0SMarek Sokolowski 
7557f7745c0SMarek Sokolowski Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
7567f7745c0SMarek Sokolowski   auto *Res = cast<DialogResource>(Base);
7577f7745c0SMarek Sokolowski 
7587f7745c0SMarek Sokolowski   // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
759420090afSZachary Turner   const uint32_t DefaultStyle = 0x80880000;
760420090afSZachary Turner   const uint32_t StyleFontFlag = 0x40;
761420090afSZachary Turner   const uint32_t StyleCaptionFlag = 0x00C00000;
762420090afSZachary Turner 
763420090afSZachary Turner   uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle);
764420090afSZachary Turner   if (ObjectData.Font)
765420090afSZachary Turner     UsedStyle |= StyleFontFlag;
766420090afSZachary Turner   else
767420090afSZachary Turner     UsedStyle &= ~StyleFontFlag;
768420090afSZachary Turner 
769420090afSZachary Turner   // Actually, in case of empty (but existent) caption, the examined field
770420090afSZachary Turner   // is equal to "\"\"". That's why empty captions are still noticed.
771420090afSZachary Turner   if (ObjectData.Caption != "")
772420090afSZachary Turner     UsedStyle |= StyleCaptionFlag;
773420090afSZachary Turner 
774420090afSZachary Turner   const uint16_t DialogExMagic = 0xFFFF;
7757f7745c0SMarek Sokolowski 
7767f7745c0SMarek Sokolowski   // Write DIALOG(EX) header prefix. These are pretty different.
7777f7745c0SMarek Sokolowski   if (!Res->IsExtended) {
778420090afSZachary Turner     // We cannot let the higher word of DefaultStyle be equal to 0xFFFF.
779420090afSZachary Turner     // In such a case, whole object (in .res file) is equivalent to a
780420090afSZachary Turner     // DIALOGEX. It might lead to access violation/segmentation fault in
781420090afSZachary Turner     // resource readers. For example,
782420090afSZachary Turner     //   1 DIALOG 0, 0, 0, 65432
783420090afSZachary Turner     //   STYLE 0xFFFF0001 {}
784420090afSZachary Turner     // would be compiled to a DIALOGEX with 65432 controls.
785420090afSZachary Turner     if ((UsedStyle >> 16) == DialogExMagic)
786420090afSZachary Turner       return createError("16 higher bits of DIALOG resource style cannot be"
787420090afSZachary Turner                          " equal to 0xFFFF");
788420090afSZachary Turner 
7897f7745c0SMarek Sokolowski     struct {
7907f7745c0SMarek Sokolowski       ulittle32_t Style;
7917f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
7927f7745c0SMarek Sokolowski     } Prefix{ulittle32_t(UsedStyle),
7937f7745c0SMarek Sokolowski              ulittle32_t(0)}; // As of now, we don't keep EXSTYLE.
7947f7745c0SMarek Sokolowski 
7957f7745c0SMarek Sokolowski     writeObject(Prefix);
7967f7745c0SMarek Sokolowski   } else {
7977f7745c0SMarek Sokolowski     struct {
7987f7745c0SMarek Sokolowski       ulittle16_t Version;
7997f7745c0SMarek Sokolowski       ulittle16_t Magic;
8007f7745c0SMarek Sokolowski       ulittle32_t HelpID;
8017f7745c0SMarek Sokolowski       ulittle32_t ExtStyle;
8027f7745c0SMarek Sokolowski       ulittle32_t Style;
8037f7745c0SMarek Sokolowski     } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
8047f7745c0SMarek Sokolowski              ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)};
8057f7745c0SMarek Sokolowski 
8067f7745c0SMarek Sokolowski     writeObject(Prefix);
8077f7745c0SMarek Sokolowski   }
8087f7745c0SMarek Sokolowski 
8097f7745c0SMarek Sokolowski   // Now, a common part. First, fixed-length fields.
8107f7745c0SMarek Sokolowski   RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
8117f7745c0SMarek Sokolowski                                             "Number of dialog controls"));
8127f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
8137f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
8147f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
8157f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
8167f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
8177f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
8187f7745c0SMarek Sokolowski   RETURN_IF_ERROR(
8197f7745c0SMarek Sokolowski       checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
8207f7745c0SMarek Sokolowski   struct {
8217f7745c0SMarek Sokolowski     ulittle16_t Count;
8227f7745c0SMarek Sokolowski     ulittle16_t PosX;
8237f7745c0SMarek Sokolowski     ulittle16_t PosY;
8247f7745c0SMarek Sokolowski     ulittle16_t DialogWidth;
8257f7745c0SMarek Sokolowski     ulittle16_t DialogHeight;
8267f7745c0SMarek Sokolowski   } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
8277f7745c0SMarek Sokolowski            ulittle16_t(Res->Y), ulittle16_t(Res->Width),
8287f7745c0SMarek Sokolowski            ulittle16_t(Res->Height)};
8297f7745c0SMarek Sokolowski   writeObject(Middle);
8307f7745c0SMarek Sokolowski 
8317f7745c0SMarek Sokolowski   // MENU field. As of now, we don't keep them in the state and can peacefully
8327f7745c0SMarek Sokolowski   // think there is no menu attached to the dialog.
8337f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
8347f7745c0SMarek Sokolowski 
8357f7745c0SMarek Sokolowski   // Window CLASS field. Not kept here.
8367f7745c0SMarek Sokolowski   writeInt<uint16_t>(0);
8377f7745c0SMarek Sokolowski 
838420090afSZachary Turner   // Window title or a single word equal to 0.
839420090afSZachary Turner   RETURN_IF_ERROR(writeCString(ObjectData.Caption));
840420090afSZachary Turner 
841420090afSZachary Turner   // If there *is* a window font declared, output its data.
842420090afSZachary Turner   auto &Font = ObjectData.Font;
843420090afSZachary Turner   if (Font) {
844420090afSZachary Turner     writeInt<uint16_t>(Font->Size);
845420090afSZachary Turner     // Additional description occurs only in DIALOGEX.
846420090afSZachary Turner     if (Res->IsExtended) {
847420090afSZachary Turner       writeInt<uint16_t>(Font->Weight);
848420090afSZachary Turner       writeInt<uint8_t>(Font->IsItalic);
849420090afSZachary Turner       writeInt<uint8_t>(Font->Charset);
850420090afSZachary Turner     }
851420090afSZachary Turner     RETURN_IF_ERROR(writeCString(Font->Typeface));
852420090afSZachary Turner   }
8537f7745c0SMarek Sokolowski 
8547f7745c0SMarek Sokolowski   auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
8557f7745c0SMarek Sokolowski     if (!Err)
8567f7745c0SMarek Sokolowski       return Error::success();
8577f7745c0SMarek Sokolowski     return joinErrors(createError("Error in " + Twine(Ctl.Type) +
8587f7745c0SMarek Sokolowski                                   " control  (ID " + Twine(Ctl.ID) + "):"),
8597f7745c0SMarek Sokolowski                       std::move(Err));
8607f7745c0SMarek Sokolowski   };
8617f7745c0SMarek Sokolowski 
8627f7745c0SMarek Sokolowski   for (auto &Ctl : Res->Controls)
8637f7745c0SMarek Sokolowski     RETURN_IF_ERROR(
8647f7745c0SMarek Sokolowski         handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
8657f7745c0SMarek Sokolowski 
8667f7745c0SMarek Sokolowski   return Error::success();
8677f7745c0SMarek Sokolowski }
8687f7745c0SMarek Sokolowski 
8697f7745c0SMarek Sokolowski // --- HTMLResource helpers. --- //
87022fccd64SMarek Sokolowski 
8718f19343aSMarek Sokolowski Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
8728f19343aSMarek Sokolowski   return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
8738f19343aSMarek Sokolowski }
8748f19343aSMarek Sokolowski 
87542f494d6SMarek Sokolowski // --- MenuResource helpers. --- //
87642f494d6SMarek Sokolowski 
87742f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinition(
87842f494d6SMarek Sokolowski     const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
87942f494d6SMarek Sokolowski   assert(Def);
88042f494d6SMarek Sokolowski   const MenuDefinition *DefPtr = Def.get();
88142f494d6SMarek Sokolowski 
88242f494d6SMarek Sokolowski   if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
88342f494d6SMarek Sokolowski     writeInt<uint16_t>(Flags);
88442f494d6SMarek Sokolowski     RETURN_IF_ERROR(
88542f494d6SMarek Sokolowski         checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
88642f494d6SMarek Sokolowski     writeInt<uint16_t>(MenuItemPtr->Id);
88742f494d6SMarek Sokolowski     RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
88842f494d6SMarek Sokolowski     return Error::success();
88942f494d6SMarek Sokolowski   }
89042f494d6SMarek Sokolowski 
89142f494d6SMarek Sokolowski   if (isa<MenuSeparator>(DefPtr)) {
89242f494d6SMarek Sokolowski     writeInt<uint16_t>(Flags);
89342f494d6SMarek Sokolowski     writeInt<uint32_t>(0);
89442f494d6SMarek Sokolowski     return Error::success();
89542f494d6SMarek Sokolowski   }
89642f494d6SMarek Sokolowski 
89742f494d6SMarek Sokolowski   auto *PopupPtr = cast<PopupItem>(DefPtr);
89842f494d6SMarek Sokolowski   writeInt<uint16_t>(Flags);
89942f494d6SMarek Sokolowski   RETURN_IF_ERROR(writeCString(PopupPtr->Name));
90042f494d6SMarek Sokolowski   return writeMenuDefinitionList(PopupPtr->SubItems);
90142f494d6SMarek Sokolowski }
90242f494d6SMarek Sokolowski 
90342f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinitionList(
90442f494d6SMarek Sokolowski     const MenuDefinitionList &List) {
90542f494d6SMarek Sokolowski   for (auto &Def : List.Definitions) {
90642f494d6SMarek Sokolowski     uint16_t Flags = Def->getResFlags();
90742f494d6SMarek Sokolowski     // Last element receives an additional 0x80 flag.
90842f494d6SMarek Sokolowski     const uint16_t LastElementFlag = 0x0080;
90942f494d6SMarek Sokolowski     if (&Def == &List.Definitions.back())
91042f494d6SMarek Sokolowski       Flags |= LastElementFlag;
91142f494d6SMarek Sokolowski 
91242f494d6SMarek Sokolowski     RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
91342f494d6SMarek Sokolowski   }
91442f494d6SMarek Sokolowski   return Error::success();
91542f494d6SMarek Sokolowski }
91642f494d6SMarek Sokolowski 
91742f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
91842f494d6SMarek Sokolowski   // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
91942f494d6SMarek Sokolowski   // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
92042f494d6SMarek Sokolowski   writeObject<uint32_t>(0);
92142f494d6SMarek Sokolowski 
92242f494d6SMarek Sokolowski   return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
92342f494d6SMarek Sokolowski }
92442f494d6SMarek Sokolowski 
9258f19343aSMarek Sokolowski } // namespace rc
9268f19343aSMarek Sokolowski } // namespace llvm
927