#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")]];