18f19343aSMarek Sokolowski //===-- ResourceFileWriter.cpp --------------------------------*- C++-*-===// 28f19343aSMarek Sokolowski // 38f19343aSMarek Sokolowski // The LLVM Compiler Infrastructure 48f19343aSMarek Sokolowski // 58f19343aSMarek Sokolowski // This file is distributed under the University of Illinois Open Source 68f19343aSMarek Sokolowski // License. See LICENSE.TXT for details. 78f19343aSMarek Sokolowski // 88f19343aSMarek Sokolowski //===---------------------------------------------------------------------===// 98f19343aSMarek Sokolowski // 108f19343aSMarek Sokolowski // This implements the visitor serializing resources to a .res stream. 118f19343aSMarek Sokolowski // 128f19343aSMarek Sokolowski //===---------------------------------------------------------------------===// 138f19343aSMarek Sokolowski 148f19343aSMarek Sokolowski #include "ResourceFileWriter.h" 158f19343aSMarek Sokolowski 168f19343aSMarek Sokolowski #include "llvm/Object/WindowsResource.h" 178f19343aSMarek Sokolowski #include "llvm/Support/ConvertUTF.h" 188f19343aSMarek Sokolowski #include "llvm/Support/Endian.h" 198f19343aSMarek Sokolowski #include "llvm/Support/EndianStream.h" 208f19343aSMarek Sokolowski 218f19343aSMarek Sokolowski using namespace llvm::support; 228f19343aSMarek Sokolowski 238f19343aSMarek Sokolowski // Take an expression returning llvm::Error and forward the error if it exists. 248f19343aSMarek Sokolowski #define RETURN_IF_ERROR(Expr) \ 258f19343aSMarek Sokolowski if (auto Err = (Expr)) \ 268f19343aSMarek Sokolowski return Err; 278f19343aSMarek Sokolowski 288f19343aSMarek Sokolowski namespace llvm { 298f19343aSMarek Sokolowski namespace rc { 308f19343aSMarek Sokolowski 3122fccd64SMarek Sokolowski // Class that employs RAII to save the current serializator object state 3222fccd64SMarek Sokolowski // and revert to it as soon as we leave the scope. This is useful if resources 3322fccd64SMarek Sokolowski // declare their own resource-local statements. 3422fccd64SMarek Sokolowski class ContextKeeper { 3522fccd64SMarek Sokolowski ResourceFileWriter *FileWriter; 3622fccd64SMarek Sokolowski ResourceFileWriter::ObjectInfo SavedInfo; 3722fccd64SMarek Sokolowski 3822fccd64SMarek Sokolowski public: 3922fccd64SMarek Sokolowski ContextKeeper(ResourceFileWriter *V) 4022fccd64SMarek Sokolowski : FileWriter(V), SavedInfo(V->ObjectData) {} 4122fccd64SMarek Sokolowski ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; } 4222fccd64SMarek Sokolowski }; 4322fccd64SMarek Sokolowski 448f19343aSMarek Sokolowski static Error createError(Twine Message, 458f19343aSMarek Sokolowski std::errc Type = std::errc::invalid_argument) { 468f19343aSMarek Sokolowski return make_error<StringError>(Message, std::make_error_code(Type)); 478f19343aSMarek Sokolowski } 488f19343aSMarek Sokolowski 498f19343aSMarek Sokolowski static Error checkNumberFits(uint32_t Number, size_t MaxBits, Twine FieldName) { 508f19343aSMarek Sokolowski assert(1 <= MaxBits && MaxBits <= 32); 518f19343aSMarek Sokolowski if (!(Number >> MaxBits)) 528f19343aSMarek Sokolowski return Error::success(); 538f19343aSMarek Sokolowski return createError(FieldName + " (" + Twine(Number) + ") does not fit in " + 548f19343aSMarek Sokolowski Twine(MaxBits) + " bits.", 558f19343aSMarek Sokolowski std::errc::value_too_large); 568f19343aSMarek Sokolowski } 578f19343aSMarek Sokolowski 588f19343aSMarek Sokolowski template <typename FitType> 598f19343aSMarek Sokolowski static Error checkNumberFits(uint32_t Number, Twine FieldName) { 608f19343aSMarek Sokolowski return checkNumberFits(Number, sizeof(FitType) * 8, FieldName); 618f19343aSMarek Sokolowski } 628f19343aSMarek Sokolowski 638f19343aSMarek Sokolowski static Error checkIntOrString(IntOrString Value, Twine FieldName) { 648f19343aSMarek Sokolowski if (!Value.isInt()) 658f19343aSMarek Sokolowski return Error::success(); 668f19343aSMarek Sokolowski return checkNumberFits<uint16_t>(Value.getInt(), FieldName); 678f19343aSMarek Sokolowski } 688f19343aSMarek Sokolowski 698f19343aSMarek Sokolowski static bool stripQuotes(StringRef &Str, bool &IsLongString) { 708f19343aSMarek Sokolowski if (!Str.contains('"')) 718f19343aSMarek Sokolowski return false; 728f19343aSMarek Sokolowski 738f19343aSMarek Sokolowski // Just take the contents of the string, checking if it's been marked long. 748f19343aSMarek Sokolowski IsLongString = Str.startswith_lower("L"); 758f19343aSMarek Sokolowski if (IsLongString) 768f19343aSMarek Sokolowski Str = Str.drop_front(); 778f19343aSMarek Sokolowski 788f19343aSMarek Sokolowski bool StripSuccess = Str.consume_front("\"") && Str.consume_back("\""); 798f19343aSMarek Sokolowski (void)StripSuccess; 808f19343aSMarek Sokolowski assert(StripSuccess && "Strings should be enclosed in quotes."); 818f19343aSMarek Sokolowski return true; 828f19343aSMarek Sokolowski } 838f19343aSMarek Sokolowski 848f19343aSMarek Sokolowski // Describes a way to handle '\0' characters when processing the string. 858f19343aSMarek Sokolowski // rc.exe tool sometimes behaves in a weird way in postprocessing. 868f19343aSMarek Sokolowski // If the string to be output is equivalent to a C-string (e.g. in MENU 878f19343aSMarek Sokolowski // titles), string is (predictably) truncated after first 0-byte. 888f19343aSMarek Sokolowski // When outputting a string table, the behavior is equivalent to appending 898f19343aSMarek Sokolowski // '\0\0' at the end of the string, and then stripping the string 908f19343aSMarek Sokolowski // before the first '\0\0' occurrence. 918f19343aSMarek Sokolowski // Finally, when handling strings in user-defined resources, 0-bytes 928f19343aSMarek Sokolowski // aren't stripped, nor do they terminate the string. 938f19343aSMarek Sokolowski 948f19343aSMarek Sokolowski enum class NullHandlingMethod { 958f19343aSMarek Sokolowski UserResource, // Don't terminate string on '\0'. 968f19343aSMarek Sokolowski CutAtNull, // Terminate string on '\0'. 978f19343aSMarek Sokolowski CutAtDoubleNull // Terminate string on '\0\0'; strip final '\0'. 988f19343aSMarek Sokolowski }; 998f19343aSMarek Sokolowski 1008f19343aSMarek Sokolowski // Parses an identifier or string and returns a processed version of it. 1018f19343aSMarek Sokolowski // For now, it only strips the string boundaries, but TODO: 1028f19343aSMarek Sokolowski // * Squash "" to a single ". 1038f19343aSMarek Sokolowski // * Replace the escape sequences with their processed version. 1048f19343aSMarek Sokolowski // For identifiers, this is no-op. 1058f19343aSMarek Sokolowski static Error processString(StringRef Str, NullHandlingMethod NullHandler, 1068f19343aSMarek Sokolowski bool &IsLongString, SmallVectorImpl<UTF16> &Result) { 1078f19343aSMarek Sokolowski assert(NullHandler == NullHandlingMethod::CutAtNull); 1088f19343aSMarek Sokolowski 1098f19343aSMarek Sokolowski bool IsString = stripQuotes(Str, IsLongString); 1108f19343aSMarek Sokolowski convertUTF8ToUTF16String(Str, Result); 1118f19343aSMarek Sokolowski 1128f19343aSMarek Sokolowski if (!IsString) { 1138f19343aSMarek Sokolowski // It's an identifier if it's not a string. Make all characters uppercase. 1148f19343aSMarek Sokolowski for (UTF16 &Ch : Result) { 1158f19343aSMarek Sokolowski assert(Ch <= 0x7F && "We didn't allow identifiers to be non-ASCII"); 1168f19343aSMarek Sokolowski Ch = toupper(Ch); 1178f19343aSMarek Sokolowski } 1188f19343aSMarek Sokolowski return Error::success(); 1198f19343aSMarek Sokolowski } 1208f19343aSMarek Sokolowski 1218f19343aSMarek Sokolowski // We don't process the string contents. Only cut at '\0'. 1228f19343aSMarek Sokolowski 1238f19343aSMarek Sokolowski for (size_t Pos = 0; Pos < Result.size(); ++Pos) 1248f19343aSMarek Sokolowski if (Result[Pos] == '\0') 1258f19343aSMarek Sokolowski Result.resize(Pos); 1268f19343aSMarek Sokolowski 1278f19343aSMarek Sokolowski return Error::success(); 1288f19343aSMarek Sokolowski } 1298f19343aSMarek Sokolowski 1308f19343aSMarek Sokolowski uint64_t ResourceFileWriter::writeObject(const ArrayRef<uint8_t> Data) { 1318f19343aSMarek Sokolowski uint64_t Result = tell(); 1328f19343aSMarek Sokolowski FS->write((const char *)Data.begin(), Data.size()); 1338f19343aSMarek Sokolowski return Result; 1348f19343aSMarek Sokolowski } 1358f19343aSMarek Sokolowski 1368f19343aSMarek Sokolowski Error ResourceFileWriter::writeCString(StringRef Str, bool WriteTerminator) { 1378f19343aSMarek Sokolowski SmallVector<UTF16, 128> ProcessedString; 1388f19343aSMarek Sokolowski bool IsLongString; 1398f19343aSMarek Sokolowski RETURN_IF_ERROR(processString(Str, NullHandlingMethod::CutAtNull, 1408f19343aSMarek Sokolowski IsLongString, ProcessedString)); 1418f19343aSMarek Sokolowski for (auto Ch : ProcessedString) 1428f19343aSMarek Sokolowski writeInt<uint16_t>(Ch); 1438f19343aSMarek Sokolowski if (WriteTerminator) 1448f19343aSMarek Sokolowski writeInt<uint16_t>(0); 1458f19343aSMarek Sokolowski return Error::success(); 1468f19343aSMarek Sokolowski } 1478f19343aSMarek Sokolowski 1488f19343aSMarek Sokolowski Error ResourceFileWriter::writeIdentifier(const IntOrString &Ident) { 1498f19343aSMarek Sokolowski return writeIntOrString(Ident); 1508f19343aSMarek Sokolowski } 1518f19343aSMarek Sokolowski 1528f19343aSMarek Sokolowski Error ResourceFileWriter::writeIntOrString(const IntOrString &Value) { 1538f19343aSMarek Sokolowski if (!Value.isInt()) 1548f19343aSMarek Sokolowski return writeCString(Value.getString()); 1558f19343aSMarek Sokolowski 1568f19343aSMarek Sokolowski writeInt<uint16_t>(0xFFFF); 1578f19343aSMarek Sokolowski writeInt<uint16_t>(Value.getInt()); 1588f19343aSMarek Sokolowski return Error::success(); 1598f19343aSMarek Sokolowski } 1608f19343aSMarek Sokolowski 1618f19343aSMarek Sokolowski Error ResourceFileWriter::appendFile(StringRef Filename) { 1628f19343aSMarek Sokolowski bool IsLong; 1638f19343aSMarek Sokolowski stripQuotes(Filename, IsLong); 1648f19343aSMarek Sokolowski 1658f19343aSMarek Sokolowski // Filename path should be relative to the current working directory. 1668f19343aSMarek Sokolowski // FIXME: docs say so, but reality is more complicated, script 1678f19343aSMarek Sokolowski // location and include paths must be taken into account. 1688f19343aSMarek Sokolowski ErrorOr<std::unique_ptr<MemoryBuffer>> File = 1698f19343aSMarek Sokolowski MemoryBuffer::getFile(Filename, -1, false); 1708f19343aSMarek Sokolowski if (!File) 1718f19343aSMarek Sokolowski return make_error<StringError>("Error opening file '" + Filename + 1728f19343aSMarek Sokolowski "': " + File.getError().message(), 1738f19343aSMarek Sokolowski File.getError()); 1748f19343aSMarek Sokolowski *FS << (*File)->getBuffer(); 1758f19343aSMarek Sokolowski return Error::success(); 1768f19343aSMarek Sokolowski } 1778f19343aSMarek Sokolowski 1788f19343aSMarek Sokolowski void ResourceFileWriter::padStream(uint64_t Length) { 1798f19343aSMarek Sokolowski assert(Length > 0); 1808f19343aSMarek Sokolowski uint64_t Location = tell(); 1818f19343aSMarek Sokolowski Location %= Length; 1828f19343aSMarek Sokolowski uint64_t Pad = (Length - Location) % Length; 1838f19343aSMarek Sokolowski for (uint64_t i = 0; i < Pad; ++i) 1848f19343aSMarek Sokolowski writeInt<uint8_t>(0); 1858f19343aSMarek Sokolowski } 1868f19343aSMarek Sokolowski 1878f19343aSMarek Sokolowski Error ResourceFileWriter::handleError(Error &&Err, const RCResource *Res) { 1888f19343aSMarek Sokolowski if (Err) 1898f19343aSMarek Sokolowski return joinErrors(createError("Error in " + Res->getResourceTypeName() + 1908f19343aSMarek Sokolowski " statement (ID " + Twine(Res->ResName) + 1918f19343aSMarek Sokolowski "): "), 1928f19343aSMarek Sokolowski std::move(Err)); 1938f19343aSMarek Sokolowski return Error::success(); 1948f19343aSMarek Sokolowski } 1958f19343aSMarek Sokolowski 1968f19343aSMarek Sokolowski Error ResourceFileWriter::visitNullResource(const RCResource *Res) { 1978f19343aSMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeNullBody); 1988f19343aSMarek Sokolowski } 1998f19343aSMarek Sokolowski 20022fccd64SMarek Sokolowski Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) { 20122fccd64SMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody); 20222fccd64SMarek Sokolowski } 20322fccd64SMarek Sokolowski 2048f19343aSMarek Sokolowski Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) { 2058f19343aSMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeHTMLBody); 2068f19343aSMarek Sokolowski } 2078f19343aSMarek Sokolowski 208*42f494d6SMarek Sokolowski Error ResourceFileWriter::visitMenuResource(const RCResource *Res) { 209*42f494d6SMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeMenuBody); 210*42f494d6SMarek Sokolowski } 211*42f494d6SMarek Sokolowski 21222fccd64SMarek Sokolowski Error ResourceFileWriter::visitCharacteristicsStmt( 21322fccd64SMarek Sokolowski const CharacteristicsStmt *Stmt) { 21422fccd64SMarek Sokolowski ObjectData.Characteristics = Stmt->Value; 21522fccd64SMarek Sokolowski return Error::success(); 21622fccd64SMarek Sokolowski } 21722fccd64SMarek Sokolowski 2188f19343aSMarek Sokolowski Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) { 2198f19343aSMarek Sokolowski RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID")); 2208f19343aSMarek Sokolowski RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID")); 2218f19343aSMarek Sokolowski ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10); 2228f19343aSMarek Sokolowski return Error::success(); 2238f19343aSMarek Sokolowski } 2248f19343aSMarek Sokolowski 22522fccd64SMarek Sokolowski Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) { 22622fccd64SMarek Sokolowski ObjectData.VersionInfo = Stmt->Value; 22722fccd64SMarek Sokolowski return Error::success(); 22822fccd64SMarek Sokolowski } 22922fccd64SMarek Sokolowski 2308f19343aSMarek Sokolowski Error ResourceFileWriter::writeResource( 2318f19343aSMarek Sokolowski const RCResource *Res, 2328f19343aSMarek Sokolowski Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) { 2338f19343aSMarek Sokolowski // We don't know the sizes yet. 2348f19343aSMarek Sokolowski object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)}; 2358f19343aSMarek Sokolowski uint64_t HeaderLoc = writeObject(HeaderPrefix); 2368f19343aSMarek Sokolowski 2378f19343aSMarek Sokolowski auto ResType = Res->getResourceType(); 2388f19343aSMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type")); 2398f19343aSMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID")); 2408f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res)); 2418f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res)); 2428f19343aSMarek Sokolowski 24322fccd64SMarek Sokolowski // Apply the resource-local optional statements. 24422fccd64SMarek Sokolowski ContextKeeper RAII(this); 24522fccd64SMarek Sokolowski RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res)); 24622fccd64SMarek Sokolowski 2478f19343aSMarek Sokolowski padStream(sizeof(uint32_t)); 2488f19343aSMarek Sokolowski object::WinResHeaderSuffix HeaderSuffix{ 2498f19343aSMarek Sokolowski ulittle32_t(0), // DataVersion; seems to always be 0 2508f19343aSMarek Sokolowski ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo), 25122fccd64SMarek Sokolowski ulittle32_t(ObjectData.VersionInfo), 25222fccd64SMarek Sokolowski ulittle32_t(ObjectData.Characteristics)}; 2538f19343aSMarek Sokolowski writeObject(HeaderSuffix); 2548f19343aSMarek Sokolowski 2558f19343aSMarek Sokolowski uint64_t DataLoc = tell(); 2568f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res)); 2578f19343aSMarek Sokolowski // RETURN_IF_ERROR(handleError(dumpResource(Ctx))); 2588f19343aSMarek Sokolowski 2598f19343aSMarek Sokolowski // Update the sizes. 2608f19343aSMarek Sokolowski HeaderPrefix.DataSize = tell() - DataLoc; 2618f19343aSMarek Sokolowski HeaderPrefix.HeaderSize = DataLoc - HeaderLoc; 2628f19343aSMarek Sokolowski writeObjectAt(HeaderPrefix, HeaderLoc); 2638f19343aSMarek Sokolowski padStream(sizeof(uint32_t)); 2648f19343aSMarek Sokolowski 2658f19343aSMarek Sokolowski return Error::success(); 2668f19343aSMarek Sokolowski } 2678f19343aSMarek Sokolowski 26822fccd64SMarek Sokolowski // --- NullResource helpers. --- // 26922fccd64SMarek Sokolowski 2708f19343aSMarek Sokolowski Error ResourceFileWriter::writeNullBody(const RCResource *) { 2718f19343aSMarek Sokolowski return Error::success(); 2728f19343aSMarek Sokolowski } 2738f19343aSMarek Sokolowski 27422fccd64SMarek Sokolowski // --- AcceleratorsResource helpers. --- // 27522fccd64SMarek Sokolowski 27622fccd64SMarek Sokolowski Error ResourceFileWriter::writeSingleAccelerator( 27722fccd64SMarek Sokolowski const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) { 27822fccd64SMarek Sokolowski using Accelerator = AcceleratorsResource::Accelerator; 27922fccd64SMarek Sokolowski using Opt = Accelerator::Options; 28022fccd64SMarek Sokolowski 28122fccd64SMarek Sokolowski struct AccelTableEntry { 28222fccd64SMarek Sokolowski ulittle16_t Flags; 28322fccd64SMarek Sokolowski ulittle16_t ANSICode; 28422fccd64SMarek Sokolowski ulittle16_t Id; 28522fccd64SMarek Sokolowski uint16_t Padding; 28622fccd64SMarek Sokolowski } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0}; 28722fccd64SMarek Sokolowski 28822fccd64SMarek Sokolowski bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY; 28922fccd64SMarek Sokolowski 29022fccd64SMarek Sokolowski // Remove ASCII flags (which doesn't occur in .res files). 29122fccd64SMarek Sokolowski Entry.Flags = Obj.Flags & ~Opt::ASCII; 29222fccd64SMarek Sokolowski 29322fccd64SMarek Sokolowski if (IsLastItem) 29422fccd64SMarek Sokolowski Entry.Flags |= 0x80; 29522fccd64SMarek Sokolowski 29622fccd64SMarek Sokolowski RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID")); 29722fccd64SMarek Sokolowski Entry.Id = ulittle16_t(Obj.Id); 29822fccd64SMarek Sokolowski 29922fccd64SMarek Sokolowski auto createAccError = [&Obj](const char *Msg) { 30022fccd64SMarek Sokolowski return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg); 30122fccd64SMarek Sokolowski }; 30222fccd64SMarek Sokolowski 30322fccd64SMarek Sokolowski if (IsASCII && IsVirtKey) 30422fccd64SMarek Sokolowski return createAccError("Accelerator can't be both ASCII and VIRTKEY"); 30522fccd64SMarek Sokolowski 30622fccd64SMarek Sokolowski if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL))) 30722fccd64SMarek Sokolowski return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY" 30822fccd64SMarek Sokolowski " accelerators"); 30922fccd64SMarek Sokolowski 31022fccd64SMarek Sokolowski if (Obj.Event.isInt()) { 31122fccd64SMarek Sokolowski if (!IsASCII && !IsVirtKey) 31222fccd64SMarek Sokolowski return createAccError( 31322fccd64SMarek Sokolowski "Accelerator with a numeric event must be either ASCII" 31422fccd64SMarek Sokolowski " or VIRTKEY"); 31522fccd64SMarek Sokolowski 31622fccd64SMarek Sokolowski uint32_t EventVal = Obj.Event.getInt(); 31722fccd64SMarek Sokolowski RETURN_IF_ERROR( 31822fccd64SMarek Sokolowski checkNumberFits<uint16_t>(EventVal, "Numeric event key ID")); 31922fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(EventVal); 32022fccd64SMarek Sokolowski writeObject(Entry); 32122fccd64SMarek Sokolowski return Error::success(); 32222fccd64SMarek Sokolowski } 32322fccd64SMarek Sokolowski 32422fccd64SMarek Sokolowski StringRef Str = Obj.Event.getString(); 32522fccd64SMarek Sokolowski bool IsWide; 32622fccd64SMarek Sokolowski stripQuotes(Str, IsWide); 32722fccd64SMarek Sokolowski 32822fccd64SMarek Sokolowski if (Str.size() == 0 || Str.size() > 2) 32922fccd64SMarek Sokolowski return createAccError( 33022fccd64SMarek Sokolowski "Accelerator string events should have length 1 or 2"); 33122fccd64SMarek Sokolowski 33222fccd64SMarek Sokolowski if (Str[0] == '^') { 33322fccd64SMarek Sokolowski if (Str.size() == 1) 33422fccd64SMarek Sokolowski return createAccError("No character following '^' in accelerator event"); 33522fccd64SMarek Sokolowski if (IsVirtKey) 33622fccd64SMarek Sokolowski return createAccError( 33722fccd64SMarek Sokolowski "VIRTKEY accelerator events can't be preceded by '^'"); 33822fccd64SMarek Sokolowski 33922fccd64SMarek Sokolowski char Ch = Str[1]; 34022fccd64SMarek Sokolowski if (Ch >= 'a' && Ch <= 'z') 34122fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(Ch - 'a' + 1); 34222fccd64SMarek Sokolowski else if (Ch >= 'A' && Ch <= 'Z') 34322fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(Ch - 'A' + 1); 34422fccd64SMarek Sokolowski else 34522fccd64SMarek Sokolowski return createAccError("Control character accelerator event should be" 34622fccd64SMarek Sokolowski " alphabetic"); 34722fccd64SMarek Sokolowski 34822fccd64SMarek Sokolowski writeObject(Entry); 34922fccd64SMarek Sokolowski return Error::success(); 35022fccd64SMarek Sokolowski } 35122fccd64SMarek Sokolowski 35222fccd64SMarek Sokolowski if (Str.size() == 2) 35322fccd64SMarek Sokolowski return createAccError("Event string should be one-character, possibly" 35422fccd64SMarek Sokolowski " preceded by '^'"); 35522fccd64SMarek Sokolowski 35622fccd64SMarek Sokolowski uint8_t EventCh = Str[0]; 35722fccd64SMarek Sokolowski // The original tool just warns in this situation. We chose to fail. 35822fccd64SMarek Sokolowski if (IsVirtKey && !isalnum(EventCh)) 35922fccd64SMarek Sokolowski return createAccError("Non-alphanumeric characters cannot describe virtual" 36022fccd64SMarek Sokolowski " keys"); 36122fccd64SMarek Sokolowski if (EventCh > 0x7F) 36222fccd64SMarek Sokolowski return createAccError("Non-ASCII description of accelerator"); 36322fccd64SMarek Sokolowski 36422fccd64SMarek Sokolowski if (IsVirtKey) 36522fccd64SMarek Sokolowski EventCh = toupper(EventCh); 36622fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(EventCh); 36722fccd64SMarek Sokolowski writeObject(Entry); 36822fccd64SMarek Sokolowski return Error::success(); 36922fccd64SMarek Sokolowski } 37022fccd64SMarek Sokolowski 37122fccd64SMarek Sokolowski Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) { 37222fccd64SMarek Sokolowski auto *Res = cast<AcceleratorsResource>(Base); 37322fccd64SMarek Sokolowski size_t AcceleratorId = 0; 37422fccd64SMarek Sokolowski for (auto &Acc : Res->Accelerators) { 37522fccd64SMarek Sokolowski ++AcceleratorId; 37622fccd64SMarek Sokolowski RETURN_IF_ERROR( 37722fccd64SMarek Sokolowski writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size())); 37822fccd64SMarek Sokolowski } 37922fccd64SMarek Sokolowski return Error::success(); 38022fccd64SMarek Sokolowski } 38122fccd64SMarek Sokolowski 38222fccd64SMarek Sokolowski // --- HTMLResource helpers. --- // 38322fccd64SMarek Sokolowski 38422fccd64SMarek Sokolowski 3858f19343aSMarek Sokolowski Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) { 3868f19343aSMarek Sokolowski return appendFile(cast<HTMLResource>(Base)->HTMLLoc); 3878f19343aSMarek Sokolowski } 3888f19343aSMarek Sokolowski 389*42f494d6SMarek Sokolowski // --- MenuResource helpers. --- // 390*42f494d6SMarek Sokolowski 391*42f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinition( 392*42f494d6SMarek Sokolowski const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) { 393*42f494d6SMarek Sokolowski assert(Def); 394*42f494d6SMarek Sokolowski const MenuDefinition *DefPtr = Def.get(); 395*42f494d6SMarek Sokolowski 396*42f494d6SMarek Sokolowski if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) { 397*42f494d6SMarek Sokolowski writeInt<uint16_t>(Flags); 398*42f494d6SMarek Sokolowski RETURN_IF_ERROR( 399*42f494d6SMarek Sokolowski checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID")); 400*42f494d6SMarek Sokolowski writeInt<uint16_t>(MenuItemPtr->Id); 401*42f494d6SMarek Sokolowski RETURN_IF_ERROR(writeCString(MenuItemPtr->Name)); 402*42f494d6SMarek Sokolowski return Error::success(); 403*42f494d6SMarek Sokolowski } 404*42f494d6SMarek Sokolowski 405*42f494d6SMarek Sokolowski if (isa<MenuSeparator>(DefPtr)) { 406*42f494d6SMarek Sokolowski writeInt<uint16_t>(Flags); 407*42f494d6SMarek Sokolowski writeInt<uint32_t>(0); 408*42f494d6SMarek Sokolowski return Error::success(); 409*42f494d6SMarek Sokolowski } 410*42f494d6SMarek Sokolowski 411*42f494d6SMarek Sokolowski auto *PopupPtr = cast<PopupItem>(DefPtr); 412*42f494d6SMarek Sokolowski writeInt<uint16_t>(Flags); 413*42f494d6SMarek Sokolowski RETURN_IF_ERROR(writeCString(PopupPtr->Name)); 414*42f494d6SMarek Sokolowski return writeMenuDefinitionList(PopupPtr->SubItems); 415*42f494d6SMarek Sokolowski } 416*42f494d6SMarek Sokolowski 417*42f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuDefinitionList( 418*42f494d6SMarek Sokolowski const MenuDefinitionList &List) { 419*42f494d6SMarek Sokolowski for (auto &Def : List.Definitions) { 420*42f494d6SMarek Sokolowski uint16_t Flags = Def->getResFlags(); 421*42f494d6SMarek Sokolowski // Last element receives an additional 0x80 flag. 422*42f494d6SMarek Sokolowski const uint16_t LastElementFlag = 0x0080; 423*42f494d6SMarek Sokolowski if (&Def == &List.Definitions.back()) 424*42f494d6SMarek Sokolowski Flags |= LastElementFlag; 425*42f494d6SMarek Sokolowski 426*42f494d6SMarek Sokolowski RETURN_IF_ERROR(writeMenuDefinition(Def, Flags)); 427*42f494d6SMarek Sokolowski } 428*42f494d6SMarek Sokolowski return Error::success(); 429*42f494d6SMarek Sokolowski } 430*42f494d6SMarek Sokolowski 431*42f494d6SMarek Sokolowski Error ResourceFileWriter::writeMenuBody(const RCResource *Base) { 432*42f494d6SMarek Sokolowski // At first, MENUHEADER structure. In fact, these are two WORDs equal to 0. 433*42f494d6SMarek Sokolowski // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx 434*42f494d6SMarek Sokolowski writeObject<uint32_t>(0); 435*42f494d6SMarek Sokolowski 436*42f494d6SMarek Sokolowski return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements); 437*42f494d6SMarek Sokolowski } 438*42f494d6SMarek Sokolowski 4398f19343aSMarek Sokolowski } // namespace rc 4408f19343aSMarek Sokolowski } // namespace llvm 441