forked from OSchip/llvm-project
[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 llvm-svn: 315112
This commit is contained in:
parent
ebaf772204
commit
da366693bf
|
@ -0,0 +1,45 @@
|
|||
STRINGTABLE
|
||||
VERSION 32
|
||||
CHARACTERISTICS 0x32 {
|
||||
0 "a"
|
||||
}
|
||||
|
||||
STRINGTABLE {
|
||||
1 "b"
|
||||
16 "bb"
|
||||
}
|
||||
|
||||
STRINGTABLE
|
||||
VERSION 100
|
||||
LANGUAGE 4, 7 {
|
||||
16 "hello"
|
||||
17 "world"
|
||||
}
|
||||
|
||||
STRINGTABLE
|
||||
VERSION 50
|
||||
CHARACTERISTICS 0x32 {
|
||||
17 "cc"
|
||||
32 "ccc"
|
||||
2 "c"
|
||||
}
|
||||
|
||||
STRINGTABLE {
|
||||
3 "d"
|
||||
4 ""
|
||||
8 ""
|
||||
}
|
||||
|
||||
STRINGTABLE
|
||||
VERSION 101
|
||||
LANGUAGE 4, 7 {
|
||||
-1 & 65535 "minus one"
|
||||
}
|
||||
|
||||
STRINGTABLE
|
||||
CHARACTERISTICS 10
|
||||
LANGUAGE 4, 7 {
|
||||
23 "something else"
|
||||
65529 "large number"
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
STRINGTABLE {
|
||||
1 "Hello"
|
||||
2 "World"
|
||||
1 "Repeat"
|
||||
}
|
|
@ -0,0 +1,170 @@
|
|||
; RUN: llvm-rc /FO %t %p/Inputs/tag-stringtable-basic.rc
|
||||
; RUN: llvm-readobj %t | FileCheck %s
|
||||
|
||||
; CHECK: Resource type (int): 6
|
||||
; CHECK-NEXT: Resource name (int): 1
|
||||
; CHECK-NEXT: Data version: 0
|
||||
; CHECK-NEXT: Memory flags: 0x1030
|
||||
; CHECK-NEXT: Language ID: 1033
|
||||
; CHECK-NEXT: Version (major): 0
|
||||
; CHECK-NEXT: Version (minor): 32
|
||||
; CHECK-NEXT: Characteristics: 50
|
||||
; CHECK-NEXT: Data size: 40
|
||||
; CHECK-NEXT: Data: (
|
||||
; CHECK-NEXT: 0000: 01006100 01006200 01006300 01006400 |..a...b...c...d.|
|
||||
; CHECK-NEXT: 0010: 00000000 00000000 00000000 00000000 |................|
|
||||
; CHECK-NEXT: 0020: 00000000 00000000 |........|
|
||||
; CHECK-NEXT: )
|
||||
|
||||
; CHECK-DAG: Resource type (int): 6
|
||||
; CHECK-NEXT: Resource name (int): 2
|
||||
; CHECK-NEXT: Data version: 0
|
||||
; CHECK-NEXT: Memory flags: 0x1030
|
||||
; CHECK-NEXT: Language ID: 1033
|
||||
; CHECK-NEXT: Version (major): 0
|
||||
; CHECK-NEXT: Version (minor): 0
|
||||
; CHECK-NEXT: Characteristics: 0
|
||||
; CHECK-NEXT: Data size: 40
|
||||
; CHECK-NEXT: Data: (
|
||||
; CHECK-NEXT: 0000: 02006200 62000200 63006300 00000000 |..b.b...c.c.....|
|
||||
; CHECK-NEXT: 0010: 00000000 00000000 00000000 00000000 |................|
|
||||
; CHECK-NEXT: 0020: 00000000 00000000 |........|
|
||||
; CHECK-NEXT: )
|
||||
|
||||
; CHECK-DAG: Resource type (int): 6
|
||||
; CHECK-NEXT: Resource name (int): 2
|
||||
; CHECK-NEXT: Data version: 0
|
||||
; CHECK-NEXT: Memory flags: 0x1030
|
||||
; CHECK-NEXT: Language ID: 7172
|
||||
; CHECK-NEXT: Version (major): 0
|
||||
; CHECK-NEXT: Version (minor): 100
|
||||
; CHECK-NEXT: Characteristics: 0
|
||||
; CHECK-NEXT: Data size: 80
|
||||
; CHECK-NEXT: Data: (
|
||||
; CHECK-NEXT: 0000: 05006800 65006C00 6C006F00 05007700 |..h.e.l.l.o...w.|
|
||||
; CHECK-NEXT: 0010: 6F007200 6C006400 00000000 00000000 |o.r.l.d.........|
|
||||
; CHECK-NEXT: 0020: 00000E00 73006F00 6D006500 74006800 |....s.o.m.e.t.h.|
|
||||
; CHECK-NEXT: 0030: 69006E00 67002000 65006C00 73006500 |i.n.g. .e.l.s.e.|
|
||||
; CHECK-NEXT: 0040: 00000000 00000000 00000000 00000000 |................|
|
||||
; CHECK-NEXT: )
|
||||
|
||||
; CHECK-DAG: Resource type (int): 6
|
||||
; CHECK-NEXT: Resource name (int): 3
|
||||
; CHECK-NEXT: Data version: 0
|
||||
; CHECK-NEXT: Memory flags: 0x1030
|
||||
; CHECK-NEXT: Language ID: 1033
|
||||
; CHECK-NEXT: Version (major): 0
|
||||
; CHECK-NEXT: Version (minor): 50
|
||||
; CHECK-NEXT: Characteristics: 50
|
||||
; CHECK-NEXT: Data size: 38
|
||||
; CHECK-NEXT: Data: (
|
||||
; CHECK-NEXT: 0000: 03006300 63006300 00000000 00000000 |..c.c.c.........|
|
||||
; CHECK-NEXT: 0010: 00000000 00000000 00000000 00000000 |................|
|
||||
; CHECK-NEXT: 0020: 00000000 0000 |......|
|
||||
; CHECK-NEXT: )
|
||||
|
||||
; CHECK-DAG: Resource type (int): 6
|
||||
; CHECK-NEXT: Resource name (int): 4096
|
||||
; CHECK-NEXT: Data version: 0
|
||||
; CHECK-NEXT: Memory flags: 0x1030
|
||||
; CHECK-NEXT: Language ID: 7172
|
||||
; CHECK-NEXT: Version (major): 0
|
||||
; CHECK-NEXT: Version (minor): 101
|
||||
; CHECK-NEXT: Characteristics: 0
|
||||
; CHECK-NEXT: Data size: 74
|
||||
; CHECK-NEXT: Data: (
|
||||
; CHECK-NEXT: 0000: 00000000 00000000 00000000 00000000 |................|
|
||||
; CHECK-NEXT: 0010: 00000C00 6C006100 72006700 65002000 |....l.a.r.g.e. .|
|
||||
; CHECK-NEXT: 0020: 6E007500 6D006200 65007200 00000000 |n.u.m.b.e.r.....|
|
||||
; CHECK-NEXT: 0030: 00000000 00000900 6D006900 6E007500 |........m.i.n.u.|
|
||||
; CHECK-NEXT: 0040: 73002000 6F006E00 6500 |s. .o.n.e.|
|
||||
; CHECK-NEXT: )
|
||||
|
||||
|
||||
; RUN: llvm-rc /N /FO %t0 %p/Inputs/tag-stringtable-basic.rc
|
||||
; RUN: llvm-readobj %t0 | FileCheck %s --check-prefix=NULL
|
||||
|
||||
; NULL: Resource type (int): 6
|
||||
; NULL-NEXT: Resource name (int): 1
|
||||
; NULL-NEXT: Data version: 0
|
||||
; NULL-NEXT: Memory flags: 0x1030
|
||||
; NULL-NEXT: Language ID: 1033
|
||||
; NULL-NEXT: Version (major): 0
|
||||
; NULL-NEXT: Version (minor): 32
|
||||
; NULL-NEXT: Characteristics: 50
|
||||
; NULL-NEXT: Data size: 52
|
||||
; NULL-NEXT: Data: (
|
||||
; NULL-NEXT: 0000: 02006100 00000200 62000000 02006300 |..a.....b.....c.|
|
||||
; NULL-NEXT: 0010: 00000200 64000000 01000000 00000000 |....d...........|
|
||||
; NULL-NEXT: 0020: 00000100 00000000 00000000 00000000 |................|
|
||||
; NULL-NEXT: 0030: 00000000 |....|
|
||||
; NULL-NEXT: )
|
||||
|
||||
; NULL-DAG: Resource type (int): 6
|
||||
; NULL-NEXT: Resource name (int): 2
|
||||
; NULL-NEXT: Data version: 0
|
||||
; NULL-NEXT: Memory flags: 0x1030
|
||||
; NULL-NEXT: Language ID: 1033
|
||||
; NULL-NEXT: Version (major): 0
|
||||
; NULL-NEXT: Version (minor): 0
|
||||
; NULL-NEXT: Characteristics: 0
|
||||
; NULL-NEXT: Data size: 44
|
||||
; NULL-NEXT: Data: (
|
||||
; NULL-NEXT: 0000: 03006200 62000000 03006300 63000000 |..b.b.....c.c...|
|
||||
; NULL-NEXT: 0010: 00000000 00000000 00000000 00000000 |................|
|
||||
; NULL-NEXT: 0020: 00000000 00000000 00000000 |............|
|
||||
; NULL-NEXT: )
|
||||
|
||||
; NULL-DAG: Resource type (int): 6
|
||||
; NULL-NEXT: Resource name (int): 2
|
||||
; NULL-NEXT: Data version: 0
|
||||
; NULL-NEXT: Memory flags: 0x1030
|
||||
; NULL-NEXT: Language ID: 7172
|
||||
; NULL-NEXT: Version (major): 0
|
||||
; NULL-NEXT: Version (minor): 100
|
||||
; NULL-NEXT: Characteristics: 0
|
||||
; NULL-NEXT: Data size: 86
|
||||
; NULL-NEXT: Data: (
|
||||
; NULL-NEXT: 0000: 06006800 65006C00 6C006F00 00000600 |..h.e.l.l.o.....|
|
||||
; NULL-NEXT: 0010: 77006F00 72006C00 64000000 00000000 |w.o.r.l.d.......|
|
||||
; NULL-NEXT: 0020: 00000000 00000F00 73006F00 6D006500 |........s.o.m.e.|
|
||||
; NULL-NEXT: 0030: 74006800 69006E00 67002000 65006C00 |t.h.i.n.g. .e.l.|
|
||||
; NULL-NEXT: 0040: 73006500 00000000 00000000 00000000 |s.e.............|
|
||||
; NULL-NEXT: 0050: 00000000 0000 |......|
|
||||
; NULL-NEXT: )
|
||||
|
||||
; NULL-DAG: Resource type (int): 6
|
||||
; NULL-NEXT: Resource name (int): 3
|
||||
; NULL-NEXT: Data version: 0
|
||||
; NULL-NEXT: Memory flags: 0x1030
|
||||
; NULL-NEXT: Language ID: 1033
|
||||
; NULL-NEXT: Version (major): 0
|
||||
; NULL-NEXT: Version (minor): 50
|
||||
; NULL-NEXT: Characteristics: 50
|
||||
; NULL-NEXT: Data size: 40
|
||||
; NULL-NEXT: Data: (
|
||||
; NULL-NEXT: 0000: 04006300 63006300 00000000 00000000 |..c.c.c.........|
|
||||
; NULL-NEXT: 0010: 00000000 00000000 00000000 00000000 |................|
|
||||
; NULL-NEXT: 0020: 00000000 00000000 |........|
|
||||
; NULL-NEXT: )
|
||||
|
||||
; NULL-DAG: Resource type (int): 6
|
||||
; NULL-NEXT: Resource name (int): 4096
|
||||
; NULL-NEXT: Data version: 0
|
||||
; NULL-NEXT: Memory flags: 0x1030
|
||||
; NULL-NEXT: Language ID: 7172
|
||||
; NULL-NEXT: Version (major): 0
|
||||
; NULL-NEXT: Version (minor): 101
|
||||
; NULL-NEXT: Characteristics: 0
|
||||
; NULL-NEXT: Data size: 78
|
||||
; NULL-NEXT: Data: (
|
||||
; NULL-NEXT: 0000: 00000000 00000000 00000000 00000000 |................|
|
||||
; NULL-NEXT: 0010: 00000D00 6C006100 72006700 65002000 |....l.a.r.g.e. .|
|
||||
; NULL-NEXT: 0020: 6E007500 6D006200 65007200 00000000 |n.u.m.b.e.r.....|
|
||||
; NULL-NEXT: 0030: 00000000 00000000 0A006D00 69006E00 |..........m.i.n.|
|
||||
; NULL-NEXT: 0040: 75007300 20006F00 6E006500 0000 |u.s. .o.n.e...|
|
||||
; NULL-NEXT: )
|
||||
|
||||
|
||||
; RUN: not llvm-rc /FO %t %p/Inputs/tag-stringtable-same-ids.rc 2>&1 | FileCheck %s --check-prefix SAMEIDS
|
||||
; SAMEIDS: llvm-rc: Multiple STRINGTABLE strings located under ID 1
|
|
@ -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'.
|
||||
|
||||
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) {
|
||||
|
|
|
@ -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 &);
|
||||
|
|
|
@ -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 --
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue