From e5d5fbb1ef12c03076740249ab054254b0c314cd Mon Sep 17 00:00:00 2001 From: Zachary Turner Date: Fri, 6 Oct 2017 21:25:44 +0000 Subject: [PATCH] [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 --- .../Inputs/tag-icon-cursor-bad-offset.rc | Bin 0 -> 35 bytes .../Inputs/tag-icon-cursor-bad-type.rc | Bin 0 -> 22 bytes .../llvm-rc/Inputs/tag-icon-cursor-eof.rc | Bin 0 -> 27 bytes .../Inputs/tag-icon-cursor-nonexistent.rc | Bin 0 -> 42 bytes .../Inputs/tag-icon-cursor-nonsense.rc | Bin 0 -> 37 bytes test/tools/llvm-rc/Inputs/tag-icon-cursor.rc | Bin 0 -> 152 bytes test/tools/llvm-rc/tag-icon-cursor.test | 357 ++++++++++++++++++ tools/llvm-rc/ResourceFileWriter.cpp | 262 +++++++++++++ tools/llvm-rc/ResourceFileWriter.h | 15 +- tools/llvm-rc/ResourceScriptStmt.h | 28 +- tools/llvm-rc/ResourceVisitor.h | 2 + 11 files changed, 660 insertions(+), 4 deletions(-) create mode 100644 test/tools/llvm-rc/Inputs/tag-icon-cursor-bad-offset.rc create mode 100644 test/tools/llvm-rc/Inputs/tag-icon-cursor-bad-type.rc create mode 100644 test/tools/llvm-rc/Inputs/tag-icon-cursor-eof.rc create mode 100644 test/tools/llvm-rc/Inputs/tag-icon-cursor-nonexistent.rc create mode 100644 test/tools/llvm-rc/Inputs/tag-icon-cursor-nonsense.rc create mode 100644 test/tools/llvm-rc/Inputs/tag-icon-cursor.rc create mode 100644 test/tools/llvm-rc/tag-icon-cursor.test 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 0000000000000000000000000000000000000000..c95434a1a5f645b6bd5942897c16c397296d018a GIT binary patch literal 35 pcmXptP;d?n3ic0DP)aT>D$Xy`O-fAB%}+}!PA$;`iYRe$0RX~73i<#5 literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..13aeef2780ebaae0f441cfea8f9a88c56849ab66 GIT binary patch literal 22 bcmXpsFi`Mx_V-gzN-ixb&M(pfQc7F^L?{Md literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..07e84484ce726d6425fd0005609504236b7ef08e GIT binary patch literal 27 gcmXpvQg99p3ic0DP)aT>D$Xy`P0df!12UDk0B`CDQvd(} literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fd6acc33b335066e08b9169db7ae54c4d19538d4 GIT binary patch literal 42 xcmXptFi>y~4GQ)TQcx<%$Sl@P%gjmDP03F!*3HW=(M_$$EH2SYE-g~x0st1u4X*$I literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..8785109048556b907b7814f381eb2ef25eebcff8 GIT binary patch literal 37 qcmXq1@O1X~Q&1{NOxMj!&d<|LE-fm~FVfA+&nr#^BE6zyB`yHr2@GEV literal 0 HcmV?d00001 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 0000000000000000000000000000000000000000..fd45e1461c230637ae67912030d7c438cc42e7d6 GIT binary patch literal 152 zcmXppF*8wc4h;(S4^mJ{E-fm~FVX{2N?ax;W~M+1pa5J#*8(DCXkeh=>Fn>Ppp==M rpQoFjlcEP?fVG$@B%_$6Yixv7PhM&{L=VWUOy~SOs96Ph=@4-MCWI_H literal 0 HcmV?d00001 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;