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" 20bd159d32SZachary Turner #include "llvm/Support/MemoryBuffer.h" 21fa0ca6cbSZachary Turner #include "llvm/Support/Path.h" 22fa0ca6cbSZachary Turner #include "llvm/Support/Process.h" 238f19343aSMarek Sokolowski 248f19343aSMarek Sokolowski using namespace llvm::support; 258f19343aSMarek Sokolowski 268f19343aSMarek Sokolowski // Take an expression returning llvm::Error and forward the error if it exists. 278f19343aSMarek Sokolowski #define RETURN_IF_ERROR(Expr) \ 288f19343aSMarek Sokolowski if (auto Err = (Expr)) \ 298f19343aSMarek Sokolowski return Err; 308f19343aSMarek Sokolowski 318f19343aSMarek Sokolowski namespace llvm { 328f19343aSMarek Sokolowski namespace rc { 338f19343aSMarek Sokolowski 3407bc04ffSZachary Turner // Class that employs RAII to save the current FileWriter object state 3522fccd64SMarek Sokolowski // and revert to it as soon as we leave the scope. This is useful if resources 3622fccd64SMarek Sokolowski // declare their own resource-local statements. 3722fccd64SMarek Sokolowski class ContextKeeper { 3822fccd64SMarek Sokolowski ResourceFileWriter *FileWriter; 3922fccd64SMarek Sokolowski ResourceFileWriter::ObjectInfo SavedInfo; 4022fccd64SMarek Sokolowski 4122fccd64SMarek Sokolowski public: 4222fccd64SMarek Sokolowski ContextKeeper(ResourceFileWriter *V) 4322fccd64SMarek Sokolowski : FileWriter(V), SavedInfo(V->ObjectData) {} 4422fccd64SMarek Sokolowski ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; } 4522fccd64SMarek Sokolowski }; 4622fccd64SMarek Sokolowski 47fa0ca6cbSZachary Turner static Error createError(const Twine &Message, 488f19343aSMarek Sokolowski std::errc Type = std::errc::invalid_argument) { 498f19343aSMarek Sokolowski return make_error<StringError>(Message, std::make_error_code(Type)); 508f19343aSMarek Sokolowski } 518f19343aSMarek Sokolowski 52fa0ca6cbSZachary Turner static Error checkNumberFits(uint32_t Number, size_t MaxBits, 53fa0ca6cbSZachary Turner const Twine &FieldName) { 548f19343aSMarek Sokolowski assert(1 <= MaxBits && MaxBits <= 32); 558f19343aSMarek Sokolowski if (!(Number >> MaxBits)) 568f19343aSMarek Sokolowski return Error::success(); 578f19343aSMarek Sokolowski return createError(FieldName + " (" + Twine(Number) + ") does not fit in " + 588f19343aSMarek Sokolowski Twine(MaxBits) + " bits.", 598f19343aSMarek Sokolowski std::errc::value_too_large); 608f19343aSMarek Sokolowski } 618f19343aSMarek Sokolowski 628f19343aSMarek Sokolowski template <typename FitType> 63fa0ca6cbSZachary Turner static Error checkNumberFits(uint32_t Number, const Twine &FieldName) { 648f19343aSMarek Sokolowski return checkNumberFits(Number, sizeof(FitType) * 8, FieldName); 658f19343aSMarek Sokolowski } 668f19343aSMarek Sokolowski 677f7745c0SMarek Sokolowski // A similar function for signed integers. 687f7745c0SMarek Sokolowski template <typename FitType> 69fa0ca6cbSZachary Turner static Error checkSignedNumberFits(uint32_t Number, const Twine &FieldName, 707f7745c0SMarek Sokolowski bool CanBeNegative) { 717f7745c0SMarek Sokolowski int32_t SignedNum = Number; 727f7745c0SMarek Sokolowski if (SignedNum < std::numeric_limits<FitType>::min() || 737f7745c0SMarek Sokolowski SignedNum > std::numeric_limits<FitType>::max()) 747f7745c0SMarek Sokolowski return createError(FieldName + " (" + Twine(SignedNum) + 757f7745c0SMarek Sokolowski ") does not fit in " + Twine(sizeof(FitType) * 8) + 767f7745c0SMarek Sokolowski "-bit signed integer type.", 777f7745c0SMarek Sokolowski std::errc::value_too_large); 787f7745c0SMarek Sokolowski 797f7745c0SMarek Sokolowski if (!CanBeNegative && SignedNum < 0) 807f7745c0SMarek Sokolowski return createError(FieldName + " (" + Twine(SignedNum) + 817f7745c0SMarek Sokolowski ") cannot be negative."); 827f7745c0SMarek Sokolowski 837f7745c0SMarek Sokolowski return Error::success(); 847f7745c0SMarek Sokolowski } 857f7745c0SMarek Sokolowski 86fa0ca6cbSZachary Turner static Error checkRCInt(RCInt Number, const Twine &FieldName) { 8707bc04ffSZachary Turner if (Number.isLong()) 8807bc04ffSZachary Turner return Error::success(); 8907bc04ffSZachary Turner return checkNumberFits<uint16_t>(Number, FieldName); 9007bc04ffSZachary Turner } 9107bc04ffSZachary Turner 92fa0ca6cbSZachary Turner static Error checkIntOrString(IntOrString Value, const Twine &FieldName) { 938f19343aSMarek Sokolowski if (!Value.isInt()) 948f19343aSMarek Sokolowski return Error::success(); 958f19343aSMarek Sokolowski return checkNumberFits<uint16_t>(Value.getInt(), FieldName); 968f19343aSMarek Sokolowski } 978f19343aSMarek Sokolowski 988f19343aSMarek Sokolowski static bool stripQuotes(StringRef &Str, bool &IsLongString) { 998f19343aSMarek Sokolowski if (!Str.contains('"')) 1008f19343aSMarek Sokolowski return false; 1018f19343aSMarek Sokolowski 1028f19343aSMarek Sokolowski // Just take the contents of the string, checking if it's been marked long. 1038f19343aSMarek Sokolowski IsLongString = Str.startswith_lower("L"); 1048f19343aSMarek Sokolowski if (IsLongString) 1058f19343aSMarek Sokolowski Str = Str.drop_front(); 1068f19343aSMarek Sokolowski 1078f19343aSMarek Sokolowski bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\""); 1088f19343aSMarek Sokolowski (void)StripSuccess; 1098f19343aSMarek Sokolowski assert(StripSuccess && "Strings should be enclosed in quotes."); 1108f19343aSMarek Sokolowski return true; 1118f19343aSMarek Sokolowski } 1128f19343aSMarek Sokolowski 113d1d046aaSMartin Storsjo static UTF16 cp1252ToUnicode(unsigned char C) { 114d1d046aaSMartin Storsjo static const UTF16 Map80[] = { 115d1d046aaSMartin Storsjo 0x20ac, 0x0081, 0x201a, 0x0192, 0x201e, 0x2026, 0x2020, 0x2021, 116d1d046aaSMartin Storsjo 0x02c6, 0x2030, 0x0160, 0x2039, 0x0152, 0x008d, 0x017d, 0x008f, 117d1d046aaSMartin Storsjo 0x0090, 0x2018, 0x2019, 0x201c, 0x201d, 0x2022, 0x2013, 0x2014, 118d1d046aaSMartin Storsjo 0x02dc, 0x2122, 0x0161, 0x203a, 0x0153, 0x009d, 0x017e, 0x0178, 119d1d046aaSMartin Storsjo }; 120d1d046aaSMartin Storsjo if (C >= 0x80 && C <= 0x9F) 121d1d046aaSMartin Storsjo return Map80[C - 0x80]; 122d1d046aaSMartin Storsjo return C; 123d1d046aaSMartin Storsjo } 124d1d046aaSMartin Storsjo 1258f19343aSMarek Sokolowski // Describes a way to handle '\0' characters when processing the string. 1268f19343aSMarek Sokolowski // rc.exe tool sometimes behaves in a weird way in postprocessing. 1278f19343aSMarek Sokolowski // If the string to be output is equivalent to a C-string (e.g. in MENU 1288f19343aSMarek Sokolowski // titles), string is (predictably) truncated after first 0-byte. 1298f19343aSMarek Sokolowski // When outputting a string table, the behavior is equivalent to appending 1308f19343aSMarek Sokolowski // '\0\0' at the end of the string, and then stripping the string 1318f19343aSMarek Sokolowski // before the first '\0\0' occurrence. 1328f19343aSMarek Sokolowski // Finally, when handling strings in user-defined resources, 0-bytes 1338f19343aSMarek Sokolowski // aren't stripped, nor do they terminate the string. 1348f19343aSMarek Sokolowski 1358f19343aSMarek Sokolowski enum class NullHandlingMethod { 1368f19343aSMarek Sokolowski UserResource, // Don't terminate string on '\0'. 1378f19343aSMarek Sokolowski CutAtNull, // Terminate string on '\0'. 1388f19343aSMarek Sokolowski CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'. 1398f19343aSMarek Sokolowski }; 1408f19343aSMarek Sokolowski 141a92eb33aSZachary Turner // Parses an identifier or string and returns a processed version of it: 142a92eb33aSZachary Turner // * String the string boundary quotes. 1438f19343aSMarek Sokolowski // * Squash "" to a single ". 1448f19343aSMarek Sokolowski // * Replace the escape sequences with their processed version. 1458f19343aSMarek Sokolowski // For identifiers, this is no-op. 1468f19343aSMarek Sokolowski static Error processString(StringRef Str, NullHandlingMethod NullHandler, 147d1d046aaSMartin Storsjo bool &IsLongString, SmallVectorImpl<UTF16> &Result, 148d1d046aaSMartin Storsjo int CodePage) { 1498f19343aSMarek Sokolowski bool IsString = stripQuotes(Str, IsLongString); 150a92eb33aSZachary Turner SmallVector<UTF16, 128> Chars; 151d1d046aaSMartin Storsjo 152d1d046aaSMartin Storsjo // Convert the input bytes according to the chosen codepage. 153d1d046aaSMartin Storsjo if (CodePage == CpUtf8) { 154a92eb33aSZachary Turner convertUTF8ToUTF16String(Str, Chars); 155d1d046aaSMartin Storsjo } else if (CodePage == CpWin1252) { 156d1d046aaSMartin Storsjo for (char C : Str) 157d1d046aaSMartin Storsjo Chars.push_back(cp1252ToUnicode((unsigned char)C)); 158d1d046aaSMartin Storsjo } else { 159d1d046aaSMartin Storsjo // For other, unknown codepages, only allow plain ASCII input. 160d1d046aaSMartin Storsjo for (char C : Str) { 161d1d046aaSMartin Storsjo if ((unsigned char)C > 0x7F) 162d1d046aaSMartin Storsjo return createError("Non-ASCII 8-bit codepoint (" + Twine(C) + 163d1d046aaSMartin Storsjo ") can't be interpreted in the current codepage"); 164d1d046aaSMartin Storsjo Chars.push_back((unsigned char)C); 165d1d046aaSMartin Storsjo } 166d1d046aaSMartin Storsjo } 1678f19343aSMarek Sokolowski 1688f19343aSMarek Sokolowski if (!IsString) { 1698f19343aSMarek Sokolowski // It's an identifier if it's not a string. Make all characters uppercase. 170a92eb33aSZachary Turner for (UTF16 &Ch : Chars) { 1718f19343aSMarek Sokolowski assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII"); 1728f19343aSMarek Sokolowski Ch = toupper(Ch); 1738f19343aSMarek Sokolowski } 174a92eb33aSZachary Turner Result.swap(Chars); 1758f19343aSMarek Sokolowski return Error::success(); 1768f19343aSMarek Sokolowski } 177a92eb33aSZachary Turner Result.reserve(Chars.size()); 178a92eb33aSZachary Turner size_t Pos = 0; 179a92eb33aSZachary Turner 180a92eb33aSZachary Turner auto AddRes = [&Result, NullHandler, IsLongString](UTF16 Char) -> Error { 181a92eb33aSZachary Turner if (!IsLongString) { 182a92eb33aSZachary Turner if (NullHandler == NullHandlingMethod::UserResource) { 183a92eb33aSZachary Turner // Narrow strings in user-defined resources are *not* output in 184a92eb33aSZachary Turner // UTF-16 format. 185a92eb33aSZachary Turner if (Char > 0xFF) 186a92eb33aSZachary Turner return createError("Non-8-bit codepoint (" + Twine(Char) + 187a92eb33aSZachary Turner ") can't occur in a user-defined narrow string"); 188d1d046aaSMartin Storsjo } 189d1d046aaSMartin Storsjo } 190a92eb33aSZachary Turner 191d1d046aaSMartin Storsjo Result.push_back(Char); 192d1d046aaSMartin Storsjo return Error::success(); 193d1d046aaSMartin Storsjo }; 194d1d046aaSMartin Storsjo auto AddEscapedChar = [AddRes, IsLongString, CodePage](UTF16 Char) -> Error { 195d1d046aaSMartin Storsjo if (!IsLongString) { 196d1d046aaSMartin Storsjo // Escaped chars in narrow strings have to be interpreted according to 197d1d046aaSMartin Storsjo // the chosen code page. 198d1d046aaSMartin Storsjo if (Char > 0xFF) 199d1d046aaSMartin Storsjo return createError("Non-8-bit escaped char (" + Twine(Char) + 200d1d046aaSMartin Storsjo ") can't occur in narrow string"); 201d1d046aaSMartin Storsjo if (CodePage == CpUtf8) { 202d1d046aaSMartin Storsjo if (Char >= 0x80) 203d1d046aaSMartin Storsjo return createError("Unable to interpret single byte (" + Twine(Char) + 204d1d046aaSMartin Storsjo ") as UTF-8"); 205d1d046aaSMartin Storsjo } else if (CodePage == CpWin1252) { 206d1d046aaSMartin Storsjo Char = cp1252ToUnicode(Char); 207a92eb33aSZachary Turner } else { 208d1d046aaSMartin Storsjo // Unknown/unsupported codepage, only allow ASCII input. 209d1d046aaSMartin Storsjo if (Char > 0x7F) 210a92eb33aSZachary Turner return createError("Non-ASCII 8-bit codepoint (" + Twine(Char) + 211a92eb33aSZachary Turner ") can't " 212a92eb33aSZachary Turner "occur in a non-Unicode string"); 213a92eb33aSZachary Turner } 214a92eb33aSZachary Turner } 215a92eb33aSZachary Turner 216d1d046aaSMartin Storsjo return AddRes(Char); 217a92eb33aSZachary Turner }; 218a92eb33aSZachary Turner 219a92eb33aSZachary Turner while (Pos < Chars.size()) { 220a92eb33aSZachary Turner UTF16 CurChar = Chars[Pos]; 221a92eb33aSZachary Turner ++Pos; 222a92eb33aSZachary Turner 223a92eb33aSZachary Turner // Strip double "". 224a92eb33aSZachary Turner if (CurChar == '"') { 225a92eb33aSZachary Turner if (Pos == Chars.size() || Chars[Pos] != '"') 226a92eb33aSZachary Turner return createError("Expected \"\""); 227a92eb33aSZachary Turner ++Pos; 228a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes('"')); 229a92eb33aSZachary Turner continue; 230a92eb33aSZachary Turner } 231a92eb33aSZachary Turner 232a92eb33aSZachary Turner if (CurChar == '\\') { 233a92eb33aSZachary Turner UTF16 TypeChar = Chars[Pos]; 234a92eb33aSZachary Turner ++Pos; 235a92eb33aSZachary Turner 236a92eb33aSZachary Turner if (TypeChar == 'x' || TypeChar == 'X') { 237a92eb33aSZachary Turner // Read a hex number. Max number of characters to read differs between 238a92eb33aSZachary Turner // narrow and wide strings. 239a92eb33aSZachary Turner UTF16 ReadInt = 0; 240a92eb33aSZachary Turner size_t RemainingChars = IsLongString ? 4 : 2; 241a92eb33aSZachary Turner // We don't want to read non-ASCII hex digits. std:: functions past 242a92eb33aSZachary Turner // 0xFF invoke UB. 243a92eb33aSZachary Turner // 244a92eb33aSZachary Turner // FIXME: actually, Microsoft version probably doesn't check this 245a92eb33aSZachary Turner // condition and uses their Unicode version of 'isxdigit'. However, 246a92eb33aSZachary Turner // there are some hex-digit Unicode character outside of ASCII, and 247a92eb33aSZachary Turner // some of these are actually accepted by rc.exe, the notable example 248a92eb33aSZachary Turner // being fullwidth forms (U+FF10..U+FF19 etc.) These can be written 249a92eb33aSZachary Turner // instead of ASCII digits in \x... escape sequence and get accepted. 250a92eb33aSZachary Turner // However, the resulting hexcodes seem totally unpredictable. 251a92eb33aSZachary Turner // We think it's infeasible to try to reproduce this behavior, nor to 252a92eb33aSZachary Turner // put effort in order to detect it. 253a92eb33aSZachary Turner while (RemainingChars && Pos < Chars.size() && Chars[Pos] < 0x80) { 254a92eb33aSZachary Turner if (!isxdigit(Chars[Pos])) 255a92eb33aSZachary Turner break; 256a92eb33aSZachary Turner char Digit = tolower(Chars[Pos]); 257a92eb33aSZachary Turner ++Pos; 258a92eb33aSZachary Turner 259a92eb33aSZachary Turner ReadInt <<= 4; 260a92eb33aSZachary Turner if (isdigit(Digit)) 261a92eb33aSZachary Turner ReadInt |= Digit - '0'; 262a92eb33aSZachary Turner else 263a92eb33aSZachary Turner ReadInt |= Digit - 'a' + 10; 264a92eb33aSZachary Turner 265a92eb33aSZachary Turner --RemainingChars; 266a92eb33aSZachary Turner } 267a92eb33aSZachary Turner 268d1d046aaSMartin Storsjo RETURN_IF_ERROR(AddEscapedChar(ReadInt)); 269a92eb33aSZachary Turner continue; 270a92eb33aSZachary Turner } 271a92eb33aSZachary Turner 272a92eb33aSZachary Turner if (TypeChar >= '0' && TypeChar < '8') { 273a92eb33aSZachary Turner // Read an octal number. Note that we've already read the first digit. 274a92eb33aSZachary Turner UTF16 ReadInt = TypeChar - '0'; 275a92eb33aSZachary Turner size_t RemainingChars = IsLongString ? 6 : 2; 276a92eb33aSZachary Turner 277a92eb33aSZachary Turner while (RemainingChars && Pos < Chars.size() && Chars[Pos] >= '0' && 278a92eb33aSZachary Turner Chars[Pos] < '8') { 279a92eb33aSZachary Turner ReadInt <<= 3; 280a92eb33aSZachary Turner ReadInt |= Chars[Pos] - '0'; 281a92eb33aSZachary Turner --RemainingChars; 282a92eb33aSZachary Turner ++Pos; 283a92eb33aSZachary Turner } 284a92eb33aSZachary Turner 285d1d046aaSMartin Storsjo RETURN_IF_ERROR(AddEscapedChar(ReadInt)); 286a92eb33aSZachary Turner 287a92eb33aSZachary Turner continue; 288a92eb33aSZachary Turner } 289a92eb33aSZachary Turner 290a92eb33aSZachary Turner switch (TypeChar) { 291a92eb33aSZachary Turner case 'A': 292a92eb33aSZachary Turner case 'a': 293a92eb33aSZachary Turner // Windows '\a' translates into '\b' (Backspace). 294a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes('\b')); 295a92eb33aSZachary Turner break; 296a92eb33aSZachary Turner 297a92eb33aSZachary Turner case 'n': // Somehow, RC doesn't recognize '\N' and '\R'. 298a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes('\n')); 299a92eb33aSZachary Turner break; 300a92eb33aSZachary Turner 301a92eb33aSZachary Turner case 'r': 302a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes('\r')); 303a92eb33aSZachary Turner break; 304a92eb33aSZachary Turner 305a92eb33aSZachary Turner case 'T': 306a92eb33aSZachary Turner case 't': 307a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes('\t')); 308a92eb33aSZachary Turner break; 309a92eb33aSZachary Turner 310a92eb33aSZachary Turner case '\\': 311a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes('\\')); 312a92eb33aSZachary Turner break; 313a92eb33aSZachary Turner 314a92eb33aSZachary Turner case '"': 315a92eb33aSZachary Turner // RC accepts \" only if another " comes afterwards; then, \"" means 316a92eb33aSZachary Turner // a single ". 317a92eb33aSZachary Turner if (Pos == Chars.size() || Chars[Pos] != '"') 318a92eb33aSZachary Turner return createError("Expected \\\"\""); 319a92eb33aSZachary Turner ++Pos; 320a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes('"')); 321a92eb33aSZachary Turner break; 322a92eb33aSZachary Turner 323a92eb33aSZachary Turner default: 324a92eb33aSZachary Turner // If TypeChar means nothing, \ is should be output to stdout with 325a92eb33aSZachary Turner // following char. However, rc.exe consumes these characters when 326a92eb33aSZachary Turner // dealing with wide strings. 327a92eb33aSZachary Turner if (!IsLongString) { 328a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes('\\')); 329a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes(TypeChar)); 330a92eb33aSZachary Turner } 331a92eb33aSZachary Turner break; 332a92eb33aSZachary Turner } 333a92eb33aSZachary Turner 334a92eb33aSZachary Turner continue; 335a92eb33aSZachary Turner } 336a92eb33aSZachary Turner 337a92eb33aSZachary Turner // If nothing interesting happens, just output the character. 338a92eb33aSZachary Turner RETURN_IF_ERROR(AddRes(CurChar)); 339a92eb33aSZachary Turner } 3408f19343aSMarek Sokolowski 341da366693SZachary Turner switch (NullHandler) { 342da366693SZachary Turner case NullHandlingMethod::CutAtNull: 3438f19343aSMarek Sokolowski for (size_t Pos = 0; Pos < Result.size(); ++Pos) 3448f19343aSMarek Sokolowski if (Result[Pos] == '\0') 3458f19343aSMarek Sokolowski Result.resize(Pos); 346da366693SZachary Turner break; 347da366693SZachary Turner 348da366693SZachary Turner case NullHandlingMethod::CutAtDoubleNull: 349da366693SZachary Turner for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos) 350da366693SZachary Turner if (Result[Pos] == '\0' && Result[Pos + 1] == '\0') 351da366693SZachary Turner Result.resize(Pos); 352da366693SZachary Turner if (Result.size() > 0 && Result.back() == '\0') 353da366693SZachary Turner Result.pop_back(); 354da366693SZachary Turner break; 355da366693SZachary Turner 356da366693SZachary Turner case NullHandlingMethod::UserResource: 357da366693SZachary Turner break; 358da366693SZachary Turner } 3598f19343aSMarek Sokolowski 3608f19343aSMarek Sokolowski return Error::success(); 3618f19343aSMarek Sokolowski } 3628f19343aSMarek Sokolowski 3638f19343aSMarek Sokolowski uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) { 3648f19343aSMarek Sokolowski uint64_t Result = tell(); 3658f19343aSMarek Sokolowski FS->write((const char *)Data.begin(), Data.size()); 3668f19343aSMarek Sokolowski return Result; 3678f19343aSMarek Sokolowski } 3688f19343aSMarek Sokolowski 3698f19343aSMarek Sokolowski Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) { 3708f19343aSMarek Sokolowski SmallVector<UTF16, 128> ProcessedString; 3718f19343aSMarek Sokolowski bool IsLongString; 3728f19343aSMarek Sokolowski RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull, 373d1d046aaSMartin Storsjo IsLongString, ProcessedString, 374d1d046aaSMartin Storsjo Params.CodePage)); 3758f19343aSMarek Sokolowski for (auto Ch : ProcessedString) 3768f19343aSMarek Sokolowski writeInt<uint16_t>(Ch); 3778f19343aSMarek Sokolowski if (WriteTerminator) 3788f19343aSMarek Sokolowski writeInt<uint16_t>(0); 3798f19343aSMarek Sokolowski return Error::success(); 3808f19343aSMarek Sokolowski } 3818f19343aSMarek Sokolowski 3828f19343aSMarek Sokolowski Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) { 3838f19343aSMarek Sokolowski return writeIntOrString(Ident); 3848f19343aSMarek Sokolowski } 3858f19343aSMarek Sokolowski 3868f19343aSMarek Sokolowski Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) { 3878f19343aSMarek Sokolowski if (!Value.isInt()) 3888f19343aSMarek Sokolowski return writeCString(Value.getString()); 3898f19343aSMarek Sokolowski 3908f19343aSMarek Sokolowski writeInt<uint16_t>(0xFFFF); 3918f19343aSMarek Sokolowski writeInt<uint16_t>(Value.getInt()); 3928f19343aSMarek Sokolowski return Error::success(); 3938f19343aSMarek Sokolowski } 3948f19343aSMarek Sokolowski 39507bc04ffSZachary Turner void ResourceFileWriter::writeRCInt(RCInt Value) { 39607bc04ffSZachary Turner if (Value.isLong()) 397b12792a4SZachary Turner writeInt<uint32_t>(Value); 39807bc04ffSZachary Turner else 399b12792a4SZachary Turner writeInt<uint16_t>(Value); 40007bc04ffSZachary Turner } 40107bc04ffSZachary Turner 4028f19343aSMarek Sokolowski Error ResourceFileWriter::appendFile(StringRef Filename) { 4038f19343aSMarek Sokolowski bool IsLong; 4048f19343aSMarek Sokolowski stripQuotes(Filename, IsLong); 4058f19343aSMarek Sokolowski 406fa0ca6cbSZachary Turner auto File = loadFile(Filename); 4078f19343aSMarek Sokolowski if (!File) 408fa0ca6cbSZachary Turner return File.takeError(); 409fa0ca6cbSZachary Turner 4108f19343aSMarek Sokolowski *FS << (*File)->getBuffer(); 4118f19343aSMarek Sokolowski return Error::success(); 4128f19343aSMarek Sokolowski } 4138f19343aSMarek Sokolowski 4148f19343aSMarek Sokolowski void ResourceFileWriter::padStream(uint64_t Length) { 4158f19343aSMarek Sokolowski assert(Length > 0); 4168f19343aSMarek Sokolowski uint64_t Location = tell(); 4178f19343aSMarek Sokolowski Location %= Length; 4188f19343aSMarek Sokolowski uint64_t Pad = (Length - Location) % Length; 4198f19343aSMarek Sokolowski for (uint64_t i = 0; i < Pad; ++i) 4208f19343aSMarek Sokolowski writeInt<uint8_t>(0); 4218f19343aSMarek Sokolowski } 4228f19343aSMarek Sokolowski 423514b7105SZachary Turner Error ResourceFileWriter::handleError(Error Err, const RCResource *Res) { 4248f19343aSMarek Sokolowski if (Err) 4258f19343aSMarek Sokolowski return joinErrors(createError("Error in " + Res->getResourceTypeName() + 4268f19343aSMarek Sokolowski " statement (ID " + Twine(Res->ResName) + 4278f19343aSMarek Sokolowski "): "), 4288f19343aSMarek Sokolowski std::move(Err)); 4298f19343aSMarek Sokolowski return Error::success(); 4308f19343aSMarek Sokolowski } 4318f19343aSMarek Sokolowski 4328f19343aSMarek Sokolowski Error ResourceFileWriter::visitNullResource(const RCResource *Res) { 4338f19343aSMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeNullBody); 4348f19343aSMarek Sokolowski } 4358f19343aSMarek Sokolowski 43622fccd64SMarek Sokolowski Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) { 43722fccd64SMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody); 43822fccd64SMarek Sokolowski } 43922fccd64SMarek Sokolowski 440c3ab013aSZachary Turner Error ResourceFileWriter::visitCursorResource(const RCResource *Res) { 441c3ab013aSZachary Turner return handleError(visitIconOrCursorResource(Res), Res); 442c3ab013aSZachary Turner } 443c3ab013aSZachary Turner 4447f7745c0SMarek Sokolowski Error ResourceFileWriter::visitDialogResource(const RCResource *Res) { 4457f7745c0SMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeDialogBody); 4467f7745c0SMarek Sokolowski } 4477f7745c0SMarek Sokolowski 448c3ab013aSZachary Turner Error ResourceFileWriter::visitIconResource(const RCResource *Res) { 449c3ab013aSZachary Turner return handleError(visitIconOrCursorResource(Res), Res); 450c3ab013aSZachary Turner } 451c3ab013aSZachary Turner 452420090afSZachary Turner Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) { 453420090afSZachary Turner ObjectData.Caption = Stmt->Value; 454420090afSZachary Turner return Error::success(); 455420090afSZachary Turner } 456420090afSZachary Turner 4578f19343aSMarek Sokolowski Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) { 4588f19343aSMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeHTMLBody); 4598f19343aSMarek Sokolowski } 4608f19343aSMarek Sokolowski 46142f494d6SMarek Sokolowski Error ResourceFileWriter::visitMenuResource(const RCResource *Res) { 46242f494d6SMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeMenuBody); 46342f494d6SMarek Sokolowski } 46442f494d6SMarek Sokolowski 465da366693SZachary Turner Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) { 466da366693SZachary Turner const auto *Res = cast<StringTableResource>(Base); 467da366693SZachary Turner 468da366693SZachary Turner ContextKeeper RAII(this); 469da366693SZachary Turner RETURN_IF_ERROR(Res->applyStmts(this)); 470da366693SZachary Turner 471da366693SZachary Turner for (auto &String : Res->Table) { 472da366693SZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID")); 473da366693SZachary Turner uint16_t BundleID = String.first >> 4; 474da366693SZachary Turner StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo); 475da366693SZachary Turner auto &BundleData = StringTableData.BundleData; 476da366693SZachary Turner auto Iter = BundleData.find(Key); 477da366693SZachary Turner 478da366693SZachary Turner if (Iter == BundleData.end()) { 479da366693SZachary Turner // Need to create a bundle. 480da366693SZachary Turner StringTableData.BundleList.push_back(Key); 481da366693SZachary Turner auto EmplaceResult = 482da366693SZachary Turner BundleData.emplace(Key, StringTableInfo::Bundle(ObjectData)); 483da366693SZachary Turner assert(EmplaceResult.second && "Could not create a bundle"); 484da366693SZachary Turner Iter = EmplaceResult.first; 485da366693SZachary Turner } 486da366693SZachary Turner 487da366693SZachary Turner RETURN_IF_ERROR( 488da366693SZachary Turner insertStringIntoBundle(Iter->second, String.first, String.second)); 489da366693SZachary Turner } 490da366693SZachary Turner 491da366693SZachary Turner return Error::success(); 492da366693SZachary Turner } 493da366693SZachary Turner 4949d8b358aSZachary Turner Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) { 4959d8b358aSZachary Turner return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody); 4969d8b358aSZachary Turner } 4979d8b358aSZachary Turner 49807bc04ffSZachary Turner Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) { 49907bc04ffSZachary Turner return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody); 50007bc04ffSZachary Turner } 50107bc04ffSZachary Turner 50222fccd64SMarek Sokolowski Error ResourceFileWriter::visitCharacteristicsStmt( 50322fccd64SMarek Sokolowski const CharacteristicsStmt *Stmt) { 50422fccd64SMarek Sokolowski ObjectData.Characteristics = Stmt->Value; 50522fccd64SMarek Sokolowski return Error::success(); 50622fccd64SMarek Sokolowski } 50722fccd64SMarek Sokolowski 508420090afSZachary Turner Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) { 509420090afSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size")); 510420090afSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight")); 511420090afSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset")); 512420090afSZachary Turner ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic, 513420090afSZachary Turner Stmt->Charset}; 514420090afSZachary Turner ObjectData.Font.emplace(Font); 515420090afSZachary Turner return Error::success(); 516420090afSZachary Turner } 517420090afSZachary Turner 5188f19343aSMarek Sokolowski Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) { 5198f19343aSMarek Sokolowski RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID")); 5208f19343aSMarek Sokolowski RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID")); 5218f19343aSMarek Sokolowski ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10); 5228f19343aSMarek Sokolowski return Error::success(); 5238f19343aSMarek Sokolowski } 5248f19343aSMarek Sokolowski 525420090afSZachary Turner Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) { 526420090afSZachary Turner ObjectData.Style = Stmt->Value; 527420090afSZachary Turner return Error::success(); 528420090afSZachary Turner } 529420090afSZachary Turner 53022fccd64SMarek Sokolowski Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) { 53122fccd64SMarek Sokolowski ObjectData.VersionInfo = Stmt->Value; 53222fccd64SMarek Sokolowski return Error::success(); 53322fccd64SMarek Sokolowski } 53422fccd64SMarek Sokolowski 5358f19343aSMarek Sokolowski Error ResourceFileWriter::writeResource( 5368f19343aSMarek Sokolowski const RCResource *Res, 5378f19343aSMarek Sokolowski Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) { 5388f19343aSMarek Sokolowski // We don't know the sizes yet. 5398f19343aSMarek Sokolowski object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)}; 5408f19343aSMarek Sokolowski uint64_t HeaderLoc = writeObject(HeaderPrefix); 5418f19343aSMarek Sokolowski 5428f19343aSMarek Sokolowski auto ResType = Res->getResourceType(); 5438f19343aSMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type")); 5448f19343aSMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID")); 5458f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res)); 5468f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res)); 5478f19343aSMarek Sokolowski 54822fccd64SMarek Sokolowski // Apply the resource-local optional statements. 54922fccd64SMarek Sokolowski ContextKeeper RAII(this); 55022fccd64SMarek Sokolowski RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res)); 55122fccd64SMarek Sokolowski 5528f19343aSMarek Sokolowski padStream(sizeof(uint32_t)); 5538f19343aSMarek Sokolowski object::WinResHeaderSuffix HeaderSuffix{ 5548f19343aSMarek Sokolowski ulittle32_t(0), // DataVersion; seems to always be 0 5558f19343aSMarek Sokolowski ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo), 55622fccd64SMarek Sokolowski ulittle32_t(ObjectData.VersionInfo), 55722fccd64SMarek Sokolowski ulittle32_t(ObjectData.Characteristics)}; 5588f19343aSMarek Sokolowski writeObject(HeaderSuffix); 5598f19343aSMarek Sokolowski 5608f19343aSMarek Sokolowski uint64_t DataLoc = tell(); 5618f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res)); 5628f19343aSMarek Sokolowski // RETURN_IF_ERROR(handleError(dumpResource(Ctx))); 5638f19343aSMarek Sokolowski 5648f19343aSMarek Sokolowski // Update the sizes. 5658f19343aSMarek Sokolowski HeaderPrefix.DataSize = tell() - DataLoc; 5668f19343aSMarek Sokolowski HeaderPrefix.HeaderSize = DataLoc - HeaderLoc; 5678f19343aSMarek Sokolowski writeObjectAt(HeaderPrefix, HeaderLoc); 5688f19343aSMarek Sokolowski padStream(sizeof(uint32_t)); 5698f19343aSMarek Sokolowski 5708f19343aSMarek Sokolowski return Error::success(); 5718f19343aSMarek Sokolowski } 5728f19343aSMarek Sokolowski 57322fccd64SMarek Sokolowski // --- NullResource helpers. --- // 57422fccd64SMarek Sokolowski 5758f19343aSMarek Sokolowski Error ResourceFileWriter::writeNullBody(const RCResource *) { 5768f19343aSMarek Sokolowski return Error::success(); 5778f19343aSMarek Sokolowski } 5788f19343aSMarek Sokolowski 57922fccd64SMarek Sokolowski // --- AcceleratorsResource helpers. --- // 58022fccd64SMarek Sokolowski 58122fccd64SMarek Sokolowski Error ResourceFileWriter::writeSingleAccelerator( 58222fccd64SMarek Sokolowski const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) { 58322fccd64SMarek Sokolowski using Accelerator = AcceleratorsResource::Accelerator; 58422fccd64SMarek Sokolowski using Opt = Accelerator::Options; 58522fccd64SMarek Sokolowski 58622fccd64SMarek Sokolowski struct AccelTableEntry { 58722fccd64SMarek Sokolowski ulittle16_t Flags; 58822fccd64SMarek Sokolowski ulittle16_t ANSICode; 58922fccd64SMarek Sokolowski ulittle16_t Id; 59022fccd64SMarek Sokolowski uint16_t Padding; 59122fccd64SMarek Sokolowski } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0}; 59222fccd64SMarek Sokolowski 59322fccd64SMarek Sokolowski bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY; 59422fccd64SMarek Sokolowski 59522fccd64SMarek Sokolowski // Remove ASCII flags (which doesn't occur in .res files). 59622fccd64SMarek Sokolowski Entry.Flags = Obj.Flags & ~Opt::ASCII; 59722fccd64SMarek Sokolowski 59822fccd64SMarek Sokolowski if (IsLastItem) 59922fccd64SMarek Sokolowski Entry.Flags |= 0x80; 60022fccd64SMarek Sokolowski 60122fccd64SMarek Sokolowski RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID")); 60222fccd64SMarek Sokolowski Entry.Id = ulittle16_t(Obj.Id); 60322fccd64SMarek Sokolowski 60422fccd64SMarek Sokolowski auto createAccError = [&Obj](const char *Msg) { 60522fccd64SMarek Sokolowski return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg); 60622fccd64SMarek Sokolowski }; 60722fccd64SMarek Sokolowski 60822fccd64SMarek Sokolowski if (IsASCII && IsVirtKey) 60922fccd64SMarek Sokolowski return createAccError("Accelerator can't be both ASCII and VIRTKEY"); 61022fccd64SMarek Sokolowski 61122fccd64SMarek Sokolowski if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL))) 61222fccd64SMarek Sokolowski return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY" 61322fccd64SMarek Sokolowski " accelerators"); 61422fccd64SMarek Sokolowski 61522fccd64SMarek Sokolowski if (Obj.Event.isInt()) { 61622fccd64SMarek Sokolowski if (!IsASCII && !IsVirtKey) 61722fccd64SMarek Sokolowski return createAccError( 61822fccd64SMarek Sokolowski "Accelerator with a numeric event must be either ASCII" 61922fccd64SMarek Sokolowski " or VIRTKEY"); 62022fccd64SMarek Sokolowski 62122fccd64SMarek Sokolowski uint32_t EventVal = Obj.Event.getInt(); 62222fccd64SMarek Sokolowski RETURN_IF_ERROR( 62322fccd64SMarek Sokolowski checkNumberFits<uint16_t>(EventVal, "Numeric event key ID")); 62422fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(EventVal); 62522fccd64SMarek Sokolowski writeObject(Entry); 62622fccd64SMarek Sokolowski return Error::success(); 62722fccd64SMarek Sokolowski } 62822fccd64SMarek Sokolowski 62922fccd64SMarek Sokolowski StringRef Str = Obj.Event.getString(); 63022fccd64SMarek Sokolowski bool IsWide; 63122fccd64SMarek Sokolowski stripQuotes(Str, IsWide); 63222fccd64SMarek Sokolowski 63322fccd64SMarek Sokolowski if (Str.size() == 0 || Str.size() > 2) 63422fccd64SMarek Sokolowski return createAccError( 63522fccd64SMarek Sokolowski "Accelerator string events should have length 1 or 2"); 63622fccd64SMarek Sokolowski 63722fccd64SMarek Sokolowski if (Str[0] == '^') { 63822fccd64SMarek Sokolowski if (Str.size() == 1) 63922fccd64SMarek Sokolowski return createAccError("No character following '^' in accelerator event"); 64022fccd64SMarek Sokolowski if (IsVirtKey) 64122fccd64SMarek Sokolowski return createAccError( 64222fccd64SMarek Sokolowski "VIRTKEY accelerator events can't be preceded by '^'"); 64322fccd64SMarek Sokolowski 64422fccd64SMarek Sokolowski char Ch = Str[1]; 64522fccd64SMarek Sokolowski if (Ch >= 'a' && Ch <= 'z') 64622fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(Ch - 'a' + 1); 64722fccd64SMarek Sokolowski else if (Ch >= 'A' && Ch <= 'Z') 64822fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(Ch - 'A' + 1); 64922fccd64SMarek Sokolowski else 65022fccd64SMarek Sokolowski return createAccError("Control character accelerator event should be" 65122fccd64SMarek Sokolowski " alphabetic"); 65222fccd64SMarek Sokolowski 65322fccd64SMarek Sokolowski writeObject(Entry); 65422fccd64SMarek Sokolowski return Error::success(); 65522fccd64SMarek Sokolowski } 65622fccd64SMarek Sokolowski 65722fccd64SMarek Sokolowski if (Str.size() == 2) 65822fccd64SMarek Sokolowski return createAccError("Event string should be one-character, possibly" 65922fccd64SMarek Sokolowski " preceded by '^'"); 66022fccd64SMarek Sokolowski 66122fccd64SMarek Sokolowski uint8_t EventCh = Str[0]; 66222fccd64SMarek Sokolowski // The original tool just warns in this situation. We chose to fail. 66322fccd64SMarek Sokolowski if (IsVirtKey && !isalnum(EventCh)) 66422fccd64SMarek Sokolowski return createAccError("Non-alphanumeric characters cannot describe virtual" 66522fccd64SMarek Sokolowski " keys"); 66622fccd64SMarek Sokolowski if (EventCh > 0x7F) 66722fccd64SMarek Sokolowski return createAccError("Non-ASCII description of accelerator"); 66822fccd64SMarek Sokolowski 66922fccd64SMarek Sokolowski if (IsVirtKey) 67022fccd64SMarek Sokolowski EventCh = toupper(EventCh); 67122fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(EventCh); 67222fccd64SMarek Sokolowski writeObject(Entry); 67322fccd64SMarek Sokolowski return Error::success(); 67422fccd64SMarek Sokolowski } 67522fccd64SMarek Sokolowski 67622fccd64SMarek Sokolowski Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) { 67722fccd64SMarek Sokolowski auto *Res = cast<AcceleratorsResource>(Base); 67822fccd64SMarek Sokolowski size_t AcceleratorId = 0; 67922fccd64SMarek Sokolowski for (auto &Acc : Res->Accelerators) { 68022fccd64SMarek Sokolowski ++AcceleratorId; 68122fccd64SMarek Sokolowski RETURN_IF_ERROR( 68222fccd64SMarek Sokolowski writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size())); 68322fccd64SMarek Sokolowski } 68422fccd64SMarek Sokolowski return Error::success(); 68522fccd64SMarek Sokolowski } 68622fccd64SMarek Sokolowski 687c3ab013aSZachary Turner // --- CursorResource and IconResource helpers. --- // 688c3ab013aSZachary Turner 689c3ab013aSZachary Turner // ICONRESDIR structure. Describes a single icon in resouce group. 690c3ab013aSZachary Turner // 691c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx 692c3ab013aSZachary Turner struct IconResDir { 693c3ab013aSZachary Turner uint8_t Width; 694c3ab013aSZachary Turner uint8_t Height; 695c3ab013aSZachary Turner uint8_t ColorCount; 696c3ab013aSZachary Turner uint8_t Reserved; 697c3ab013aSZachary Turner }; 698c3ab013aSZachary Turner 699c3ab013aSZachary Turner // CURSORDIR structure. Describes a single cursor in resource group. 700c3ab013aSZachary Turner // 701c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx 702c3ab013aSZachary Turner struct CursorDir { 703c3ab013aSZachary Turner ulittle16_t Width; 704c3ab013aSZachary Turner ulittle16_t Height; 705c3ab013aSZachary Turner }; 706c3ab013aSZachary Turner 707c3ab013aSZachary Turner // RESDIRENTRY structure, stripped from the last item. Stripping made 708c3ab013aSZachary Turner // for compatibility with RESDIR. 709c3ab013aSZachary Turner // 710c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx 711c3ab013aSZachary Turner struct ResourceDirEntryStart { 712c3ab013aSZachary Turner union { 713c3ab013aSZachary Turner CursorDir Cursor; // Used in CURSOR resources. 714c3ab013aSZachary Turner IconResDir Icon; // Used in .ico and .cur files, and ICON resources. 715c3ab013aSZachary Turner }; 716c3ab013aSZachary Turner ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource). 717c3ab013aSZachary Turner ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource). 718c3ab013aSZachary Turner ulittle32_t Size; 719c3ab013aSZachary Turner // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only). 720c3ab013aSZachary Turner // ulittle16_t IconID; // Resource icon ID (RESDIR only). 721c3ab013aSZachary Turner }; 722c3ab013aSZachary Turner 723c3ab013aSZachary Turner // BITMAPINFOHEADER structure. Describes basic information about the bitmap 724c3ab013aSZachary Turner // being read. 725c3ab013aSZachary Turner // 726c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx 727c3ab013aSZachary Turner struct BitmapInfoHeader { 728c3ab013aSZachary Turner ulittle32_t Size; 729c3ab013aSZachary Turner ulittle32_t Width; 730c3ab013aSZachary Turner ulittle32_t Height; 731c3ab013aSZachary Turner ulittle16_t Planes; 732c3ab013aSZachary Turner ulittle16_t BitCount; 733c3ab013aSZachary Turner ulittle32_t Compression; 734c3ab013aSZachary Turner ulittle32_t SizeImage; 735c3ab013aSZachary Turner ulittle32_t XPelsPerMeter; 736c3ab013aSZachary Turner ulittle32_t YPelsPerMeter; 737c3ab013aSZachary Turner ulittle32_t ClrUsed; 738c3ab013aSZachary Turner ulittle32_t ClrImportant; 739c3ab013aSZachary Turner }; 740c3ab013aSZachary Turner 741c3ab013aSZachary Turner // Group icon directory header. Called ICONDIR in .ico/.cur files and 742c3ab013aSZachary Turner // NEWHEADER in .res files. 743c3ab013aSZachary Turner // 744c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx 745c3ab013aSZachary Turner struct GroupIconDir { 746c3ab013aSZachary Turner ulittle16_t Reserved; // Always 0. 747c3ab013aSZachary Turner ulittle16_t ResType; // 1 for icons, 2 for cursors. 748c3ab013aSZachary Turner ulittle16_t ResCount; // Number of items. 749c3ab013aSZachary Turner }; 750c3ab013aSZachary Turner 751c3ab013aSZachary Turner enum class IconCursorGroupType { Icon, Cursor }; 752c3ab013aSZachary Turner 753c3ab013aSZachary Turner class SingleIconCursorResource : public RCResource { 754c3ab013aSZachary Turner public: 755c3ab013aSZachary Turner IconCursorGroupType Type; 756c3ab013aSZachary Turner const ResourceDirEntryStart &Header; 757c3ab013aSZachary Turner ArrayRef<uint8_t> Image; 758c3ab013aSZachary Turner 759c3ab013aSZachary Turner SingleIconCursorResource(IconCursorGroupType ResourceType, 760c3ab013aSZachary Turner const ResourceDirEntryStart &HeaderEntry, 761c3ab013aSZachary Turner ArrayRef<uint8_t> ImageData) 762c3ab013aSZachary Turner : Type(ResourceType), Header(HeaderEntry), Image(ImageData) {} 763c3ab013aSZachary Turner 764c3ab013aSZachary Turner Twine getResourceTypeName() const override { return "Icon/cursor image"; } 765c3ab013aSZachary Turner IntOrString getResourceType() const override { 766c3ab013aSZachary Turner return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor; 767c3ab013aSZachary Turner } 768c3ab013aSZachary Turner uint16_t getMemoryFlags() const override { 769c3ab013aSZachary Turner return MfDiscardable | MfMoveable; 770c3ab013aSZachary Turner } 771c3ab013aSZachary Turner ResourceKind getKind() const override { return RkSingleCursorOrIconRes; } 772c3ab013aSZachary Turner static bool classof(const RCResource *Res) { 773c3ab013aSZachary Turner return Res->getKind() == RkSingleCursorOrIconRes; 774c3ab013aSZachary Turner } 775c3ab013aSZachary Turner }; 776c3ab013aSZachary Turner 777c3ab013aSZachary Turner class IconCursorGroupResource : public RCResource { 778c3ab013aSZachary Turner public: 779c3ab013aSZachary Turner IconCursorGroupType Type; 780c3ab013aSZachary Turner GroupIconDir Header; 781c3ab013aSZachary Turner std::vector<ResourceDirEntryStart> ItemEntries; 782c3ab013aSZachary Turner 783c3ab013aSZachary Turner IconCursorGroupResource(IconCursorGroupType ResourceType, 784c3ab013aSZachary Turner const GroupIconDir &HeaderData, 785c3ab013aSZachary Turner std::vector<ResourceDirEntryStart> &&Entries) 786c3ab013aSZachary Turner : Type(ResourceType), Header(HeaderData), 787c3ab013aSZachary Turner ItemEntries(std::move(Entries)) {} 788c3ab013aSZachary Turner 789c3ab013aSZachary Turner Twine getResourceTypeName() const override { return "Icon/cursor group"; } 790c3ab013aSZachary Turner IntOrString getResourceType() const override { 791c3ab013aSZachary Turner return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup; 792c3ab013aSZachary Turner } 793c3ab013aSZachary Turner ResourceKind getKind() const override { return RkCursorOrIconGroupRes; } 794c3ab013aSZachary Turner static bool classof(const RCResource *Res) { 795c3ab013aSZachary Turner return Res->getKind() == RkCursorOrIconGroupRes; 796c3ab013aSZachary Turner } 797c3ab013aSZachary Turner }; 798c3ab013aSZachary Turner 799c3ab013aSZachary Turner Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) { 800c3ab013aSZachary Turner auto *Res = cast<SingleIconCursorResource>(Base); 801c3ab013aSZachary Turner if (Res->Type == IconCursorGroupType::Cursor) { 802c3ab013aSZachary Turner // In case of cursors, two WORDS are appended to the beginning 803c3ab013aSZachary Turner // of the resource: HotspotX (Planes in RESDIRENTRY), 804c3ab013aSZachary Turner // and HotspotY (BitCount). 805c3ab013aSZachary Turner // 806c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx 807c3ab013aSZachary Turner // (Remarks section). 808c3ab013aSZachary Turner writeObject(Res->Header.Planes); 809c3ab013aSZachary Turner writeObject(Res->Header.BitCount); 810c3ab013aSZachary Turner } 811c3ab013aSZachary Turner 812c3ab013aSZachary Turner writeObject(Res->Image); 813c3ab013aSZachary Turner return Error::success(); 814c3ab013aSZachary Turner } 815c3ab013aSZachary Turner 816c3ab013aSZachary Turner Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) { 817c3ab013aSZachary Turner auto *Res = cast<IconCursorGroupResource>(Base); 818c3ab013aSZachary Turner writeObject(Res->Header); 819c3ab013aSZachary Turner for (auto Item : Res->ItemEntries) { 820c3ab013aSZachary Turner writeObject(Item); 821b12792a4SZachary Turner writeInt(IconCursorID++); 822c3ab013aSZachary Turner } 823c3ab013aSZachary Turner return Error::success(); 824c3ab013aSZachary Turner } 825c3ab013aSZachary Turner 826c3ab013aSZachary Turner Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) { 827c3ab013aSZachary Turner return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody); 828c3ab013aSZachary Turner } 829c3ab013aSZachary Turner 830c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) { 831c3ab013aSZachary Turner return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody); 832c3ab013aSZachary Turner } 833c3ab013aSZachary Turner 834c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) { 835c3ab013aSZachary Turner IconCursorGroupType Type; 836c3ab013aSZachary Turner StringRef FileStr; 837c3ab013aSZachary Turner IntOrString ResName = Base->ResName; 838c3ab013aSZachary Turner 839c3ab013aSZachary Turner if (auto *IconRes = dyn_cast<IconResource>(Base)) { 840c3ab013aSZachary Turner FileStr = IconRes->IconLoc; 841c3ab013aSZachary Turner Type = IconCursorGroupType::Icon; 842c3ab013aSZachary Turner } else { 843c3ab013aSZachary Turner auto *CursorRes = dyn_cast<CursorResource>(Base); 844c3ab013aSZachary Turner FileStr = CursorRes->CursorLoc; 845c3ab013aSZachary Turner Type = IconCursorGroupType::Cursor; 846c3ab013aSZachary Turner } 847c3ab013aSZachary Turner 848c3ab013aSZachary Turner bool IsLong; 849c3ab013aSZachary Turner stripQuotes(FileStr, IsLong); 850fa0ca6cbSZachary Turner auto File = loadFile(FileStr); 851c3ab013aSZachary Turner 852c3ab013aSZachary Turner if (!File) 853fa0ca6cbSZachary Turner return File.takeError(); 854c3ab013aSZachary Turner 855c3ab013aSZachary Turner BinaryStreamReader Reader((*File)->getBuffer(), support::little); 856c3ab013aSZachary Turner 857c3ab013aSZachary Turner // Read the file headers. 858c3ab013aSZachary Turner // - At the beginning, ICONDIR/NEWHEADER header. 859c3ab013aSZachary Turner // - Then, a number of RESDIR headers follow. These contain offsets 860c3ab013aSZachary Turner // to data. 861c3ab013aSZachary Turner const GroupIconDir *Header; 862c3ab013aSZachary Turner 863c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readObject(Header)); 864c3ab013aSZachary Turner if (Header->Reserved != 0) 865c3ab013aSZachary Turner return createError("Incorrect icon/cursor Reserved field; should be 0."); 866c3ab013aSZachary Turner uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2; 867c3ab013aSZachary Turner if (Header->ResType != NeededType) 868c3ab013aSZachary Turner return createError("Incorrect icon/cursor ResType field; should be " + 869c3ab013aSZachary Turner Twine(NeededType) + "."); 870c3ab013aSZachary Turner 871c3ab013aSZachary Turner uint16_t NumItems = Header->ResCount; 872c3ab013aSZachary Turner 873c3ab013aSZachary Turner // Read single ico/cur headers. 874c3ab013aSZachary Turner std::vector<ResourceDirEntryStart> ItemEntries; 875c3ab013aSZachary Turner ItemEntries.reserve(NumItems); 876c3ab013aSZachary Turner std::vector<uint32_t> ItemOffsets(NumItems); 877c3ab013aSZachary Turner for (size_t ID = 0; ID < NumItems; ++ID) { 878c3ab013aSZachary Turner const ResourceDirEntryStart *Object; 879c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readObject(Object)); 880c3ab013aSZachary Turner ItemEntries.push_back(*Object); 881c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID])); 882c3ab013aSZachary Turner } 883c3ab013aSZachary Turner 884c3ab013aSZachary Turner // Now write each icon/cursors one by one. At first, all the contents 885c3ab013aSZachary Turner // without ICO/CUR header. This is described by SingleIconCursorResource. 886c3ab013aSZachary Turner for (size_t ID = 0; ID < NumItems; ++ID) { 887c3ab013aSZachary Turner // Load the fragment of file. 888c3ab013aSZachary Turner Reader.setOffset(ItemOffsets[ID]); 889c3ab013aSZachary Turner ArrayRef<uint8_t> Image; 890c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size)); 891c3ab013aSZachary Turner SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image); 892c3ab013aSZachary Turner SingleRes.setName(IconCursorID + ID); 893c3ab013aSZachary Turner RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes)); 894c3ab013aSZachary Turner } 895c3ab013aSZachary Turner 896c3ab013aSZachary Turner // Now, write all the headers concatenated into a separate resource. 897c3ab013aSZachary Turner for (size_t ID = 0; ID < NumItems; ++ID) { 898c3ab013aSZachary Turner if (Type == IconCursorGroupType::Icon) { 899c3ab013aSZachary Turner // rc.exe seems to always set NumPlanes to 1. No idea why it happens. 900c3ab013aSZachary Turner ItemEntries[ID].Planes = 1; 901c3ab013aSZachary Turner continue; 902c3ab013aSZachary Turner } 903c3ab013aSZachary Turner 904c3ab013aSZachary Turner // We need to rewrite the cursor headers. 905c3ab013aSZachary Turner const auto &OldHeader = ItemEntries[ID]; 906c3ab013aSZachary Turner ResourceDirEntryStart NewHeader; 907c3ab013aSZachary Turner NewHeader.Cursor.Width = OldHeader.Icon.Width; 908c3ab013aSZachary Turner // Each cursor in fact stores two bitmaps, one under another. 909c3ab013aSZachary Turner // Height provided in cursor definition describes the height of the 910c3ab013aSZachary Turner // cursor, whereas the value existing in resource definition describes 911c3ab013aSZachary Turner // the height of the bitmap. Therefore, we need to double this height. 912c3ab013aSZachary Turner NewHeader.Cursor.Height = OldHeader.Icon.Height * 2; 913c3ab013aSZachary Turner 914c3ab013aSZachary Turner // Now, we actually need to read the bitmap header to find 915c3ab013aSZachary Turner // the number of planes and the number of bits per pixel. 916c3ab013aSZachary Turner Reader.setOffset(ItemOffsets[ID]); 917c3ab013aSZachary Turner const BitmapInfoHeader *BMPHeader; 918c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readObject(BMPHeader)); 919c3ab013aSZachary Turner NewHeader.Planes = BMPHeader->Planes; 920c3ab013aSZachary Turner NewHeader.BitCount = BMPHeader->BitCount; 921c3ab013aSZachary Turner 922c3ab013aSZachary Turner // Two WORDs were written at the beginning of the resource (hotspot 923c3ab013aSZachary Turner // location). This is reflected in Size field. 924c3ab013aSZachary Turner NewHeader.Size = OldHeader.Size + 2 * sizeof(uint16_t); 925c3ab013aSZachary Turner 926c3ab013aSZachary Turner ItemEntries[ID] = NewHeader; 927c3ab013aSZachary Turner } 928c3ab013aSZachary Turner 929c3ab013aSZachary Turner IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries)); 930c3ab013aSZachary Turner HeaderRes.setName(ResName); 931c3ab013aSZachary Turner RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes)); 932c3ab013aSZachary Turner 933c3ab013aSZachary Turner return Error::success(); 934c3ab013aSZachary Turner } 935c3ab013aSZachary Turner 9367f7745c0SMarek Sokolowski // --- DialogResource helpers. --- // 93722fccd64SMarek Sokolowski 9387f7745c0SMarek Sokolowski Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl, 9397f7745c0SMarek Sokolowski bool IsExtended) { 9407f7745c0SMarek Sokolowski // Each control should be aligned to DWORD. 9417f7745c0SMarek Sokolowski padStream(sizeof(uint32_t)); 9427f7745c0SMarek Sokolowski 9437f7745c0SMarek Sokolowski auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type); 9447f7745c0SMarek Sokolowski uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0); 9457f7745c0SMarek Sokolowski uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0); 9467f7745c0SMarek Sokolowski 9477f7745c0SMarek Sokolowski // DIALOG(EX) item header prefix. 9487f7745c0SMarek Sokolowski if (!IsExtended) { 9497f7745c0SMarek Sokolowski struct { 9507f7745c0SMarek Sokolowski ulittle32_t Style; 9517f7745c0SMarek Sokolowski ulittle32_t ExtStyle; 9527f7745c0SMarek Sokolowski } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)}; 9537f7745c0SMarek Sokolowski writeObject(Prefix); 9547f7745c0SMarek Sokolowski } else { 9557f7745c0SMarek Sokolowski struct { 9567f7745c0SMarek Sokolowski ulittle32_t HelpID; 9577f7745c0SMarek Sokolowski ulittle32_t ExtStyle; 9587f7745c0SMarek Sokolowski ulittle32_t Style; 9597f7745c0SMarek Sokolowski } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle), 9607f7745c0SMarek Sokolowski ulittle32_t(CtlStyle)}; 9617f7745c0SMarek Sokolowski writeObject(Prefix); 9627f7745c0SMarek Sokolowski } 9637f7745c0SMarek Sokolowski 9647f7745c0SMarek Sokolowski // Common fixed-length part. 9657f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( 9667f7745c0SMarek Sokolowski Ctl.X, "Dialog control x-coordinate", true)); 9677f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( 9687f7745c0SMarek Sokolowski Ctl.Y, "Dialog control y-coordinate", true)); 9697f7745c0SMarek Sokolowski RETURN_IF_ERROR( 9707f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false)); 9717f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( 9727f7745c0SMarek Sokolowski Ctl.Height, "Dialog control height", false)); 9737f7745c0SMarek Sokolowski struct { 9747f7745c0SMarek Sokolowski ulittle16_t X; 9757f7745c0SMarek Sokolowski ulittle16_t Y; 9767f7745c0SMarek Sokolowski ulittle16_t Width; 9777f7745c0SMarek Sokolowski ulittle16_t Height; 9787f7745c0SMarek Sokolowski } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width), 9797f7745c0SMarek Sokolowski ulittle16_t(Ctl.Height)}; 9807f7745c0SMarek Sokolowski writeObject(Middle); 9817f7745c0SMarek Sokolowski 9827f7745c0SMarek Sokolowski // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX. 9837f7745c0SMarek Sokolowski if (!IsExtended) { 9847f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkNumberFits<uint16_t>( 9857f7745c0SMarek Sokolowski Ctl.ID, "Control ID in simple DIALOG resource")); 9867f7745c0SMarek Sokolowski writeInt<uint16_t>(Ctl.ID); 9877f7745c0SMarek Sokolowski } else { 9887f7745c0SMarek Sokolowski writeInt<uint32_t>(Ctl.ID); 9897f7745c0SMarek Sokolowski } 9907f7745c0SMarek Sokolowski 9917f7745c0SMarek Sokolowski // Window class - either 0xFFFF + 16-bit integer or a string. 9927f7745c0SMarek Sokolowski RETURN_IF_ERROR(writeIntOrString(IntOrString(TypeInfo.CtlClass))); 9937f7745c0SMarek Sokolowski 9947f7745c0SMarek Sokolowski // Element caption/reference ID. ID is preceded by 0xFFFF. 9957f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID")); 9967f7745c0SMarek Sokolowski RETURN_IF_ERROR(writeIntOrString(Ctl.Title)); 9977f7745c0SMarek Sokolowski 9987f7745c0SMarek Sokolowski // # bytes of extra creation data count. Don't pass any. 9997f7745c0SMarek Sokolowski writeInt<uint16_t>(0); 10007f7745c0SMarek Sokolowski 10017f7745c0SMarek Sokolowski return Error::success(); 10027f7745c0SMarek Sokolowski } 10037f7745c0SMarek Sokolowski 10047f7745c0SMarek Sokolowski Error ResourceFileWriter::writeDialogBody(const RCResource *Base) { 10057f7745c0SMarek Sokolowski auto *Res = cast<DialogResource>(Base); 10067f7745c0SMarek Sokolowski 10077f7745c0SMarek Sokolowski // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU. 1008420090afSZachary Turner const uint32_t DefaultStyle = 0x80880000; 1009420090afSZachary Turner const uint32_t StyleFontFlag = 0x40; 1010420090afSZachary Turner const uint32_t StyleCaptionFlag = 0x00C00000; 1011420090afSZachary Turner 1012420090afSZachary Turner uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle); 1013420090afSZachary Turner if (ObjectData.Font) 1014420090afSZachary Turner UsedStyle |= StyleFontFlag; 1015420090afSZachary Turner else 1016420090afSZachary Turner UsedStyle &= ~StyleFontFlag; 1017420090afSZachary Turner 1018420090afSZachary Turner // Actually, in case of empty (but existent) caption, the examined field 1019420090afSZachary Turner // is equal to "\"\"". That's why empty captions are still noticed. 1020420090afSZachary Turner if (ObjectData.Caption != "") 1021420090afSZachary Turner UsedStyle |= StyleCaptionFlag; 1022420090afSZachary Turner 1023420090afSZachary Turner const uint16_t DialogExMagic = 0xFFFF; 10247f7745c0SMarek Sokolowski 10257f7745c0SMarek Sokolowski // Write DIALOG(EX) header prefix. These are pretty different. 10267f7745c0SMarek Sokolowski if (!Res->IsExtended) { 1027420090afSZachary Turner // We cannot let the higher word of DefaultStyle be equal to 0xFFFF. 1028420090afSZachary Turner // In such a case, whole object (in .res file) is equivalent to a 1029420090afSZachary Turner // DIALOGEX. It might lead to access violation/segmentation fault in 1030420090afSZachary Turner // resource readers. For example, 1031420090afSZachary Turner // 1 DIALOG 0, 0, 0, 65432 1032420090afSZachary Turner // STYLE 0xFFFF0001 {} 1033420090afSZachary Turner // would be compiled to a DIALOGEX with 65432 controls. 1034420090afSZachary Turner if ((UsedStyle >> 16) == DialogExMagic) 1035420090afSZachary Turner return createError("16 higher bits of DIALOG resource style cannot be" 1036420090afSZachary Turner " equal to 0xFFFF"); 1037420090afSZachary Turner 10387f7745c0SMarek Sokolowski struct { 10397f7745c0SMarek Sokolowski ulittle32_t Style; 10407f7745c0SMarek Sokolowski ulittle32_t ExtStyle; 10417f7745c0SMarek Sokolowski } Prefix{ulittle32_t(UsedStyle), 10427f7745c0SMarek Sokolowski ulittle32_t(0)}; // As of now, we don't keep EXSTYLE. 10437f7745c0SMarek Sokolowski 10447f7745c0SMarek Sokolowski writeObject(Prefix); 10457f7745c0SMarek Sokolowski } else { 10467f7745c0SMarek Sokolowski struct { 10477f7745c0SMarek Sokolowski ulittle16_t Version; 10487f7745c0SMarek Sokolowski ulittle16_t Magic; 10497f7745c0SMarek Sokolowski ulittle32_t HelpID; 10507f7745c0SMarek Sokolowski ulittle32_t ExtStyle; 10517f7745c0SMarek Sokolowski ulittle32_t Style; 10527f7745c0SMarek Sokolowski } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic), 10537f7745c0SMarek Sokolowski ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)}; 10547f7745c0SMarek Sokolowski 10557f7745c0SMarek Sokolowski writeObject(Prefix); 10567f7745c0SMarek Sokolowski } 10577f7745c0SMarek Sokolowski 10587f7745c0SMarek Sokolowski // Now, a common part. First, fixed-length fields. 10597f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(), 10607f7745c0SMarek Sokolowski "Number of dialog controls")); 10617f7745c0SMarek Sokolowski RETURN_IF_ERROR( 10627f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true)); 10637f7745c0SMarek Sokolowski RETURN_IF_ERROR( 10647f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true)); 10657f7745c0SMarek Sokolowski RETURN_IF_ERROR( 10667f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false)); 10677f7745c0SMarek Sokolowski RETURN_IF_ERROR( 10687f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false)); 10697f7745c0SMarek Sokolowski struct { 10707f7745c0SMarek Sokolowski ulittle16_t Count; 10717f7745c0SMarek Sokolowski ulittle16_t PosX; 10727f7745c0SMarek Sokolowski ulittle16_t PosY; 10737f7745c0SMarek Sokolowski ulittle16_t DialogWidth; 10747f7745c0SMarek Sokolowski ulittle16_t DialogHeight; 10757f7745c0SMarek Sokolowski } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X), 10767f7745c0SMarek Sokolowski ulittle16_t(Res->Y), ulittle16_t(Res->Width), 10777f7745c0SMarek Sokolowski ulittle16_t(Res->Height)}; 10787f7745c0SMarek Sokolowski writeObject(Middle); 10797f7745c0SMarek Sokolowski 10807f7745c0SMarek Sokolowski // MENU field. As of now, we don't keep them in the state and can peacefully 10817f7745c0SMarek Sokolowski // think there is no menu attached to the dialog. 10827f7745c0SMarek Sokolowski writeInt<uint16_t>(0); 10837f7745c0SMarek Sokolowski 10847f7745c0SMarek Sokolowski // Window CLASS field. Not kept here. 10857f7745c0SMarek Sokolowski writeInt<uint16_t>(0); 10867f7745c0SMarek Sokolowski 1087420090afSZachary Turner // Window title or a single word equal to 0. 1088420090afSZachary Turner RETURN_IF_ERROR(writeCString(ObjectData.Caption)); 1089420090afSZachary Turner 1090420090afSZachary Turner // If there *is* a window font declared, output its data. 1091420090afSZachary Turner auto &Font = ObjectData.Font; 1092420090afSZachary Turner if (Font) { 1093420090afSZachary Turner writeInt<uint16_t>(Font->Size); 1094420090afSZachary Turner // Additional description occurs only in DIALOGEX. 1095420090afSZachary Turner if (Res->IsExtended) { 1096420090afSZachary Turner writeInt<uint16_t>(Font->Weight); 1097420090afSZachary Turner writeInt<uint8_t>(Font->IsItalic); 1098420090afSZachary Turner writeInt<uint8_t>(Font->Charset); 1099420090afSZachary Turner } 1100420090afSZachary Turner RETURN_IF_ERROR(writeCString(Font->Typeface)); 1101420090afSZachary Turner } 11027f7745c0SMarek Sokolowski 11037f7745c0SMarek Sokolowski auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error { 11047f7745c0SMarek Sokolowski if (!Err) 11057f7745c0SMarek Sokolowski return Error::success(); 11067f7745c0SMarek Sokolowski return joinErrors(createError("Error in " + Twine(Ctl.Type) + 11077f7745c0SMarek Sokolowski " control (ID " + Twine(Ctl.ID) + "):"), 11087f7745c0SMarek Sokolowski std::move(Err)); 11097f7745c0SMarek Sokolowski }; 11107f7745c0SMarek Sokolowski 11117f7745c0SMarek Sokolowski for (auto &Ctl : Res->Controls) 11127f7745c0SMarek Sokolowski RETURN_IF_ERROR( 11137f7745c0SMarek Sokolowski handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl)); 11147f7745c0SMarek Sokolowski 11157f7745c0SMarek Sokolowski return Error::success(); 11167f7745c0SMarek Sokolowski } 11177f7745c0SMarek Sokolowski 11187f7745c0SMarek Sokolowski // --- HTMLResource helpers. --- // 111922fccd64SMarek Sokolowski 11208f19343aSMarek Sokolowski Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) { 11218f19343aSMarek Sokolowski return appendFile(cast<HTMLResource>(Base)->HTMLLoc); 11228f19343aSMarek Sokolowski } 11238f19343aSMarek Sokolowski 112442f494d6SMarek Sokolowski // --- MenuResource helpers. --- // 112542f494d6SMarek Sokolowski 112642f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinition( 112742f494d6SMarek Sokolowski const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) { 112842f494d6SMarek Sokolowski assert(Def); 112942f494d6SMarek Sokolowski const MenuDefinition *DefPtr = Def.get(); 113042f494d6SMarek Sokolowski 113142f494d6SMarek Sokolowski if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) { 113242f494d6SMarek Sokolowski writeInt<uint16_t>(Flags); 113342f494d6SMarek Sokolowski RETURN_IF_ERROR( 113442f494d6SMarek Sokolowski checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID")); 113542f494d6SMarek Sokolowski writeInt<uint16_t>(MenuItemPtr->Id); 113642f494d6SMarek Sokolowski RETURN_IF_ERROR(writeCString(MenuItemPtr->Name)); 113742f494d6SMarek Sokolowski return Error::success(); 113842f494d6SMarek Sokolowski } 113942f494d6SMarek Sokolowski 114042f494d6SMarek Sokolowski if (isa<MenuSeparator>(DefPtr)) { 114142f494d6SMarek Sokolowski writeInt<uint16_t>(Flags); 114242f494d6SMarek Sokolowski writeInt<uint32_t>(0); 114342f494d6SMarek Sokolowski return Error::success(); 114442f494d6SMarek Sokolowski } 114542f494d6SMarek Sokolowski 114642f494d6SMarek Sokolowski auto *PopupPtr = cast<PopupItem>(DefPtr); 114742f494d6SMarek Sokolowski writeInt<uint16_t>(Flags); 114842f494d6SMarek Sokolowski RETURN_IF_ERROR(writeCString(PopupPtr->Name)); 114942f494d6SMarek Sokolowski return writeMenuDefinitionList(PopupPtr->SubItems); 115042f494d6SMarek Sokolowski } 115142f494d6SMarek Sokolowski 115242f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinitionList( 115342f494d6SMarek Sokolowski const MenuDefinitionList &List) { 115442f494d6SMarek Sokolowski for (auto &Def : List.Definitions) { 115542f494d6SMarek Sokolowski uint16_t Flags = Def->getResFlags(); 115642f494d6SMarek Sokolowski // Last element receives an additional 0x80 flag. 115742f494d6SMarek Sokolowski const uint16_t LastElementFlag = 0x0080; 115842f494d6SMarek Sokolowski if (&Def == &List.Definitions.back()) 115942f494d6SMarek Sokolowski Flags |= LastElementFlag; 116042f494d6SMarek Sokolowski 116142f494d6SMarek Sokolowski RETURN_IF_ERROR(writeMenuDefinition(Def, Flags)); 116242f494d6SMarek Sokolowski } 116342f494d6SMarek Sokolowski return Error::success(); 116442f494d6SMarek Sokolowski } 116542f494d6SMarek Sokolowski 116642f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuBody(const RCResource *Base) { 116742f494d6SMarek Sokolowski // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0. 116842f494d6SMarek Sokolowski // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx 11691e2c8b11SZachary Turner writeInt<uint32_t>(0); 117042f494d6SMarek Sokolowski 117142f494d6SMarek Sokolowski return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements); 117242f494d6SMarek Sokolowski } 117342f494d6SMarek Sokolowski 1174da366693SZachary Turner // --- StringTableResource helpers. --- // 1175da366693SZachary Turner 1176da366693SZachary Turner class BundleResource : public RCResource { 1177da366693SZachary Turner public: 1178da366693SZachary Turner using BundleType = ResourceFileWriter::StringTableInfo::Bundle; 1179da366693SZachary Turner BundleType Bundle; 1180da366693SZachary Turner 1181da366693SZachary Turner BundleResource(const BundleType &StrBundle) : Bundle(StrBundle) {} 1182da366693SZachary Turner IntOrString getResourceType() const override { return 6; } 1183da366693SZachary Turner 1184da366693SZachary Turner ResourceKind getKind() const override { return RkStringTableBundle; } 1185da366693SZachary Turner static bool classof(const RCResource *Res) { 1186da366693SZachary Turner return Res->getKind() == RkStringTableBundle; 1187da366693SZachary Turner } 1188d1d046aaSMartin Storsjo Twine getResourceTypeName() const override { return "STRINGTABLE"; } 1189da366693SZachary Turner }; 1190da366693SZachary Turner 1191da366693SZachary Turner Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) { 1192da366693SZachary Turner return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody); 1193da366693SZachary Turner } 1194da366693SZachary Turner 1195da366693SZachary Turner Error ResourceFileWriter::insertStringIntoBundle( 1196da366693SZachary Turner StringTableInfo::Bundle &Bundle, uint16_t StringID, StringRef String) { 1197da366693SZachary Turner uint16_t StringLoc = StringID & 15; 1198da366693SZachary Turner if (Bundle.Data[StringLoc]) 1199da366693SZachary Turner return createError("Multiple STRINGTABLE strings located under ID " + 1200da366693SZachary Turner Twine(StringID)); 1201da366693SZachary Turner Bundle.Data[StringLoc] = String; 1202da366693SZachary Turner return Error::success(); 1203da366693SZachary Turner } 1204da366693SZachary Turner 1205da366693SZachary Turner Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) { 1206da366693SZachary Turner auto *Res = cast<BundleResource>(Base); 1207da366693SZachary Turner for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) { 1208da366693SZachary Turner // The string format is a tiny bit different here. We 1209da366693SZachary Turner // first output the size of the string, and then the string itself 1210da366693SZachary Turner // (which is not null-terminated). 1211da366693SZachary Turner bool IsLongString; 1212da366693SZachary Turner SmallVector<UTF16, 128> Data; 1213da366693SZachary Turner RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()), 1214da366693SZachary Turner NullHandlingMethod::CutAtDoubleNull, 1215d1d046aaSMartin Storsjo IsLongString, Data, Params.CodePage)); 1216da366693SZachary Turner if (AppendNull && Res->Bundle.Data[ID]) 1217da366693SZachary Turner Data.push_back('\0'); 1218da366693SZachary Turner RETURN_IF_ERROR( 1219da366693SZachary Turner checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size")); 1220b12792a4SZachary Turner writeInt<uint16_t>(Data.size()); 1221da366693SZachary Turner for (auto Char : Data) 1222b12792a4SZachary Turner writeInt(Char); 1223da366693SZachary Turner } 1224da366693SZachary Turner return Error::success(); 1225da366693SZachary Turner } 1226da366693SZachary Turner 1227da366693SZachary Turner Error ResourceFileWriter::dumpAllStringTables() { 1228da366693SZachary Turner for (auto Key : StringTableData.BundleList) { 1229da366693SZachary Turner auto Iter = StringTableData.BundleData.find(Key); 1230da366693SZachary Turner assert(Iter != StringTableData.BundleData.end()); 1231da366693SZachary Turner 1232da366693SZachary Turner // For a moment, revert the context info to moment of bundle declaration. 1233da366693SZachary Turner ContextKeeper RAII(this); 1234da366693SZachary Turner ObjectData = Iter->second.DeclTimeInfo; 1235da366693SZachary Turner 1236da366693SZachary Turner BundleResource Res(Iter->second); 1237da366693SZachary Turner // Bundle #(k+1) contains keys [16k, 16k + 15]. 1238da366693SZachary Turner Res.setName(Key.first + 1); 1239da366693SZachary Turner RETURN_IF_ERROR(visitStringTableBundle(&Res)); 1240da366693SZachary Turner } 1241da366693SZachary Turner return Error::success(); 1242da366693SZachary Turner } 1243da366693SZachary Turner 12449d8b358aSZachary Turner // --- UserDefinedResource helpers. --- // 12459d8b358aSZachary Turner 12469d8b358aSZachary Turner Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) { 12479d8b358aSZachary Turner auto *Res = cast<UserDefinedResource>(Base); 12489d8b358aSZachary Turner 12499d8b358aSZachary Turner if (Res->IsFileResource) 12509d8b358aSZachary Turner return appendFile(Res->FileLoc); 12519d8b358aSZachary Turner 12529d8b358aSZachary Turner for (auto &Elem : Res->Contents) { 12539d8b358aSZachary Turner if (Elem.isInt()) { 12549d8b358aSZachary Turner RETURN_IF_ERROR( 12559d8b358aSZachary Turner checkRCInt(Elem.getInt(), "Number in user-defined resource")); 12569d8b358aSZachary Turner writeRCInt(Elem.getInt()); 12579d8b358aSZachary Turner continue; 12589d8b358aSZachary Turner } 12599d8b358aSZachary Turner 12609d8b358aSZachary Turner SmallVector<UTF16, 128> ProcessedString; 12619d8b358aSZachary Turner bool IsLongString; 1262d1d046aaSMartin Storsjo RETURN_IF_ERROR( 1263d1d046aaSMartin Storsjo processString(Elem.getString(), NullHandlingMethod::UserResource, 1264d1d046aaSMartin Storsjo IsLongString, ProcessedString, Params.CodePage)); 12659d8b358aSZachary Turner 12669d8b358aSZachary Turner for (auto Ch : ProcessedString) { 12679d8b358aSZachary Turner if (IsLongString) { 1268b12792a4SZachary Turner writeInt(Ch); 12699d8b358aSZachary Turner continue; 12709d8b358aSZachary Turner } 12719d8b358aSZachary Turner 12729d8b358aSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint8_t>( 127375f9ae2bSSimon Dardis Ch, "Character in narrow string in user-defined resource")); 1274b12792a4SZachary Turner writeInt<uint8_t>(Ch); 12759d8b358aSZachary Turner } 12769d8b358aSZachary Turner } 12779d8b358aSZachary Turner 12789d8b358aSZachary Turner return Error::success(); 12799d8b358aSZachary Turner } 12809d8b358aSZachary Turner 128107bc04ffSZachary Turner // --- VersionInfoResourceResource helpers. --- // 128207bc04ffSZachary Turner 128307bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) { 128407bc04ffSZachary Turner // Output the header if the block has name. 128507bc04ffSZachary Turner bool OutputHeader = Blk.Name != ""; 128607bc04ffSZachary Turner uint64_t LengthLoc; 128707bc04ffSZachary Turner 1288*28ae894aSMartin Storsjo padStream(sizeof(uint32_t)); 128907bc04ffSZachary Turner if (OutputHeader) { 12901e2c8b11SZachary Turner LengthLoc = writeInt<uint16_t>(0); 12911e2c8b11SZachary Turner writeInt<uint16_t>(0); 12921e2c8b11SZachary Turner writeInt<uint16_t>(1); // true 129307bc04ffSZachary Turner RETURN_IF_ERROR(writeCString(Blk.Name)); 129407bc04ffSZachary Turner padStream(sizeof(uint32_t)); 129507bc04ffSZachary Turner } 129607bc04ffSZachary Turner 129707bc04ffSZachary Turner for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) { 129807bc04ffSZachary Turner VersionInfoStmt *ItemPtr = Item.get(); 129907bc04ffSZachary Turner 130007bc04ffSZachary Turner if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) { 130107bc04ffSZachary Turner RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr)); 130207bc04ffSZachary Turner continue; 130307bc04ffSZachary Turner } 130407bc04ffSZachary Turner 130507bc04ffSZachary Turner auto *ValuePtr = cast<VersionInfoValue>(ItemPtr); 130607bc04ffSZachary Turner RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr)); 130707bc04ffSZachary Turner } 130807bc04ffSZachary Turner 130907bc04ffSZachary Turner if (OutputHeader) { 131007bc04ffSZachary Turner uint64_t CurLoc = tell(); 131107bc04ffSZachary Turner writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); 131207bc04ffSZachary Turner } 131307bc04ffSZachary Turner 131407bc04ffSZachary Turner return Error::success(); 131507bc04ffSZachary Turner } 131607bc04ffSZachary Turner 131707bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) { 131807bc04ffSZachary Turner // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE 131907bc04ffSZachary Turner // is a mapping from the key (string) to the value (a sequence of ints or 132007bc04ffSZachary Turner // a sequence of strings). 132107bc04ffSZachary Turner // 132207bc04ffSZachary Turner // If integers are to be written: width of each integer written depends on 132307bc04ffSZachary Turner // whether it's been declared 'long' (it's DWORD then) or not (it's WORD). 132407bc04ffSZachary Turner // ValueLength defined in structure referenced below is then the total 132507bc04ffSZachary Turner // number of bytes taken by these integers. 132607bc04ffSZachary Turner // 132707bc04ffSZachary Turner // If strings are to be written: characters are always WORDs. 132807bc04ffSZachary Turner // Moreover, '\0' character is written after the last string, and between 132907bc04ffSZachary Turner // every two strings separated by comma (if strings are not comma-separated, 133007bc04ffSZachary Turner // they're simply concatenated). ValueLength is equal to the number of WORDs 133107bc04ffSZachary Turner // written (that is, half of the bytes written). 133207bc04ffSZachary Turner // 133307bc04ffSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx 133407bc04ffSZachary Turner bool HasStrings = false, HasInts = false; 133507bc04ffSZachary Turner for (auto &Item : Val.Values) 133607bc04ffSZachary Turner (Item.isInt() ? HasInts : HasStrings) = true; 133707bc04ffSZachary Turner 133807bc04ffSZachary Turner assert((HasStrings || HasInts) && "VALUE must have at least one argument"); 133907bc04ffSZachary Turner if (HasStrings && HasInts) 134007bc04ffSZachary Turner return createError(Twine("VALUE ") + Val.Key + 134107bc04ffSZachary Turner " cannot contain both strings and integers"); 134207bc04ffSZachary Turner 1343*28ae894aSMartin Storsjo padStream(sizeof(uint32_t)); 13441e2c8b11SZachary Turner auto LengthLoc = writeInt<uint16_t>(0); 13451e2c8b11SZachary Turner auto ValLengthLoc = writeInt<uint16_t>(0); 13461e2c8b11SZachary Turner writeInt<uint16_t>(HasStrings); 134707bc04ffSZachary Turner RETURN_IF_ERROR(writeCString(Val.Key)); 134807bc04ffSZachary Turner padStream(sizeof(uint32_t)); 134907bc04ffSZachary Turner 135007bc04ffSZachary Turner auto DataLoc = tell(); 135107bc04ffSZachary Turner for (size_t Id = 0; Id < Val.Values.size(); ++Id) { 135207bc04ffSZachary Turner auto &Item = Val.Values[Id]; 135307bc04ffSZachary Turner if (Item.isInt()) { 135407bc04ffSZachary Turner auto Value = Item.getInt(); 135507bc04ffSZachary Turner RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value")); 135607bc04ffSZachary Turner writeRCInt(Value); 135707bc04ffSZachary Turner continue; 135807bc04ffSZachary Turner } 135907bc04ffSZachary Turner 136007bc04ffSZachary Turner bool WriteTerminator = 136107bc04ffSZachary Turner Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1]; 136207bc04ffSZachary Turner RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator)); 136307bc04ffSZachary Turner } 136407bc04ffSZachary Turner 136507bc04ffSZachary Turner auto CurLoc = tell(); 136607bc04ffSZachary Turner auto ValueLength = CurLoc - DataLoc; 136707bc04ffSZachary Turner if (HasStrings) { 136807bc04ffSZachary Turner assert(ValueLength % 2 == 0); 136907bc04ffSZachary Turner ValueLength /= 2; 137007bc04ffSZachary Turner } 137107bc04ffSZachary Turner writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); 137207bc04ffSZachary Turner writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc); 137307bc04ffSZachary Turner return Error::success(); 137407bc04ffSZachary Turner } 137507bc04ffSZachary Turner 137607bc04ffSZachary Turner template <typename Ty> 137707bc04ffSZachary Turner static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key, 137807bc04ffSZachary Turner const Ty &Default) { 137907bc04ffSZachary Turner auto Iter = Map.find(Key); 138007bc04ffSZachary Turner if (Iter != Map.end()) 138107bc04ffSZachary Turner return Iter->getValue(); 138207bc04ffSZachary Turner return Default; 138307bc04ffSZachary Turner } 138407bc04ffSZachary Turner 138507bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) { 138607bc04ffSZachary Turner auto *Res = cast<VersionInfoResource>(Base); 138707bc04ffSZachary Turner 138807bc04ffSZachary Turner const auto &FixedData = Res->FixedData; 138907bc04ffSZachary Turner 139007bc04ffSZachary Turner struct /* VS_FIXEDFILEINFO */ { 139107bc04ffSZachary Turner ulittle32_t Signature = ulittle32_t(0xFEEF04BD); 139207bc04ffSZachary Turner ulittle32_t StructVersion = ulittle32_t(0x10000); 139307bc04ffSZachary Turner // It's weird to have most-significant DWORD first on the little-endian 139407bc04ffSZachary Turner // machines, but let it be this way. 139507bc04ffSZachary Turner ulittle32_t FileVersionMS; 139607bc04ffSZachary Turner ulittle32_t FileVersionLS; 139707bc04ffSZachary Turner ulittle32_t ProductVersionMS; 139807bc04ffSZachary Turner ulittle32_t ProductVersionLS; 139907bc04ffSZachary Turner ulittle32_t FileFlagsMask; 140007bc04ffSZachary Turner ulittle32_t FileFlags; 140107bc04ffSZachary Turner ulittle32_t FileOS; 140207bc04ffSZachary Turner ulittle32_t FileType; 140307bc04ffSZachary Turner ulittle32_t FileSubtype; 140407bc04ffSZachary Turner // MS implementation seems to always set these fields to 0. 140507bc04ffSZachary Turner ulittle32_t FileDateMS = ulittle32_t(0); 140607bc04ffSZachary Turner ulittle32_t FileDateLS = ulittle32_t(0); 140707bc04ffSZachary Turner } FixedInfo; 140807bc04ffSZachary Turner 140907bc04ffSZachary Turner // First, VS_VERSIONINFO. 1410b12792a4SZachary Turner auto LengthLoc = writeInt<uint16_t>(0); 1411b12792a4SZachary Turner writeInt<uint16_t>(sizeof(FixedInfo)); 1412b12792a4SZachary Turner writeInt<uint16_t>(0); 141307bc04ffSZachary Turner cantFail(writeCString("VS_VERSION_INFO")); 141407bc04ffSZachary Turner padStream(sizeof(uint32_t)); 141507bc04ffSZachary Turner 141607bc04ffSZachary Turner using VersionInfoFixed = VersionInfoResource::VersionInfoFixed; 141707bc04ffSZachary Turner auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) { 141807bc04ffSZachary Turner static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0}; 141907bc04ffSZachary Turner if (!FixedData.IsTypePresent[(int)Type]) 142007bc04ffSZachary Turner return DefaultOut; 142107bc04ffSZachary Turner return FixedData.FixedInfo[(int)Type]; 142207bc04ffSZachary Turner }; 142307bc04ffSZachary Turner 142407bc04ffSZachary Turner auto FileVer = GetField(VersionInfoFixed::FtFileVersion); 142507bc04ffSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>( 142607bc04ffSZachary Turner *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields")); 142707bc04ffSZachary Turner FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1]; 142807bc04ffSZachary Turner FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3]; 142907bc04ffSZachary Turner 143007bc04ffSZachary Turner auto ProdVer = GetField(VersionInfoFixed::FtProductVersion); 143107bc04ffSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>( 143207bc04ffSZachary Turner *std::max_element(ProdVer.begin(), ProdVer.end()), 143307bc04ffSZachary Turner "PRODUCTVERSION fields")); 143407bc04ffSZachary Turner FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1]; 143507bc04ffSZachary Turner FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3]; 143607bc04ffSZachary Turner 143707bc04ffSZachary Turner FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0]; 143807bc04ffSZachary Turner FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0]; 143907bc04ffSZachary Turner FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0]; 144007bc04ffSZachary Turner FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0]; 144107bc04ffSZachary Turner FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0]; 144207bc04ffSZachary Turner 144307bc04ffSZachary Turner writeObject(FixedInfo); 144407bc04ffSZachary Turner padStream(sizeof(uint32_t)); 144507bc04ffSZachary Turner 144607bc04ffSZachary Turner RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock)); 144707bc04ffSZachary Turner 144807bc04ffSZachary Turner // FIXME: check overflow? 144907bc04ffSZachary Turner writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc); 145007bc04ffSZachary Turner 145107bc04ffSZachary Turner return Error::success(); 145207bc04ffSZachary Turner } 145307bc04ffSZachary Turner 1454fa0ca6cbSZachary Turner Expected<std::unique_ptr<MemoryBuffer>> 1455fa0ca6cbSZachary Turner ResourceFileWriter::loadFile(StringRef File) const { 1456fa0ca6cbSZachary Turner SmallString<128> Path; 1457fa0ca6cbSZachary Turner SmallString<128> Cwd; 1458fa0ca6cbSZachary Turner std::unique_ptr<MemoryBuffer> Result; 1459fa0ca6cbSZachary Turner 1460fa0ca6cbSZachary Turner // 1. The current working directory. 1461fa0ca6cbSZachary Turner sys::fs::current_path(Cwd); 1462fa0ca6cbSZachary Turner Path.assign(Cwd.begin(), Cwd.end()); 1463fa0ca6cbSZachary Turner sys::path::append(Path, File); 1464fa0ca6cbSZachary Turner if (sys::fs::exists(Path)) 1465f22a25e6SZachary Turner return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); 1466fa0ca6cbSZachary Turner 1467fa0ca6cbSZachary Turner // 2. The directory of the input resource file, if it is different from the 1468fa0ca6cbSZachary Turner // current 1469fa0ca6cbSZachary Turner // working directory. 1470fa0ca6cbSZachary Turner StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath); 1471fa0ca6cbSZachary Turner Path.assign(InputFileDir.begin(), InputFileDir.end()); 1472fa0ca6cbSZachary Turner sys::path::append(Path, File); 1473fa0ca6cbSZachary Turner if (sys::fs::exists(Path)) 1474f22a25e6SZachary Turner return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); 1475fa0ca6cbSZachary Turner 1476fa0ca6cbSZachary Turner // 3. All of the include directories specified on the command line. 1477fa0ca6cbSZachary Turner for (StringRef ForceInclude : Params.Include) { 1478fa0ca6cbSZachary Turner Path.assign(ForceInclude.begin(), ForceInclude.end()); 1479fa0ca6cbSZachary Turner sys::path::append(Path, File); 1480fa0ca6cbSZachary Turner if (sys::fs::exists(Path)) 1481f22a25e6SZachary Turner return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); 1482fa0ca6cbSZachary Turner } 1483fa0ca6cbSZachary Turner 1484fa0ca6cbSZachary Turner if (auto Result = 1485fa0ca6cbSZachary Turner llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude)) 1486f22a25e6SZachary Turner return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false)); 1487fa0ca6cbSZachary Turner 1488fa0ca6cbSZachary Turner return make_error<StringError>("error : file not found : " + Twine(File), 1489fa0ca6cbSZachary Turner inconvertibleErrorCode()); 1490fa0ca6cbSZachary Turner } 1491fa0ca6cbSZachary Turner 14928f19343aSMarek Sokolowski } // namespace rc 14938f19343aSMarek Sokolowski } // namespace llvm 1494