Skip to content

Commit

Permalink
[llvm-rc] add support for MENUEX (#67464)
Browse files Browse the repository at this point in the history
Fixes #39455.
  • Loading branch information
oltolm committed Oct 6, 2023
1 parent be382de commit 8b356f4
Show file tree
Hide file tree
Showing 9 changed files with 293 additions and 11 deletions.
14 changes: 14 additions & 0 deletions llvm/test/tools/llvm-rc/Inputs/menuex.rc
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
101 MENUEX
BEGIN
POPUP "File", 40182
BEGIN
MENUITEM "Load", 40011
MENUITEM "", 0, 0x00000800L

POPUP "Savestate Slot", 40187
BEGIN
MENUITEM "&1", 40099
MENUITEM "&2", 40100
END
END
END
25 changes: 25 additions & 0 deletions llvm/test/tools/llvm-rc/menuex.test
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
; RUN: llvm-rc -no-preprocess /FO %t -- %p/Inputs/menuex.rc
; RUN: llvm-readobj %t | FileCheck %s

CHECK: Resource type (int): MENU (ID 4)
CHECK-NEXT: Resource name (int): 101
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: 164
CHECK-NEXT: Data: (
CHECK-NEXT: 0000: 01000400 00000000 00000000 00000000 |................|
CHECK-NEXT: 0010: F69C0000 81004600 69006C00 65000000 |......F.i.l.e...|
CHECK-NEXT: 0020: 00000000 00000000 00000000 4B9C0000 |............K...|
CHECK-NEXT: 0030: 00004C00 6F006100 64000000 00080000 |..L.o.a.d.......|
CHECK-NEXT: 0040: 00000000 00000000 00000000 00000000 |................|
CHECK-NEXT: 0050: 00000000 FB9C0000 81005300 61007600 |..........S.a.v.|
CHECK-NEXT: 0060: 65007300 74006100 74006500 20005300 |e.s.t.a.t.e. .S.|
CHECK-NEXT: 0070: 6C006F00 74000000 00000000 00000000 |l.o.t...........|
CHECK-NEXT: 0080: 00000000 A39C0000 00002600 31000000 |..........&.1...|
CHECK-NEXT: 0090: 00000000 00000000 A49C0000 80002600 |..............&.|
CHECK-NEXT: 00A0: 32000000 |2...|
CHECK-NEXT: )
58 changes: 58 additions & 0 deletions llvm/tools/llvm-rc/ResourceFileWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -471,6 +471,10 @@ Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
return writeResource(Res, &ResourceFileWriter::writeMenuBody);
}

Error ResourceFileWriter::visitMenuExResource(const RCResource *Res) {
return writeResource(Res, &ResourceFileWriter::writeMenuExBody);
}

Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
const auto *Res = cast<StringTableResource>(Base);

