diff options
author | Zachary Turner <zturner@google.com> | 2017-10-06 21:30:55 +0000 |
---|---|---|
committer | Zachary Turner <zturner@google.com> | 2017-10-06 21:30:55 +0000 |
commit | 84ad96bc3f02e6389bde1d35ab219819ab034f02 (patch) | |
tree | 7169a63950c26c0313b70bf8dab46751e80173c4 /tools/llvm-rc | |
parent | 93bb30da3fff23193de80f377c0822ec160a3a2a (diff) |
[llvm-rc] Serialize STRINGTABLE statements to .res file.
This allows llvm-rc to serialize STRINGTABLE resources.
These are output in an unusual way: we locate them at the end of the
file, and strings are merged into bundles of max 16 strings, depending
on their IDs, language, and characteristics.
Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050.aspx
Patch by: Marek Sokolowski
Differential Revision: https://reviews.llvm.org/D38420
git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@315112 91177308-0d34-0410-b5e6-96231b3b80d8
Diffstat (limited to 'tools/llvm-rc')
-rw-r--r-- | tools/llvm-rc/ResourceFileWriter.cpp | 123 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceFileWriter.h | 28 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceScriptStmt.h | 10 | ||||
-rw-r--r-- | tools/llvm-rc/ResourceVisitor.h | 1 | ||||
-rw-r--r-- | tools/llvm-rc/llvm-rc.cpp | 5 |
5 files changed, 158 insertions, 9 deletions
diff --git a/tools/llvm-rc/ResourceFileWriter.cpp b/tools/llvm-rc/ResourceFileWriter.cpp index e6480f126ef..f41ffcc8ac5 100644 --- a/tools/llvm-rc/ResourceFileWriter.cpp +++ b/tools/llvm-rc/ResourceFileWriter.cpp @@ -129,8 +129,6 @@ enum class NullHandlingMethod { // For identifiers, this is no-op. static Error processString(StringRef Str, NullHandlingMethod NullHandler, bool &IsLongString, SmallVectorImpl<UTF16> &Result) { - assert(NullHandler == NullHandlingMethod::CutAtNull); - bool IsString = stripQuotes(Str, IsLongString); convertUTF8ToUTF16String(Str, Result); @@ -143,11 +141,24 @@ static Error processString(StringRef Str, NullHandlingMethod NullHandler, return Error::success(); } - // We don't process the string contents. Only cut at '\0'. - - for (size_t Pos = 0; Pos < Result.size(); ++Pos) - if (Result[Pos] == '\0') - Result.resize(Pos); + switch (NullHandler) { + case NullHandlingMethod::CutAtNull: + for (size_t Pos = 0; Pos < Result.size(); ++Pos) + if (Result[Pos] == '\0') + Result.resize(Pos); + break; + + case NullHandlingMethod::CutAtDoubleNull: + for (size_t Pos = 0; Pos + 1 < Result.size(); ++Pos) + if (Result[Pos] == '\0' && Result[Pos + 1] == '\0') + Result.resize(Pos); + if (Result.size() > 0 && Result.back() == '\0') + Result.pop_back(); + break; + + case NullHandlingMethod::UserResource: + break; + } return Error::success(); } @@ -258,6 +269,35 @@ Error ResourceFileWriter::visitMenuResource(const RCResource *Res) { return writeResource(Res, &ResourceFileWriter::writeMenuBody); } +Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) { + const auto *Res = cast<StringTableResource>(Base); + + ContextKeeper RAII(this); + RETURN_IF_ERROR(Res->applyStmts(this)); + + for (auto &String : Res->Table) { + RETURN_IF_ERROR(checkNumberFits<uint16_t>(String.first, "String ID")); + uint16_t BundleID = String.first >> 4; + StringTableInfo::BundleKey Key(BundleID, ObjectData.LanguageInfo); + auto &BundleData = StringTableData.BundleData; + auto Iter = BundleData.find(Key); + + if (Iter == BundleData.end()) { + // Need to create a bundle. + StringTableData.BundleList.push_back(Key); + auto EmplaceResult = + BundleData.emplace(Key, StringTableInfo::Bundle(ObjectData)); + assert(EmplaceResult.second && "Could not create a bundle"); + Iter = EmplaceResult.first; + } + + RETURN_IF_ERROR( + insertStringIntoBundle(Iter->second, String.first, String.second)); + } + + return Error::success(); +} + Error ResourceFileWriter::visitVersionInfoResource(const RCResource *Res) { return writeResource(Res, &ResourceFileWriter::writeVersionInfoBody); } @@ -939,6 +979,75 @@ Error ResourceFileWriter::writeMenuBody(const RCResource *Base) { return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements); } +// --- StringTableResource helpers. --- // + +class BundleResource : public RCResource { +public: + using BundleType = ResourceFileWriter::StringTableInfo::Bundle; + BundleType Bundle; + + BundleResource(const BundleType &StrBundle) : Bundle(StrBundle) {} + IntOrString getResourceType() const override { return 6; } + + ResourceKind getKind() const override { return RkStringTableBundle; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkStringTableBundle; + } +}; + +Error ResourceFileWriter::visitStringTableBundle(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeStringTableBundleBody); +} + +Error ResourceFileWriter::insertStringIntoBundle( + StringTableInfo::Bundle &Bundle, uint16_t StringID, StringRef String) { + uint16_t StringLoc = StringID & 15; + if (Bundle.Data[StringLoc]) + return createError("Multiple STRINGTABLE strings located under ID " + + Twine(StringID)); + Bundle.Data[StringLoc] = String; + return Error::success(); +} + +Error ResourceFileWriter::writeStringTableBundleBody(const RCResource *Base) { + auto *Res = cast<BundleResource>(Base); + for (size_t ID = 0; ID < Res->Bundle.Data.size(); ++ID) { + // The string format is a tiny bit different here. We + // first output the size of the string, and then the string itself + // (which is not null-terminated). + bool IsLongString; + SmallVector<UTF16, 128> Data; + RETURN_IF_ERROR(processString(Res->Bundle.Data[ID].getValueOr(StringRef()), + NullHandlingMethod::CutAtDoubleNull, + IsLongString, Data)); + if (AppendNull && Res->Bundle.Data[ID]) + Data.push_back('\0'); + RETURN_IF_ERROR( + checkNumberFits<uint16_t>(Data.size(), "STRINGTABLE string size")); + writeObject(ulittle16_t(Data.size())); + for (auto Char : Data) + writeObject(ulittle16_t(Char)); + } + return Error::success(); +} + +Error ResourceFileWriter::dumpAllStringTables() { + for (auto Key : StringTableData.BundleList) { + auto Iter = StringTableData.BundleData.find(Key); + assert(Iter != StringTableData.BundleData.end()); + + // For a moment, revert the context info to moment of bundle declaration. + ContextKeeper RAII(this); + ObjectData = Iter->second.DeclTimeInfo; + + BundleResource Res(Iter->second); + // Bundle #(k+1) contains keys [16k, 16k + 15]. + Res.setName(Key.first + 1); + RETURN_IF_ERROR(visitStringTableBundle(&Res)); + } + return Error::success(); +} + // --- VersionInfoResourceResource helpers. --- // Error ResourceFileWriter::writeVersionInfoBlock(const VersionInfoBlock &Blk) { diff --git a/tools/llvm-rc/ResourceFileWriter.h b/tools/llvm-rc/ResourceFileWriter.h index 0d248c0349e..905f56f5381 100644 --- a/tools/llvm-rc/ResourceFileWriter.h +++ b/tools/llvm-rc/ResourceFileWriter.h @@ -37,6 +37,7 @@ public: Error visitIconResource(const RCResource *) override; Error visitMenuResource(const RCResource *) override; Error visitVersionInfoResource(const RCResource *) override; + Error visitStringTableResource(const RCResource *) override; Error visitCaptionStmt(const CaptionStmt *) override; Error visitCharacteristicsStmt(const CharacteristicsStmt *) override; @@ -45,6 +46,12 @@ public: Error visitStyleStmt(const StyleStmt *) override; Error visitVersionStmt(const VersionStmt *) override; + // Stringtables are output at the end of .res file. We need a separate + // function to do it. + Error dumpAllStringTables(); + + bool AppendNull; // Append '\0' to each existing STRINGTABLE element? + struct ObjectInfo { uint16_t LanguageInfo; uint32_t Characteristics; @@ -64,6 +71,21 @@ public: ObjectInfo() : LanguageInfo(0), Characteristics(0), VersionInfo(0) {} } ObjectData; + struct StringTableInfo { + // Each STRINGTABLE bundle depends on ID of the bundle and language + // description. + using BundleKey = std::pair<uint16_t, uint16_t>; + // Each bundle is in fact an array of 16 strings. + struct Bundle { + std::array<Optional<StringRef>, 16> Data; + ObjectInfo DeclTimeInfo; + Bundle(const ObjectInfo &Info) : DeclTimeInfo(Info) {} + }; + std::map<BundleKey, Bundle> BundleData; + // Bundles are listed in the order of their first occurence. + std::vector<BundleKey> BundleList; + } StringTableData; + private: Error handleError(Error &&Err, const RCResource *Res); @@ -99,6 +121,12 @@ private: Error writeMenuDefinitionList(const MenuDefinitionList &List); Error writeMenuBody(const RCResource *); + // StringTableResource + Error visitStringTableBundle(const RCResource *); + Error writeStringTableBundleBody(const RCResource *); + Error insertStringIntoBundle(StringTableInfo::Bundle &Bundle, + uint16_t StringID, StringRef String); + // VersionInfoResource Error writeVersionInfoBody(const RCResource *); Error writeVersionInfoBlock(const VersionInfoBlock &); diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h index 2457d831b1e..915d4f108b5 100644 --- a/tools/llvm-rc/ResourceScriptStmt.h +++ b/tools/llvm-rc/ResourceScriptStmt.h @@ -124,6 +124,7 @@ enum ResourceKind { RkSingleIcon = 3, RkMenu = 4, RkDialog = 5, + RkStringTableBundle = 6, RkAccelerators = 9, RkCursorGroup = 12, RkIconGroup = 14, @@ -138,9 +139,10 @@ enum ResourceKind { RkBase, RkCursor, RkIcon, + RkStringTable, RkUser, RkSingleCursorOrIconRes, - RkCursorOrIconGroupRes + RkCursorOrIconGroupRes, }; // Non-zero memory flags. @@ -488,14 +490,18 @@ public: // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381050(v=vs.85).aspx class StringTableResource : public OptStatementsRCResource { +public: std::vector<std::pair<uint32_t, StringRef>> Table; -public: using OptStatementsRCResource::OptStatementsRCResource; void addString(uint32_t ID, StringRef String) { Table.emplace_back(ID, String); } raw_ostream &log(raw_ostream &) const override; + Twine getResourceTypeName() const override { return "STRINGTABLE"; } + Error visit(Visitor *V) const override { + return V->visitStringTableResource(this); + } }; // -- DIALOG(EX) resource and its helper classes -- diff --git a/tools/llvm-rc/ResourceVisitor.h b/tools/llvm-rc/ResourceVisitor.h index c0d00287e8a..328200bc00f 100644 --- a/tools/llvm-rc/ResourceVisitor.h +++ b/tools/llvm-rc/ResourceVisitor.h @@ -37,6 +37,7 @@ public: virtual Error visitHTMLResource(const RCResource *) = 0; virtual Error visitIconResource(const RCResource *) = 0; virtual Error visitMenuResource(const RCResource *) = 0; + virtual Error visitStringTableResource(const RCResource *) = 0; virtual Error visitVersionInfoResource(const RCResource *) = 0; virtual Error visitCaptionStmt(const CaptionStmt *) = 0; diff --git a/tools/llvm-rc/llvm-rc.cpp b/tools/llvm-rc/llvm-rc.cpp index c62a037874b..2a4faeb2d2e 100644 --- a/tools/llvm-rc/llvm-rc.cpp +++ b/tools/llvm-rc/llvm-rc.cpp @@ -154,6 +154,7 @@ int main(int argc_, const char *argv_[]) { fatalError("Error opening output file '" + OutArgsInfo[0] + "': " + EC.message()); Visitor = llvm::make_unique<ResourceFileWriter>(std::move(FOut)); + Visitor->AppendNull = InputArgs.hasArg(OPT_ADD_NULL); ExitOnErr(NullResource().visit(Visitor.get())); @@ -170,5 +171,9 @@ int main(int argc_, const char *argv_[]) { ExitOnErr(Resource->visit(Visitor.get())); } + // STRINGTABLE resources come at the very end. + if (!IsDryRun) + ExitOnErr(Visitor->dumpAllStringTables()); + return 0; } |