forked from OSchip/llvm-project
[llvm-rc] Serialize MENU resources to .res files (serialization, pt 3).
This allows MENU resources to be serialized. MENU resource statement doc: msdn.microsoft.com/en-us/library/windows/desktop/aa381025.aspx POPUP sub-statement doc: msdn.microsoft.com/en-us/library/windows/desktop/aa381030.aspx MENUITEM sub-statement doc: msdn.microsoft.com/en-us/library/windows/desktop/aa381024.aspx MENUHEADER structure: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx (and NORMALMENUITEM, POPUPMENUITEM structs). Thanks for Nico Weber for his original work in this area. Differential Revision: https://reviews.llvm.org/D37828 llvm-svn: 314562
This commit is contained in:
parent
5b9d96825b
commit
42f494d6a6
|
@ -0,0 +1,3 @@
|
|||
1 MENU {
|
||||
MENUITEM "Wrong", 100000, CHECKED
|
||||
}
|
|
@ -0,0 +1,60 @@
|
|||
CheckRecursion MENU {
|
||||
POPUP "A" {
|
||||
POPUP "B" {
|
||||
MENUITEM "a", 1
|
||||
MENUITEM "b", 2
|
||||
MENUITEM "c", 3
|
||||
POPUP "C" {
|
||||
POPUP "D" { POPUP "E" { POPUP "F" { POPUP "G" { POPUP "H" { POPUP "I" {
|
||||
MENUITEM "d", 57134
|
||||
}}}}}}
|
||||
}
|
||||
}
|
||||
MENUITEM "efg", 23333
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CheckFlags MENU {
|
||||
MENUITEM "a", 1, CHECKED
|
||||
MENUITEM "b", 2, GRAYED
|
||||
MENUITEM "c", 3, HELP
|
||||
MENUITEM "d", 4, INACTIVE
|
||||
MENUITEM "e", 5, MENUBARBREAK
|
||||
MENUITEM "f", 6, MENUBREAK
|
||||
MENUITEM "ad", 7, CHECKED, INACTIVE
|
||||
MENUITEM SEPARATOR
|
||||
POPUP "A", CHECKED { MENUITEM "x", 100 }
|
||||
POPUP "B", GRAYED { MENUITEM "x", 101 }
|
||||
POPUP "C", HELP { MENUITEM "x", 102 }
|
||||
POPUP "D", INACTIVE { MENUITEM "x", 103 }
|
||||
POPUP "E", MENUBARBREAK { MENUITEM "x", 104 }
|
||||
POPUP "F", MENUBREAK { MENUITEM "x", 105 }
|
||||
POPUP "G", HELP, MENUBARBREAK, GRAYED {
|
||||
POPUP "H", CHECKED, MENUBREAK, HELP, INACTIVE {
|
||||
MENUITEM SEPARATOR
|
||||
MENUITEM "x", 106, INACTIVE, MENUBARBREAK
|
||||
MENUITEM SEPARATOR
|
||||
}
|
||||
}
|
||||
MENUITEM "abcdef", 8, help, inactive, menubarbreak, checked, grayed, menubreak
|
||||
}
|
||||
|
||||
|
||||
CheckOpts MENU
|
||||
CHARACTERISTICS 500
|
||||
LANGUAGE 1, 1
|
||||
VERSION 128
|
||||
BEGIN
|
||||
POPUP "&Only separator" {
|
||||
MENUITEM SEPARATOR
|
||||
}
|
||||
POPUP "O&ther things" {
|
||||
MENUITEM "&abcde", 1
|
||||
MENUITEM "a&bcde", 2
|
||||
MENUITEM "ab&cde", 3
|
||||
MENUITEM "abc&de", 4
|
||||
MENUITEM "abcd&e", 5
|
||||
}
|
||||
END
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
; RUN: llvm-rc /FO %t %p/Inputs/tag-menu.rc
|
||||
; RUN: llvm-readobj %t | FileCheck %s --check-prefix=MENU
|
||||
|
||||
; MENU: Resource type (int): 4
|
||||
; MENU-NEXT: Resource name (string): CHECKRECURSION
|
||||
; MENU-NEXT: Data version: 0
|
||||
; MENU-NEXT: Memory flags: 0x1030
|
||||
; MENU-NEXT: Language ID: 1033
|
||||
; MENU-NEXT: Version (major): 0
|
||||
; MENU-NEXT: Version (minor): 0
|
||||
; MENU-NEXT: Characteristics: 0
|
||||
; MENU-NEXT: Data size: 102
|
||||
; MENU-NEXT: Data: (
|
||||
; MENU-NEXT: 0000: 00000000 90004100 00001000 42000000 |......A.....B...|
|
||||
; MENU-NEXT: 0010: 00000100 61000000 00000200 62000000 |....a.......b...|
|
||||
; MENU-NEXT: 0020: 00000300 63000000 90004300 00009000 |....c.....C.....|
|
||||
; MENU-NEXT: 0030: 44000000 90004500 00009000 46000000 |D.....E.....F...|
|
||||
; MENU-NEXT: 0040: 90004700 00009000 48000000 90004900 |..G.....H.....I.|
|
||||
; MENU-NEXT: 0050: 00008000 2EDF6400 00008000 255B6500 |......d.....%[e.|
|
||||
; MENU-NEXT: 0060: 66006700 0000 |f.g...|
|
||||
; MENU-NEXT: )
|
||||
|
||||
; MENU-DAG: Resource type (int): 4
|
||||
; MENU-NEXT: Resource name (string): CHECKFLAGS
|
||||
; MENU-NEXT: Data version: 0
|
||||
; MENU-NEXT: Memory flags: 0x1030
|
||||
; MENU-NEXT: Language ID: 1033
|
||||
; MENU-NEXT: Version (major): 0
|
||||
; MENU-NEXT: Version (minor): 0
|
||||
; MENU-NEXT: Characteristics: 0
|
||||
; MENU-NEXT: Data size: 202
|
||||
; MENU-NEXT: Data: (
|
||||
; MENU-NEXT: 0000: 00000000 08000100 61000000 01000200 |........a.......|
|
||||
; MENU-NEXT: 0010: 62000000 00400300 63000000 02000400 |b....@..c.......|
|
||||
; MENU-NEXT: 0020: 64000000 20000500 65000000 40000600 |d... ...e...@...|
|
||||
; MENU-NEXT: 0030: 66000000 0A000700 61006400 00000000 |f.......a.d.....|
|
||||
; MENU-NEXT: 0040: 00000000 18004100 00008000 64007800 |......A.....d.x.|
|
||||
; MENU-NEXT: 0050: 00001100 42000000 80006500 78000000 |....B.....e.x...|
|
||||
; MENU-NEXT: 0060: 10404300 00008000 66007800 00001200 |.@C.....f.x.....|
|
||||
; MENU-NEXT: 0070: 44000000 80006700 78000000 30004500 |D.....g.x...0.E.|
|
||||
; MENU-NEXT: 0080: 00008000 68007800 00005000 46000000 |....h.x...P.F...|
|
||||
; MENU-NEXT: 0090: 80006900 78000000 31404700 0000DA40 |..i.x...1@G....@|
|
||||
; MENU-NEXT: 00A0: 48000000 00000000 00002200 6A007800 |H.........".j.x.|
|
||||
; MENU-NEXT: 00B0: 00008000 00000000 EB400800 61006200 |.........@..a.b.|
|
||||
; MENU-NEXT: 00C0: 63006400 65006600 0000 |c.d.e.f...|
|
||||
; MENU-NEXT: )
|
||||
|
||||
; MENU-DAG: Resource type (int): 4
|
||||
; MENU-NEXT: Resource name (string): CHECKOPTS
|
||||
; MENU-NEXT: Data version: 0
|
||||
; MENU-NEXT: Memory flags: 0x1030
|
||||
; MENU-NEXT: Language ID: 1025
|
||||
; MENU-NEXT: Version (major): 0
|
||||
; MENU-NEXT: Version (minor): 128
|
||||
; MENU-NEXT: Characteristics: 500
|
||||
; MENU-NEXT: Data size: 164
|
||||
; MENU-NEXT: Data: (
|
||||
; MENU-NEXT: 0000: 00000000 10002600 4F006E00 6C007900 |......&.O.n.l.y.|
|
||||
; MENU-NEXT: 0010: 20007300 65007000 61007200 61007400 | .s.e.p.a.r.a.t.|
|
||||
; MENU-NEXT: 0020: 6F007200 00008000 00000000 90004F00 |o.r...........O.|
|
||||
; MENU-NEXT: 0030: 26007400 68006500 72002000 74006800 |&.t.h.e.r. .t.h.|
|
||||
; MENU-NEXT: 0040: 69006E00 67007300 00000000 01002600 |i.n.g.s.......&.|
|
||||
; MENU-NEXT: 0050: 61006200 63006400 65000000 00000200 |a.b.c.d.e.......|
|
||||
; MENU-NEXT: 0060: 61002600 62006300 64006500 00000000 |a.&.b.c.d.e.....|
|
||||
; MENU-NEXT: 0070: 03006100 62002600 63006400 65000000 |..a.b.&.c.d.e...|
|
||||
; MENU-NEXT: 0080: 00000400 61006200 63002600 64006500 |....a.b.c.&.d.e.|
|
||||
; MENU-NEXT: 0090: 00008000 05006100 62006300 64002600 |......a.b.c.d.&.|
|
||||
; MENU-NEXT: 00A0: 65000000 |e...|
|
||||
; MENU-NEXT: )
|
||||
|
||||
|
||||
; RUN: not llvm-rc /FO %t %p/Inputs/tag-menu-bad-menuitem-id.rc 2>&1 | FileCheck %s --check-prefix BADID
|
||||
|
||||
; BADID: llvm-rc: Error in MENU statement (ID 1):
|
||||
; BADID-NEXT: MENUITEM action ID (100000) does not fit in 16 bits.
|
|
@ -205,6 +205,10 @@ Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
|
|||
return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
|
||||
}
|
||||
|
||||
Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
|
||||
return writeResource(Res, &ResourceFileWriter::writeMenuBody);
|
||||
}
|
||||
|
||||
Error ResourceFileWriter::visitCharacteristicsStmt(
|
||||
const CharacteristicsStmt *Stmt) {
|
||||
ObjectData.Characteristics = Stmt->Value;
|
||||
|
@ -382,5 +386,55 @@ Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
|
|||
return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
|
||||
}
|
||||
|
||||
// --- MenuResource helpers. --- //
|
||||
|
||||
Error ResourceFileWriter::writeMenuDefinition(
|
||||
const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
|
||||
assert(Def);
|
||||
const MenuDefinition *DefPtr = Def.get();
|
||||
|
||||
if (auto *MenuItemPtr = dyn_cast<MenuItem>(DefPtr)) {
|
||||
writeInt<uint16_t>(Flags);
|
||||
RETURN_IF_ERROR(
|
||||
checkNumberFits<uint16_t>(MenuItemPtr->Id, "MENUITEM action ID"));
|
||||
writeInt<uint16_t>(MenuItemPtr->Id);
|
||||
RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
if (isa<MenuSeparator>(DefPtr)) {
|
||||
writeInt<uint16_t>(Flags);
|
||||
writeInt<uint32_t>(0);
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
auto *PopupPtr = cast<PopupItem>(DefPtr);
|
||||
writeInt<uint16_t>(Flags);
|
||||
RETURN_IF_ERROR(writeCString(PopupPtr->Name));
|
||||
return writeMenuDefinitionList(PopupPtr->SubItems);
|
||||
}
|
||||
|
||||
Error ResourceFileWriter::writeMenuDefinitionList(
|
||||
const MenuDefinitionList &List) {
|
||||
for (auto &Def : List.Definitions) {
|
||||
uint16_t Flags = Def->getResFlags();
|
||||
// Last element receives an additional 0x80 flag.
|
||||
const uint16_t LastElementFlag = 0x0080;
|
||||
if (&Def == &List.Definitions.back())
|
||||
Flags |= LastElementFlag;
|
||||
|
||||
RETURN_IF_ERROR(writeMenuDefinition(Def, Flags));
|
||||
}
|
||||
return Error::success();
|
||||
}
|
||||
|
||||
Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
|
||||
// At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
|
||||
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
|
||||
writeObject<uint32_t>(0);
|
||||
|
||||
return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
|
||||
}
|
||||
|
||||
} // namespace rc
|
||||
} // namespace llvm
|
||||
|
|
|
@ -32,6 +32,7 @@ public:
|
|||
Error visitNullResource(const RCResource *) override;
|
||||
Error visitAcceleratorsResource(const RCResource *) override;
|
||||
Error visitHTMLResource(const RCResource *) override;
|
||||
Error visitMenuResource(const RCResource *) override;
|
||||
|
||||
Error visitCharacteristicsStmt(const CharacteristicsStmt *) override;
|
||||
Error visitLanguageStmt(const LanguageResource *) override;
|
||||
|
@ -63,6 +64,12 @@ private:
|
|||
// HTMLResource
|
||||
Error writeHTMLBody(const RCResource *);
|
||||
|
||||
// MenuResource
|
||||
Error writeMenuDefinition(const std::unique_ptr<MenuDefinition> &,
|
||||
uint16_t Flags);
|
||||
Error writeMenuDefinitionList(const MenuDefinitionList &List);
|
||||
Error writeMenuBody(const RCResource *);
|
||||
|
||||
// Output stream handling.
|
||||
std::unique_ptr<raw_fd_ostream> FS;
|
||||
|
||||
|
|
|
@ -315,6 +315,8 @@ public:
|
|||
MENUBREAK = 0x0040
|
||||
};
|
||||
|
||||
enum MenuDefKind { MkBase, MkSeparator, MkMenuItem, MkPopup };
|
||||
|
||||
static constexpr size_t NumFlags = 6;
|
||||
static StringRef OptionsStr[NumFlags];
|
||||
static uint32_t OptionsFlags[NumFlags];
|
||||
|
@ -323,13 +325,16 @@ public:
|
|||
return OS << "Base menu definition\n";
|
||||
}
|
||||
virtual ~MenuDefinition() {}
|
||||
|
||||
virtual uint16_t getResFlags() const { return 0; }
|
||||
virtual MenuDefKind getKind() const { return MkBase; }
|
||||
};
|
||||
|
||||
// Recursive description of a whole submenu.
|
||||
class MenuDefinitionList : public MenuDefinition {
|
||||
public:
|
||||
std::vector<std::unique_ptr<MenuDefinition>> Definitions;
|
||||
|
||||
public:
|
||||
void addDefinition(std::unique_ptr<MenuDefinition> Def) {
|
||||
Definitions.push_back(std::move(Def));
|
||||
}
|
||||
|
@ -342,46 +347,73 @@ public:
|
|||
class MenuSeparator : public MenuDefinition {
|
||||
public:
|
||||
raw_ostream &log(raw_ostream &) const override;
|
||||
|
||||
MenuDefKind getKind() const override { return MkSeparator; }
|
||||
static bool classof(const MenuDefinition *D) {
|
||||
return D->getKind() == MkSeparator;
|
||||
}
|
||||
};
|
||||
|
||||
// MENUITEM statement definition.
|
||||
//
|
||||
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381024(v=vs.85).aspx
|
||||
class MenuItem : public MenuDefinition {
|
||||
public:
|
||||
StringRef Name;
|
||||
uint32_t Id;
|
||||
uint16_t Flags;
|
||||
|
||||
public:
|
||||
MenuItem(StringRef Caption, uint32_t ItemId, uint16_t ItemFlags)
|
||||
: Name(Caption), Id(ItemId), Flags(ItemFlags) {}
|
||||
raw_ostream &log(raw_ostream &) const override;
|
||||
|
||||
uint16_t getResFlags() const override { return Flags; }
|
||||
MenuDefKind getKind() const override { return MkMenuItem; }
|
||||
static bool classof(const MenuDefinition *D) {
|
||||
return D->getKind() == MkMenuItem;
|
||||
}
|
||||
};
|
||||
|
||||
// POPUP statement definition.
|
||||
//
|
||||
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381030(v=vs.85).aspx
|
||||
class PopupItem : public MenuDefinition {
|
||||
public:
|
||||
StringRef Name;
|
||||
uint16_t Flags;
|
||||
MenuDefinitionList SubItems;
|
||||
|
||||
public:
|
||||
PopupItem(StringRef Caption, uint16_t ItemFlags,
|
||||
MenuDefinitionList &&SubItemsList)
|
||||
: Name(Caption), Flags(ItemFlags), SubItems(std::move(SubItemsList)) {}
|
||||
raw_ostream &log(raw_ostream &) const override;
|
||||
|
||||
// This has an additional (0x10) flag. It doesn't match with documented
|
||||
// 0x01 flag, though.
|
||||
uint16_t getResFlags() const override { return Flags | 0x10; }
|
||||
MenuDefKind getKind() const override { return MkPopup; }
|
||||
static bool classof(const MenuDefinition *D) {
|
||||
return D->getKind() == MkPopup;
|
||||
}
|
||||
};
|
||||
|
||||
// Menu resource definition.
|
||||
class MenuResource : public OptStatementsRCResource {
|
||||
public:
|
||||
MenuDefinitionList Elements;
|
||||
|
||||
public:
|
||||
MenuResource(OptionalStmtList &&OptStmts, MenuDefinitionList &&Items)
|
||||
: OptStatementsRCResource(std::move(OptStmts)),
|
||||
Elements(std::move(Items)) {}
|
||||
raw_ostream &log(raw_ostream &) const override;
|
||||
|
||||
IntOrString getResourceType() const override { return RkMenu; }
|
||||
Twine getResourceTypeName() const override { return "MENU"; }
|
||||
Error visit(Visitor *V) const override { return V->visitMenuResource(this); }
|
||||
ResourceKind getKind() const override { return RkMenu; }
|
||||
static bool classof(const RCResource *Res) {
|
||||
return Res->getKind() == RkMenu;
|
||||
}
|
||||
};
|
||||
|
||||
// STRINGTABLE resource. Contains a list of strings, each having its unique ID.
|
||||
|
|
|
@ -30,6 +30,7 @@ public:
|
|||
virtual Error visitNullResource(const RCResource *) = 0;
|
||||
virtual Error visitAcceleratorsResource(const RCResource *) = 0;
|
||||
virtual Error visitHTMLResource(const RCResource *) = 0;
|
||||
virtual Error visitMenuResource(const RCResource *) = 0;
|
||||
|
||||
virtual Error visitCharacteristicsStmt(const CharacteristicsStmt *) = 0;
|
||||
virtual Error visitLanguageStmt(const LanguageResource *) = 0;
|
||||
|
|
Loading…
Reference in New Issue