From 6e3b3c8013e6d8814dbf70c854e55d062bedbdf4 Mon Sep 17 00:00:00 2001 From: memdmp Date: Mon, 13 Jan 2025 16:54:34 +0100 Subject: chore: initial commit --- patterns/xcur.hexpat | 107 +++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 107 insertions(+) create mode 100644 patterns/xcur.hexpat (limited to 'patterns/xcur.hexpat') diff --git a/patterns/xcur.hexpat b/patterns/xcur.hexpat new file mode 100644 index 0000000..c14a2ee --- /dev/null +++ b/patterns/xcur.hexpat @@ -0,0 +1,107 @@ +#pragma author memdmp +#pragma description XCursor (.xcur) file format + +#pragma MIME image/x-xcursor + +#pragma endian little +#pragma magic [ 58 63 75 72 ] @ 0x00 +#pragma pattern_limit 4294967295 + +import std.sys; +import type.magic; + +struct ARGBPixel { + u8 alpha [[color("ffffff")]]; + u8 red [[color("ff0000")]]; + u8 green [[color("00ff00")]]; + u8 blue [[color("0000ff")]]; +} [[static, color(std::format("{:02X}{:02X}{:02X}", red, green, blue))]]; + +struct ARGBPixelRow { + ARGBPixel columns[rowSize] [[name("Columns"), inline]]; +}; +struct ARGBImageData { + ARGBPixelRow rows[colSize] [[name("Rows"), inline]]; +}; + +enum ChunkHeaderSize : u32 { + Comment = 20, + Image = 36, +}; +enum ChunkType : u32 { + Comment = 0xfffe0001, + Image = 0xfffd0002 +}; +enum CommentSubtype : u32 { + Copyright = 1, + License = 2, + Other = 3, +}; + +struct Chunk { + /** Must match chunkType */ + ChunkHeaderSize chunkHeaderSize [[name("Chunk Header Size"), single_color]]; + /** Must match chunkHeaderSize */ + ChunkType chunkType [[name("Chunk Type"), single_color]]; + if (chunkType == ChunkType::Image) { + std::assert_warn(chunkHeaderSize == ChunkHeaderSize::Image, "ChunkType::Image requires ChunkHeaderSize::Image. Behaviour is undefined."); + /** The subtype - should(/must) match the one in the table of contents */ + u32 subtype [[name("Subtype"), single_color]]; + std::assert_warn(tocSubtype == subtype, "TOC subtype should match Chunk subtype for images."); + /** Must be 1 */ + u32 version [[name("Image Spec Version"), single_color]]; + std::assert_warn(version == 1, "version != 1 is not supported for ChunkType::Image"); + /** Width of the cursor */ + u32 width [[name("Width"), single_color]]; + std::assert_warn(width <= 0x7fff, "0x7fff is the maximum allowed width!"); + /** Height of the cursor */ + u32 height [[name("Height"), single_color]]; + std::assert_warn(height <= 0x7fff, "0x7fff is the maximum allowed height!"); + /** Real cursor X position within the bitmap */ + u32 xhot [[name("X Position (xhot)"), single_color]]; + std::assert_warn(xhot <= width, "xhot must be less than or equal to width"); + /** Real cursor Y position within the bitmap */ + u32 yhot [[name("Y Position (yhot)"), single_color]]; + std::assert_warn(yhot <= height, "yhot must be less than or equal to height"); + /** Delay between frames with the same subtype */ + u32 delay_ms [[name("Delay (ms)"), single_color]]; + ARGBImageData pixels; + } else if (chunkType == ChunkType::Comment) { + std::assert_warn(chunkHeaderSize == ChunkHeaderSize::Comment, "ChunkType::Comment requires ChunkHeaderSize::Comment. Behaviour is undefined."); + /** What kinda comment it is */ + CommentSubtype subtype [[name("Comment Type"), single_color]]; + /** Must be 1 */ + u32 version [[name("Comment Version"), single_color]]; + std::assert_warn(version == 1, "version != 1 is not supported for ChunkType::Comment"); + /** The length of the contents */ + u32 length [[name("Comment Length"), single_color]]; + /** The contents */ + char contents[length] [[name("Comment Contents"), single_color]]; + } +}; + +struct Toc { + /** Entry Type */ + char type[4] [[name("Entry Type"), single_color]]; + /** The subtype - used to distinguish between different sizes */ + u32 subtype [[name("Entry Subtype"), single_color]]; + /** Pointer to the chunk */ + Chunk *position : u32 [[name("Chunk Pointer"), single_color]]; +}; + +struct XCursor { + /** Magic string, must be "Xcur" (0x58 0x63 0x75 0x72) */ + type::Magic<"Xcur"> magic [[name("Magic"), comment("The XCursor Magic string"), color("000000")]]; + /** bytes in this header */ + u32 header_size [[name("Header Size"), comment("Indicates the size of the current file header, in bytes"), color("99aaff")]]; + std::assert_warn(header_size == 12, "Cursor is required to have a 12 byte header size"); + /** File version number */ + u32 version [[name("File Version"), color("7744ff")]]; + std::assert_warn(version == 1, "Only Xcur v1 is supported"); + /** Number of table of contents entries */ + u32 ntoc [[name("TOC Length"), comment("Indicates the amount of entries in the table of contents"), color("cc99bb")]]; + /** Table of contents entries */ + Toc toc[ntoc] [[name("Table of Contents")]]; +}; + +XCursor xcursor @ 0x00 [[name("XCursor")]]; -- cgit v1.2.3