Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-coord-neg.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-coord-neg.rc @@ -0,0 +1,3 @@ +1 DIALOG 1, 1, 1, 1 { + LTEXT "u", 1, 5, -32769, 5, 5 +} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-coord.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-coord.rc @@ -0,0 +1,3 @@ +1 DIALOGEX 1, 1, 1, 1 { + LTEXT "a", 1, 44444, 5, 6, 7 +} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-id.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-id.rc @@ -0,0 +1,3 @@ +5 DIALOG 1, 2, 3, 4 { + RTEXT "Too large ID", 100000, 1, 2, 3, 4 +} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-ref-id.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-ref-id.rc @@ -0,0 +1,3 @@ +1 DIALOGEX 1, 2, 3, 4 { + CTEXT 65536, 42, 1, 1, 1, 1 +} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-size.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-large-size.rc @@ -0,0 +1,3 @@ +1 DIALOGEX 1, 2, 3, 4 { + LTEXT "L", 1, 15, 15, 40000, 15 +} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-negative-size.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-ctl-negative-size.rc @@ -0,0 +1,3 @@ +1 DIALOG 1, 1, 1, 1 { + LTEXT "u", 1, 5, 5, 5, -700 +} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-large-coord-neg.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-large-coord-neg.rc @@ -0,0 +1 @@ +1 DIALOG 1, -40000, 14, 15 {} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-large-coord.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-large-coord.rc @@ -0,0 +1 @@ +1 DIALOGEX 50000, 654321, 100, 100 {} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-large-size.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-large-size.rc @@ -0,0 +1 @@ +1 DIALOGEX 100, 100, 12345, 32768 {} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog-negative-size.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog-negative-size.rc @@ -0,0 +1 @@ +1 DIALOGEX 100, 100, -50, 13 {} Index: llvm/test/tools/llvm-rc/Inputs/tag-dialog.rc =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/Inputs/tag-dialog.rc @@ -0,0 +1,44 @@ +Empty DIALOGEX 2, 3, 4, 5 {} + +Args DIALOGEX 2, 3, 4, 5 { + LTEXT "Left text", 1, 0, 0, 50, 10 + RTEXT "Right text", 2, 12, 0, 50, 10, 42 + LTEXT "Left text 2", 3, 24, 0, 50, 10, 0xBADCAFE, 0xBAD00BAD + RTEXT "Right text 2", 4, 36, 0, 50, 10, 1, 2, 0x12345678 + + EDITTEXT 16, 100, 0, 60, 10 + EDITTEXT 17, 100, 16, 60, 10, 0xAABB0000 + EDITTEXT 18, 100, 32, 60, 10, 0xA000000B, 0xCC0000DD + EDITTEXT 19, 100, 32, 60, 10, 0, 0, 3456789012 + + PUSHBUTTON "Push 1", 32, 200, 0, 54, 11 + PUSHBUTTON "Push 2", 33, 201, 15, 54, 11, 12345 + PUSHBUTTON "Push 3", 34, 202, 30, 54, 11, 0xA, 0xC0000042 + PUSHBUTTON "Push 4", 35, 200, 45, 54, 11, 0, 1, 2 +} + +Types DIALOGEX 12345, -11215, 0x1234, 0x1EED, 0x51525354 { + LTEXT "L", 1, 2, 3, 4, 5 + CTEXT "C", 6, 7, 8, 9, 10 + RTEXT "R", 11, 12, 13, 14, 15 + + PUSHBUTTON "PB", 1001, 1002, 1003, 1004, 1005 + DEFPUSHBUTTON "DPB", 1006, 1007, 1008, 1009, 1010 + + EDITTEXT 2001, 2002, 2003, 2004, 2005 + + LTEXT 65535, 3001, 3002, 3003, 3004, 3005 +} + +EmptyOld DIALOG 1, 2, 3, 4 {} + +ArgsOld DIALOG 1, 2, 3, 4 { + LTEXT "L", 1, 2, 3, 4, 5 + LTEXT "L2", 6, 7, 8, 9, 10, 11 + LTEXT "L3", 12, 13, 14, 15, 16, 17, 18 + + EDITTEXT 19, 20, 21, 22, 23 + EDITTEXT 24, 25, 26, 27, 28, 29 + EDITTEXT 30, 31, 32, 33, 34, 35, 36 +} + Index: llvm/test/tools/llvm-rc/tag-dialog.test =================================================================== --- /dev/null +++ llvm/test/tools/llvm-rc/tag-dialog.test @@ -0,0 +1,199 @@ +; RUN: llvm-rc /FO %t %p/Inputs/tag-dialog.rc +; RUN: llvm-readobj %t | FileCheck %s --check-prefix=DIALOG + +; DIALOG: Resource type (int): 5 +; DIALOG-NEXT: Resource name (string): EMPTY +; DIALOG-NEXT: Data version: 0 +; DIALOG-NEXT: Memory flags: 0x1030 +; DIALOG-NEXT: Language ID: 1033 +; DIALOG-NEXT: Version (major): 0 +; DIALOG-NEXT: Version (minor): 0 +; DIALOG-NEXT: Characteristics: 0 +; DIALOG-NEXT: Data size: 32 +; DIALOG-NEXT: Data: ( +; DIALOG-NEXT: 0000: 0100FFFF 00000000 00000000 00008880 |................| +; DIALOG-NEXT: 0010: 00000200 03000400 05000000 00000000 |................| +; DIALOG-NEXT: ) + +; DIALOG-DAG: Resource type (int): 5 +; DIALOG-NEXT: Resource name (string): ARGS +; DIALOG-NEXT: Data version: 0 +; DIALOG-NEXT: Memory flags: 0x1030 +; DIALOG-NEXT: Language ID: 1033 +; DIALOG-NEXT: Version (major): 0 +; DIALOG-NEXT: Version (minor): 0 +; DIALOG-NEXT: Characteristics: 0 +; DIALOG-NEXT: Data size: 552 +; DIALOG-NEXT: Data: ( +; DIALOG-NEXT: 0000: 0100FFFF 00000000 00000000 00008880 |................| +; DIALOG-NEXT: 0010: 0C000200 03000400 05000000 00000000 |................| +; DIALOG-NEXT: 0020: 00000000 00000000 00000250 00000000 |...........P....| +; DIALOG-NEXT: 0030: 32000A00 01000000 FFFF8200 4C006500 |2...........L.e.| +; DIALOG-NEXT: 0040: 66007400 20007400 65007800 74000000 |f.t. .t.e.x.t...| +; DIALOG-NEXT: 0050: 00000000 00000000 00000000 2A000250 |............*..P| +; DIALOG-NEXT: 0060: 0C000000 32000A00 02000000 FFFF8200 |....2...........| +; DIALOG-NEXT: 0070: 52006900 67006800 74002000 74006500 |R.i.g.h.t. .t.e.| +; DIALOG-NEXT: 0080: 78007400 00000000 00000000 AD0BD0BA |x.t.............| +; DIALOG-NEXT: 0090: FECAAF5B 18000000 32000A00 03000000 |...[....2.......| +; DIALOG-NEXT: 00A0: FFFF8200 4C006500 66007400 20007400 |....L.e.f.t. .t.| +; DIALOG-NEXT: 00B0: 65007800 74002000 32000000 00000000 |e.x.t. .2.......| +; DIALOG-NEXT: 00C0: 78563412 02000000 03000250 24000000 |xV4........P$...| +; DIALOG-NEXT: 00D0: 32000A00 04000000 FFFF8200 52006900 |2...........R.i.| +; DIALOG-NEXT: 00E0: 67006800 74002000 74006500 78007400 |g.h.t. .t.e.x.t.| +; DIALOG-NEXT: 00F0: 20003200 00000000 00000000 00000000 | .2.............| +; DIALOG-NEXT: 0100: 00008150 64000000 3C000A00 10000000 |...Pd...<.......| +; DIALOG-NEXT: 0110: FFFF8100 00000000 00000000 00000000 |................| +; DIALOG-NEXT: 0120: 0000BBFA 64001000 3C000A00 11000000 |....d...<.......| +; DIALOG-NEXT: 0130: FFFF8100 00000000 00000000 DD0000CC |................| +; DIALOG-NEXT: 0140: 0B0081F0 64002000 3C000A00 12000000 |....d. .<.......| +; DIALOG-NEXT: 0150: FFFF8100 00000000 146A0ACE 00000000 |.........j......| +; DIALOG-NEXT: 0160: 00008150 64002000 3C000A00 13000000 |...Pd. .<.......| +; DIALOG-NEXT: 0170: FFFF8100 00000000 00000000 00000000 |................| +; DIALOG-NEXT: 0180: 00000150 C8000000 36000B00 20000000 |...P....6... ...| +; DIALOG-NEXT: 0190: FFFF8000 50007500 73006800 20003100 |....P.u.s.h. .1.| +; DIALOG-NEXT: 01A0: 00000000 00000000 00000000 39300150 |............90.P| +; DIALOG-NEXT: 01B0: C9000F00 36000B00 21000000 FFFF8000 |....6...!.......| +; DIALOG-NEXT: 01C0: 50007500 73006800 20003200 00000000 |P.u.s.h. .2.....| +; DIALOG-NEXT: 01D0: 00000000 420000C0 0A000150 CA001E00 |....B......P....| +; DIALOG-NEXT: 01E0: 36000B00 22000000 FFFF8000 50007500 |6...".......P.u.| +; DIALOG-NEXT: 01F0: 73006800 20003300 00000000 02000000 |s.h. .3.........| +; DIALOG-NEXT: 0200: 01000000 00000150 C8002D00 36000B00 |.......P..-.6...| +; DIALOG-NEXT: 0210: 23000000 FFFF8000 50007500 73006800 |#.......P.u.s.h.| +; DIALOG-NEXT: 0220: 20003400 00000000 | .4.....| +; DIALOG-NEXT: ) + +; DIALOG-DAG: Resource type (int): 5 +; DIALOG-NEXT: Resource name (string): TYPES +; DIALOG-NEXT: Data version: 0 +; DIALOG-NEXT: Memory flags: 0x1030 +; DIALOG-NEXT: Language ID: 1033 +; DIALOG-NEXT: Version (major): 0 +; DIALOG-NEXT: Version (minor): 0 +; DIALOG-NEXT: Characteristics: 0 +; DIALOG-NEXT: Data size: 282 +; DIALOG-NEXT: Data: ( +; DIALOG-NEXT: 0000: 0100FFFF 54535251 00000000 00008880 |....TSRQ........| +; DIALOG-NEXT: 0010: 07003930 31D43412 ED1E0000 00000000 |..901.4.........| +; DIALOG-NEXT: 0020: 00000000 00000000 00000250 02000300 |...........P....| +; DIALOG-NEXT: 0030: 04000500 01000000 FFFF8200 4C000000 |............L...| +; DIALOG-NEXT: 0040: 00000000 00000000 00000000 01000250 |...............P| +; DIALOG-NEXT: 0050: 07000800 09000A00 06000000 FFFF8200 |................| +; DIALOG-NEXT: 0060: 43000000 00000000 00000000 00000000 |C...............| +; DIALOG-NEXT: 0070: 02000250 0C000D00 0E000F00 0B000000 |...P............| +; DIALOG-NEXT: 0080: FFFF8200 52000000 00000000 00000000 |....R...........| +; DIALOG-NEXT: 0090: 00000000 00000150 EA03EB03 EC03ED03 |.......P........| +; DIALOG-NEXT: 00A0: E9030000 FFFF8000 50004200 00000000 |........P.B.....| +; DIALOG-NEXT: 00B0: 00000000 00000000 01000150 EF03F003 |...........P....| +; DIALOG-NEXT: 00C0: F103F203 EE030000 FFFF8000 44005000 |............D.P.| +; DIALOG-NEXT: 00D0: 42000000 00000000 00000000 00000000 |B...............| +; DIALOG-NEXT: 00E0: 00008150 D207D307 D407D507 D1070000 |...P............| +; DIALOG-NEXT: 00F0: FFFF8100 00000000 00000000 00000000 |................| +; DIALOG-NEXT: 0100: 00000250 BA0BBB0B BC0BBD0B B90B0000 |...P............| +; DIALOG-NEXT: 0110: FFFF8200 FFFFFFFF 0000 |..........| +; DIALOG-NEXT: ) + +; DIALOG-DAG: Resource type (int): 5 +; DIALOG-NEXT: Resource name (string): EMPTYOLD +; DIALOG-NEXT: Data version: 0 +; DIALOG-NEXT: Memory flags: 0x1030 +; DIALOG-NEXT: Language ID: 1033 +; DIALOG-NEXT: Version (major): 0 +; DIALOG-NEXT: Version (minor): 0 +; DIALOG-NEXT: Characteristics: 0 +; DIALOG-NEXT: Data size: 24 +; DIALOG-NEXT: Data: ( +; DIALOG-NEXT: 0000: 00008880 00000000 00000100 02000300 |................| +; DIALOG-NEXT: 0010: 04000000 00000000 |........| +; DIALOG-NEXT: ) + +; DIALOG-DAG: Resource type (int): 5 +; DIALOG-NEXT: Resource name (string): ARGSOLD +; DIALOG-NEXT: Data version: 0 +; DIALOG-NEXT: Memory flags: 0x1030 +; DIALOG-NEXT: Language ID: 1033 +; DIALOG-NEXT: Version (major): 0 +; DIALOG-NEXT: Version (minor): 0 +; DIALOG-NEXT: Characteristics: 0 +; DIALOG-NEXT: Data size: 198 +; DIALOG-NEXT: Data: ( +; DIALOG-NEXT: 0000: 00008880 00000000 06000100 02000300 |................| +; DIALOG-NEXT: 0010: 04000000 00000000 00000250 00000000 |...........P....| +; DIALOG-NEXT: 0020: 02000300 04000500 0100FFFF 82004C00 |..............L.| +; DIALOG-NEXT: 0030: 00000000 0B000250 00000000 07000800 |.......P........| +; DIALOG-NEXT: 0040: 09000A00 0600FFFF 82004C00 32000000 |..........L.2...| +; DIALOG-NEXT: 0050: 00000000 11000250 12000000 0D000E00 |.......P........| +; DIALOG-NEXT: 0060: 0F001000 0C00FFFF 82004C00 33000000 |..........L.3...| +; DIALOG-NEXT: 0070: 00000000 00008150 00000000 14001500 |.......P........| +; DIALOG-NEXT: 0080: 16001700 1300FFFF 81000000 00000000 |................| +; DIALOG-NEXT: 0090: 1D008150 00000000 19001A00 1B001C00 |...P............| +; DIALOG-NEXT: 00A0: 1800FFFF 81000000 00000000 23008150 |............#..P| +; DIALOG-NEXT: 00B0: 24000000 1F002000 21002200 1E00FFFF |$..... .!.".....| +; DIALOG-NEXT: 00C0: 81000000 0000 |......| +; DIALOG-NEXT: ) + + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-large-coord.rc 2>&1 | FileCheck %s --check-prefix COORD1 + +; COORD1: llvm-rc: Error in DIALOGEX statement (ID 1): +; COORD1-NEXT: Dialog x-coordinate (50000) does not fit in 16-bit signed integer type. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-large-coord-neg.rc 2>&1 | FileCheck %s --check-prefix COORD2 + +; COORD2: llvm-rc: Error in DIALOG statement (ID 1): +; COORD2-NEXT: Dialog y-coordinate (-40000) does not fit in 16-bit signed integer type. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-large-size.rc 2>&1 | FileCheck %s --check-prefix COORD3 + +; COORD3: llvm-rc: Error in DIALOGEX statement (ID 1): +; COORD3-NEXT: Dialog height (32768) does not fit in 16-bit signed integer type. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-negative-size.rc 2>&1 | FileCheck %s --check-prefix COORD4 + +; COORD4: llvm-rc: Error in DIALOGEX statement (ID 1): +; COORD4-NEXT: Dialog width (-50) cannot be negative. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-ctl-large-coord.rc 2>&1 | FileCheck %s --check-prefix CTL-COORD1 + +; CTL-COORD1: llvm-rc: Error in DIALOGEX statement (ID 1): +; CTL-COORD1-NEXT: Error in LTEXT control (ID 1): +; CTL-COORD1-NEXT: Dialog control x-coordinate (44444) does not fit in 16-bit signed integer type. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-ctl-large-coord-neg.rc 2>&1 | FileCheck %s --check-prefix CTL-COORD2 + +; CTL-COORD2: llvm-rc: Error in DIALOG statement (ID 1): +; CTL-COORD2-NEXT: Error in LTEXT control (ID 1): +; CTL-COORD2-NEXT: Dialog control y-coordinate (-32769) does not fit in 16-bit signed integer type. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-ctl-large-size.rc 2>&1 | FileCheck %s --check-prefix CTL-COORD3 + +; CTL-COORD3: llvm-rc: Error in DIALOGEX statement (ID 1): +; CTL-COORD3-NEXT: Error in LTEXT control (ID 1): +; CTL-COORD3-NEXT: Dialog control width (40000) does not fit in 16-bit signed integer type. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-ctl-negative-size.rc 2>&1 | FileCheck %s --check-prefix CTL-COORD4 + +; CTL-COORD4: llvm-rc: Error in DIALOG statement (ID 1): +; CTL-COORD4-NEXT: Error in LTEXT control (ID 1): +; CTL-COORD4-NEXT: Dialog control height (-700) cannot be negative. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-ctl-large-id.rc 2>&1 | FileCheck %s --check-prefix CTL-ID + +; CTL-ID: llvm-rc: Error in DIALOG statement (ID 5): +; CTL-ID-NEXT: Error in RTEXT control (ID 100000): +; CTL-ID-NEXT: Control ID in simple DIALOG resource (100000) does not fit in 16 bits. + + +; RUN: not llvm-rc /FO %t %p/Inputs/tag-dialog-ctl-large-ref-id.rc 2>&1 | FileCheck %s --check-prefix CTL-REF-ID + +; CTL-REF-ID: llvm-rc: Error in DIALOGEX statement (ID 1): +; CTL-REF-ID-NEXT: Error in CTEXT control (ID 42): +; CTL-REF-ID-NEXT: Control reference ID (65536) does not fit in 16 bits. Index: llvm/tools/llvm-rc/ResourceFileWriter.h =================================================================== --- llvm/tools/llvm-rc/ResourceFileWriter.h +++ llvm/tools/llvm-rc/ResourceFileWriter.h @@ -31,6 +31,7 @@ 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 @@ bool IsLastItem); Error writeAcceleratorsBody(const RCResource *); + // DialogResource + Error writeSingleDialogControl(const Control &, bool IsExtended); + Error writeDialogBody(const RCResource *); + // HTMLResource Error writeHTMLBody(const RCResource *); Index: llvm/tools/llvm-rc/ResourceFileWriter.cpp =================================================================== --- llvm/tools/llvm-rc/ResourceFileWriter.cpp +++ llvm/tools/llvm-rc/ResourceFileWriter.cpp @@ -60,6 +60,25 @@ return checkNumberFits(Number, sizeof(FitType) * 8, FieldName); } +// A similar function for signed integers. +template +static Error checkSignedNumberFits(uint32_t Number, Twine FieldName, + bool CanBeNegative) { + int32_t SignedNum = Number; + if (SignedNum < std::numeric_limits::min() || + SignedNum > std::numeric_limits::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 @@ 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 @@ 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( + Ctl.X, "Dialog control x-coordinate", true)); + RETURN_IF_ERROR(checkSignedNumberFits( + Ctl.Y, "Dialog control y-coordinate", true)); + RETURN_IF_ERROR( + checkSignedNumberFits(Ctl.Width, "Dialog control width", false)); + RETURN_IF_ERROR(checkSignedNumberFits( + 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( + Ctl.ID, "Control ID in simple DIALOG resource")); + writeInt(Ctl.ID); + } else { + writeInt(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(0); + + return Error::success(); +} + +Error ResourceFileWriter::writeDialogBody(const RCResource *Base) { + auto *Res = cast(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(Res->Controls.size(), + "Number of dialog controls")); + RETURN_IF_ERROR( + checkSignedNumberFits(Res->X, "Dialog x-coordinate", true)); + RETURN_IF_ERROR( + checkSignedNumberFits(Res->Y, "Dialog y-coordinate", true)); + RETURN_IF_ERROR( + checkSignedNumberFits(Res->Width, "Dialog width", false)); + RETURN_IF_ERROR( + checkSignedNumberFits(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(0); + + // Window CLASS field. Not kept here. + writeInt(0); + + // Window title. There is no title for now, so all we output is '\0'. + writeInt(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(Base)->HTMLLoc); Index: llvm/tools/llvm-rc/ResourceScriptParser.cpp =================================================================== --- llvm/tools/llvm-rc/ResourceScriptParser.cpp +++ llvm/tools/llvm-rc/ResourceScriptParser.cpp @@ -457,15 +457,17 @@ // [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; } Index: llvm/tools/llvm-rc/ResourceScriptStmt.h =================================================================== --- llvm/tools/llvm-rc/ResourceScriptStmt.h +++ llvm/tools/llvm-rc/ResourceScriptStmt.h @@ -442,21 +442,40 @@ // Single control definition. class Control { - StringRef Type, Title; +public: + StringRef Type; + IntOrString Title; uint32_t ID, X, Y, Width, Height; Optional 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 ItemStyle, Optional ExtItemStyle, Optional 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 SupportedCtls; raw_ostream &log(raw_ostream &) const; }; @@ -465,11 +484,11 @@ // 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 Controls; bool IsExtended; -public: DialogResource(uint32_t PosX, uint32_t PosY, uint32_t DlgWidth, uint32_t DlgHeight, uint32_t DlgHelpID, OptionalStmtList &&OptStmts, bool IsDialogEx) @@ -480,6 +499,21 @@ 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: Index: llvm/tools/llvm-rc/ResourceScriptStmt.cpp =================================================================== --- llvm/tools/llvm-rc/ResourceScriptStmt.cpp +++ llvm/tools/llvm-rc/ResourceScriptStmt.cpp @@ -120,11 +120,14 @@ return OS; } -const StringSet<> Control::SupportedCtls = { - "LTEXT", "RTEXT", "CTEXT", "PUSHBUTTON", "DEFPUSHBUTTON", "EDITTEXT"}; - -const StringSet<> Control::CtlsWithTitle = {"LTEXT", "RTEXT", "CTEXT", - "PUSHBUTTON", "DEFPUSHBUTTON"}; +const StringMap 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 Index: llvm/tools/llvm-rc/ResourceVisitor.h =================================================================== --- llvm/tools/llvm-rc/ResourceVisitor.h +++ llvm/tools/llvm-rc/ResourceVisitor.h @@ -29,6 +29,7 @@ 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;