Skip to content

Commit 8b356f4

Browse files
authored
[llvm-rc] add support for MENUEX (llvm#67464)
Fixes llvm#39455.
1 parent be382de commit 8b356f4

9 files changed

+293
-11
lines changed
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
101 MENUEX
2+
BEGIN
3+
POPUP "File", 40182
4+
BEGIN
5+
MENUITEM "Load", 40011
6+
MENUITEM "", 0, 0x00000800L
7+
8+
POPUP "Savestate Slot", 40187
9+
BEGIN
10+
MENUITEM "&1", 40099
11+
MENUITEM "&2", 40100
12+
END
13+
END
14+
END

llvm/test/tools/llvm-rc/menuex.test

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
; RUN: llvm-rc -no-preprocess /FO %t -- %p/Inputs/menuex.rc
2+
; RUN: llvm-readobj %t | FileCheck %s
3+
4+
CHECK: Resource type (int): MENU (ID 4)
5+
CHECK-NEXT: Resource name (int): 101
6+
CHECK-NEXT: Data version: 0
7+
CHECK-NEXT: Memory flags: 0x1030
8+
CHECK-NEXT: Language ID: 1033
9+
CHECK-NEXT: Version (major): 0
10+
CHECK-NEXT: Version (minor): 0
11+
CHECK-NEXT: Characteristics: 0
12+
CHECK-NEXT: Data size: 164
13+
CHECK-NEXT: Data: (
14+
CHECK-NEXT: 0000: 01000400 00000000 00000000 00000000 |................|
15+
CHECK-NEXT: 0010: F69C0000 81004600 69006C00 65000000 |......F.i.l.e...|
16+
CHECK-NEXT: 0020: 00000000 00000000 00000000 4B9C0000 |............K...|
17+
CHECK-NEXT: 0030: 00004C00 6F006100 64000000 00080000 |..L.o.a.d.......|
18+
CHECK-NEXT: 0040: 00000000 00000000 00000000 00000000 |................|
19+
CHECK-NEXT: 0050: 00000000 FB9C0000 81005300 61007600 |..........S.a.v.|
20+
CHECK-NEXT: 0060: 65007300 74006100 74006500 20005300 |e.s.t.a.t.e. .S.|
21+
CHECK-NEXT: 0070: 6C006F00 74000000 00000000 00000000 |l.o.t...........|
22+
CHECK-NEXT: 0080: 00000000 A39C0000 00002600 31000000 |..........&.1...|
23+
CHECK-NEXT: 0090: 00000000 00000000 A49C0000 80002600 |..............&.|
24+
CHECK-NEXT: 00A0: 32000000 |2...|
25+
CHECK-NEXT: )

llvm/tools/llvm-rc/ResourceFileWriter.cpp

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -471,6 +471,10 @@ Error ResourceFileWriter::visitMenuResource(const RCResource *Res) {
471471
return writeResource(Res, &ResourceFileWriter::writeMenuBody);
472472
}
473473

474+
Error ResourceFileWriter::visitMenuExResource(const RCResource *Res) {
475+
return writeResource(Res, &ResourceFileWriter::writeMenuExBody);
476+
}
477+
474478
Error ResourceFileWriter::visitStringTableResource(const RCResource *Base) {
475479
const auto *Res = cast<StringTableResource>(Base);
476480

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

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

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

1210+
Error ResourceFileWriter::writeMenuExDefinition(
1211+
const std::unique_ptr<MenuDefinition> &Def, uint16_t Flags) {
1212+
// https://learn.microsoft.com/en-us/windows/win32/menurc/menuex-template-item
1213+
assert(Def);
1214+
const MenuDefinition *DefPtr = Def.get();
1215+
1216+
padStream(sizeof(uint32_t));
1217+
if (auto *MenuItemPtr = dyn_cast<MenuExItem>(DefPtr)) {
1218+
writeInt<uint32_t>(MenuItemPtr->Type);
1219+
writeInt<uint32_t>(MenuItemPtr->State);
1220+
writeInt<uint32_t>(MenuItemPtr->Id);
1221+
writeInt<uint16_t>(Flags);
1222+
padStream(sizeof(uint16_t));
1223+
RETURN_IF_ERROR(writeCString(MenuItemPtr->Name));
1224+
return Error::success();
1225+
}
1226+
1227+
auto *PopupPtr = cast<PopupExItem>(DefPtr);
1228+
writeInt<uint32_t>(PopupPtr->Type);
1229+
writeInt<uint32_t>(PopupPtr->State);
1230+
writeInt<uint32_t>(PopupPtr->Id);
1231+
writeInt<uint16_t>(Flags);
1232+
padStream(sizeof(uint16_t));
1233+
RETURN_IF_ERROR(writeCString(PopupPtr->Name));
1234+
writeInt<uint32_t>(PopupPtr->HelpId);
1235+
return writeMenuExDefinitionList(PopupPtr->SubItems);
1236+
}
1237+
12051238
Error ResourceFileWriter::writeMenuDefinitionList(
12061239
const MenuDefinitionList &List) {
12071240
for (auto &Def : List.Definitions) {
@@ -1216,6 +1249,20 @@ Error ResourceFileWriter::writeMenuDefinitionList(
12161249
return Error::success();
12171250
}
12181251

1252+
Error ResourceFileWriter::writeMenuExDefinitionList(
1253+
const MenuDefinitionList &List) {
1254+
for (auto &Def : List.Definitions) {
1255+
uint16_t Flags = Def->getResFlags();
1256+
// Last element receives an additional 0x80 flag.
1257+
const uint16_t LastElementFlag = 0x0080;
1258+
if (&Def == &List.Definitions.back())
1259+
Flags |= LastElementFlag;
1260+
1261+
RETURN_IF_ERROR(writeMenuExDefinition(Def, Flags));
1262+
}
1263+
return Error::success();
1264+
}
1265+
12191266
Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
12201267
// At first, MENUHEADER structure. In fact, these are two WORDs equal to 0.
12211268
// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648018.aspx
@@ -1224,6 +1271,17 @@ Error ResourceFileWriter::writeMenuBody(const RCResource *Base) {
12241271
return writeMenuDefinitionList(cast<MenuResource>(Base)->Elements);
12251272
}
12261273

1274+
Error ResourceFileWriter::writeMenuExBody(const RCResource *Base) {
1275+
// At first, MENUEX_TEMPLATE_HEADER structure.
1276+
// Ref:
1277+
// https://learn.microsoft.com/en-us/windows/win32/menurc/menuex-template-header
1278+
writeInt<uint16_t>(1);
1279+
writeInt<uint16_t>(4);
1280+
writeInt<uint32_t>(0);
1281+
1282+
return writeMenuExDefinitionList(cast<MenuExResource>(Base)->Elements);
1283+
}
1284+
12271285
// --- StringTableResource helpers. --- //
12281286

12291287
class BundleResource : public RCResource {

llvm/tools/llvm-rc/ResourceFileWriter.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,7 @@ class ResourceFileWriter : public Visitor {
5555
Error visitHTMLResource(const RCResource *) override;
5656
Error visitIconResource(const RCResource *) override;
5757
Error visitMenuResource(const RCResource *) override;
58+
Error visitMenuExResource(const RCResource *) override;
5859
Error visitVersionInfoResource(const RCResource *) override;
5960
Error visitStringTableResource(const RCResource *) override;
6061
Error visitUserDefinedResource(const RCResource *) override;
@@ -150,8 +151,12 @@ class ResourceFileWriter : public Visitor {
150151
// MenuResource
151152
Error writeMenuDefinition(const std::unique_ptr<MenuDefinition> &,
152153
uint16_t Flags);
154+
Error writeMenuExDefinition(const std::unique_ptr<MenuDefinition> &,
155+
uint16_t Flags);
153156
Error writeMenuDefinitionList(const MenuDefinitionList &List);
157+
Error writeMenuExDefinitionList(const MenuDefinitionList &List);
154158
Error writeMenuBody(const RCResource *);
159+
Error writeMenuExBody(const RCResource *);
155160

156161
// StringTableResource
157162
Error visitStringTableBundle(const RCResource *);

llvm/tools/llvm-rc/ResourceScriptParser.cpp

Lines changed: 108 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,8 @@ RCParser::ParseType RCParser::parseSingleResource() {
8080
Result = parseIconResource();
8181
else if (TypeToken->equalsLower("MENU"))
8282
Result = parseMenuResource();
83+
else if (TypeToken->equalsLower("MENUEX"))
84+
Result = parseMenuExResource();
8385
else if (TypeToken->equalsLower("RCDATA"))
8486
Result = parseUserDefinedResource(RkRcData);
8587
else if (TypeToken->equalsLower("VERSIONINFO"))
@@ -499,7 +501,7 @@ RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
499501
case Kind::String:
500502
case Kind::Identifier:
501503
return std::make_unique<UserDefinedResource>(Type, read().value(),
502-
MemoryFlags);
504+
MemoryFlags);
503505
default:
504506
break;
505507
}
@@ -518,7 +520,7 @@ RCParser::ParseType RCParser::parseUserDefinedResource(IntOrString Type) {
518520
}
519521

520522
return std::make_unique<UserDefinedResource>(Type, std::move(Data),
521-
MemoryFlags);
523+
MemoryFlags);
522524
}
523525

524526
RCParser::ParseType RCParser::parseVersionInfoResource() {
@@ -557,7 +559,8 @@ Expected<Control> RCParser::parseControl() {
557559
IntOrString Class;
558560
std::optional<IntWithNotMask> Style;
559561
if (ClassUpper == "CONTROL") {
560-
// CONTROL text, id, class, style, x, y, width, height [, exstyle] [, helpID]
562+
// CONTROL text, id, class, style, x, y, width, height [, exstyle] [,
563+
// helpID]
561564
ASSIGN_OR_RETURN(ClassStr, readString());
562565
RETURN_IF_ERROR(consumeType(Kind::Comma));
563566
Class = *ClassStr;
@@ -589,8 +592,8 @@ Expected<Control> RCParser::parseControl() {
589592
HelpID = *Val;
590593
}
591594

592-
return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1],
593-
(*Args)[2], (*Args)[3], Style, ExStyle, HelpID, Class);
595+
return Control(*ClassResult, Caption, *ID, (*Args)[0], (*Args)[1], (*Args)[2],
596+
(*Args)[3], Style, ExStyle, HelpID, Class);
594597
}
595598

596599
RCParser::ParseType RCParser::parseBitmapResource() {
@@ -620,7 +623,14 @@ RCParser::ParseType RCParser::parseMenuResource() {
620623
ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
621624
ASSIGN_OR_RETURN(Items, parseMenuItemsList());
622625
return std::make_unique<MenuResource>(std::move(*OptStatements),
623-
std::move(*Items), MemoryFlags);
626+
std::move(*Items), MemoryFlags);
627+
}
628+
629+
RCParser::ParseType RCParser::parseMenuExResource() {
630+
uint16_t MemoryFlags =
631+
parseMemoryFlags(MenuExResource::getDefaultMemoryFlags());
632+
ASSIGN_OR_RETURN(Items, parseMenuExItemsList());
633+
return std::make_unique<MenuExResource>(std::move(*Items), MemoryFlags);
624634
}
625635

626636
Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
@@ -682,14 +692,103 @@ Expected<MenuDefinitionList> RCParser::parseMenuItemsList() {
682692
return std::move(List);
683693
}
684694

695+
Expected<MenuDefinitionList> RCParser::parseMenuExItemsList() {
696+
RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
697+
698+
MenuDefinitionList List;
699+
700+
// Read a set of items. Each item is of one of two kinds:
701+
// MENUITEM caption:String [,[id][, [type][, state]]]]
702+
// POPUP caption:String [,[id][, [type][, [state][, helpID]]]] { popupBody }
703+
while (!consumeOptionalType(Kind::BlockEnd)) {
704+
ASSIGN_OR_RETURN(ItemTypeResult, readIdentifier());
705+
706+
bool IsMenuItem = ItemTypeResult->equals_insensitive("MENUITEM");
707+
bool IsPopup = ItemTypeResult->equals_insensitive("POPUP");
708+
if (!IsMenuItem && !IsPopup)
709+
return getExpectedError("MENUITEM, POPUP, END or '}'", true);
710+
711+
// Not a separator. Read the caption.
712+
ASSIGN_OR_RETURN(CaptionResult, readString());
713+
714+
// If MENUITEM, expect [,[id][, [type][, state]]]]
715+
if (IsMenuItem) {
716+
uint32_t MenuId = 0;
717+
uint32_t MenuType = 0;
718+
uint32_t MenuState = 0;
719+
720+
if (consumeOptionalType(Kind::Comma)) {
721+
auto IntId = readInt();
722+
if (IntId) {
723+
MenuId = *IntId;
724+
}
725+
if (consumeOptionalType(Kind::Comma)) {
726+
auto IntType = readInt();
727+
if (IntType) {
728+
MenuType = *IntType;
729+
}
730+
if (consumeOptionalType(Kind::Comma)) {
731+
auto IntState = readInt();
732+
if (IntState) {
733+
MenuState = *IntState;
734+
}
735+
}
736+
}
737+
}
738+
List.addDefinition(std::make_unique<MenuExItem>(*CaptionResult, MenuId,
739+
MenuType, MenuState));
740+
continue;
741+
}
742+
743+
assert(IsPopup);
744+
745+
uint32_t PopupId = 0;
746+
uint32_t PopupType = 0;
747+
uint32_t PopupState = 0;
748+
uint32_t PopupHelpID = 0;
749+
750+
if (consumeOptionalType(Kind::Comma)) {
751+
auto IntId = readInt();
752+
if (IntId) {
753+
PopupId = *IntId;
754+
}
755+
if (consumeOptionalType(Kind::Comma)) {
756+
auto IntType = readInt();
757+
if (IntType) {
758+
PopupType = *IntType;
759+
}
760+
if (consumeOptionalType(Kind::Comma)) {
761+
auto IntState = readInt();
762+
if (IntState) {
763+
PopupState = *IntState;
764+
}
765+
if (consumeOptionalType(Kind::Comma)) {
766+
auto IntHelpID = readInt();
767+
if (IntHelpID) {
768+
PopupHelpID = *IntHelpID;
769+
}
770+
}
771+
}
772+
}
773+
}
774+
// If POPUP, read submenu items recursively.
775+
ASSIGN_OR_RETURN(SubMenuResult, parseMenuExItemsList());
776+
List.addDefinition(std::make_unique<PopupExItem>(
777+
*CaptionResult, PopupId, PopupType, PopupState, PopupHelpID,
778+
std::move(*SubMenuResult)));
779+
}
780+
781+
return std::move(List);
782+
}
783+
685784
RCParser::ParseType RCParser::parseStringTableResource() {
686785
uint16_t MemoryFlags =
687786
parseMemoryFlags(StringTableResource::getDefaultMemoryFlags());
688787
ASSIGN_OR_RETURN(OptStatements, parseOptionalStatements());
689788
RETURN_IF_ERROR(consumeType(Kind::BlockBegin));
690789

691790
auto Table = std::make_unique<StringTableResource>(std::move(*OptStatements),
692-
MemoryFlags);
791+
MemoryFlags);
693792

694793
// Read strings until we reach the end of the block.
695794
while (!consumeOptionalType(Kind::BlockEnd)) {
@@ -753,7 +852,7 @@ Expected<std::unique_ptr<VersionInfoStmt>> RCParser::parseVersionInfoStmt() {
753852
PrecedingCommas.push_back(HadComma);
754853
}
755854
return std::make_unique<VersionInfoValue>(*KeyResult, std::move(Values),
756-
std::move(PrecedingCommas));
855+
std::move(PrecedingCommas));
757856
}
758857

759858
return getExpectedError("BLOCK or VALUE", true);
@@ -835,7 +934,7 @@ RCParser::ParseOptionType RCParser::parseFontStmt(OptStmtType DialogType) {
835934
}
836935
}
837936
return std::make_unique<FontStmt>(*SizeResult, *NameResult, FontWeight,
838-
FontItalic, FontCharset);
937+
FontItalic, FontCharset);
839938
}
840939

841940
RCParser::ParseOptionType RCParser::parseStyleStmt() {

llvm/tools/llvm-rc/ResourceScriptParser.h

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -143,6 +143,7 @@ class RCParser {
143143
ParseType parseIconResource();
144144
ParseType parseHTMLResource();
145145
ParseType parseMenuResource();
146+
ParseType parseMenuExResource();
146147
ParseType parseStringTableResource();
147148
ParseType parseUserDefinedResource(IntOrString Type);
148149
ParseType parseVersionInfoResource();
@@ -153,6 +154,9 @@ class RCParser {
153154
// Helper MENU parser.
154155
Expected<MenuDefinitionList> parseMenuItemsList();
155156

157+
// Helper MENUEX parser.
158+
Expected<MenuDefinitionList> parseMenuExItemsList();
159+
156160
// Helper VERSIONINFO parser - read the contents of a single BLOCK statement,
157161
// from BEGIN to END.
158162
Expected<std::unique_ptr<VersionInfoBlock>>

llvm/tools/llvm-rc/ResourceScriptStmt.cpp

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -102,19 +102,38 @@ raw_ostream &MenuSeparator::log(raw_ostream &OS) const {
102102
return OS << " Menu separator\n";
103103
}
104104

105+
raw_ostream &MenuExItem::log(raw_ostream &OS) const {
106+
OS << " MenuExItem (" << Name << "), ID = " << Id;
107+
OS << ", type: " << Type << ", state: " << State;
108+
return OS << "\n";
109+
}
110+
105111
raw_ostream &PopupItem::log(raw_ostream &OS) const {
106112
OS << " Popup (" << Name << ")";
107113
logFlags(OS, Flags);
108114
OS << ":\n";
109115
return SubItems.log(OS);
110116
}
111117

118+
raw_ostream &PopupExItem::log(raw_ostream &OS) const {
119+
OS << " Popup (" << Name << ")";
120+
OS << ", type: " << Type << ", state: " << State << ", help ID: " << HelpId;
121+
OS << ":\n";
122+
return SubItems.log(OS);
123+
}
124+
112125
raw_ostream &MenuResource::log(raw_ostream &OS) const {
113126
OS << "Menu (" << ResName << "):\n";
114127
OptStatements->log(OS);
115128
return Elements.log(OS);
116129
}
117130

131+
raw_ostream &MenuExResource::log(raw_ostream &OS) const {
132+
OS << "MenuEx (" << ResName << "):\n";
133+
OptStatements->log(OS);
134+
return Elements.log(OS);
135+
}
136+
118137
raw_ostream &StringTableResource::log(raw_ostream &OS) const {
119138
OS << "StringTable:\n";
120139
OptStatements->log(OS);

0 commit comments

Comments
 (0)