From b6df1742b6e64d9d81462f544a7ab237d3416a3a Mon Sep 17 00:00:00 2001 From: Caleb Denio Date: Sat, 10 May 2025 05:51:04 -0400 Subject: [PATCH] patterns: Add ZIM pattern (#367) * Add ZIM pattern file * Add test file * namespace! --- README.md | 1 + patterns/zim.hexpat | 79 ++++++++++++++++++++++++ tests/patterns/test_data/zim.hexpat.zim | Bin 0 -> 46486 bytes 3 files changed, 80 insertions(+) create mode 100644 patterns/zim.hexpat create mode 100644 tests/patterns/test_data/zim.hexpat.zim diff --git a/README.md b/README.md index 502946b..a1c0db4 100644 --- a/README.md +++ b/README.md @@ -179,6 +179,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | XGT | | [`patterns/xgt.hexpat`](patterns/xgstexture.hexpat) | Exient XGS Engine Texture | | Xilinx BIT | | [`patterns/xilinx_bit.hexpat`](patterns/xilinx_bit.hexpat) | Xilinx FPGA Bitstreams | | Xilinx Bootgen | `application/x-xilinx-boot-zynqmp` | [`patterns/xilinx_bootgen.hexpat`](patterns/xilinx_bootgen.hexpat) | Xilinx ZynqMP Boot Images | +| ZIM | | [`patterns/zim.hexpat`](patterns/zim.hexpat) | [ZIM](https://openzim.org) file format | | ZIP | `application/zip` | [`patterns/zip.hexpat`](patterns/zip.hexpat) | End of Central Directory Header, Central Directory File Headers | | ZLIB | `application/zlib` | [`patterns/zlib.hexpat`](patterns/zlib.hexpat) | ZLIB compressed data format | | ZSTD | `application/zstd` | [`patterns/zstd.hexpat`](patterns/zstd.hexpat) | Zstandard compressed data format | diff --git a/patterns/zim.hexpat b/patterns/zim.hexpat new file mode 100644 index 0000000..4eeb086 --- /dev/null +++ b/patterns/zim.hexpat @@ -0,0 +1,79 @@ +#pragma endian little +#pragma description ZIM file format +#pragma magic [ 5A 49 4D 04 ] @ 0x00 + +u32 maxEntries in; + +import std.string; +import std.mem; +import std.math; +import std.core; + +struct Header { + char signature[4]; + u16 majorVersion; + u16 minorVersion; + u128 uid; + u32 entryCount, clusterCount; + u64 pathPtrPos, titlePtrPos, clusterPtrPos, mimeListPos; + u32 mainPage; + u32 layoutPage; + u64 checksumPos; +}; + + +Header header @ 0x00; +std::string::NullString mimeTypes[while(std::mem::read_unsigned($, 1) != 0x00)] @ header.mimeListPos; + +fn mimename(u16 index) { + if(index == 0xFFFF) { + return "redirect"; + } else { + return mimeTypes[index]; + } +}; + +enum CompressionType : u8 { + None = 0x01, + LZMA2 = 0x04, + zstd = 0x05 +}; + +bitfield ClusterInfo { + CompressionType compression: 4; + offsetSize: 1; +} [[bitfield_order(std::core::BitfieldOrder::LeastToMostSignificant, 8)]]; + +struct Cluster { + ClusterInfo info; +} [[inline]]; + +struct ClusterPointer { + Cluster *cluster: u64; +} [[inline]]; + +struct Entry { + u16 mimetype [[format("mimename")]]; + u8 parameterlen [[hidden]]; + char _namespace [[name("namespace")]]; + u32 revision; + if (mimetype == 0xFFFF) { + u32 redirectIndex; + } else { + u32 clusterNumber; + ClusterPointer cluster @ (header.clusterPtrPos + clusterNumber * 8); + u32 blobNumber; + } + + std::string::NullString path; + std::string::NullString title; + padding[parameterlen]; +} [[inline]]; + +struct EntryPointer { + Entry *entry : u64; +} [[inline]]; + +EntryPointer pathPointerList[std::math::min(header.entryCount, maxEntries == 0 ? 1500 : maxEntries)] @ header.pathPtrPos; +u32 titlePointerList[header.entryCount] @ header.titlePtrPos; + diff --git a/tests/patterns/test_data/zim.hexpat.zim b/tests/patterns/test_data/zim.hexpat.zim new file mode 100644 index 0000000000000000000000000000000000000000..99ea0989736329dbd3a1c2f5d96dd493d28e4bf4 GIT binary patch literal 46486 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