diff --git a/test/tools/llvm-rc/Inputs/tag-icon-cursor-bad-offset.rc b/test/tools/llvm-rc/Inputs/tag-icon-cursor-bad-offset.rc new file mode 100644 index 00000000000..c95434a1a5f Binary files /dev/null and b/test/tools/llvm-rc/Inputs/tag-icon-cursor-bad-offset.rc differ diff --git a/test/tools/llvm-rc/Inputs/tag-icon-cursor-bad-type.rc b/test/tools/llvm-rc/Inputs/tag-icon-cursor-bad-type.rc new file mode 100644 index 00000000000..13aeef2780e Binary files /dev/null and b/test/tools/llvm-rc/Inputs/tag-icon-cursor-bad-type.rc differ diff --git a/test/tools/llvm-rc/Inputs/tag-icon-cursor-eof.rc b/test/tools/llvm-rc/Inputs/tag-icon-cursor-eof.rc new file mode 100644 index 00000000000..07e84484ce7 Binary files /dev/null and b/test/tools/llvm-rc/Inputs/tag-icon-cursor-eof.rc differ diff --git a/test/tools/llvm-rc/Inputs/tag-icon-cursor-nonexistent.rc b/test/tools/llvm-rc/Inputs/tag-icon-cursor-nonexistent.rc new file mode 100644 index 00000000000..fd6acc33b33 Binary files /dev/null and b/test/tools/llvm-rc/Inputs/tag-icon-cursor-nonexistent.rc differ diff --git a/test/tools/llvm-rc/Inputs/tag-icon-cursor-nonsense.rc b/test/tools/llvm-rc/Inputs/tag-icon-cursor-nonsense.rc new file mode 100644 index 00000000000..87851090485 Binary files /dev/null and b/test/tools/llvm-rc/Inputs/tag-icon-cursor-nonsense.rc differ diff --git a/test/tools/llvm-rc/Inputs/tag-icon-cursor.rc b/test/tools/llvm-rc/Inputs/tag-icon-cursor.rc new file mode 100644 index 00000000000..fd45e1461c2 Binary files /dev/null and b/test/tools/llvm-rc/Inputs/tag-icon-cursor.rc differ diff --git a/test/tools/llvm-rc/tag-icon-cursor.test b/test/tools/llvm-rc/tag-icon-cursor.test new file mode 100644 index 00000000000..406ec1a144e --- /dev/null +++ b/test/tools/llvm-rc/tag-icon-cursor.test @@ -0,0 +1,357 @@ +; This .ico file used to write this test was lost when the author left +; before comitting, and the binary wasn't uploaded to Phabricator. This +; will be fixed as soon as we can recover the file. +; XFAIL: * +; RUN: rm -rf %t +; RUN: mkdir %t +; RUN: cd %t +; RUN: cp %p/Inputs/icon*.ico . +; RUN: cp %p/Inputs/cursor*.cur . +; RUN: cp %p/Inputs/tag-icon-cursor-nonsense.rc . + +; RUN: llvm-rc /FO %t/tag-icon-cursor.res %p/Inputs/tag-icon-cursor.rc +; RUN: llvm-readobj %t/tag-icon-cursor.res | FileCheck %s + +; CHECK: Resource type (int): 1 +; CHECK-NEXT: Resource name (int): 1 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 308 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 0A000B00 28000000 20000000 40000000 |....(... ...@...| +; CHECK-NEXT: 0010: 01000100 00000000 80000000 00000000 |................| +; CHECK-NEXT: 0020: 00000000 02000000 00000000 00000000 |................| +; (...) +; CHECK-DAG: 0110: FFFFFFFF FFFFFFFF FFFFFFFF F3CFFFFF |................| +; CHECK-NEXT: 0120: F3CFFFFF FFFFFFFF FFFFFFFF FFFFFFFF |................| +; CHECK-NEXT: 0130: FFFFFFFF |....| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 12 +; CHECK-NEXT: Resource name (int): 4464 +; 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: 20 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 00000200 01002000 40000100 01003401 |...... .@.....4.| +; CHECK-NEXT: 0010: 00000100 |....| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 1 +; CHECK-NEXT: Resource name (int): 2 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 2220 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 01000C00 28000000 20000000 40000000 |....(... ...@...| +; CHECK-NEXT: 0010: 01000800 00000000 00040000 00000000 |................| +; CHECK-NEXT: 0020: 00000000 00010000 00000000 00000000 |................| +; (...) +; CHECK-DAG: 0880: C001FFFF F557FFFF F557FFFF F551FFFF |.....W...W...Q..| +; CHECK-NEXT: 0890: C005FFFF B557FFFF F557FFFF F557FFFF |.....W...W...W..| +; CHECK-NEXT: 08A0: C001FFFF F557FFFF FFFFFFFF |.....W......| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 12 +; CHECK-NEXT: Resource name (int): 4465 +; 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: 20 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 00000200 01002000 40000100 0800AC08 |...... .@.......| +; CHECK-NEXT: 0010: 00000200 |....| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 3 +; CHECK-NEXT: Resource name (int): 3 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 1128 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 28000000 10000000 20000000 01002000 |(....... ..... .| +; CHECK-NEXT: 0010: 00000000 00040000 C30E0000 C30E0000 |................| +; CHECK-NEXT: 0020: 00000000 00000000 FFFFFFFF FFFFFFFF |................| +; (...) +; CHECK-DAG: 0440: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 0450: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 0460: 00000000 00000000 |........| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 3 +; CHECK-NEXT: Resource name (int): 4 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 2440 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 28000000 18000000 30000000 01002000 |(.......0..... .| +; CHECK-NEXT: 0010: 00000000 00090000 C30E0000 C30E0000 |................| +; CHECK-NEXT: 0020: 00000000 00000000 FFFFFFFF FFFFFFFF |................| +; (...) +; CHECK-DAG: 0960: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 0970: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 0980: 00000000 00000000 |........| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 3 +; CHECK-NEXT: Resource name (int): 5 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 4264 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 28000000 20000000 40000000 01002000 |(... ...@..... .| +; CHECK-NEXT: 0010: 00000000 00100000 C30E0000 C30E0000 |................| +; CHECK-NEXT: 0020: 00000000 00000000 FFFFFFFF FFFFFFFF |................| +; (...) +; CHECK-DAG: 1080: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 1090: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 10A0: 00000000 00000000 |........| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 3 +; CHECK-NEXT: Resource name (int): 6 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 9640 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 28000000 30000000 60000000 01002000 |(...0...`..... .| +; CHECK-NEXT: 0010: 00000000 00240000 C30E0000 C30E0000 |.....$..........| +; CHECK-NEXT: 0020: 00000000 00000000 FFFFFFFF FFFFFFFF |................| +; (...) +; CHECK-DAG: 2580: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 2590: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 25A0: 00000000 00000000 |........| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 14 +; CHECK-NEXT: Resource name (int): 100 +; 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: 62 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 00000100 04001010 00000100 20006804 |............ .h.| +; CHECK-NEXT: 0010: 00000300 18180000 01002000 88090000 |.......... .....| +; CHECK-NEXT: 0020: 04002020 00000100 2000A810 00000500 |.. .... .......| +; CHECK-NEXT: 0030: 30300000 01002000 A8250000 0600 |00.... ..%....| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 1 +; CHECK-NEXT: Resource name (int): 7 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 4268 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 0D000600 28000000 20000000 40000000 |....(... ...@...| +; CHECK-NEXT: 0010: 01002000 00000000 00100000 00000000 |.. .............| +; CHECK-NEXT: 0020: 00000000 00000000 00000000 00000000 |................| +; (...) +; CHECK-DAG: 1080: E027FFFF C3F3FFFF FFFFFFFF FFFFFFFF |.'..............| +; CHECK-NEXT: 1090: FFFFFFFF F3CFFFFF F3CFFFFF FFFFFFFF |................| +; CHECK-NEXT: 10A0: FFFFFFFF FFFFFFFF FFFFFFFF |............| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 12 +; CHECK-NEXT: Resource name (int): 4466 +; 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: 20 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 00000200 01002000 40000100 2000AC10 |...... .@... ...| +; CHECK-NEXT: 0010: 00000700 |....| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 3 +; CHECK-NEXT: Resource name (int): 8 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 1128 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 28000000 10000000 20000000 01002000 |(....... ..... .| +; CHECK-NEXT: 0010: 00000000 00040000 C30E0000 C30E0000 |................| +; CHECK-NEXT: 0020: 00000000 00000000 FFFFFFFF FFFFFFFF |................| +; (...) +; CHECK-DAG: 0440: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 0450: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 0460: 00000000 00000000 |........| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 3 +; CHECK-NEXT: Resource name (int): 9 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 2440 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 28000000 18000000 30000000 01002000 |(.......0..... .| +; CHECK-NEXT: 0010: 00000000 00090000 C30E0000 C30E0000 |................| +; CHECK-NEXT: 0020: 00000000 00000000 FFFFFFFF FFFFFFFF |................| +; (...) +; CHECK-DAG: 0960: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 0970: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 0980: 00000000 00000000 |........| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 3 +; CHECK-NEXT: Resource name (int): 10 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 4264 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 28000000 20000000 40000000 01002000 |(... ...@..... .| +; CHECK-NEXT: 0010: 00000000 00100000 C30E0000 C30E0000 |................| +; CHECK-NEXT: 0020: 00000000 00000000 FFFFFFFF FFFFFFFF |................| +; (...) +; CHECK-DAG: 1080: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 1090: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 10A0: 00000000 00000000 |........| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 3 +; CHECK-NEXT: Resource name (int): 11 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 9640 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 28000000 30000000 60000000 01002000 |(...0...`..... .| +; CHECK-NEXT: 0010: 00000000 00240000 C30E0000 C30E0000 |.....$..........| +; CHECK-NEXT: 0020: 00000000 00000000 FFFFFFFF FFFFFFFF |................| +; (...) +; CHECK-DAG: 2580: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 2590: 00000000 00000000 00000000 00000000 |................| +; CHECK-NEXT: 25A0: 00000000 00000000 |........| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 14 +; CHECK-NEXT: Resource name (int): 100 +; 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: 62 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 00000100 04001010 00000100 20006804 |............ .h.| +; CHECK-NEXT: 0010: 00000800 18180000 01002000 88090000 |.......... .....| +; CHECK-NEXT: 0020: 09002020 00000100 2000A810 00000A00 |.. .... .......| +; CHECK-NEXT: 0030: 30300000 01002000 A8250000 0B00 |00.... ..%....| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 3 +; CHECK-NEXT: Resource name (int): 12 +; CHECK-NEXT: Data version: 0 +; CHECK-NEXT: Memory flags: 0x1010 +; CHECK-NEXT: Language ID: 1033 +; CHECK-NEXT: Version (major): 0 +; CHECK-NEXT: Version (minor): 0 +; CHECK-NEXT: Characteristics: 0 +; CHECK-NEXT: Data size: 82 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 89504E47 0D0A1A0A 0000000D 49484452 |.PNG........IHDR| +; CHECK-NEXT: 0010: 00000010 00000010 08060000 001FF3FF |................| +; CHECK-NEXT: 0020: 61000000 19494441 5438CB63 FC0F040C |a....IDAT8.c....| +; CHECK-NEXT: 0030: 1400C651 03460D18 3560B818 0000251F |...Q.F..5`....%.| +; CHECK-NEXT: 0040: 3FD1D6DC 546E0000 00004945 4E44AE42 |?...Tn....IEND.B| +; CHECK-NEXT: 0050: 6082 |`.| +; CHECK-NEXT: ) + +; CHECK-DAG: Resource type (int): 14 +; CHECK-NEXT: Resource name (int): 100 +; 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: 20 +; CHECK-NEXT: Data: ( +; CHECK-NEXT: 0000: 00000100 01001010 00000100 20005200 |............ .R.| +; CHECK-NEXT: 0010: 00000C00 |....| +; CHECK-NEXT: ) + + +; RUN: not llvm-rc /FO %t/1 %p/Inputs/tag-icon-cursor-nonexistent.rc 2>&1 | FileCheck %s --check-prefix NOFILE +; NOFILE: llvm-rc: Error in CURSOR statement (ID 500): +; NOFILE-NEXT: Error opening cursor 'this-file-does-not-exist.cur': + + +; RUN: not llvm-rc /FO %t/1 %p/Inputs/tag-icon-cursor-nonsense.rc 2>&1 | FileCheck %s --check-prefix NONSENSE + +; NONSENSE: llvm-rc: Error in ICON statement (ID 1): +; NONSENSE-NEXT: Incorrect icon/cursor Reserved field; should be 0. + + +; RUN: not llvm-rc /FO %t/1 %p/Inputs/tag-icon-cursor-eof.rc 2>&1 | FileCheck %s --check-prefix EOF + +; EOF: llvm-rc: Error in CURSOR statement (ID 72): +; EOF-NEXT: Stream Error: The stream is too short to perform the requested operation. + + +; RUN: not llvm-rc /FO %t/1 %p/Inputs/tag-icon-cursor-bad-offset.rc 2>&1 | FileCheck %s --check-prefix OFFSET + +; OFFSET: llvm-rc: Error in CURSOR statement (ID 50): +; OFFSET-NEXT: Stream Error: The specified offset is invalid for the current stream. + + +; RUN: not llvm-rc /FO %t/1 %p/Inputs/tag-icon-cursor-bad-type.rc 2>&1 | FileCheck %s --check-prefix BADTYPE + +; BADTYPE: llvm-rc: Error in ICON statement (ID 100): +; BADTYPE-NEXT: Incorrect icon/cursor ResType field; should be 1. diff --git a/tools/llvm-rc/ResourceFileWriter.cpp b/tools/llvm-rc/ResourceFileWriter.cpp index 243b3da404b..a15166e0edf 100644 --- a/tools/llvm-rc/ResourceFileWriter.cpp +++ b/tools/llvm-rc/ResourceFileWriter.cpp @@ -220,10 +220,18 @@ Error ResourceFileWriter::visitAcceleratorsResource(const RCResource *Res) { return writeResource(Res, &ResourceFileWriter::writeAcceleratorsBody); } +Error ResourceFileWriter::visitCursorResource(const RCResource *Res) { + return handleError(visitIconOrCursorResource(Res), Res); +} + Error ResourceFileWriter::visitDialogResource(const RCResource *Res) { return writeResource(Res, &ResourceFileWriter::writeDialogBody); } +Error ResourceFileWriter::visitIconResource(const RCResource *Res) { + return handleError(visitIconOrCursorResource(Res), Res); +} + Error ResourceFileWriter::visitCaptionStmt(const CaptionStmt *Stmt) { ObjectData.Caption = Stmt->Value; return Error::success(); @@ -422,6 +430,260 @@ Error ResourceFileWriter::writeAcceleratorsBody(const RCResource *Base) { return Error::success(); } +// --- CursorResource and IconResource helpers. --- // + +// ICONRESDIR structure. Describes a single icon in resouce group. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648016.aspx +struct IconResDir { + uint8_t Width; + uint8_t Height; + uint8_t ColorCount; + uint8_t Reserved; +}; + +// CURSORDIR structure. Describes a single cursor in resource group. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648011(v=vs.85).aspx +struct CursorDir { + ulittle16_t Width; + ulittle16_t Height; +}; + +// RESDIRENTRY structure, stripped from the last item. Stripping made +// for compatibility with RESDIR. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026(v=vs.85).aspx +struct ResourceDirEntryStart { + union { + CursorDir Cursor; // Used in CURSOR resources. + IconResDir Icon; // Used in .ico and .cur files, and ICON resources. + }; + ulittle16_t Planes; // HotspotX (.cur files but not CURSOR resource). + ulittle16_t BitCount; // HotspotY (.cur files but not CURSOR resource). + ulittle32_t Size; + // ulittle32_t ImageOffset; // Offset to image data (ICONDIRENTRY only). + // ulittle16_t IconID; // Resource icon ID (RESDIR only). +}; + +// BITMAPINFOHEADER structure. Describes basic information about the bitmap +// being read. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/dd183376(v=vs.85).aspx +struct BitmapInfoHeader { + ulittle32_t Size; + ulittle32_t Width; + ulittle32_t Height; + ulittle16_t Planes; + ulittle16_t BitCount; + ulittle32_t Compression; + ulittle32_t SizeImage; + ulittle32_t XPelsPerMeter; + ulittle32_t YPelsPerMeter; + ulittle32_t ClrUsed; + ulittle32_t ClrImportant; +}; + +// Group icon directory header. Called ICONDIR in .ico/.cur files and +// NEWHEADER in .res files. +// +// Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648023(v=vs.85).aspx +struct GroupIconDir { + ulittle16_t Reserved; // Always 0. + ulittle16_t ResType; // 1 for icons, 2 for cursors. + ulittle16_t ResCount; // Number of items. +}; + +enum class IconCursorGroupType { Icon, Cursor }; + +class SingleIconCursorResource : public RCResource { +public: + IconCursorGroupType Type; + const ResourceDirEntryStart &Header; + ArrayRef Image; + + SingleIconCursorResource(IconCursorGroupType ResourceType, + const ResourceDirEntryStart &HeaderEntry, + ArrayRef ImageData) + : Type(ResourceType), Header(HeaderEntry), Image(ImageData) {} + + Twine getResourceTypeName() const override { return "Icon/cursor image"; } + IntOrString getResourceType() const override { + return Type == IconCursorGroupType::Icon ? RkSingleIcon : RkSingleCursor; + } + uint16_t getMemoryFlags() const override { + return MfDiscardable | MfMoveable; + } + ResourceKind getKind() const override { return RkSingleCursorOrIconRes; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkSingleCursorOrIconRes; + } +}; + +class IconCursorGroupResource : public RCResource { +public: + IconCursorGroupType Type; + GroupIconDir Header; + std::vector ItemEntries; + + IconCursorGroupResource(IconCursorGroupType ResourceType, + const GroupIconDir &HeaderData, + std::vector &&Entries) + : Type(ResourceType), Header(HeaderData), + ItemEntries(std::move(Entries)) {} + + Twine getResourceTypeName() const override { return "Icon/cursor group"; } + IntOrString getResourceType() const override { + return Type == IconCursorGroupType::Icon ? RkIconGroup : RkCursorGroup; + } + ResourceKind getKind() const override { return RkCursorOrIconGroupRes; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkCursorOrIconGroupRes; + } +}; + +Error ResourceFileWriter::writeSingleIconOrCursorBody(const RCResource *Base) { + auto *Res = cast(Base); + if (Res->Type == IconCursorGroupType::Cursor) { + // In case of cursors, two WORDS are appended to the beginning + // of the resource: HotspotX (Planes in RESDIRENTRY), + // and HotspotY (BitCount). + // + // Ref: msdn.microsoft.com/en-us/library/windows/desktop/ms648026.aspx + // (Remarks section). + writeObject(Res->Header.Planes); + writeObject(Res->Header.BitCount); + } + + writeObject(Res->Image); + return Error::success(); +} + +Error ResourceFileWriter::writeIconOrCursorGroupBody(const RCResource *Base) { + auto *Res = cast(Base); + writeObject(Res->Header); + for (auto Item : Res->ItemEntries) { + writeObject(Item); + writeObject(ulittle16_t(IconCursorID++)); + } + return Error::success(); +} + +Error ResourceFileWriter::visitSingleIconOrCursor(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeSingleIconOrCursorBody); +} + +Error ResourceFileWriter::visitIconOrCursorGroup(const RCResource *Res) { + return writeResource(Res, &ResourceFileWriter::writeIconOrCursorGroupBody); +} + +Error ResourceFileWriter::visitIconOrCursorResource(const RCResource *Base) { + IconCursorGroupType Type; + StringRef FileStr; + IntOrString ResName = Base->ResName; + + if (auto *IconRes = dyn_cast(Base)) { + FileStr = IconRes->IconLoc; + Type = IconCursorGroupType::Icon; + } else { + auto *CursorRes = dyn_cast(Base); + FileStr = CursorRes->CursorLoc; + Type = IconCursorGroupType::Cursor; + } + + bool IsLong; + stripQuotes(FileStr, IsLong); + ErrorOr> File = + MemoryBuffer::getFile(FileStr, -1, false); + + if (!File) + return make_error( + "Error opening " + + Twine(Type == IconCursorGroupType::Icon ? "icon" : "cursor") + + " '" + FileStr + "': " + File.getError().message(), + File.getError()); + + BinaryStreamReader Reader((*File)->getBuffer(), support::little); + + // Read the file headers. + // - At the beginning, ICONDIR/NEWHEADER header. + // - Then, a number of RESDIR headers follow. These contain offsets + // to data. + const GroupIconDir *Header; + + RETURN_IF_ERROR(Reader.readObject(Header)); + if (Header->Reserved != 0) + return createError("Incorrect icon/cursor Reserved field; should be 0."); + uint16_t NeededType = Type == IconCursorGroupType::Icon ? 1 : 2; + if (Header->ResType != NeededType) + return createError("Incorrect icon/cursor ResType field; should be " + + Twine(NeededType) + "."); + + uint16_t NumItems = Header->ResCount; + + // Read single ico/cur headers. + std::vector ItemEntries; + ItemEntries.reserve(NumItems); + std::vector ItemOffsets(NumItems); + for (size_t ID = 0; ID < NumItems; ++ID) { + const ResourceDirEntryStart *Object; + RETURN_IF_ERROR(Reader.readObject(Object)); + ItemEntries.push_back(*Object); + RETURN_IF_ERROR(Reader.readInteger(ItemOffsets[ID])); + } + + // Now write each icon/cursors one by one. At first, all the contents + // without ICO/CUR header. This is described by SingleIconCursorResource. + for (size_t ID = 0; ID < NumItems; ++ID) { + // Load the fragment of file. + Reader.setOffset(ItemOffsets[ID]); + ArrayRef Image; + RETURN_IF_ERROR(Reader.readArray(Image, ItemEntries[ID].Size)); + SingleIconCursorResource SingleRes(Type, ItemEntries[ID], Image); + SingleRes.setName(IconCursorID + ID); + RETURN_IF_ERROR(visitSingleIconOrCursor(&SingleRes)); + } + + // Now, write all the headers concatenated into a separate resource. + for (size_t ID = 0; ID < NumItems; ++ID) { + if (Type == IconCursorGroupType::Icon) { + // rc.exe seems to always set NumPlanes to 1. No idea why it happens. + ItemEntries[ID].Planes = 1; + continue; + } + + // We need to rewrite the cursor headers. + const auto &OldHeader = ItemEntries[ID]; + ResourceDirEntryStart NewHeader; + NewHeader.Cursor.Width = OldHeader.Icon.Width; + // Each cursor in fact stores two bitmaps, one under another. + // Height provided in cursor definition describes the height of the + // cursor, whereas the value existing in resource definition describes + // the height of the bitmap. Therefore, we need to double this height. + NewHeader.Cursor.Height = OldHeader.Icon.Height * 2; + + // Now, we actually need to read the bitmap header to find + // the number of planes and the number of bits per pixel. + Reader.setOffset(ItemOffsets[ID]); + const BitmapInfoHeader *BMPHeader; + RETURN_IF_ERROR(Reader.readObject(BMPHeader)); + NewHeader.Planes = BMPHeader->Planes; + NewHeader.BitCount = BMPHeader->BitCount; + + // Two WORDs were written at the beginning of the resource (hotspot + // location). This is reflected in Size field. + NewHeader.Size = OldHeader.Size + 2 * sizeof(uint16_t); + + ItemEntries[ID] = NewHeader; + } + + IconCursorGroupResource HeaderRes(Type, *Header, std::move(ItemEntries)); + HeaderRes.setName(ResName); + RETURN_IF_ERROR(visitIconOrCursorGroup(&HeaderRes)); + + return Error::success(); +} + // --- DialogResource helpers. --- // Error ResourceFileWriter::writeSingleDialogControl(const Control &Ctl, diff --git a/tools/llvm-rc/ResourceFileWriter.h b/tools/llvm-rc/ResourceFileWriter.h index 1370c6168d2..8093dfe7968 100644 --- a/tools/llvm-rc/ResourceFileWriter.h +++ b/tools/llvm-rc/ResourceFileWriter.h @@ -25,14 +25,16 @@ namespace rc { class ResourceFileWriter : public Visitor { public: ResourceFileWriter(std::unique_ptr Stream) - : FS(std::move(Stream)) { + : FS(std::move(Stream)), IconCursorID(1) { assert(FS && "Output stream needs to be provided to the serializator"); } Error visitNullResource(const RCResource *) override; Error visitAcceleratorsResource(const RCResource *) override; + Error visitCursorResource(const RCResource *) override; Error visitDialogResource(const RCResource *) override; Error visitHTMLResource(const RCResource *) override; + Error visitIconResource(const RCResource *) override; Error visitMenuResource(const RCResource *) override; Error visitCaptionStmt(const CaptionStmt *) override; @@ -76,6 +78,13 @@ private: bool IsLastItem); Error writeAcceleratorsBody(const RCResource *); + // CursorResource and IconResource + Error visitIconOrCursorResource(const RCResource *); + Error visitIconOrCursorGroup(const RCResource *); + Error visitSingleIconOrCursor(const RCResource *); + Error writeSingleIconOrCursorBody(const RCResource *); + Error writeIconOrCursorGroupBody(const RCResource *); + // DialogResource Error writeSingleDialogControl(const Control &, bool IsExtended); Error writeDialogBody(const RCResource *); @@ -120,6 +129,10 @@ private: Error appendFile(StringRef Filename); void padStream(uint64_t Length); + + // Icon and cursor IDs are allocated starting from 1 and increasing for + // each icon/cursor dumped. This maintains the current ID to be allocated. + uint16_t IconCursorID; }; } // namespace rc diff --git a/tools/llvm-rc/ResourceScriptStmt.h b/tools/llvm-rc/ResourceScriptStmt.h index 908232940a5..9addca90b8a 100644 --- a/tools/llvm-rc/ResourceScriptStmt.h +++ b/tools/llvm-rc/ResourceScriptStmt.h @@ -74,9 +74,13 @@ enum ResourceKind { // (TYPE in RESOURCEHEADER structure). The numeric value assigned to each // kind is equal to this type ID. RkNull = 0, + RkSingleCursor = 1, + RkSingleIcon = 3, RkMenu = 4, RkDialog = 5, RkAccelerators = 9, + RkCursorGroup = 12, + RkIconGroup = 14, RkVersionInfo = 16, RkHTML = 23, @@ -88,7 +92,9 @@ enum ResourceKind { RkBase, RkCursor, RkIcon, - RkUser + RkUser, + RkSingleCursorOrIconRes, + RkCursorOrIconGroupRes }; // Non-zero memory flags. @@ -255,22 +261,38 @@ public: // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa380920(v=vs.85).aspx class CursorResource : public RCResource { +public: StringRef CursorLoc; -public: CursorResource(StringRef Location) : CursorLoc(Location) {} raw_ostream &log(raw_ostream &) const override; + + Twine getResourceTypeName() const override { return "CURSOR"; } + Error visit(Visitor *V) const override { + return V->visitCursorResource(this); + } + ResourceKind getKind() const override { return RkCursor; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkCursor; + } }; // ICON resource. Represents a single ".ico" file containing a group of icons. // // Ref: msdn.microsoft.com/en-us/library/windows/desktop/aa381018(v=vs.85).aspx class IconResource : public RCResource { +public: StringRef IconLoc; -public: IconResource(StringRef Location) : IconLoc(Location) {} raw_ostream &log(raw_ostream &) const override; + + Twine getResourceTypeName() const override { return "ICON"; } + Error visit(Visitor *V) const override { return V->visitIconResource(this); } + ResourceKind getKind() const override { return RkIcon; } + static bool classof(const RCResource *Res) { + return Res->getKind() == RkIcon; + } }; // HTML resource. Represents a local webpage that is to be embedded into the diff --git a/tools/llvm-rc/ResourceVisitor.h b/tools/llvm-rc/ResourceVisitor.h index 376a20b336d..16ffba6b9bd 100644 --- a/tools/llvm-rc/ResourceVisitor.h +++ b/tools/llvm-rc/ResourceVisitor.h @@ -32,8 +32,10 @@ class Visitor { public: virtual Error visitNullResource(const RCResource *) = 0; virtual Error visitAcceleratorsResource(const RCResource *) = 0; + virtual Error visitCursorResource(const RCResource *) = 0; virtual Error visitDialogResource(const RCResource *) = 0; virtual Error visitHTMLResource(const RCResource *) = 0; + virtual Error visitIconResource(const RCResource *) = 0; virtual Error visitMenuResource(const RCResource *) = 0; virtual Error visitCaptionStmt(const CaptionStmt *) = 0;