[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:
Marek Sokolowski 2017-09-29 22:25:05 +00:00
parent 5b9d96825b
commit 42f494d6a6
7 changed files with 236 additions and 4 deletions

View File

@ -0,0 +1,3 @@
1 MENU {
MENUITEM "Wrong", 100000, CHECKED
}

View File

@ -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

View File

@ -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.

View File

@ -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

View File

@ -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;

View File

@ -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.

View File

@ -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;