Expand Down Expand Up @@ -1176,6 +1180,7 @@ Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {

Error ResourceFileWriter::writeMenuDefinition(
const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
// https://learn.microsoft.com/en-us/windows/win32/api/winuser/ns-winuser-menuitemtemplate
assert(Def);
const MenuDefinition *DefPtr = Def.get();

Expand All @@ -1202,6 +1207,34 @@ Error ResourceFileWriter::writeMenuDefinition(
return writeMenuDefinitionList(PopupPtr->SubItems);
}

Error ResourceFileWriter::writeMenuExDefinition(
const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
// https://learn.microsoft.com/en-us/windows/win32/menurc/menuex-template-item
assert(Def);
const MenuDefinition *DefPtr = Def.get();

padStream(sizeof(uint32_t));
if (auto *MenuItemPtr = dyn_cast<MenuExItem>(DefPtr)) {
writeInt<uint32_t>(MenuItemPtr->Type);
writeInt<uint32_t>(MenuItemPtr->State);
writeInt<uint32_t>(MenuItemPtr->Id);
writeInt<uint16_t>(Flags);
padStream(sizeof(uint16_t));
RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
return Error::success();
}

auto *PopupPtr = cast<PopupExItem>(DefPtr);
writeInt<uint32_t>(PopupPtr->Type);
writeInt<uint32_t>(PopupPtr->State);
writeInt<uint32_t>(PopupPtr->Id);
writeInt<uint16_t>(Flags);
padStream(sizeof(uint16_t));
RETURN_IF_ERROR(writeCString(PopupPtr->Name));
writeInt<uint32_t>(PopupPtr->HelpId);
return writeMenuExDefinitionList(PopupPtr->SubItems);
}

Error ResourceFileWriter::writeMenuDefinitionList(
const MenuDefinitionList &List) {
for (auto &Def : List.Definitions) {
Expand All @@ -1216,6 +1249,20 @@ Error ResourceFileWriter::writeMenuDefinitionList(
return Error::success();
}

Error ResourceFileWriter::writeMenuExDefinitionList(
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(writeMenuExDefinition(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
Expand All @@ -1224,6 +1271,17 @@ Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
}

Error ResourceFileWriter::writeMenuExBody(const RCResource *Base) {
// At first, MENUEX_TEMPLATE_HEADER structure.
// Ref:
// https://learn.microsoft.com/en-us/windows/win32/menurc/menuex-template-header
writeInt<uint16_t>(1);
writeInt<uint16_t>(4);
writeInt<uint32_t>(0);

return writeMenuExDefinitionList(cast<MenuExResource>(Base)->Elements);
}

// --- StringTableResource helpers. --- //

class BundleResource : public RCResource {
Expand Down
5 changes: 5 additions & 0 deletions llvm/tools/llvm-rc/ResourceFileWriter.h
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ class ResourceFileWriter : public Visitor {
Error visitHTMLResource(const RCResource *) override;
Error visitIconResource(const RCResource *) override;
Error visitMenuResource(const RCResource *) override;
Error visitMenuExResource(const RCResource *) override;
Error visitVersionInfoResource(const RCResource *) override;
Error visitStringTableResource(const RCResource *) override;
Error visitUserDefinedResource(const RCResource *) override;
Expand Down Expand Up @@ -150,8 +151,12 @@ class ResourceFileWriter : public Visitor {
// MenuResource
Error writeMenuDefinition(const std::unique_ptr<MenuDefinition> &,
uint16_t Flags);
Error writeMenuExDefinition(const std::unique_ptr<MenuDefinition> &,
uint16_t Flags);
Error writeMenuDefinitionList(const MenuDefinitionList &List);
Error writeMenuExDefinitionList(const MenuDefinitionList &List);
Error writeMenuBody(const RCResource *);
Error writeMenuExBody(const RCResource *);

// StringTableResource
Error visitStringTableBundle(const RCResource *);
Expand Down
117 changes: 108 additions & 9 deletions llvm/tools/llvm-rc/ResourceScriptParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,8 @@ RCParser::ParseType RCParser::parseSingleResource() {
Result = parseIconResource();
else if (TypeToken->equalsLower("MENU"))
Result = parseMenuResource();
else if (TypeToken->equalsLower("MENUEX"))
Result = parseMenuExResource();
else if (TypeToken->equalsLower("RCDATA"))
Result = parseUserDefinedResource(RkRcData);
else if (TypeToken->equalsLower("VERSIONINFO"))
Expand Down Expand Up @@ -499,7 +501,7 @@ RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
case Kind::String:
case Kind::Identifier:
return std::make_unique<UserDefinedResource>(Type, read().value(),
MemoryFlags);
MemoryFlags);
default:
break;
}
Expand All @@ -518,7 +520,7 @@ RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
}

return std::make_unique<UserDefinedResource>(Type, std::move(Data),
MemoryFlags);
MemoryFlags);
}

RCParser::ParseType RCParser::parseVersionInfoResource() {
Expand Down Expand Up @@ -557,7 +559,8 @@ Expected<Control> RCParser::parseControl() {
IntOrString Class;
std::optional<IntWithNotMask> Style;
if (ClassUpper == "CONTROL") {
// CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
// CONTROL text, id, class, style, x, y, width, height [, exstyle] [,
// helpID]
ASSIGN_OR_RETURN(ClassStr, readString());
RETURN_IF_ERROR(consumeType(Kind::Comma));
Class = *ClassStr;
Expand Down Expand Up @@ -589,8 +592,8 @@ Expected<Control> RCParser::parseControl() {
HelpID = *Val;
}

return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
(*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1], (*Args)[2],
(*Args)[3], Style, ExStyle, HelpID, Class);
}

RCParser::ParseType RCParser::parseBitmapResource() {
Expand Down Expand Up @@ -620,7 +623,14 @@ RCParser::ParseType RCParser::parseMenuResource() {
ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
ASSIGN_OR_RETURN(Items, parseMenuItemsList());
return std::make_unique<MenuResource>(std::move(*OptStatements),
std::move(*Items), MemoryFlags);
std::move(*Items), MemoryFlags);
}

RCParser::ParseType RCParser::parseMenuExResource() {
uint16_t MemoryFlags =
parseMemoryFlags(MenuExResource::getDefaultMemoryFlags());
ASSIGN_OR_RETURN(Items, parseMenuExItemsList());
return std::make_unique<MenuExResource>(std::move(*Items), MemoryFlags);
}

Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
Expand Down Expand Up @@ -682,14 +692,103 @@ Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
return std::move(List);
}

Expected<MenuDefinitionList> RCParser::parseMenuExItemsList() {
RETURN_IF_ERROR(consumeType(Kind::BlockBegin));

MenuDefinitionList List;

// Read a set of items. Each item is of one of two kinds:
// MENUITEM caption:String [,[id][, [type][, state]]]]
// POPUP caption:String [,[id][, [type][, [state][, helpID]]]] { popupBody }
while (!consumeOptionalType(Kind::BlockEnd)) {
ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());

bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");
bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");
if (!IsMenuItem && !IsPopup)
return getExpectedError("MENUITEM, POPUP, END or '}'", true);

// Not a separator. Read the caption.
ASSIGN_OR_RETURN(CaptionResult, readString());

// If MENUITEM, expect [,[id][, [type][, state]]]]
if (IsMenuItem) {
uint32_t MenuId = 0;
uint32_t MenuType = 0;
uint32_t MenuState = 0;

if (consumeOptionalType(Kind::Comma)) {
auto IntId = readInt();
if (IntId) {
MenuId = *IntId;
}
if (consumeOptionalType(Kind::Comma)) {
auto IntType = readInt();
if (IntType) {
MenuType = *IntType;
}
if (consumeOptionalType(Kind::Comma)) {
auto IntState = readInt();
if (IntState) {
MenuState = *IntState;
}
}
}
}
List.addDefinition(std::make_unique<MenuExItem>(*CaptionResult, MenuId,
MenuType, MenuState));
continue;
}

assert(IsPopup);

uint32_t PopupId = 0;
uint32_t PopupType = 0;
uint32_t PopupState = 0;
uint32_t PopupHelpID = 0;

if (consumeOptionalType(Kind::Comma)) {
auto IntId = readInt();
if (IntId) {
PopupId = *IntId;
}
if (consumeOptionalType(Kind::Comma)) {
auto IntType = readInt();
if (IntType) {
PopupType = *IntType;
}
if (consumeOptionalType(Kind::Comma)) {
auto IntState = readInt();
if (IntState) {
PopupState = *IntState;
}
if (consumeOptionalType(Kind::Comma)) {
auto IntHelpID = readInt();
if (IntHelpID) {
PopupHelpID = *IntHelpID;
}
}
}
}
}
// If POPUP, read submenu items recursively.
ASSIGN_OR_RETURN(SubMenuResult, parseMenuExItemsList());
List.addDefinition(std::make_unique<PopupExItem>(
*CaptionResult, PopupId, PopupType, PopupState, PopupHelpID,
std::move(*SubMenuResult)));
}

return std::move(List);
}

RCParser::ParseType RCParser::parseStringTableResource() {
uint16_t MemoryFlags =
parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
RETURN_IF_ERROR(consumeType(Kind::BlockBegin));

auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
MemoryFlags);
MemoryFlags);

// Read strings until we reach the end of the block.
while (!consumeOptionalType(Kind::BlockEnd)) {
Expand Down Expand Up @@ -753,7 +852,7 @@ Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
PrecedingCommas.push_back(HadComma);
}
return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
std::move(PrecedingCommas));
std::move(PrecedingCommas));
}

