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 440577b9817SMartin Storsjo Error ResourceFileWriter::visitBitmapResource(const RCResource *Res) { 441577b9817SMartin Storsjo return writeResource(Res, &ResourceFileWriter::writeBitmapBody); 442577b9817SMartin Storsjo } 443577b9817SMartin Storsjo 444c3ab013aSZachary Turner Error ResourceFileWriter::visitCursorResource(const RCResource *Res) { 445c3ab013aSZachary Turner return handleError(visitIconOrCursorResource(Res), Res); 446c3ab013aSZachary Turner } 447c3ab013aSZachary Turner 4487f7745c0SMarek Sokolowski Error ResourceFileWriter::visitDialogResource(const RCResource *Res) { 4497f7745c0SMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeDialogBody); 4507f7745c0SMarek Sokolowski } 4517f7745c0SMarek Sokolowski 452c3ab013aSZachary Turner Error ResourceFileWriter::visitIconResource(const RCResource *Res) { 453c3ab013aSZachary Turner return handleError(visitIconOrCursorResource(Res), Res); 454c3ab013aSZachary Turner } 455c3ab013aSZachary Turner 456420090afSZachary Turner Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) { 457420090afSZachary Turner ObjectData.Caption = Stmt->Value; 458420090afSZachary Turner return Error::success(); 459420090afSZachary Turner } 460420090afSZachary Turner 4618f19343aSMarek Sokolowski Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) { 4628f19343aSMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeHTMLBody); 4638f19343aSMarek Sokolowski } 4648f19343aSMarek Sokolowski 46542f494d6SMarek Sokolowski Error ResourceFileWriter::visitMenuResource(const RCResource *Res) { 46642f494d6SMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeMenuBody); 46742f494d6SMarek Sokolowski } 46842f494d6SMarek Sokolowski 469da366693SZachary Turner Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) { 470da366693SZachary Turner const auto *Res = cast<StringTableResource>(Base); 471da366693SZachary Turner 472da366693SZachary Turner ContextKeeper RAII(this); 473da366693SZachary Turner RETURN_IF_ERROR(Res->applyStmts(this)); 474da366693SZachary Turner 475da366693SZachary Turner for (auto &String : Res->Table) { 476da366693SZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID")); 477da366693SZachary Turner uint16_t BundleID = String.first >> 4; 478da366693SZachary Turner StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo); 479da366693SZachary Turner auto &BundleData = StringTableData.BundleData; 480da366693SZachary Turner auto Iter = BundleData.find(Key); 481da366693SZachary Turner 482da366693SZachary Turner if (Iter == BundleData.end()) { 483da366693SZachary Turner // Need to create a bundle. 484da366693SZachary Turner StringTableData.BundleList.push_back(Key); 485*11adbacaSMartin Storsjo auto EmplaceResult = BundleData.emplace( 486*11adbacaSMartin Storsjo Key, StringTableInfo::Bundle(ObjectData, Res->MemoryFlags)); 487da366693SZachary Turner assert(EmplaceResult.second && "Could not create a bundle"); 488da366693SZachary Turner Iter = EmplaceResult.first; 489da366693SZachary Turner } 490da366693SZachary Turner 491da366693SZachary Turner RETURN_IF_ERROR( 492da366693SZachary Turner insertStringIntoBundle(Iter->second, String.first, String.second)); 493da366693SZachary Turner } 494da366693SZachary Turner 495da366693SZachary Turner return Error::success(); 496da366693SZachary Turner } 497da366693SZachary Turner 4989d8b358aSZachary Turner Error ResourceFileWriter::visitUserDefinedResource(const RCResource *Res) { 4999d8b358aSZachary Turner return writeResource(Res, &ResourceFileWriter::writeUserDefinedBody); 5009d8b358aSZachary Turner } 5019d8b358aSZachary Turner 50207bc04ffSZachary Turner Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) { 50307bc04ffSZachary Turner return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody); 50407bc04ffSZachary Turner } 50507bc04ffSZachary Turner 50622fccd64SMarek Sokolowski Error ResourceFileWriter::visitCharacteristicsStmt( 50722fccd64SMarek Sokolowski const CharacteristicsStmt *Stmt) { 50822fccd64SMarek Sokolowski ObjectData.Characteristics = Stmt->Value; 50922fccd64SMarek Sokolowski return Error::success(); 51022fccd64SMarek Sokolowski } 51122fccd64SMarek Sokolowski 512420090afSZachary Turner Error ResourceFileWriter::visitFontStmt(const FontStmt *Stmt) { 513420090afSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Size, "Font size")); 514420090afSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>(Stmt->Weight, "Font weight")); 515420090afSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint8_t>(Stmt->Charset, "Font charset")); 516420090afSZachary Turner ObjectInfo::FontInfo Font{Stmt->Size, Stmt->Name, Stmt->Weight, Stmt->Italic, 517420090afSZachary Turner Stmt->Charset}; 518420090afSZachary Turner ObjectData.Font.emplace(Font); 519420090afSZachary Turner return Error::success(); 520420090afSZachary Turner } 521420090afSZachary Turner 5228f19343aSMarek Sokolowski Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) { 5238f19343aSMarek Sokolowski RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID")); 5248f19343aSMarek Sokolowski RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID")); 5258f19343aSMarek Sokolowski ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10); 5268f19343aSMarek Sokolowski return Error::success(); 5278f19343aSMarek Sokolowski } 5288f19343aSMarek Sokolowski 529420090afSZachary Turner Error ResourceFileWriter::visitStyleStmt(const StyleStmt *Stmt) { 530420090afSZachary Turner ObjectData.Style = Stmt->Value; 531420090afSZachary Turner return Error::success(); 532420090afSZachary Turner } 533420090afSZachary Turner 53422fccd64SMarek Sokolowski Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) { 53522fccd64SMarek Sokolowski ObjectData.VersionInfo = Stmt->Value; 53622fccd64SMarek Sokolowski return Error::success(); 53722fccd64SMarek Sokolowski } 53822fccd64SMarek Sokolowski 5398f19343aSMarek Sokolowski Error ResourceFileWriter::writeResource( 5408f19343aSMarek Sokolowski const RCResource *Res, 5418f19343aSMarek Sokolowski Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) { 5428f19343aSMarek Sokolowski // We don't know the sizes yet. 5438f19343aSMarek Sokolowski object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)}; 5448f19343aSMarek Sokolowski uint64_t HeaderLoc = writeObject(HeaderPrefix); 5458f19343aSMarek Sokolowski 5468f19343aSMarek Sokolowski auto ResType = Res->getResourceType(); 5478f19343aSMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type")); 5488f19343aSMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID")); 5498f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res)); 5508f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res)); 5518f19343aSMarek Sokolowski 55222fccd64SMarek Sokolowski // Apply the resource-local optional statements. 55322fccd64SMarek Sokolowski ContextKeeper RAII(this); 55422fccd64SMarek Sokolowski RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res)); 55522fccd64SMarek Sokolowski 5568f19343aSMarek Sokolowski padStream(sizeof(uint32_t)); 5578f19343aSMarek Sokolowski object::WinResHeaderSuffix HeaderSuffix{ 5588f19343aSMarek Sokolowski ulittle32_t(0), // DataVersion; seems to always be 0 559*11adbacaSMartin Storsjo ulittle16_t(Res->MemoryFlags), ulittle16_t(ObjectData.LanguageInfo), 56022fccd64SMarek Sokolowski ulittle32_t(ObjectData.VersionInfo), 56122fccd64SMarek Sokolowski ulittle32_t(ObjectData.Characteristics)}; 5628f19343aSMarek Sokolowski writeObject(HeaderSuffix); 5638f19343aSMarek Sokolowski 5648f19343aSMarek Sokolowski uint64_t DataLoc = tell(); 5658f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res)); 5668f19343aSMarek Sokolowski // RETURN_IF_ERROR(handleError(dumpResource(Ctx))); 5678f19343aSMarek Sokolowski 5688f19343aSMarek Sokolowski // Update the sizes. 5698f19343aSMarek Sokolowski HeaderPrefix.DataSize = tell() - DataLoc; 5708f19343aSMarek Sokolowski HeaderPrefix.HeaderSize = DataLoc - HeaderLoc; 5718f19343aSMarek Sokolowski writeObjectAt(HeaderPrefix, HeaderLoc); 5728f19343aSMarek Sokolowski padStream(sizeof(uint32_t)); 5738f19343aSMarek Sokolowski 5748f19343aSMarek Sokolowski return Error::success(); 5758f19343aSMarek Sokolowski } 5768f19343aSMarek Sokolowski 57722fccd64SMarek Sokolowski // --- NullResource helpers. --- // 57822fccd64SMarek Sokolowski 5798f19343aSMarek Sokolowski Error ResourceFileWriter::writeNullBody(const RCResource *) { 5808f19343aSMarek Sokolowski return Error::success(); 5818f19343aSMarek Sokolowski } 5828f19343aSMarek Sokolowski 58322fccd64SMarek Sokolowski // --- AcceleratorsResource helpers. --- // 58422fccd64SMarek Sokolowski 58522fccd64SMarek Sokolowski Error ResourceFileWriter::writeSingleAccelerator( 58622fccd64SMarek Sokolowski const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) { 58722fccd64SMarek Sokolowski using Accelerator = AcceleratorsResource::Accelerator; 58822fccd64SMarek Sokolowski using Opt = Accelerator::Options; 58922fccd64SMarek Sokolowski 59022fccd64SMarek Sokolowski struct AccelTableEntry { 59122fccd64SMarek Sokolowski ulittle16_t Flags; 59222fccd64SMarek Sokolowski ulittle16_t ANSICode; 59322fccd64SMarek Sokolowski ulittle16_t Id; 59422fccd64SMarek Sokolowski uint16_t Padding; 59522fccd64SMarek Sokolowski } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0}; 59622fccd64SMarek Sokolowski 59722fccd64SMarek Sokolowski bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY; 59822fccd64SMarek Sokolowski 59922fccd64SMarek Sokolowski // Remove ASCII flags (which doesn't occur in .res files). 60022fccd64SMarek Sokolowski Entry.Flags = Obj.Flags & ~Opt::ASCII; 60122fccd64SMarek Sokolowski 60222fccd64SMarek Sokolowski if (IsLastItem) 60322fccd64SMarek Sokolowski Entry.Flags |= 0x80; 60422fccd64SMarek Sokolowski 60522fccd64SMarek Sokolowski RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID")); 60622fccd64SMarek Sokolowski Entry.Id = ulittle16_t(Obj.Id); 60722fccd64SMarek Sokolowski 60822fccd64SMarek Sokolowski auto createAccError = [&Obj](const char *Msg) { 60922fccd64SMarek Sokolowski return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg); 61022fccd64SMarek Sokolowski }; 61122fccd64SMarek Sokolowski 61222fccd64SMarek Sokolowski if (IsASCII && IsVirtKey) 61322fccd64SMarek Sokolowski return createAccError("Accelerator can't be both ASCII and VIRTKEY"); 61422fccd64SMarek Sokolowski 61522fccd64SMarek Sokolowski if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL))) 61622fccd64SMarek Sokolowski return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY" 61722fccd64SMarek Sokolowski " accelerators"); 61822fccd64SMarek Sokolowski 61922fccd64SMarek Sokolowski if (Obj.Event.isInt()) { 62022fccd64SMarek Sokolowski if (!IsASCII && !IsVirtKey) 62122fccd64SMarek Sokolowski return createAccError( 62222fccd64SMarek Sokolowski "Accelerator with a numeric event must be either ASCII" 62322fccd64SMarek Sokolowski " or VIRTKEY"); 62422fccd64SMarek Sokolowski 62522fccd64SMarek Sokolowski uint32_t EventVal = Obj.Event.getInt(); 62622fccd64SMarek Sokolowski RETURN_IF_ERROR( 62722fccd64SMarek Sokolowski checkNumberFits<uint16_t>(EventVal, "Numeric event key ID")); 62822fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(EventVal); 62922fccd64SMarek Sokolowski writeObject(Entry); 63022fccd64SMarek Sokolowski return Error::success(); 63122fccd64SMarek Sokolowski } 63222fccd64SMarek Sokolowski 63322fccd64SMarek Sokolowski StringRef Str = Obj.Event.getString(); 63422fccd64SMarek Sokolowski bool IsWide; 63522fccd64SMarek Sokolowski stripQuotes(Str, IsWide); 63622fccd64SMarek Sokolowski 63722fccd64SMarek Sokolowski if (Str.size() == 0 || Str.size() > 2) 63822fccd64SMarek Sokolowski return createAccError( 63922fccd64SMarek Sokolowski "Accelerator string events should have length 1 or 2"); 64022fccd64SMarek Sokolowski 64122fccd64SMarek Sokolowski if (Str[0] == '^') { 64222fccd64SMarek Sokolowski if (Str.size() == 1) 64322fccd64SMarek Sokolowski return createAccError("No character following '^' in accelerator event"); 64422fccd64SMarek Sokolowski if (IsVirtKey) 64522fccd64SMarek Sokolowski return createAccError( 64622fccd64SMarek Sokolowski "VIRTKEY accelerator events can't be preceded by '^'"); 64722fccd64SMarek Sokolowski 64822fccd64SMarek Sokolowski char Ch = Str[1]; 64922fccd64SMarek Sokolowski if (Ch >= 'a' && Ch <= 'z') 65022fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(Ch - 'a' + 1); 65122fccd64SMarek Sokolowski else if (Ch >= 'A' && Ch <= 'Z') 65222fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(Ch - 'A' + 1); 65322fccd64SMarek Sokolowski else 65422fccd64SMarek Sokolowski return createAccError("Control character accelerator event should be" 65522fccd64SMarek Sokolowski " alphabetic"); 65622fccd64SMarek Sokolowski 65722fccd64SMarek Sokolowski writeObject(Entry); 65822fccd64SMarek Sokolowski return Error::success(); 65922fccd64SMarek Sokolowski } 66022fccd64SMarek Sokolowski 66122fccd64SMarek Sokolowski if (Str.size() == 2) 66222fccd64SMarek Sokolowski return createAccError("Event string should be one-character, possibly" 66322fccd64SMarek Sokolowski " preceded by '^'"); 66422fccd64SMarek Sokolowski 66522fccd64SMarek Sokolowski uint8_t EventCh = Str[0]; 66622fccd64SMarek Sokolowski // The original tool just warns in this situation. We chose to fail. 66722fccd64SMarek Sokolowski if (IsVirtKey && !isalnum(EventCh)) 66822fccd64SMarek Sokolowski return createAccError("Non-alphanumeric characters cannot describe virtual" 66922fccd64SMarek Sokolowski " keys"); 67022fccd64SMarek Sokolowski if (EventCh > 0x7F) 67122fccd64SMarek Sokolowski return createAccError("Non-ASCII description of accelerator"); 67222fccd64SMarek Sokolowski 67322fccd64SMarek Sokolowski if (IsVirtKey) 67422fccd64SMarek Sokolowski EventCh = toupper(EventCh); 67522fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(EventCh); 67622fccd64SMarek Sokolowski writeObject(Entry); 67722fccd64SMarek Sokolowski return Error::success(); 67822fccd64SMarek Sokolowski } 67922fccd64SMarek Sokolowski 68022fccd64SMarek Sokolowski Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) { 68122fccd64SMarek Sokolowski auto *Res = cast<AcceleratorsResource>(Base); 68222fccd64SMarek Sokolowski size_t AcceleratorId = 0; 68322fccd64SMarek Sokolowski for (auto &Acc : Res->Accelerators) { 68422fccd64SMarek Sokolowski ++AcceleratorId; 68522fccd64SMarek Sokolowski RETURN_IF_ERROR( 68622fccd64SMarek Sokolowski writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size())); 68722fccd64SMarek Sokolowski } 68822fccd64SMarek Sokolowski return Error::success(); 68922fccd64SMarek Sokolowski } 69022fccd64SMarek Sokolowski 691577b9817SMartin Storsjo // --- BitmapResource helpers. --- // 692577b9817SMartin Storsjo 693577b9817SMartin Storsjo Error ResourceFileWriter::writeBitmapBody(const RCResource *Base) { 694577b9817SMartin Storsjo StringRef Filename = cast<BitmapResource>(Base)->BitmapLoc; 695577b9817SMartin Storsjo bool IsLong; 696577b9817SMartin Storsjo stripQuotes(Filename, IsLong); 697577b9817SMartin Storsjo 698577b9817SMartin Storsjo auto File = loadFile(Filename); 699577b9817SMartin Storsjo if (!File) 700577b9817SMartin Storsjo return File.takeError(); 701577b9817SMartin Storsjo 702577b9817SMartin Storsjo StringRef Buffer = (*File)->getBuffer(); 703577b9817SMartin Storsjo 704577b9817SMartin Storsjo // Skip the 14 byte BITMAPFILEHEADER. 705577b9817SMartin Storsjo constexpr size_t BITMAPFILEHEADER_size = 14; 706577b9817SMartin Storsjo if (Buffer.size() < BITMAPFILEHEADER_size || Buffer[0] != 'B' || 707577b9817SMartin Storsjo Buffer[1] != 'M') 708577b9817SMartin Storsjo return createError("Incorrect bitmap file."); 709577b9817SMartin Storsjo 710577b9817SMartin Storsjo *FS << Buffer.substr(BITMAPFILEHEADER_size); 711577b9817SMartin Storsjo return Error::success(); 712577b9817SMartin Storsjo } 713577b9817SMartin Storsjo 714c3ab013aSZachary Turner // --- CursorResource and IconResource helpers. --- // 715c3ab013aSZachary Turner 716c3ab013aSZachary Turner // ICONRESDIR structure. Describes a single icon in resouce group. 717c3ab013aSZachary Turner // 718c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx 719c3ab013aSZachary Turner struct IconResDir { 720c3ab013aSZachary Turner uint8_t Width; 721c3ab013aSZachary Turner uint8_t Height; 722c3ab013aSZachary Turner uint8_t ColorCount; 723c3ab013aSZachary Turner uint8_t Reserved; 724c3ab013aSZachary Turner }; 725c3ab013aSZachary Turner 726c3ab013aSZachary Turner // CURSORDIR structure. Describes a single cursor in resource group. 727c3ab013aSZachary Turner // 728c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx 729c3ab013aSZachary Turner struct CursorDir { 730c3ab013aSZachary Turner ulittle16_t Width; 731c3ab013aSZachary Turner ulittle16_t Height; 732c3ab013aSZachary Turner }; 733c3ab013aSZachary Turner 734c3ab013aSZachary Turner // RESDIRENTRY structure, stripped from the last item. Stripping made 735c3ab013aSZachary Turner // for compatibility with RESDIR. 736c3ab013aSZachary Turner // 737c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx 738c3ab013aSZachary Turner struct ResourceDirEntryStart { 739c3ab013aSZachary Turner union { 740c3ab013aSZachary Turner CursorDir Cursor; // Used in CURSOR resources. 741c3ab013aSZachary Turner IconResDir Icon; // Used in .ico and .cur files, and ICON resources. 742c3ab013aSZachary Turner }; 743c3ab013aSZachary Turner ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource). 744c3ab013aSZachary Turner ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource). 745c3ab013aSZachary Turner ulittle32_t Size; 746c3ab013aSZachary Turner // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only). 747c3ab013aSZachary Turner // ulittle16_t IconID; // Resource icon ID (RESDIR only). 748c3ab013aSZachary Turner }; 749c3ab013aSZachary Turner 750c3ab013aSZachary Turner // BITMAPINFOHEADER structure. Describes basic information about the bitmap 751c3ab013aSZachary Turner // being read. 752c3ab013aSZachary Turner // 753c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx 754c3ab013aSZachary Turner struct BitmapInfoHeader { 755c3ab013aSZachary Turner ulittle32_t Size; 756c3ab013aSZachary Turner ulittle32_t Width; 757c3ab013aSZachary Turner ulittle32_t Height; 758c3ab013aSZachary Turner ulittle16_t Planes; 759c3ab013aSZachary Turner ulittle16_t BitCount; 760c3ab013aSZachary Turner ulittle32_t Compression; 761c3ab013aSZachary Turner ulittle32_t SizeImage; 762c3ab013aSZachary Turner ulittle32_t XPelsPerMeter; 763c3ab013aSZachary Turner ulittle32_t YPelsPerMeter; 764c3ab013aSZachary Turner ulittle32_t ClrUsed; 765c3ab013aSZachary Turner ulittle32_t ClrImportant; 766c3ab013aSZachary Turner }; 767c3ab013aSZachary Turner 768c3ab013aSZachary Turner // Group icon directory header. Called ICONDIR in .ico/.cur files and 769c3ab013aSZachary Turner // NEWHEADER in .res files. 770c3ab013aSZachary Turner // 771c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx 772c3ab013aSZachary Turner struct GroupIconDir { 773c3ab013aSZachary Turner ulittle16_t Reserved; // Always 0. 774c3ab013aSZachary Turner ulittle16_t ResType; // 1 for icons, 2 for cursors. 775c3ab013aSZachary Turner ulittle16_t ResCount; // Number of items. 776c3ab013aSZachary Turner }; 777c3ab013aSZachary Turner 778c3ab013aSZachary Turner enum class IconCursorGroupType { Icon, Cursor }; 779c3ab013aSZachary Turner 780c3ab013aSZachary Turner class SingleIconCursorResource : public RCResource { 781c3ab013aSZachary Turner public: 782c3ab013aSZachary Turner IconCursorGroupType Type; 783c3ab013aSZachary Turner const ResourceDirEntryStart &Header; 784c3ab013aSZachary Turner ArrayRef<uint8_t> Image; 785c3ab013aSZachary Turner 786c3ab013aSZachary Turner SingleIconCursorResource(IconCursorGroupType ResourceType, 787c3ab013aSZachary Turner const ResourceDirEntryStart &HeaderEntry, 788*11adbacaSMartin Storsjo ArrayRef<uint8_t> ImageData, uint16_t Flags) 789*11adbacaSMartin Storsjo : RCResource(Flags), Type(ResourceType), Header(HeaderEntry), 790*11adbacaSMartin Storsjo Image(ImageData) {} 791c3ab013aSZachary Turner 792c3ab013aSZachary Turner Twine getResourceTypeName() const override { return "Icon/cursor image"; } 793c3ab013aSZachary Turner IntOrString getResourceType() const override { 794c3ab013aSZachary Turner return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor; 795c3ab013aSZachary Turner } 796c3ab013aSZachary Turner ResourceKind getKind() const override { return RkSingleCursorOrIconRes; } 797c3ab013aSZachary Turner static bool classof(const RCResource *Res) { 798c3ab013aSZachary Turner return Res->getKind() == RkSingleCursorOrIconRes; 799c3ab013aSZachary Turner } 800c3ab013aSZachary Turner }; 801c3ab013aSZachary Turner 802c3ab013aSZachary Turner class IconCursorGroupResource : public RCResource { 803c3ab013aSZachary Turner public: 804c3ab013aSZachary Turner IconCursorGroupType Type; 805c3ab013aSZachary Turner GroupIconDir Header; 806c3ab013aSZachary Turner std::vector<ResourceDirEntryStart> ItemEntries; 807c3ab013aSZachary Turner 808c3ab013aSZachary Turner IconCursorGroupResource(IconCursorGroupType ResourceType, 809c3ab013aSZachary Turner const GroupIconDir &HeaderData, 810c3ab013aSZachary Turner std::vector<ResourceDirEntryStart> &&Entries) 811c3ab013aSZachary Turner : Type(ResourceType), Header(HeaderData), 812c3ab013aSZachary Turner ItemEntries(std::move(Entries)) {} 813c3ab013aSZachary Turner 814c3ab013aSZachary Turner Twine getResourceTypeName() const override { return "Icon/cursor group"; } 815c3ab013aSZachary Turner IntOrString getResourceType() const override { 816c3ab013aSZachary Turner return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup; 817c3ab013aSZachary Turner } 818c3ab013aSZachary Turner ResourceKind getKind() const override { return RkCursorOrIconGroupRes; } 819c3ab013aSZachary Turner static bool classof(const RCResource *Res) { 820c3ab013aSZachary Turner return Res->getKind() == RkCursorOrIconGroupRes; 821c3ab013aSZachary Turner } 822c3ab013aSZachary Turner }; 823c3ab013aSZachary Turner 824c3ab013aSZachary Turner Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) { 825c3ab013aSZachary Turner auto *Res = cast<SingleIconCursorResource>(Base); 826c3ab013aSZachary Turner if (Res->Type == IconCursorGroupType::Cursor) { 827c3ab013aSZachary Turner // In case of cursors, two WORDS are appended to the beginning 828c3ab013aSZachary Turner // of the resource: HotspotX (Planes in RESDIRENTRY), 829c3ab013aSZachary Turner // and HotspotY (BitCount). 830c3ab013aSZachary Turner // 831c3ab013aSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx 832c3ab013aSZachary Turner // (Remarks section). 833c3ab013aSZachary Turner writeObject(Res->Header.Planes); 834c3ab013aSZachary Turner writeObject(Res->Header.BitCount); 835c3ab013aSZachary Turner } 836c3ab013aSZachary Turner 837c3ab013aSZachary Turner writeObject(Res->Image); 838c3ab013aSZachary Turner return Error::success(); 839c3ab013aSZachary Turner } 840c3ab013aSZachary Turner 841c3ab013aSZachary Turner Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) { 842c3ab013aSZachary Turner auto *Res = cast<IconCursorGroupResource>(Base); 843c3ab013aSZachary Turner writeObject(Res->Header); 844c3ab013aSZachary Turner for (auto Item : Res->ItemEntries) { 845c3ab013aSZachary Turner writeObject(Item); 846b12792a4SZachary Turner writeInt(IconCursorID++); 847c3ab013aSZachary Turner } 848c3ab013aSZachary Turner return Error::success(); 849c3ab013aSZachary Turner } 850c3ab013aSZachary Turner 851c3ab013aSZachary Turner Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) { 852c3ab013aSZachary Turner return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody); 853c3ab013aSZachary Turner } 854c3ab013aSZachary Turner 855c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) { 856c3ab013aSZachary Turner return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody); 857c3ab013aSZachary Turner } 858c3ab013aSZachary Turner 859c3ab013aSZachary Turner Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) { 860c3ab013aSZachary Turner IconCursorGroupType Type; 861c3ab013aSZachary Turner StringRef FileStr; 862c3ab013aSZachary Turner IntOrString ResName = Base->ResName; 863c3ab013aSZachary Turner 864c3ab013aSZachary Turner if (auto *IconRes = dyn_cast<IconResource>(Base)) { 865c3ab013aSZachary Turner FileStr = IconRes->IconLoc; 866c3ab013aSZachary Turner Type = IconCursorGroupType::Icon; 867c3ab013aSZachary Turner } else { 868c3ab013aSZachary Turner auto *CursorRes = dyn_cast<CursorResource>(Base); 869c3ab013aSZachary Turner FileStr = CursorRes->CursorLoc; 870c3ab013aSZachary Turner Type = IconCursorGroupType::Cursor; 871c3ab013aSZachary Turner } 872c3ab013aSZachary Turner 873c3ab013aSZachary Turner bool IsLong; 874c3ab013aSZachary Turner stripQuotes(FileStr, IsLong); 875fa0ca6cbSZachary Turner auto File = loadFile(FileStr); 876c3ab013aSZachary Turner 877c3ab013aSZachary Turner if (!File) 878fa0ca6cbSZachary Turner return File.takeError(); 879c3ab013aSZachary Turner 880c3ab013aSZachary Turner BinaryStreamReader Reader((*File)->getBuffer(), support::little); 881c3ab013aSZachary Turner 882c3ab013aSZachary Turner // Read the file headers. 883c3ab013aSZachary Turner // - At the beginning, ICONDIR/NEWHEADER header. 884c3ab013aSZachary Turner // - Then, a number of RESDIR headers follow. These contain offsets 885c3ab013aSZachary Turner // to data. 886c3ab013aSZachary Turner const GroupIconDir *Header; 887c3ab013aSZachary Turner 888c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readObject(Header)); 889c3ab013aSZachary Turner if (Header->Reserved != 0) 890c3ab013aSZachary Turner return createError("Incorrect icon/cursor Reserved field; should be 0."); 891c3ab013aSZachary Turner uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2; 892c3ab013aSZachary Turner if (Header->ResType != NeededType) 893c3ab013aSZachary Turner return createError("Incorrect icon/cursor ResType field; should be " + 894c3ab013aSZachary Turner Twine(NeededType) + "."); 895c3ab013aSZachary Turner 896c3ab013aSZachary Turner uint16_t NumItems = Header->ResCount; 897c3ab013aSZachary Turner 898c3ab013aSZachary Turner // Read single ico/cur headers. 899c3ab013aSZachary Turner std::vector<ResourceDirEntryStart> ItemEntries; 900c3ab013aSZachary Turner ItemEntries.reserve(NumItems); 901c3ab013aSZachary Turner std::vector<uint32_t> ItemOffsets(NumItems); 902c3ab013aSZachary Turner for (size_t ID = 0; ID < NumItems; ++ID) { 903c3ab013aSZachary Turner const ResourceDirEntryStart *Object; 904c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readObject(Object)); 905c3ab013aSZachary Turner ItemEntries.push_back(*Object); 906c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID])); 907c3ab013aSZachary Turner } 908c3ab013aSZachary Turner 909c3ab013aSZachary Turner // Now write each icon/cursors one by one. At first, all the contents 910c3ab013aSZachary Turner // without ICO/CUR header. This is described by SingleIconCursorResource. 911c3ab013aSZachary Turner for (size_t ID = 0; ID < NumItems; ++ID) { 912c3ab013aSZachary Turner // Load the fragment of file. 913c3ab013aSZachary Turner Reader.setOffset(ItemOffsets[ID]); 914c3ab013aSZachary Turner ArrayRef<uint8_t> Image; 915c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size)); 916*11adbacaSMartin Storsjo SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image, 917*11adbacaSMartin Storsjo Base->MemoryFlags); 918c3ab013aSZachary Turner SingleRes.setName(IconCursorID + ID); 919c3ab013aSZachary Turner RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes)); 920c3ab013aSZachary Turner } 921c3ab013aSZachary Turner 922c3ab013aSZachary Turner // Now, write all the headers concatenated into a separate resource. 923c3ab013aSZachary Turner for (size_t ID = 0; ID < NumItems; ++ID) { 924860e5fcdSMartin Storsjo // We need to rewrite the cursor headers, and fetch actual values 925860e5fcdSMartin Storsjo // for Planes/BitCount. 926c3ab013aSZachary Turner const auto &OldHeader = ItemEntries[ID]; 927860e5fcdSMartin Storsjo ResourceDirEntryStart NewHeader = OldHeader; 928860e5fcdSMartin Storsjo 929860e5fcdSMartin Storsjo if (Type == IconCursorGroupType::Cursor) { 930c3ab013aSZachary Turner NewHeader.Cursor.Width = OldHeader.Icon.Width; 931c3ab013aSZachary Turner // Each cursor in fact stores two bitmaps, one under another. 932c3ab013aSZachary Turner // Height provided in cursor definition describes the height of the 933c3ab013aSZachary Turner // cursor, whereas the value existing in resource definition describes 934c3ab013aSZachary Turner // the height of the bitmap. Therefore, we need to double this height. 935c3ab013aSZachary Turner NewHeader.Cursor.Height = OldHeader.Icon.Height * 2; 936c3ab013aSZachary Turner 937860e5fcdSMartin Storsjo // Two WORDs were written at the beginning of the resource (hotspot 938860e5fcdSMartin Storsjo // location). This is reflected in Size field. 939860e5fcdSMartin Storsjo NewHeader.Size += 2 * sizeof(uint16_t); 940860e5fcdSMartin Storsjo } 941860e5fcdSMartin Storsjo 942c3ab013aSZachary Turner // Now, we actually need to read the bitmap header to find 943c3ab013aSZachary Turner // the number of planes and the number of bits per pixel. 944c3ab013aSZachary Turner Reader.setOffset(ItemOffsets[ID]); 945c3ab013aSZachary Turner const BitmapInfoHeader *BMPHeader; 946c3ab013aSZachary Turner RETURN_IF_ERROR(Reader.readObject(BMPHeader)); 947860e5fcdSMartin Storsjo if (BMPHeader->Size == sizeof(BitmapInfoHeader)) { 948c3ab013aSZachary Turner NewHeader.Planes = BMPHeader->Planes; 949c3ab013aSZachary Turner NewHeader.BitCount = BMPHeader->BitCount; 950860e5fcdSMartin Storsjo } else { 951860e5fcdSMartin Storsjo // A PNG .ico file. 952860e5fcdSMartin Storsjo // https://blogs.msdn.microsoft.com/oldnewthing/20101022-00/?p=12473 953860e5fcdSMartin Storsjo // "The image must be in 32bpp" 954860e5fcdSMartin Storsjo NewHeader.Planes = 1; 955860e5fcdSMartin Storsjo NewHeader.BitCount = 32; 956860e5fcdSMartin Storsjo } 957c3ab013aSZachary Turner 958c3ab013aSZachary Turner ItemEntries[ID] = NewHeader; 959c3ab013aSZachary Turner } 960c3ab013aSZachary Turner 961c3ab013aSZachary Turner IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries)); 962c3ab013aSZachary Turner HeaderRes.setName(ResName); 963*11adbacaSMartin Storsjo if (Base->MemoryFlags & MfPreload) { 964*11adbacaSMartin Storsjo HeaderRes.MemoryFlags |= MfPreload; 965*11adbacaSMartin Storsjo HeaderRes.MemoryFlags &= ~MfPure; 966*11adbacaSMartin Storsjo } 967c3ab013aSZachary Turner RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes)); 968c3ab013aSZachary Turner 969c3ab013aSZachary Turner return Error::success(); 970c3ab013aSZachary Turner } 971c3ab013aSZachary Turner 9727f7745c0SMarek Sokolowski // --- DialogResource helpers. --- // 97322fccd64SMarek Sokolowski 9747f7745c0SMarek Sokolowski Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl, 9757f7745c0SMarek Sokolowski bool IsExtended) { 9767f7745c0SMarek Sokolowski // Each control should be aligned to DWORD. 9777f7745c0SMarek Sokolowski padStream(sizeof(uint32_t)); 9787f7745c0SMarek Sokolowski 9797f7745c0SMarek Sokolowski auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type); 9807f7745c0SMarek Sokolowski uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0); 9817f7745c0SMarek Sokolowski uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0); 9827f7745c0SMarek Sokolowski 9837f7745c0SMarek Sokolowski // DIALOG(EX) item header prefix. 9847f7745c0SMarek Sokolowski if (!IsExtended) { 9857f7745c0SMarek Sokolowski struct { 9867f7745c0SMarek Sokolowski ulittle32_t Style; 9877f7745c0SMarek Sokolowski ulittle32_t ExtStyle; 9887f7745c0SMarek Sokolowski } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)}; 9897f7745c0SMarek Sokolowski writeObject(Prefix); 9907f7745c0SMarek Sokolowski } else { 9917f7745c0SMarek Sokolowski struct { 9927f7745c0SMarek Sokolowski ulittle32_t HelpID; 9937f7745c0SMarek Sokolowski ulittle32_t ExtStyle; 9947f7745c0SMarek Sokolowski ulittle32_t Style; 9957f7745c0SMarek Sokolowski } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle), 9967f7745c0SMarek Sokolowski ulittle32_t(CtlStyle)}; 9977f7745c0SMarek Sokolowski writeObject(Prefix); 9987f7745c0SMarek Sokolowski } 9997f7745c0SMarek Sokolowski 10007f7745c0SMarek Sokolowski // Common fixed-length part. 10017f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( 10027f7745c0SMarek Sokolowski Ctl.X, "Dialog control x-coordinate", true)); 10037f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( 10047f7745c0SMarek Sokolowski Ctl.Y, "Dialog control y-coordinate", true)); 10057f7745c0SMarek Sokolowski RETURN_IF_ERROR( 10067f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false)); 10077f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkSignedNumberFits<int16_t>( 10087f7745c0SMarek Sokolowski Ctl.Height, "Dialog control height", false)); 10097f7745c0SMarek Sokolowski struct { 10107f7745c0SMarek Sokolowski ulittle16_t X; 10117f7745c0SMarek Sokolowski ulittle16_t Y; 10127f7745c0SMarek Sokolowski ulittle16_t Width; 10137f7745c0SMarek Sokolowski ulittle16_t Height; 10147f7745c0SMarek Sokolowski } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width), 10157f7745c0SMarek Sokolowski ulittle16_t(Ctl.Height)}; 10167f7745c0SMarek Sokolowski writeObject(Middle); 10177f7745c0SMarek Sokolowski 10187f7745c0SMarek Sokolowski // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX. 10197f7745c0SMarek Sokolowski if (!IsExtended) { 1020c1a67857SMartin Storsjo // It's common to use -1, i.e. UINT32_MAX, for controls one doesn't 1021c1a67857SMartin Storsjo // want to refer to later. 1022c1a67857SMartin Storsjo if (Ctl.ID != static_cast<uint32_t>(-1)) 10237f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkNumberFits<uint16_t>( 10247f7745c0SMarek Sokolowski Ctl.ID, "Control ID in simple DIALOG resource")); 10257f7745c0SMarek Sokolowski writeInt<uint16_t>(Ctl.ID); 10267f7745c0SMarek Sokolowski } else { 10277f7745c0SMarek Sokolowski writeInt<uint32_t>(Ctl.ID); 10287f7745c0SMarek Sokolowski } 10297f7745c0SMarek Sokolowski 10307f7745c0SMarek Sokolowski // Window class - either 0xFFFF + 16-bit integer or a string. 1031818bd568SMartin Storsjo RETURN_IF_ERROR(writeIntOrString(Ctl.Class)); 10327f7745c0SMarek Sokolowski 10337f7745c0SMarek Sokolowski // Element caption/reference ID. ID is preceded by 0xFFFF. 10347f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID")); 10357f7745c0SMarek Sokolowski RETURN_IF_ERROR(writeIntOrString(Ctl.Title)); 10367f7745c0SMarek Sokolowski 10377f7745c0SMarek Sokolowski // # bytes of extra creation data count. Don't pass any. 10387f7745c0SMarek Sokolowski writeInt<uint16_t>(0); 10397f7745c0SMarek Sokolowski 10407f7745c0SMarek Sokolowski return Error::success(); 10417f7745c0SMarek Sokolowski } 10427f7745c0SMarek Sokolowski 10437f7745c0SMarek Sokolowski Error ResourceFileWriter::writeDialogBody(const RCResource *Base) { 10447f7745c0SMarek Sokolowski auto *Res = cast<DialogResource>(Base); 10457f7745c0SMarek Sokolowski 10467f7745c0SMarek Sokolowski // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU. 1047420090afSZachary Turner const uint32_t DefaultStyle = 0x80880000; 1048420090afSZachary Turner const uint32_t StyleFontFlag = 0x40; 1049420090afSZachary Turner const uint32_t StyleCaptionFlag = 0x00C00000; 1050420090afSZachary Turner 1051420090afSZachary Turner uint32_t UsedStyle = ObjectData.Style.getValueOr(DefaultStyle); 1052420090afSZachary Turner if (ObjectData.Font) 1053420090afSZachary Turner UsedStyle |= StyleFontFlag; 1054420090afSZachary Turner else 1055420090afSZachary Turner UsedStyle &= ~StyleFontFlag; 1056420090afSZachary Turner 1057420090afSZachary Turner // Actually, in case of empty (but existent) caption, the examined field 1058420090afSZachary Turner // is equal to "\"\"". That's why empty captions are still noticed. 1059420090afSZachary Turner if (ObjectData.Caption != "") 1060420090afSZachary Turner UsedStyle |= StyleCaptionFlag; 1061420090afSZachary Turner 1062420090afSZachary Turner const uint16_t DialogExMagic = 0xFFFF; 10637f7745c0SMarek Sokolowski 10647f7745c0SMarek Sokolowski // Write DIALOG(EX) header prefix. These are pretty different. 10657f7745c0SMarek Sokolowski if (!Res->IsExtended) { 1066420090afSZachary Turner // We cannot let the higher word of DefaultStyle be equal to 0xFFFF. 1067420090afSZachary Turner // In such a case, whole object (in .res file) is equivalent to a 1068420090afSZachary Turner // DIALOGEX. It might lead to access violation/segmentation fault in 1069420090afSZachary Turner // resource readers. For example, 1070420090afSZachary Turner // 1 DIALOG 0, 0, 0, 65432 1071420090afSZachary Turner // STYLE 0xFFFF0001 {} 1072420090afSZachary Turner // would be compiled to a DIALOGEX with 65432 controls. 1073420090afSZachary Turner if ((UsedStyle >> 16) == DialogExMagic) 1074420090afSZachary Turner return createError("16 higher bits of DIALOG resource style cannot be" 1075420090afSZachary Turner " equal to 0xFFFF"); 1076420090afSZachary Turner 10777f7745c0SMarek Sokolowski struct { 10787f7745c0SMarek Sokolowski ulittle32_t Style; 10797f7745c0SMarek Sokolowski ulittle32_t ExtStyle; 10807f7745c0SMarek Sokolowski } Prefix{ulittle32_t(UsedStyle), 10817f7745c0SMarek Sokolowski ulittle32_t(0)}; // As of now, we don't keep EXSTYLE. 10827f7745c0SMarek Sokolowski 10837f7745c0SMarek Sokolowski writeObject(Prefix); 10847f7745c0SMarek Sokolowski } else { 10857f7745c0SMarek Sokolowski struct { 10867f7745c0SMarek Sokolowski ulittle16_t Version; 10877f7745c0SMarek Sokolowski ulittle16_t Magic; 10887f7745c0SMarek Sokolowski ulittle32_t HelpID; 10897f7745c0SMarek Sokolowski ulittle32_t ExtStyle; 10907f7745c0SMarek Sokolowski ulittle32_t Style; 10917f7745c0SMarek Sokolowski } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic), 10927f7745c0SMarek Sokolowski ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)}; 10937f7745c0SMarek Sokolowski 10947f7745c0SMarek Sokolowski writeObject(Prefix); 10957f7745c0SMarek Sokolowski } 10967f7745c0SMarek Sokolowski 10977f7745c0SMarek Sokolowski // Now, a common part. First, fixed-length fields. 10987f7745c0SMarek Sokolowski RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(), 10997f7745c0SMarek Sokolowski "Number of dialog controls")); 11007f7745c0SMarek Sokolowski RETURN_IF_ERROR( 11017f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true)); 11027f7745c0SMarek Sokolowski RETURN_IF_ERROR( 11037f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true)); 11047f7745c0SMarek Sokolowski RETURN_IF_ERROR( 11057f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false)); 11067f7745c0SMarek Sokolowski RETURN_IF_ERROR( 11077f7745c0SMarek Sokolowski checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false)); 11087f7745c0SMarek Sokolowski struct { 11097f7745c0SMarek Sokolowski ulittle16_t Count; 11107f7745c0SMarek Sokolowski ulittle16_t PosX; 11117f7745c0SMarek Sokolowski ulittle16_t PosY; 11127f7745c0SMarek Sokolowski ulittle16_t DialogWidth; 11137f7745c0SMarek Sokolowski ulittle16_t DialogHeight; 11147f7745c0SMarek Sokolowski } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X), 11157f7745c0SMarek Sokolowski ulittle16_t(Res->Y), ulittle16_t(Res->Width), 11167f7745c0SMarek Sokolowski ulittle16_t(Res->Height)}; 11177f7745c0SMarek Sokolowski writeObject(Middle); 11187f7745c0SMarek Sokolowski 11197f7745c0SMarek Sokolowski // MENU field. As of now, we don't keep them in the state and can peacefully 11207f7745c0SMarek Sokolowski // think there is no menu attached to the dialog. 11217f7745c0SMarek Sokolowski writeInt<uint16_t>(0); 11227f7745c0SMarek Sokolowski 11237f7745c0SMarek Sokolowski // Window CLASS field. Not kept here. 11247f7745c0SMarek Sokolowski writeInt<uint16_t>(0); 11257f7745c0SMarek Sokolowski 1126420090afSZachary Turner // Window title or a single word equal to 0. 1127420090afSZachary Turner RETURN_IF_ERROR(writeCString(ObjectData.Caption)); 1128420090afSZachary Turner 1129420090afSZachary Turner // If there *is* a window font declared, output its data. 1130420090afSZachary Turner auto &Font = ObjectData.Font; 1131420090afSZachary Turner if (Font) { 1132420090afSZachary Turner writeInt<uint16_t>(Font->Size); 1133420090afSZachary Turner // Additional description occurs only in DIALOGEX. 1134420090afSZachary Turner if (Res->IsExtended) { 1135420090afSZachary Turner writeInt<uint16_t>(Font->Weight); 1136420090afSZachary Turner writeInt<uint8_t>(Font->IsItalic); 1137420090afSZachary Turner writeInt<uint8_t>(Font->Charset); 1138420090afSZachary Turner } 1139420090afSZachary Turner RETURN_IF_ERROR(writeCString(Font->Typeface)); 1140420090afSZachary Turner } 11417f7745c0SMarek Sokolowski 11427f7745c0SMarek Sokolowski auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error { 11437f7745c0SMarek Sokolowski if (!Err) 11447f7745c0SMarek Sokolowski return Error::success(); 11457f7745c0SMarek Sokolowski return joinErrors(createError("Error in " + Twine(Ctl.Type) + 11467f7745c0SMarek Sokolowski " control (ID " + Twine(Ctl.ID) + "):"), 11477f7745c0SMarek Sokolowski std::move(Err)); 11487f7745c0SMarek Sokolowski }; 11497f7745c0SMarek Sokolowski 11507f7745c0SMarek Sokolowski for (auto &Ctl : Res->Controls) 11517f7745c0SMarek Sokolowski RETURN_IF_ERROR( 11527f7745c0SMarek Sokolowski handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl)); 11537f7745c0SMarek Sokolowski 11547f7745c0SMarek Sokolowski return Error::success(); 11557f7745c0SMarek Sokolowski } 11567f7745c0SMarek Sokolowski 11577f7745c0SMarek Sokolowski // --- HTMLResource helpers. --- // 115822fccd64SMarek Sokolowski 11598f19343aSMarek Sokolowski Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) { 11608f19343aSMarek Sokolowski return appendFile(cast<HTMLResource>(Base)->HTMLLoc); 11618f19343aSMarek Sokolowski } 11628f19343aSMarek Sokolowski 116342f494d6SMarek Sokolowski // --- MenuResource helpers. --- // 116442f494d6SMarek Sokolowski 116542f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinition( 116642f494d6SMarek Sokolowski const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) { 116742f494d6SMarek Sokolowski assert(Def); 116842f494d6SMarek Sokolowski const MenuDefinition *DefPtr = Def.get(); 116942f494d6SMarek Sokolowski 117042f494d6SMarek Sokolowski if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) { 117142f494d6SMarek Sokolowski writeInt<uint16_t>(Flags); 117242f494d6SMarek Sokolowski RETURN_IF_ERROR( 117342f494d6SMarek Sokolowski checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID")); 117442f494d6SMarek Sokolowski writeInt<uint16_t>(MenuItemPtr->Id); 117542f494d6SMarek Sokolowski RETURN_IF_ERROR(writeCString(MenuItemPtr->Name)); 117642f494d6SMarek Sokolowski return Error::success(); 117742f494d6SMarek Sokolowski } 117842f494d6SMarek Sokolowski 117942f494d6SMarek Sokolowski if (isa<MenuSeparator>(DefPtr)) { 118042f494d6SMarek Sokolowski writeInt<uint16_t>(Flags); 118142f494d6SMarek Sokolowski writeInt<uint32_t>(0); 118242f494d6SMarek Sokolowski return Error::success(); 118342f494d6SMarek Sokolowski } 118442f494d6SMarek Sokolowski 118542f494d6SMarek Sokolowski auto *PopupPtr = cast<PopupItem>(DefPtr); 118642f494d6SMarek Sokolowski writeInt<uint16_t>(Flags); 118742f494d6SMarek Sokolowski RETURN_IF_ERROR(writeCString(PopupPtr->Name)); 118842f494d6SMarek Sokolowski return writeMenuDefinitionList(PopupPtr->SubItems); 118942f494d6SMarek Sokolowski } 119042f494d6SMarek Sokolowski 119142f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinitionList( 119242f494d6SMarek Sokolowski const MenuDefinitionList &List) { 119342f494d6SMarek Sokolowski for (auto &Def : List.Definitions) { 119442f494d6SMarek Sokolowski uint16_t Flags = Def->getResFlags(); 119542f494d6SMarek Sokolowski // Last element receives an additional 0x80 flag. 119642f494d6SMarek Sokolowski const uint16_t LastElementFlag = 0x0080; 119742f494d6SMarek Sokolowski if (&Def == &List.Definitions.back()) 119842f494d6SMarek Sokolowski Flags |= LastElementFlag; 119942f494d6SMarek Sokolowski 120042f494d6SMarek Sokolowski RETURN_IF_ERROR(writeMenuDefinition(Def, Flags)); 120142f494d6SMarek Sokolowski } 120242f494d6SMarek Sokolowski return Error::success(); 120342f494d6SMarek Sokolowski } 120442f494d6SMarek Sokolowski 120542f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuBody(const RCResource *Base) { 120642f494d6SMarek Sokolowski // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0. 120742f494d6SMarek Sokolowski // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx 12081e2c8b11SZachary Turner writeInt<uint32_t>(0); 120942f494d6SMarek Sokolowski 121042f494d6SMarek Sokolowski return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements); 121142f494d6SMarek Sokolowski } 121242f494d6SMarek Sokolowski 1213da366693SZachary Turner // --- StringTableResource helpers. --- // 1214da366693SZachary Turner 1215da366693SZachary Turner class BundleResource : public RCResource { 1216da366693SZachary Turner public: 1217da366693SZachary Turner using BundleType = ResourceFileWriter::StringTableInfo::Bundle; 1218da366693SZachary Turner BundleType Bundle; 1219da366693SZachary Turner 1220*11adbacaSMartin Storsjo BundleResource(const BundleType &StrBundle) 1221*11adbacaSMartin Storsjo : RCResource(StrBundle.MemoryFlags), Bundle(StrBundle) {} 1222da366693SZachary Turner IntOrString getResourceType() const override { return 6; } 1223da366693SZachary Turner 1224da366693SZachary Turner ResourceKind getKind() const override { return RkStringTableBundle; } 1225da366693SZachary Turner static bool classof(const RCResource *Res) { 1226da366693SZachary Turner return Res->getKind() == RkStringTableBundle; 1227da366693SZachary Turner } 1228d1d046aaSMartin Storsjo Twine getResourceTypeName() const override { return "STRINGTABLE"; } 1229da366693SZachary Turner }; 1230da366693SZachary Turner 1231da366693SZachary Turner Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) { 1232da366693SZachary Turner return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody); 1233da366693SZachary Turner } 1234da366693SZachary Turner 1235da366693SZachary Turner Error ResourceFileWriter::insertStringIntoBundle( 1236da366693SZachary Turner StringTableInfo::Bundle &Bundle, uint16_t StringID, StringRef String) { 1237da366693SZachary Turner uint16_t StringLoc = StringID & 15; 1238da366693SZachary Turner if (Bundle.Data[StringLoc]) 1239da366693SZachary Turner return createError("Multiple STRINGTABLE strings located under ID " + 1240da366693SZachary Turner Twine(StringID)); 1241da366693SZachary Turner Bundle.Data[StringLoc] = String; 1242da366693SZachary Turner return Error::success(); 1243da366693SZachary Turner } 1244da366693SZachary Turner 1245da366693SZachary Turner Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) { 1246da366693SZachary Turner auto *Res = cast<BundleResource>(Base); 1247da366693SZachary Turner for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) { 1248da366693SZachary Turner // The string format is a tiny bit different here. We 1249da366693SZachary Turner // first output the size of the string, and then the string itself 1250da366693SZachary Turner // (which is not null-terminated). 1251da366693SZachary Turner bool IsLongString; 1252da366693SZachary Turner SmallVector<UTF16, 128> Data; 1253da366693SZachary Turner RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()), 1254da366693SZachary Turner NullHandlingMethod::CutAtDoubleNull, 1255d1d046aaSMartin Storsjo IsLongString, Data, Params.CodePage)); 1256da366693SZachary Turner if (AppendNull && Res->Bundle.Data[ID]) 1257da366693SZachary Turner Data.push_back('\0'); 1258da366693SZachary Turner RETURN_IF_ERROR( 1259da366693SZachary Turner checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size")); 1260b12792a4SZachary Turner writeInt<uint16_t>(Data.size()); 1261da366693SZachary Turner for (auto Char : Data) 1262b12792a4SZachary Turner writeInt(Char); 1263da366693SZachary Turner } 1264da366693SZachary Turner return Error::success(); 1265da366693SZachary Turner } 1266da366693SZachary Turner 1267da366693SZachary Turner Error ResourceFileWriter::dumpAllStringTables() { 1268da366693SZachary Turner for (auto Key : StringTableData.BundleList) { 1269da366693SZachary Turner auto Iter = StringTableData.BundleData.find(Key); 1270da366693SZachary Turner assert(Iter != StringTableData.BundleData.end()); 1271da366693SZachary Turner 1272da366693SZachary Turner // For a moment, revert the context info to moment of bundle declaration. 1273da366693SZachary Turner ContextKeeper RAII(this); 1274da366693SZachary Turner ObjectData = Iter->second.DeclTimeInfo; 1275da366693SZachary Turner 1276da366693SZachary Turner BundleResource Res(Iter->second); 1277da366693SZachary Turner // Bundle #(k+1) contains keys [16k, 16k + 15]. 1278da366693SZachary Turner Res.setName(Key.first + 1); 1279da366693SZachary Turner RETURN_IF_ERROR(visitStringTableBundle(&Res)); 1280da366693SZachary Turner } 1281da366693SZachary Turner return Error::success(); 1282da366693SZachary Turner } 1283da366693SZachary Turner 12849d8b358aSZachary Turner // --- UserDefinedResource helpers. --- // 12859d8b358aSZachary Turner 12869d8b358aSZachary Turner Error ResourceFileWriter::writeUserDefinedBody(const RCResource *Base) { 12879d8b358aSZachary Turner auto *Res = cast<UserDefinedResource>(Base); 12889d8b358aSZachary Turner 12899d8b358aSZachary Turner if (Res->IsFileResource) 12909d8b358aSZachary Turner return appendFile(Res->FileLoc); 12919d8b358aSZachary Turner 12929d8b358aSZachary Turner for (auto &Elem : Res->Contents) { 12939d8b358aSZachary Turner if (Elem.isInt()) { 12949d8b358aSZachary Turner RETURN_IF_ERROR( 12959d8b358aSZachary Turner checkRCInt(Elem.getInt(), "Number in user-defined resource")); 12969d8b358aSZachary Turner writeRCInt(Elem.getInt()); 12979d8b358aSZachary Turner continue; 12989d8b358aSZachary Turner } 12999d8b358aSZachary Turner 13009d8b358aSZachary Turner SmallVector<UTF16, 128> ProcessedString; 13019d8b358aSZachary Turner bool IsLongString; 1302d1d046aaSMartin Storsjo RETURN_IF_ERROR( 1303d1d046aaSMartin Storsjo processString(Elem.getString(), NullHandlingMethod::UserResource, 1304d1d046aaSMartin Storsjo IsLongString, ProcessedString, Params.CodePage)); 13059d8b358aSZachary Turner 13069d8b358aSZachary Turner for (auto Ch : ProcessedString) { 13079d8b358aSZachary Turner if (IsLongString) { 1308b12792a4SZachary Turner writeInt(Ch); 13099d8b358aSZachary Turner continue; 13109d8b358aSZachary Turner } 13119d8b358aSZachary Turner 13129d8b358aSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint8_t>( 131375f9ae2bSSimon Dardis Ch, "Character in narrow string in user-defined resource")); 1314b12792a4SZachary Turner writeInt<uint8_t>(Ch); 13159d8b358aSZachary Turner } 13169d8b358aSZachary Turner } 13179d8b358aSZachary Turner 13189d8b358aSZachary Turner return Error::success(); 13199d8b358aSZachary Turner } 13209d8b358aSZachary Turner 132107bc04ffSZachary Turner // --- VersionInfoResourceResource helpers. --- // 132207bc04ffSZachary Turner 132307bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) { 132407bc04ffSZachary Turner // Output the header if the block has name. 132507bc04ffSZachary Turner bool OutputHeader = Blk.Name != ""; 132607bc04ffSZachary Turner uint64_t LengthLoc; 132707bc04ffSZachary Turner 132828ae894aSMartin Storsjo padStream(sizeof(uint32_t)); 132907bc04ffSZachary Turner if (OutputHeader) { 13301e2c8b11SZachary Turner LengthLoc = writeInt<uint16_t>(0); 13311e2c8b11SZachary Turner writeInt<uint16_t>(0); 13321e2c8b11SZachary Turner writeInt<uint16_t>(1); // true 133307bc04ffSZachary Turner RETURN_IF_ERROR(writeCString(Blk.Name)); 133407bc04ffSZachary Turner padStream(sizeof(uint32_t)); 133507bc04ffSZachary Turner } 133607bc04ffSZachary Turner 133707bc04ffSZachary Turner for (const std::unique_ptr<VersionInfoStmt> &Item : Blk.Stmts) { 133807bc04ffSZachary Turner VersionInfoStmt *ItemPtr = Item.get(); 133907bc04ffSZachary Turner 134007bc04ffSZachary Turner if (auto *BlockPtr = dyn_cast<VersionInfoBlock>(ItemPtr)) { 134107bc04ffSZachary Turner RETURN_IF_ERROR(writeVersionInfoBlock(*BlockPtr)); 134207bc04ffSZachary Turner continue; 134307bc04ffSZachary Turner } 134407bc04ffSZachary Turner 134507bc04ffSZachary Turner auto *ValuePtr = cast<VersionInfoValue>(ItemPtr); 134607bc04ffSZachary Turner RETURN_IF_ERROR(writeVersionInfoValue(*ValuePtr)); 134707bc04ffSZachary Turner } 134807bc04ffSZachary Turner 134907bc04ffSZachary Turner if (OutputHeader) { 135007bc04ffSZachary Turner uint64_t CurLoc = tell(); 135107bc04ffSZachary Turner writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); 135207bc04ffSZachary Turner } 135307bc04ffSZachary Turner 135407bc04ffSZachary Turner return Error::success(); 135507bc04ffSZachary Turner } 135607bc04ffSZachary Turner 135707bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoValue(const VersionInfoValue &Val) { 135807bc04ffSZachary Turner // rc has a peculiar algorithm to output VERSIONINFO VALUEs. Each VALUE 135907bc04ffSZachary Turner // is a mapping from the key (string) to the value (a sequence of ints or 136007bc04ffSZachary Turner // a sequence of strings). 136107bc04ffSZachary Turner // 136207bc04ffSZachary Turner // If integers are to be written: width of each integer written depends on 136307bc04ffSZachary Turner // whether it's been declared 'long' (it's DWORD then) or not (it's WORD). 136407bc04ffSZachary Turner // ValueLength defined in structure referenced below is then the total 136507bc04ffSZachary Turner // number of bytes taken by these integers. 136607bc04ffSZachary Turner // 136707bc04ffSZachary Turner // If strings are to be written: characters are always WORDs. 136807bc04ffSZachary Turner // Moreover, '\0' character is written after the last string, and between 136907bc04ffSZachary Turner // every two strings separated by comma (if strings are not comma-separated, 137007bc04ffSZachary Turner // they're simply concatenated). ValueLength is equal to the number of WORDs 137107bc04ffSZachary Turner // written (that is, half of the bytes written). 137207bc04ffSZachary Turner // 137307bc04ffSZachary Turner // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms646994.aspx 137407bc04ffSZachary Turner bool HasStrings = false, HasInts = false; 137507bc04ffSZachary Turner for (auto &Item : Val.Values) 137607bc04ffSZachary Turner (Item.isInt() ? HasInts : HasStrings) = true; 137707bc04ffSZachary Turner 137807bc04ffSZachary Turner assert((HasStrings || HasInts) && "VALUE must have at least one argument"); 137907bc04ffSZachary Turner if (HasStrings && HasInts) 138007bc04ffSZachary Turner return createError(Twine("VALUE ") + Val.Key + 138107bc04ffSZachary Turner " cannot contain both strings and integers"); 138207bc04ffSZachary Turner 138328ae894aSMartin Storsjo padStream(sizeof(uint32_t)); 13841e2c8b11SZachary Turner auto LengthLoc = writeInt<uint16_t>(0); 13851e2c8b11SZachary Turner auto ValLengthLoc = writeInt<uint16_t>(0); 13861e2c8b11SZachary Turner writeInt<uint16_t>(HasStrings); 138707bc04ffSZachary Turner RETURN_IF_ERROR(writeCString(Val.Key)); 138807bc04ffSZachary Turner padStream(sizeof(uint32_t)); 138907bc04ffSZachary Turner 139007bc04ffSZachary Turner auto DataLoc = tell(); 139107bc04ffSZachary Turner for (size_t Id = 0; Id < Val.Values.size(); ++Id) { 139207bc04ffSZachary Turner auto &Item = Val.Values[Id]; 139307bc04ffSZachary Turner if (Item.isInt()) { 139407bc04ffSZachary Turner auto Value = Item.getInt(); 139507bc04ffSZachary Turner RETURN_IF_ERROR(checkRCInt(Value, "VERSIONINFO integer value")); 139607bc04ffSZachary Turner writeRCInt(Value); 139707bc04ffSZachary Turner continue; 139807bc04ffSZachary Turner } 139907bc04ffSZachary Turner 140007bc04ffSZachary Turner bool WriteTerminator = 140107bc04ffSZachary Turner Id == Val.Values.size() - 1 || Val.HasPrecedingComma[Id + 1]; 140207bc04ffSZachary Turner RETURN_IF_ERROR(writeCString(Item.getString(), WriteTerminator)); 140307bc04ffSZachary Turner } 140407bc04ffSZachary Turner 140507bc04ffSZachary Turner auto CurLoc = tell(); 140607bc04ffSZachary Turner auto ValueLength = CurLoc - DataLoc; 140707bc04ffSZachary Turner if (HasStrings) { 140807bc04ffSZachary Turner assert(ValueLength % 2 == 0); 140907bc04ffSZachary Turner ValueLength /= 2; 141007bc04ffSZachary Turner } 141107bc04ffSZachary Turner writeObjectAt(ulittle16_t(CurLoc - LengthLoc), LengthLoc); 141207bc04ffSZachary Turner writeObjectAt(ulittle16_t(ValueLength), ValLengthLoc); 141307bc04ffSZachary Turner return Error::success(); 141407bc04ffSZachary Turner } 141507bc04ffSZachary Turner 141607bc04ffSZachary Turner template <typename Ty> 141707bc04ffSZachary Turner static Ty getWithDefault(const StringMap<Ty> &Map, StringRef Key, 141807bc04ffSZachary Turner const Ty &Default) { 141907bc04ffSZachary Turner auto Iter = Map.find(Key); 142007bc04ffSZachary Turner if (Iter != Map.end()) 142107bc04ffSZachary Turner return Iter->getValue(); 142207bc04ffSZachary Turner return Default; 142307bc04ffSZachary Turner } 142407bc04ffSZachary Turner 142507bc04ffSZachary Turner Error ResourceFileWriter::writeVersionInfoBody(const RCResource *Base) { 142607bc04ffSZachary Turner auto *Res = cast<VersionInfoResource>(Base); 142707bc04ffSZachary Turner 142807bc04ffSZachary Turner const auto &FixedData = Res->FixedData; 142907bc04ffSZachary Turner 143007bc04ffSZachary Turner struct /* VS_FIXEDFILEINFO */ { 143107bc04ffSZachary Turner ulittle32_t Signature = ulittle32_t(0xFEEF04BD); 143207bc04ffSZachary Turner ulittle32_t StructVersion = ulittle32_t(0x10000); 143307bc04ffSZachary Turner // It's weird to have most-significant DWORD first on the little-endian 143407bc04ffSZachary Turner // machines, but let it be this way. 143507bc04ffSZachary Turner ulittle32_t FileVersionMS; 143607bc04ffSZachary Turner ulittle32_t FileVersionLS; 143707bc04ffSZachary Turner ulittle32_t ProductVersionMS; 143807bc04ffSZachary Turner ulittle32_t ProductVersionLS; 143907bc04ffSZachary Turner ulittle32_t FileFlagsMask; 144007bc04ffSZachary Turner ulittle32_t FileFlags; 144107bc04ffSZachary Turner ulittle32_t FileOS; 144207bc04ffSZachary Turner ulittle32_t FileType; 144307bc04ffSZachary Turner ulittle32_t FileSubtype; 144407bc04ffSZachary Turner // MS implementation seems to always set these fields to 0. 144507bc04ffSZachary Turner ulittle32_t FileDateMS = ulittle32_t(0); 144607bc04ffSZachary Turner ulittle32_t FileDateLS = ulittle32_t(0); 144707bc04ffSZachary Turner } FixedInfo; 144807bc04ffSZachary Turner 144907bc04ffSZachary Turner // First, VS_VERSIONINFO. 1450b12792a4SZachary Turner auto LengthLoc = writeInt<uint16_t>(0); 1451b12792a4SZachary Turner writeInt<uint16_t>(sizeof(FixedInfo)); 1452b12792a4SZachary Turner writeInt<uint16_t>(0); 145307bc04ffSZachary Turner cantFail(writeCString("VS_VERSION_INFO")); 145407bc04ffSZachary Turner padStream(sizeof(uint32_t)); 145507bc04ffSZachary Turner 145607bc04ffSZachary Turner using VersionInfoFixed = VersionInfoResource::VersionInfoFixed; 145707bc04ffSZachary Turner auto GetField = [&](VersionInfoFixed::VersionInfoFixedType Type) { 145807bc04ffSZachary Turner static const SmallVector<uint32_t, 4> DefaultOut{0, 0, 0, 0}; 145907bc04ffSZachary Turner if (!FixedData.IsTypePresent[(int)Type]) 146007bc04ffSZachary Turner return DefaultOut; 146107bc04ffSZachary Turner return FixedData.FixedInfo[(int)Type]; 146207bc04ffSZachary Turner }; 146307bc04ffSZachary Turner 146407bc04ffSZachary Turner auto FileVer = GetField(VersionInfoFixed::FtFileVersion); 146507bc04ffSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>( 146607bc04ffSZachary Turner *std::max_element(FileVer.begin(), FileVer.end()), "FILEVERSION fields")); 146707bc04ffSZachary Turner FixedInfo.FileVersionMS = (FileVer[0] << 16) | FileVer[1]; 146807bc04ffSZachary Turner FixedInfo.FileVersionLS = (FileVer[2] << 16) | FileVer[3]; 146907bc04ffSZachary Turner 147007bc04ffSZachary Turner auto ProdVer = GetField(VersionInfoFixed::FtProductVersion); 147107bc04ffSZachary Turner RETURN_IF_ERROR(checkNumberFits<uint16_t>( 147207bc04ffSZachary Turner *std::max_element(ProdVer.begin(), ProdVer.end()), 147307bc04ffSZachary Turner "PRODUCTVERSION fields")); 147407bc04ffSZachary Turner FixedInfo.ProductVersionMS = (ProdVer[0] << 16) | ProdVer[1]; 147507bc04ffSZachary Turner FixedInfo.ProductVersionLS = (ProdVer[2] << 16) | ProdVer[3]; 147607bc04ffSZachary Turner 147707bc04ffSZachary Turner FixedInfo.FileFlagsMask = GetField(VersionInfoFixed::FtFileFlagsMask)[0]; 147807bc04ffSZachary Turner FixedInfo.FileFlags = GetField(VersionInfoFixed::FtFileFlags)[0]; 147907bc04ffSZachary Turner FixedInfo.FileOS = GetField(VersionInfoFixed::FtFileOS)[0]; 148007bc04ffSZachary Turner FixedInfo.FileType = GetField(VersionInfoFixed::FtFileType)[0]; 148107bc04ffSZachary Turner FixedInfo.FileSubtype = GetField(VersionInfoFixed::FtFileSubtype)[0]; 148207bc04ffSZachary Turner 148307bc04ffSZachary Turner writeObject(FixedInfo); 148407bc04ffSZachary Turner padStream(sizeof(uint32_t)); 148507bc04ffSZachary Turner 148607bc04ffSZachary Turner RETURN_IF_ERROR(writeVersionInfoBlock(Res->MainBlock)); 148707bc04ffSZachary Turner 148807bc04ffSZachary Turner // FIXME: check overflow? 148907bc04ffSZachary Turner writeObjectAt(ulittle16_t(tell() - LengthLoc), LengthLoc); 149007bc04ffSZachary Turner 149107bc04ffSZachary Turner return Error::success(); 149207bc04ffSZachary Turner } 149307bc04ffSZachary Turner 1494fa0ca6cbSZachary Turner Expected<std::unique_ptr<MemoryBuffer>> 1495fa0ca6cbSZachary Turner ResourceFileWriter::loadFile(StringRef File) const { 1496fa0ca6cbSZachary Turner SmallString<128> Path; 1497fa0ca6cbSZachary Turner SmallString<128> Cwd; 1498fa0ca6cbSZachary Turner std::unique_ptr<MemoryBuffer> Result; 1499fa0ca6cbSZachary Turner 1500fa0ca6cbSZachary Turner // 1. The current working directory. 1501fa0ca6cbSZachary Turner sys::fs::current_path(Cwd); 1502fa0ca6cbSZachary Turner Path.assign(Cwd.begin(), Cwd.end()); 1503fa0ca6cbSZachary Turner sys::path::append(Path, File); 1504fa0ca6cbSZachary Turner if (sys::fs::exists(Path)) 1505f22a25e6SZachary Turner return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); 1506fa0ca6cbSZachary Turner 1507fa0ca6cbSZachary Turner // 2. The directory of the input resource file, if it is different from the 1508fa0ca6cbSZachary Turner // current 1509fa0ca6cbSZachary Turner // working directory. 1510fa0ca6cbSZachary Turner StringRef InputFileDir = sys::path::parent_path(Params.InputFilePath); 1511fa0ca6cbSZachary Turner Path.assign(InputFileDir.begin(), InputFileDir.end()); 1512fa0ca6cbSZachary Turner sys::path::append(Path, File); 1513fa0ca6cbSZachary Turner if (sys::fs::exists(Path)) 1514f22a25e6SZachary Turner return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); 1515fa0ca6cbSZachary Turner 1516fa0ca6cbSZachary Turner // 3. All of the include directories specified on the command line. 1517fa0ca6cbSZachary Turner for (StringRef ForceInclude : Params.Include) { 1518fa0ca6cbSZachary Turner Path.assign(ForceInclude.begin(), ForceInclude.end()); 1519fa0ca6cbSZachary Turner sys::path::append(Path, File); 1520fa0ca6cbSZachary Turner if (sys::fs::exists(Path)) 1521f22a25e6SZachary Turner return errorOrToExpected(MemoryBuffer::getFile(Path, -1, false)); 1522fa0ca6cbSZachary Turner } 1523fa0ca6cbSZachary Turner 1524fa0ca6cbSZachary Turner if (auto Result = 1525fa0ca6cbSZachary Turner llvm::sys::Process::FindInEnvPath("INCLUDE", File, Params.NoInclude)) 1526f22a25e6SZachary Turner return errorOrToExpected(MemoryBuffer::getFile(*Result, -1, false)); 1527fa0ca6cbSZachary Turner 1528fa0ca6cbSZachary Turner return make_error<StringError>("error : file not found : " + Twine(File), 1529fa0ca6cbSZachary Turner inconvertibleErrorCode()); 1530fa0ca6cbSZachary Turner } 1531fa0ca6cbSZachary Turner 15328f19343aSMarek Sokolowski } // namespace rc 15338f19343aSMarek Sokolowski } // namespace llvm 1534