--- /dev/null
+; 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.
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();
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<uint8_t> Image;
+
+ SingleIconCursorResource(IconCursorGroupType ResourceType,
+ const ResourceDirEntryStart &HeaderEntry,
+ ArrayRef<uint8_t> 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<ResourceDirEntryStart> ItemEntries;
+
+ IconCursorGroupResource(IconCursorGroupType ResourceType,
+ const GroupIconDir &HeaderData,
+ std::vector<ResourceDirEntryStart> &&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<SingleIconCursorResource>(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<IconCursorGroupResource>(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<IconResource>(Base)) {
+ FileStr = IconRes->IconLoc;
+ Type = IconCursorGroupType::Icon;
+ } else {
+ auto *CursorRes = dyn_cast<CursorResource>(Base);
+ FileStr = CursorRes->CursorLoc;
+ Type = IconCursorGroupType::Cursor;
+ }
+
+ bool IsLong;
+ stripQuotes(FileStr, IsLong);
+ ErrorOr<std::unique_ptr<MemoryBuffer>> File =
+ MemoryBuffer::getFile(FileStr, -1, false);
+
+ if (!File)
+ return make_error<StringError>(
+ "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<ResourceDirEntryStart> ItemEntries;
+ ItemEntries.reserve(NumItems);
+ std::vector<uint32_t> 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<uint8_t> 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,