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 31*22fccd64SMarek Sokolowski // Class that employs RAII to save the current serializator object state 32*22fccd64SMarek Sokolowski // and revert to it as soon as we leave the scope. This is useful if resources 33*22fccd64SMarek Sokolowski // declare their own resource-local statements. 34*22fccd64SMarek Sokolowski class ContextKeeper { 35*22fccd64SMarek Sokolowski ResourceFileWriter *FileWriter; 36*22fccd64SMarek Sokolowski ResourceFileWriter::ObjectInfo SavedInfo; 37*22fccd64SMarek Sokolowski 38*22fccd64SMarek Sokolowski public: 39*22fccd64SMarek Sokolowski ContextKeeper(ResourceFileWriter *V) 40*22fccd64SMarek Sokolowski : FileWriter(V), SavedInfo(V->ObjectData) {} 41*22fccd64SMarek Sokolowski ~ContextKeeper() { FileWriter->ObjectData = SavedInfo; } 42*22fccd64SMarek Sokolowski }; 43*22fccd64SMarek 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 200*22fccd64SMarek Sokolowski Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) { 201*22fccd64SMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody); 202*22fccd64SMarek Sokolowski } 203*22fccd64SMarek Sokolowski 2048f19343aSMarek Sokolowski Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) { 2058f19343aSMarek Sokolowski return writeResource(Res, &ResourceFileWriter::writeHTMLBody); 2068f19343aSMarek Sokolowski } 2078f19343aSMarek Sokolowski 208*22fccd64SMarek Sokolowski Error ResourceFileWriter::visitCharacteristicsStmt( 209*22fccd64SMarek Sokolowski const CharacteristicsStmt *Stmt) { 210*22fccd64SMarek Sokolowski ObjectData.Characteristics = Stmt->Value; 211*22fccd64SMarek Sokolowski return Error::success(); 212*22fccd64SMarek Sokolowski } 213*22fccd64SMarek Sokolowski 2148f19343aSMarek Sokolowski Error ResourceFileWriter::visitLanguageStmt(const LanguageResource *Stmt) { 2158f19343aSMarek Sokolowski RETURN_IF_ERROR(checkNumberFits(Stmt->Lang, 10, "Primary language ID")); 2168f19343aSMarek Sokolowski RETURN_IF_ERROR(checkNumberFits(Stmt->SubLang, 6, "Sublanguage ID")); 2178f19343aSMarek Sokolowski ObjectData.LanguageInfo = Stmt->Lang | (Stmt->SubLang << 10); 2188f19343aSMarek Sokolowski return Error::success(); 2198f19343aSMarek Sokolowski } 2208f19343aSMarek Sokolowski 221*22fccd64SMarek Sokolowski Error ResourceFileWriter::visitVersionStmt(const VersionStmt *Stmt) { 222*22fccd64SMarek Sokolowski ObjectData.VersionInfo = Stmt->Value; 223*22fccd64SMarek Sokolowski return Error::success(); 224*22fccd64SMarek Sokolowski } 225*22fccd64SMarek Sokolowski 2268f19343aSMarek Sokolowski Error ResourceFileWriter::writeResource( 2278f19343aSMarek Sokolowski const RCResource *Res, 2288f19343aSMarek Sokolowski Error (ResourceFileWriter::*BodyWriter)(const RCResource *)) { 2298f19343aSMarek Sokolowski // We don't know the sizes yet. 2308f19343aSMarek Sokolowski object::WinResHeaderPrefix HeaderPrefix{ulittle32_t(0U), ulittle32_t(0U)}; 2318f19343aSMarek Sokolowski uint64_t HeaderLoc = writeObject(HeaderPrefix); 2328f19343aSMarek Sokolowski 2338f19343aSMarek Sokolowski auto ResType = Res->getResourceType(); 2348f19343aSMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(ResType, "Resource type")); 2358f19343aSMarek Sokolowski RETURN_IF_ERROR(checkIntOrString(Res->ResName, "Resource ID")); 2368f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError(writeIdentifier(ResType), Res)); 2378f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError(writeIdentifier(Res->ResName), Res)); 2388f19343aSMarek Sokolowski 239*22fccd64SMarek Sokolowski // Apply the resource-local optional statements. 240*22fccd64SMarek Sokolowski ContextKeeper RAII(this); 241*22fccd64SMarek Sokolowski RETURN_IF_ERROR(handleError(Res->applyStmts(this), Res)); 242*22fccd64SMarek Sokolowski 2438f19343aSMarek Sokolowski padStream(sizeof(uint32_t)); 2448f19343aSMarek Sokolowski object::WinResHeaderSuffix HeaderSuffix{ 2458f19343aSMarek Sokolowski ulittle32_t(0), // DataVersion; seems to always be 0 2468f19343aSMarek Sokolowski ulittle16_t(Res->getMemoryFlags()), ulittle16_t(ObjectData.LanguageInfo), 247*22fccd64SMarek Sokolowski ulittle32_t(ObjectData.VersionInfo), 248*22fccd64SMarek Sokolowski ulittle32_t(ObjectData.Characteristics)}; 2498f19343aSMarek Sokolowski writeObject(HeaderSuffix); 2508f19343aSMarek Sokolowski 2518f19343aSMarek Sokolowski uint64_t DataLoc = tell(); 2528f19343aSMarek Sokolowski RETURN_IF_ERROR(handleError((this->*BodyWriter)(Res), Res)); 2538f19343aSMarek Sokolowski // RETURN_IF_ERROR(handleError(dumpResource(Ctx))); 2548f19343aSMarek Sokolowski 2558f19343aSMarek Sokolowski // Update the sizes. 2568f19343aSMarek Sokolowski HeaderPrefix.DataSize = tell() - DataLoc; 2578f19343aSMarek Sokolowski HeaderPrefix.HeaderSize = DataLoc - HeaderLoc; 2588f19343aSMarek Sokolowski writeObjectAt(HeaderPrefix, HeaderLoc); 2598f19343aSMarek Sokolowski padStream(sizeof(uint32_t)); 2608f19343aSMarek Sokolowski 2618f19343aSMarek Sokolowski return Error::success(); 2628f19343aSMarek Sokolowski } 2638f19343aSMarek Sokolowski 264*22fccd64SMarek Sokolowski // --- NullResource helpers. --- // 265*22fccd64SMarek Sokolowski 2668f19343aSMarek Sokolowski Error ResourceFileWriter::writeNullBody(const RCResource *) { 2678f19343aSMarek Sokolowski return Error::success(); 2688f19343aSMarek Sokolowski } 2698f19343aSMarek Sokolowski 270*22fccd64SMarek Sokolowski // --- AcceleratorsResource helpers. --- // 271*22fccd64SMarek Sokolowski 272*22fccd64SMarek Sokolowski Error ResourceFileWriter::writeSingleAccelerator( 273*22fccd64SMarek Sokolowski const AcceleratorsResource::Accelerator &Obj, bool IsLastItem) { 274*22fccd64SMarek Sokolowski using Accelerator = AcceleratorsResource::Accelerator; 275*22fccd64SMarek Sokolowski using Opt = Accelerator::Options; 276*22fccd64SMarek Sokolowski 277*22fccd64SMarek Sokolowski struct AccelTableEntry { 278*22fccd64SMarek Sokolowski ulittle16_t Flags; 279*22fccd64SMarek Sokolowski ulittle16_t ANSICode; 280*22fccd64SMarek Sokolowski ulittle16_t Id; 281*22fccd64SMarek Sokolowski uint16_t Padding; 282*22fccd64SMarek Sokolowski } Entry{ulittle16_t(0), ulittle16_t(0), ulittle16_t(0), 0}; 283*22fccd64SMarek Sokolowski 284*22fccd64SMarek Sokolowski bool IsASCII = Obj.Flags & Opt::ASCII, IsVirtKey = Obj.Flags & Opt::VIRTKEY; 285*22fccd64SMarek Sokolowski 286*22fccd64SMarek Sokolowski // Remove ASCII flags (which doesn't occur in .res files). 287*22fccd64SMarek Sokolowski Entry.Flags = Obj.Flags & ~Opt::ASCII; 288*22fccd64SMarek Sokolowski 289*22fccd64SMarek Sokolowski if (IsLastItem) 290*22fccd64SMarek Sokolowski Entry.Flags |= 0x80; 291*22fccd64SMarek Sokolowski 292*22fccd64SMarek Sokolowski RETURN_IF_ERROR(checkNumberFits<uint16_t>(Obj.Id, "ACCELERATORS entry ID")); 293*22fccd64SMarek Sokolowski Entry.Id = ulittle16_t(Obj.Id); 294*22fccd64SMarek Sokolowski 295*22fccd64SMarek Sokolowski auto createAccError = [&Obj](const char *Msg) { 296*22fccd64SMarek Sokolowski return createError("Accelerator ID " + Twine(Obj.Id) + ": " + Msg); 297*22fccd64SMarek Sokolowski }; 298*22fccd64SMarek Sokolowski 299*22fccd64SMarek Sokolowski if (IsASCII && IsVirtKey) 300*22fccd64SMarek Sokolowski return createAccError("Accelerator can't be both ASCII and VIRTKEY"); 301*22fccd64SMarek Sokolowski 302*22fccd64SMarek Sokolowski if (!IsVirtKey && (Obj.Flags & (Opt::ALT | Opt::SHIFT | Opt::CONTROL))) 303*22fccd64SMarek Sokolowski return createAccError("Can only apply ALT, SHIFT or CONTROL to VIRTKEY" 304*22fccd64SMarek Sokolowski " accelerators"); 305*22fccd64SMarek Sokolowski 306*22fccd64SMarek Sokolowski if (Obj.Event.isInt()) { 307*22fccd64SMarek Sokolowski if (!IsASCII && !IsVirtKey) 308*22fccd64SMarek Sokolowski return createAccError( 309*22fccd64SMarek Sokolowski "Accelerator with a numeric event must be either ASCII" 310*22fccd64SMarek Sokolowski " or VIRTKEY"); 311*22fccd64SMarek Sokolowski 312*22fccd64SMarek Sokolowski uint32_t EventVal = Obj.Event.getInt(); 313*22fccd64SMarek Sokolowski RETURN_IF_ERROR( 314*22fccd64SMarek Sokolowski checkNumberFits<uint16_t>(EventVal, "Numeric event key ID")); 315*22fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(EventVal); 316*22fccd64SMarek Sokolowski writeObject(Entry); 317*22fccd64SMarek Sokolowski return Error::success(); 318*22fccd64SMarek Sokolowski } 319*22fccd64SMarek Sokolowski 320*22fccd64SMarek Sokolowski StringRef Str = Obj.Event.getString(); 321*22fccd64SMarek Sokolowski bool IsWide; 322*22fccd64SMarek Sokolowski stripQuotes(Str, IsWide); 323*22fccd64SMarek Sokolowski 324*22fccd64SMarek Sokolowski if (Str.size() == 0 || Str.size() > 2) 325*22fccd64SMarek Sokolowski return createAccError( 326*22fccd64SMarek Sokolowski "Accelerator string events should have length 1 or 2"); 327*22fccd64SMarek Sokolowski 328*22fccd64SMarek Sokolowski if (Str[0] == '^') { 329*22fccd64SMarek Sokolowski if (Str.size() == 1) 330*22fccd64SMarek Sokolowski return createAccError("No character following '^' in accelerator event"); 331*22fccd64SMarek Sokolowski if (IsVirtKey) 332*22fccd64SMarek Sokolowski return createAccError( 333*22fccd64SMarek Sokolowski "VIRTKEY accelerator events can't be preceded by '^'"); 334*22fccd64SMarek Sokolowski 335*22fccd64SMarek Sokolowski char Ch = Str[1]; 336*22fccd64SMarek Sokolowski if (Ch >= 'a' && Ch <= 'z') 337*22fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(Ch - 'a' + 1); 338*22fccd64SMarek Sokolowski else if (Ch >= 'A' && Ch <= 'Z') 339*22fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(Ch - 'A' + 1); 340*22fccd64SMarek Sokolowski else 341*22fccd64SMarek Sokolowski return createAccError("Control character accelerator event should be" 342*22fccd64SMarek Sokolowski " alphabetic"); 343*22fccd64SMarek Sokolowski 344*22fccd64SMarek Sokolowski writeObject(Entry); 345*22fccd64SMarek Sokolowski return Error::success(); 346*22fccd64SMarek Sokolowski } 347*22fccd64SMarek Sokolowski 348*22fccd64SMarek Sokolowski if (Str.size() == 2) 349*22fccd64SMarek Sokolowski return createAccError("Event string should be one-character, possibly" 350*22fccd64SMarek Sokolowski " preceded by '^'"); 351*22fccd64SMarek Sokolowski 352*22fccd64SMarek Sokolowski uint8_t EventCh = Str[0]; 353*22fccd64SMarek Sokolowski // The original tool just warns in this situation. We chose to fail. 354*22fccd64SMarek Sokolowski if (IsVirtKey && !isalnum(EventCh)) 355*22fccd64SMarek Sokolowski return createAccError("Non-alphanumeric characters cannot describe virtual" 356*22fccd64SMarek Sokolowski " keys"); 357*22fccd64SMarek Sokolowski if (EventCh > 0x7F) 358*22fccd64SMarek Sokolowski return createAccError("Non-ASCII description of accelerator"); 359*22fccd64SMarek Sokolowski 360*22fccd64SMarek Sokolowski if (IsVirtKey) 361*22fccd64SMarek Sokolowski EventCh = toupper(EventCh); 362*22fccd64SMarek Sokolowski Entry.ANSICode = ulittle16_t(EventCh); 363*22fccd64SMarek Sokolowski writeObject(Entry); 364*22fccd64SMarek Sokolowski return Error::success(); 365*22fccd64SMarek Sokolowski } 366*22fccd64SMarek Sokolowski 367*22fccd64SMarek Sokolowski Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) { 368*22fccd64SMarek Sokolowski auto *Res = cast<AcceleratorsResource>(Base); 369*22fccd64SMarek Sokolowski size_t AcceleratorId = 0; 370*22fccd64SMarek Sokolowski for (auto &Acc : Res->Accelerators) { 371*22fccd64SMarek Sokolowski ++AcceleratorId; 372*22fccd64SMarek Sokolowski RETURN_IF_ERROR( 373*22fccd64SMarek Sokolowski writeSingleAccelerator(Acc, AcceleratorId == Res->Accelerators.size())); 374*22fccd64SMarek Sokolowski } 375*22fccd64SMarek Sokolowski return Error::success(); 376*22fccd64SMarek Sokolowski } 377*22fccd64SMarek Sokolowski 378*22fccd64SMarek Sokolowski // --- HTMLResource helpers. --- // 379*22fccd64SMarek Sokolowski 380*22fccd64SMarek Sokolowski 3818f19343aSMarek Sokolowski Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) { 3828f19343aSMarek Sokolowski return appendFile(cast<HTMLResource>(Base)->HTMLLoc); 3838f19343aSMarek Sokolowski } 3848f19343aSMarek Sokolowski 3858f19343aSMarek Sokolowski } // namespace rc 3868f19343aSMarek Sokolowski } // namespace llvm 387