return getExpectedError("BLOCK or VALUE", true);
Expand Down Expand Up @@ -835,7 +934,7 @@ RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
}
}
return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
FontItalic, FontCharset);
FontItalic, FontCharset);
}

RCParser::ParseOptionType RCParser::parseStyleStmt() {
Expand Down
4 changes: 4 additions & 0 deletions llvm/tools/llvm-rc/ResourceScriptParser.h
Original file line number Diff line number Diff line change
Expand Up @@ -143,6 +143,7 @@ class RCParser {
ParseType parseIconResource();
ParseType parseHTMLResource();
ParseType parseMenuResource();
ParseType parseMenuExResource();
ParseType parseStringTableResource();
ParseType parseUserDefinedResource(IntOrString Type);
ParseType parseVersionInfoResource();
Expand All @@ -153,6 +154,9 @@ class RCParser {
// Helper MENU parser.
Expected<MenuDefinitionList> parseMenuItemsList();

// Helper MENUEX parser.
Expected<MenuDefinitionList> parseMenuExItemsList();

// Helper VERSIONINFO parser - read the contents of a single BLOCK statement,
// from BEGIN to END.
Expected<std::unique_ptr<VersionInfoBlock>>
Expand Down
19 changes: 19 additions & 0 deletions llvm/tools/llvm-rc/ResourceScriptStmt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -102,19 +102,38 @@ raw_ostream &MenuSeparator::log(raw_ostream &OS) const {
return OS << " Menu separator\n";
}

raw_ostream &MenuExItem::log(raw_ostream &OS) const {
OS << " MenuExItem (" << Name << "), ID = " << Id;
OS << ", type: " << Type << ", state: " << State;
return OS << "\n";
}

raw_ostream &PopupItem::log(raw_ostream &OS) const {
OS << " Popup (" << Name << ")";
logFlags(OS, Flags);
OS << ":\n";
return SubItems.log(OS);
}

raw_ostream &PopupExItem::log(raw_ostream &OS) const {
OS << " Popup (" << Name << ")";
OS << ", type: " << Type << ", state: " << State << ", help ID: " << HelpId;
OS << ":\n";
return SubItems.log(OS);
}

raw_ostream &MenuResource::log(raw_ostream &OS) const {
OS << "Menu (" << ResName << "):\n";
OptStatements->log(OS);
return Elements.log(OS);
}

raw_ostream &MenuExResource::log(raw_ostream &OS) const {
OS << "MenuEx (" << ResName << "):\n";
OptStatements->log(OS);
return Elements.log(OS);
}

raw_ostream &StringTableResource::log(raw_ostream &OS) const {
OS << "StringTable:\n";
OptStatements->log(OS);
Expand Down

0 comments on commit 8b356f4

Please sign in to comment.