summaryrefslogtreecommitdiffstats
path: root/llvm/tools/llvm-rc
diff options
context:
space:
mode:
authorMarek Sokolowski <mnbvmar@gmail.com>2017-09-30 00:38:52 +0000
committerMarek Sokolowski <mnbvmar@gmail.com>2017-09-30 00:38:52 +0000
commit7f7745c03815507ddda56914e25bdb0751333571 (patch)
treea8daf25fff429e336cfdace4bc4cdec28b53f6d2 /llvm/tools/llvm-rc
parent17d0bb9611beb3e170caf9e1f4462c041265724b (diff)
downloadbcm5719-llvm-7f7745c03815507ddda56914e25bdb0751333571.tar.gz
bcm5719-llvm-7f7745c03815507ddda56914e25bdb0751333571.zip
[llvm-rc] Serialize DIALOG(EX) to .res files (serialization, pt 4).
This is now able to serialize DIALOG and DIALOGEX resources to .res files. It still can't parse dialog-specific CAPTION, FONT, and STYLE optional statement - these will be added in the following patch. A limited set of controls is included. However, more can be easily added by extending SupportedCtls map defined in ResourceScriptStmt.cpp. Differential Revision: https://reviews.llvm.org/D37862 llvm-svn: 314578
Diffstat (limited to 'llvm/tools/llvm-rc')
-rw-r--r--llvm/tools/llvm-rc/ResourceFileWriter.cpp169
-rw-r--r--llvm/tools/llvm-rc/ResourceFileWriter.h5
-rw-r--r--llvm/tools/llvm-rc/ResourceScriptParser.cpp10
-rw-r--r--llvm/tools/llvm-rc/ResourceScriptStmt.cpp13
-rw-r--r--llvm/tools/llvm-rc/ResourceScriptStmt.h48
-rw-r--r--llvm/tools/llvm-rc/ResourceVisitor.h1
6 files changed, 229 insertions, 17 deletions
diff --git a/llvm/tools/llvm-rc/ResourceFileWriter.cpp b/llvm/tools/llvm-rc/ResourceFileWriter.cpp
index af002e347be..701c4449ffa 100644
--- a/llvm/tools/llvm-rc/ResourceFileWriter.cpp
+++ b/llvm/tools/llvm-rc/ResourceFileWriter.cpp
@@ -60,6 +60,25 @@ static Error checkNumberFits(uint32_t Number, Twine FieldName) {
return checkNumberFits(Number, sizeof(FitType) * 8, FieldName);
}
+// A similar function for signed integers.
+template <typename FitType>
+static Error checkSignedNumberFits(uint32_t Number, Twine FieldName,
+ bool CanBeNegative) {
+ int32_t SignedNum = Number;
+ if (SignedNum < std::numeric_limits<FitType>::min() ||
+ SignedNum > std::numeric_limits<FitType>::max())
+ return createError(FieldName + " (" + Twine(SignedNum) +
+ ") does not fit in " + Twine(sizeof(FitType) * 8) +
+ "-bit signed integer type.",
+ std::errc::value_too_large);
+
+ if (!CanBeNegative && SignedNum < 0)
+ return createError(FieldName + " (" + Twine(SignedNum) +
+ ") cannot be negative.");
+
+ return Error::success();
+}
+
static Error checkIntOrString(IntOrString Value, Twine FieldName) {
if (!Value.isInt())
return Error::success();
@@ -201,6 +220,10 @@ Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) {
return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody);
}
+Error ResourceFileWriter::visitDialogResource(const RCResource *Res) {
+ return writeResource(Res, &ResourceFileWriter::writeDialogBody);
+}
+
Error ResourceFileWriter::visitHTMLResource(const RCResource *Res) {
return writeResource(Res, &ResourceFileWriter::writeHTMLBody);
}
@@ -379,8 +402,152 @@ Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) {
return Error::success();
}
-// --- HTMLResource helpers. --- //
+// --- DialogResource helpers. --- //
+
+Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl,
+ bool IsExtended) {
+ // Each control should be aligned to DWORD.
+ padStream(sizeof(uint32_t));
+
+ auto TypeInfo = Control::SupportedCtls.lookup(Ctl.Type);
+ uint32_t CtlStyle = TypeInfo.Style | Ctl.Style.getValueOr(0);
+ uint32_t CtlExtStyle = Ctl.ExtStyle.getValueOr(0);
+
+ // DIALOG(EX) item header prefix.
+ if (!IsExtended) {
+ struct {
+ ulittle32_t Style;
+ ulittle32_t ExtStyle;
+ } Prefix{ulittle32_t(CtlStyle), ulittle32_t(CtlExtStyle)};
+ writeObject(Prefix);
+ } else {
+ struct {
+ ulittle32_t HelpID;
+ ulittle32_t ExtStyle;
+ ulittle32_t Style;
+ } Prefix{ulittle32_t(Ctl.HelpID.getValueOr(0)), ulittle32_t(CtlExtStyle),
+ ulittle32_t(CtlStyle)};
+ writeObject(Prefix);
+ }
+
+ // Common fixed-length part.
+ RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
+ Ctl.X, "Dialog control x-coordinate", true));
+ RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
+ Ctl.Y, "Dialog control y-coordinate", true));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Ctl.Width, "Dialog control width", false));
+ RETURN_IF_ERROR(checkSignedNumberFits<int16_t>(
+ Ctl.Height, "Dialog control height", false));
+ struct {
+ ulittle16_t X;
+ ulittle16_t Y;
+ ulittle16_t Width;
+ ulittle16_t Height;
+ } Middle{ulittle16_t(Ctl.X), ulittle16_t(Ctl.Y), ulittle16_t(Ctl.Width),
+ ulittle16_t(Ctl.Height)};
+ writeObject(Middle);
+
+ // ID; it's 16-bit in DIALOG and 32-bit in DIALOGEX.
+ if (!IsExtended) {
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(
+ Ctl.ID, "Control ID in simple DIALOG resource"));
+ writeInt<uint16_t>(Ctl.ID);
+ } else {
+ writeInt<uint32_t>(Ctl.ID);
+ }
+
+ // Window class - either 0xFFFF + 16-bit integer or a string.
+ RETURN_IF_ERROR(writeIntOrString(IntOrString(TypeInfo.CtlClass)));
+
+ // Element caption/reference ID. ID is preceded by 0xFFFF.
+ RETURN_IF_ERROR(checkIntOrString(Ctl.Title, "Control reference ID"));
+ RETURN_IF_ERROR(writeIntOrString(Ctl.Title));
+ // # bytes of extra creation data count. Don't pass any.
+ writeInt<uint16_t>(0);
+
+ return Error::success();
+}
+
+Error ResourceFileWriter::writeDialogBody(const RCResource *Base) {
+ auto *Res = cast<DialogResource>(Base);
+
+ // Default style: WS_POPUP | WS_BORDER | WS_SYSMENU.
+ const uint32_t UsedStyle = 0x80880000;
+
+ // Write DIALOG(EX) header prefix. These are pretty different.
+ if (!Res->IsExtended) {
+ struct {
+ ulittle32_t Style;
+ ulittle32_t ExtStyle;
+ } Prefix{ulittle32_t(UsedStyle),
+ ulittle32_t(0)}; // As of now, we don't keep EXSTYLE.
+
+ writeObject(Prefix);
+ } else {
+ const uint16_t DialogExMagic = 0xFFFF;
+
+ struct {
+ ulittle16_t Version;
+ ulittle16_t Magic;
+ ulittle32_t HelpID;
+ ulittle32_t ExtStyle;
+ ulittle32_t Style;
+ } Prefix{ulittle16_t(1), ulittle16_t(DialogExMagic),
+ ulittle32_t(Res->HelpID), ulittle32_t(0), ulittle32_t(UsedStyle)};
+
+ writeObject(Prefix);
+ }
+
+ // Now, a common part. First, fixed-length fields.
+ RETURN_IF_ERROR(checkNumberFits<uint16_t>(Res->Controls.size(),
+ "Number of dialog controls"));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->X, "Dialog x-coordinate", true));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->Y, "Dialog y-coordinate", true));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->Width, "Dialog width", false));
+ RETURN_IF_ERROR(
+ checkSignedNumberFits<int16_t>(Res->Height, "Dialog height", false));
+ struct {
+ ulittle16_t Count;
+ ulittle16_t PosX;
+ ulittle16_t PosY;
+ ulittle16_t DialogWidth;
+ ulittle16_t DialogHeight;
+ } Middle{ulittle16_t(Res->Controls.size()), ulittle16_t(Res->X),
+ ulittle16_t(Res->Y), ulittle16_t(Res->Width),
+ ulittle16_t(Res->Height)};
+ writeObject(Middle);
+
+ // MENU field. As of now, we don't keep them in the state and can peacefully
+ // think there is no menu attached to the dialog.
+ writeInt<uint16_t>(0);
+
+ // Window CLASS field. Not kept here.
+ writeInt<uint16_t>(0);
+
+ // Window title. There is no title for now, so all we output is '\0'.
+ writeInt<uint16_t>(0);
+
+ auto handleCtlError = [&](Error &&Err, const Control &Ctl) -> Error {
+ if (!Err)
+ return Error::success();
+ return joinErrors(createError("Error in " + Twine(Ctl.Type) +
+ " control (ID " + Twine(Ctl.ID) + "):"),
+ std::move(Err));
+ };
+
+ for (auto &Ctl : Res->Controls)
+ RETURN_IF_ERROR(
+ handleCtlError(writeSingleDialogControl(Ctl, Res->IsExtended), Ctl));
+
+ return Error::success();
+}
+
+// --- HTMLResource helpers. --- //
Error ResourceFileWriter::writeHTMLBody(const RCResource *Base) {
return appendFile(cast<HTMLResource>(Base)->HTMLLoc);
diff --git a/llvm/tools/llvm-rc/ResourceFileWriter.h b/llvm/tools/llvm-rc/ResourceFileWriter.h
index ef736baa169..e9f9695cca6 100644
--- a/llvm/tools/llvm-rc/ResourceFileWriter.h
+++ b/llvm/tools/llvm-rc/ResourceFileWriter.h
@@ -31,6 +31,7 @@ public:
Error visitNullResource(const RCResource *) override;
Error visitAcceleratorsResource(const RCResource *) override;
+ Error visitDialogResource(const RCResource *) override;
Error visitHTMLResource(const RCResource *) override;
Error visitMenuResource(const RCResource *) override;
@@ -61,6 +62,10 @@ private:
bool IsLastItem);
Error writeAcceleratorsBody(const RCResource *);
+ // DialogResource
+ Error writeSingleDialogControl(const Control &, bool IsExtended);
+ Error writeDialogBody(const RCResource *);
+
// HTMLResource
Error writeHTMLBody(const RCResource *);
diff --git a/llvm/tools/llvm-rc/ResourceScriptParser.cpp b/llvm/tools/llvm-rc/ResourceScriptParser.cpp
index d4aa4b24379..1e32d4d1a9d 100644
--- a/llvm/tools/llvm-rc/ResourceScriptParser.cpp
+++ b/llvm/tools/llvm-rc/ResourceScriptParser.cpp
@@ -458,15 +458,17 @@ Expected<Control> RCParser::parseControl() {
// [class] text, id, x, y, width, height [, style] [, exstyle] [, helpID]
// [class] id, x, y, width, height [, style] [, exstyle] [, helpID]
// Note that control ids must be integers.
+ // Text might be either a string or an integer pointing to resource ID.
ASSIGN_OR_RETURN(ClassResult, readIdentifier());
std::string ClassUpper = ClassResult->upper();
- if (Control::SupportedCtls.find(ClassUpper) == Control::SupportedCtls.end())
+ auto CtlInfo = Control::SupportedCtls.find(ClassUpper);
+ if (CtlInfo == Control::SupportedCtls.end())
return getExpectedError("control type, END or '}'", true);
// Read caption if necessary.
- StringRef Caption;
- if (Control::CtlsWithTitle.find(ClassUpper) != Control::CtlsWithTitle.end()) {
- ASSIGN_OR_RETURN(CaptionResult, readString());
+ IntOrString Caption{StringRef()};
+ if (CtlInfo->getValue().HasTitle) {
+ ASSIGN_OR_RETURN(CaptionResult, readIntOrString());
RETURN_IF_ERROR(consumeType(Kind::Comma));
Caption = *CaptionResult;
}
diff --git a/llvm/tools/llvm-rc/ResourceScriptStmt.cpp b/llvm/tools/llvm-rc/ResourceScriptStmt.cpp
index 3e5a0e4d32b..3897ca3a512 100644
--- a/llvm/tools/llvm-rc/ResourceScriptStmt.cpp
+++ b/llvm/tools/llvm-rc/ResourceScriptStmt.cpp
@@ -120,11 +120,14 @@ raw_ostream &StringTableResource::log(raw_ostream &OS) const {
return OS;
}
-const StringSet<> Control::SupportedCtls = {
- "LTEXT", "RTEXT", "CTEXT", "PUSHBUTTON", "DEFPUSHBUTTON", "EDITTEXT"};
-
-const StringSet<> Control::CtlsWithTitle = {"LTEXT", "RTEXT", "CTEXT",
- "PUSHBUTTON", "DEFPUSHBUTTON"};
+const StringMap<Control::CtlInfo> Control::SupportedCtls = {
+ {"LTEXT", CtlInfo{0x50020000, ClsStatic, true}},
+ {"CTEXT", CtlInfo{0x50020001, ClsStatic, true}},
+ {"RTEXT", CtlInfo{0x50020002, ClsStatic, true}},
+ {"PUSHBUTTON", CtlInfo{0x50010000, ClsButton, true}},
+ {"DEFPUSHBUTTON", CtlInfo{0x50010001, ClsButton, true}},
+ {"EDITTEXT", CtlInfo{0x50810000, ClsEdit, false}},
+};
raw_ostream &Control::log(raw_ostream &OS) const {
OS << " Control (" << ID << "): " << Type << ", title: " << Title
diff --git a/llvm/tools/llvm-rc/ResourceScriptStmt.h b/llvm/tools/llvm-rc/ResourceScriptStmt.h
index d00e178426d..692d2aec5e7 100644
--- a/llvm/tools/llvm-rc/ResourceScriptStmt.h
+++ b/llvm/tools/llvm-rc/ResourceScriptStmt.h
@@ -439,21 +439,40 @@ public:
// Single control definition.
class Control {
- StringRef Type, Title;
+public:
+ StringRef Type;
+ IntOrString Title;
uint32_t ID, X, Y, Width, Height;
Optional<uint32_t> Style, ExtStyle, HelpID;
-public:
- Control(StringRef CtlType, StringRef CtlTitle, uint32_t CtlID, uint32_t PosX,
- uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight,
+ // Control classes as described in DLGITEMTEMPLATEEX documentation.
+ //
+ // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms645389.aspx
+ enum CtlClasses {
+ ClsButton = 0x80,
+ ClsEdit = 0x81,
+ ClsStatic = 0x82,
+ ClsListBox = 0x83,
+ ClsScrollBar = 0x84,
+ ClsComboBox = 0x85
+ };
+
+ // Simple information about a single control type.
+ struct CtlInfo {
+ uint32_t Style;
+ uint16_t CtlClass;
+ bool HasTitle;
+ };
+
+ Control(StringRef CtlType, IntOrString CtlTitle, uint32_t CtlID,
+ uint32_t PosX, uint32_t PosY, uint32_t ItemWidth, uint32_t ItemHeight,
Optional<uint32_t> ItemStyle, Optional<uint32_t> ExtItemStyle,
Optional<uint32_t> CtlHelpID)
: Type(CtlType), Title(CtlTitle), ID(CtlID), X(PosX), Y(PosY),
Width(ItemWidth), Height(ItemHeight), Style(ItemStyle),
ExtStyle(ExtItemStyle), HelpID(CtlHelpID) {}
- static const StringSet<> SupportedCtls;
- static const StringSet<> CtlsWithTitle;
+ static const StringMap<CtlInfo> SupportedCtls;
raw_ostream &log(raw_ostream &) const;
};
@@ -462,11 +481,11 @@ public:
// DIALOGEX because of their being too similar to each other. We only have a
// flag determining the type of the dialog box.
class DialogResource : public OptStatementsRCResource {
+public:
uint32_t X, Y, Width, Height, HelpID;
std::vector<Control> Controls;
bool IsExtended;
-public:
DialogResource(uint32_t PosX, uint32_t PosY, uint32_t DlgWidth,
uint32_t DlgHeight, uint32_t DlgHelpID,
OptionalStmtList &&OptStmts, bool IsDialogEx)
@@ -477,6 +496,21 @@ public:
void addControl(Control &&Ctl) { Controls.push_back(std::move(Ctl)); }
raw_ostream &log(raw_ostream &) const override;
+
+ // It was a weird design decision to assign the same resource type number
+ // both for DIALOG and DIALOGEX (and the same structure version number).
+ // It makes it possible for DIALOG to be mistaken for DIALOGEX.
+ IntOrString getResourceType() const override { return RkDialog; }
+ Twine getResourceTypeName() const override {
+ return "DIALOG" + Twine(IsExtended ? "EX" : "");
+ }
+ Error visit(Visitor *V) const override {
+ return V->visitDialogResource(this);
+ }
+ ResourceKind getKind() const override { return RkDialog; }
+ static bool classof(const RCResource *Res) {
+ return Res->getKind() == RkDialog;
+ }
};
// User-defined resource. It is either:
diff --git a/llvm/tools/llvm-rc/ResourceVisitor.h b/llvm/tools/llvm-rc/ResourceVisitor.h
index 14992dac5af..5655a53333a 100644
--- a/llvm/tools/llvm-rc/ResourceVisitor.h
+++ b/llvm/tools/llvm-rc/ResourceVisitor.h
@@ -29,6 +29,7 @@ class Visitor {
public:
virtual Error visitNullResource(const RCResource *) = 0;
virtual Error visitAcceleratorsResource(const RCResource *) = 0;
+ virtual Error visitDialogResource(const RCResource *) = 0;
virtual Error visitHTMLResource(const RCResource *) = 0;
virtual Error visitMenuResource(const RCResource *) = 0;
OpenPOWER on IntegriCloud