Skip to content

[llvm-rc] add support for MENUEX #67464

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 2 commits into from
Oct 6, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading