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