diff options
| author | Marek Sokolowski <mnbvmar@gmail.com> | 2017-09-30 00:38:52 +0000 |
|---|---|---|
| committer | Marek Sokolowski <mnbvmar@gmail.com> | 2017-09-30 00:38:52 +0000 |
| commit | 7f7745c03815507ddda56914e25bdb0751333571 (patch) | |
| tree | a8daf25fff429e336cfdace4bc4cdec28b53f6d2 /llvm/tools/llvm-rc | |
| parent | 17d0bb9611beb3e170caf9e1f4462c041265724b (diff) | |
| download | bcm5719-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.cpp | 169 | ||||
| -rw-r--r-- | llvm/tools/llvm-rc/ResourceFileWriter.h | 5 | ||||
| -rw-r--r-- | llvm/tools/llvm-rc/ResourceScriptParser.cpp | 10 | ||||
| -rw-r--r-- | llvm/tools/llvm-rc/ResourceScriptStmt.cpp | 13 | ||||
| -rw-r--r-- | llvm/tools/llvm-rc/ResourceScriptStmt.h | 48 | ||||
| -rw-r--r-- | llvm/tools/llvm-rc/ResourceVisitor.h | 1 |
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; |

