1
0
mirror of https://github.com/RPCS3/llvm-mirror.git synced 2024-11-23 11:13:28 +01:00

[llvm-rc] Serialize CURSOR and ICON resources to .res

This is part 6 of llvm-rc serialization.

This adds ability to output cursors and icons as resources.

Unfortunately, we can't just copy .cur or .ico files to output - as each
file might contain multiple images, each of them needs to be unpacked
and stored as a separate resource. This forces us to parse cursor and
icon contents. (Fortunately, these formats are pretty similar and can be
processed by mostly common code).

As test files are binary, here is a short explanation of .cur and .ico
files stored:

cursor.cur, cursor-8.cur, cursor-32.cur are sample correct cursor files,
differing in their bit depth.

icon-old.ico, icon-new.ico are sample correct icon files;

icon-png.ico is a sample correct icon file in PNG format (instead of
usual BMP);

cursor-eof.cur is an incorrect cursor file - this is cursor.cur with
some of its final bytes removed.

cursor-bad-offset.cur is an incorrect cursor file - image header states
that image data begins at offset 0xFFFFFFFF.

Sample correct cursors and icons were created by Nico Weber.

Patch by Marek Sokolowski
Differential Revision: https://reviews.llvm.org/D37878

llvm-svn: 315109
This commit is contained in:
Zachary Turner 2017-10-06 21:25:44 +00:00
parent 09bbca6bf0
commit e5d5fbb1ef
11 changed files with 660 additions and 4 deletions

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -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.

View File

@ -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<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,

View File

@ -25,14 +25,16 @@ namespace rc {
class ResourceFileWriter : public Visitor {
public:
ResourceFileWriter(std::unique_ptr<raw_fd_ostream> 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

View File

@ -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

View File

@ -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;