From b6469781afa0e9db8f94972069c0204d030f1134 Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:40:21 +0100 Subject: [PATCH 01/10] patterns/texheaders: Added pattern for Arma 3 texHeaders.bin --- README.md | 1 + patterns/a3_texheaders.hexpat | 131 ++++++++++++++++++++++++++++++++++ 2 files changed, 132 insertions(+) create mode 100644 patterns/a3_texheaders.hexpat diff --git a/README.md b/README.md index ccd8378b..fef624ae 100644 --- a/README.md +++ b/README.md @@ -39,6 +39,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | Arma 3 PAA | `image/x.a3-paa` | [`patterns/a3_paa.hexpat`](patterns/a3_paa.hexpat) | Arma 3 PAA texture file | | Arma 3 RTM | `application/x.a3-rtm` | [`patterns/a3_rtm.hexpat`](patterns/a3_rtm.hexpat) | Arma 3 RTM animation file (plain) | | Arma 3 RTM (binarized) | `application/x.a3-bmtr` | [`patterns/a3_bmtr.hexpat`](patterns/a3_bmtr.hexpat) | Arma 3 RTM animation file (binarized) | +| Arma 3 texHeaders.bin | `application/x.a3-texheaders` | [`patterns/a3_texheaders.hexpat`](patterns/a3_texheaders.hexpat) | Arma 3 texture index file | | Assassin's Creed: Unity | | [`patterns/AC Unity`](patterns/Assassin's Creed: Unity) | Assassin's Creed: Unity archive files -- .forge & .data (compressed and decompressed) -- | | Bastion | | [`patterns/bastion/*`](https://gitlab.com/EvelynTSMG/imhex-bastion-pats) | Various [Bastion](https://en.wikipedia.org/wiki/Bastion_(video_game)) files | | BeyondCompare BCSS | | [`patterns/bcss.hexpat`](patterns/bcss.hexpat) | BeyondCompare Snapshot (BCSS) file | diff --git a/patterns/a3_texheaders.hexpat b/patterns/a3_texheaders.hexpat new file mode 100644 index 00000000..3d579104 --- /dev/null +++ b/patterns/a3_texheaders.hexpat @@ -0,0 +1,131 @@ +#pragma author MrClock +#pragma description Arma 3 texture index file format + +#pragma endian little + +#pragma MIME application/x.a3-texheaders + +fn get_data_description() { + return "TexHeaders.bin files are texture index files used in Arma 3 PBO archives.\nThe files are generated during the PBO packing process, and contain basic information about all the PAA texture files in the PBO.\nThe index for each texture includes the file paths relative to the PBO root, pixel format, suffix, number of mipmaps and their resolutions among other things."; +}; + +import std.string; +import std.math; +import type.color; + +using asciiz = std::string::NullString; + +struct BGRA8 { + u8 b; + u8 g; + u8 r; + u8 a; +} [[ + static, + sealed, + format("type::impl::format_color"), + color(std::format("{0:02X}{1:02X}{2:02X}", r, g, b)) +]]; + +struct RGBAfloat { + float r [[transform("float2u8")]]; + float g [[transform("float2u8")]]; + float b [[transform("float2u8")]]; + float a [[transform("float2u8")]]; +} [[ + static, + sealed, + format("type::impl::format_color"), + color(std::format("{0:02X}{1:02X}{2:02X}", r, g, b)) +]]; + +enum PixelFormat: u8 { + INDEXED = 0, + GRAY = 1, + RGB565 = 2, + RGBA5551 = 3, + RGBA4444 = 4, + RGBA8888 = 5, + DXT1 = 6, + DXT2 = 7, + DXT3 = 8, + DXT4 = 9, + DXT5 = 10 +}; + +enum Suffix: u32 { + DEFAULT = 0, + DIFFUSE_LINEAR = 1, + DETAIL = 2, + NORMAL = 3, + IRRADIANCE = 4, + RANDOM = 5, + TREECROWN = 6, + MACRO = 7, + SHADOW = 8, + SPECILAR = 9, + DITHERING = 10, + DETAIL_SPECULAR = 11, + MASK = 12, + THERMAL = 13 +}; + +struct Mipmap { + u16 width; + u16 height; + padding[2]; + PixelFormat format; + padding[1]; + u32 offset [[comment("Byte offset in file")]]; +} [[static,format("formatMipmap")]]; + +struct Texture { + u32 count_color_pallets [[comment("Always 1")]]; + u32 pallet_pointer [[comment("Always 0")]]; + RGBAfloat average_color_float; + BGRA8 average_color; + BGRA8 max_color; + u32 clamp_flags [[comment("Always 0")]]; + u32 transparency [[comment("Always 0xffffffff")]]; + bool maxcolor_defined [[comment("GGATCXAM was found in PAA")]]; + bool alpha_interpolated; + bool alpha_binary; + bool non_opaque [[comment("Interpolated alpha and average alpha < 127")]]; + u32 count_mipmaps; + /* + Technically the format is listed as u32 on the community wiki, but + for the sake of reusability in the Mipmap struct, it is broken up into + a u8 + 3 padding bytes here. (The enum values are in the u8 range + anyway.) + */ + PixelFormat format; + padding[3]; + bool little_endian [[comment("Always true")]]; + bool is_paa [[comment("File is PAA not PAC")]]; + asciiz path [[comment("Path relative to texHeaders.bin file")]]; + Suffix suffix; + u32 count_mipmaps_again; + Mipmap mipmaps[count_mipmaps]; + u32 filesize; +}[[format("formatTexture")]]; + +struct TexHeaders { + char signature[4]; + u32 version [[comment("Always 1")]]; + u32 count_textures; + Texture textures[count_textures]; +}; + +fn float2u8(float value) { + return u8(std::math::round(value * 255)); +}; + +fn formatMipmap(ref Mipmap mip) { + return std::format("{0:d} x {1:d}", mip.width, mip.height); +}; + +fn formatTexture(ref Texture tex) { + return std::format("{0:d} x {1:d} @ {2:s}", tex.mipmaps[0].width, tex.mipmaps[0].height, tex.path); +}; + +TexHeaders file @ 0x0000; From 1f8c456426669dab9106006e74790b8eea8195a7 Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:40:44 +0100 Subject: [PATCH 02/10] magic/arma3: Added texHeaders.bin magic --- magic/arma3_magic | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/magic/arma3_magic b/magic/arma3_magic index 7da0b4c6..7c3eb0b7 100644 --- a/magic/arma3_magic +++ b/magic/arma3_magic @@ -23,3 +23,8 @@ 0 string BMTR Arma 3 binarized RTM animation file !:mime application/x.a3-bmtr !:ext rtm + +# Arma 3 texture index +0 string 0DHT Arma 3 texture index file +!:mime application/x.a3-texheaders +!:ext bin From 909bd2fbe02f22c4175335c5e30986a941226589 Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Tue, 13 Jan 2026 10:41:50 +0100 Subject: [PATCH 03/10] patterns/texheaders: Added test file --- tests/patterns/test_data/a3_texheaders.hexpat.bin | Bin 0 -> 996 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/patterns/test_data/a3_texheaders.hexpat.bin diff --git a/tests/patterns/test_data/a3_texheaders.hexpat.bin b/tests/patterns/test_data/a3_texheaders.hexpat.bin new file mode 100644 index 0000000000000000000000000000000000000000..71bf35b9da79b7a2bd06725ddf459d5663e65ed0 GIT binary patch literal 996 zcmb7DO-sW-5Z!fSstZC7p2c5K73{T96zN$+4+o1eisB%K@f~-G%tc^@ zkh10X{Ig!W-@CtUH@2$PYUSGZnVRI3MHxt`5bF}0_ywy2Cw{}a04M&$%Cm_zwlxHw zjQybrA5s%fEojI2u`ffL8OLaf7y-9v%8va(vO9H*7lYV{bFR#trNu0*nUU7aNNZ-~)Xebz#1JC%7;zAARPvbt*AHE| S>R#L=n)p7ihMAb(`tSuzLe=H~ literal 0 HcmV?d00001 From f664152d5478b69c9c97802922908b36abe8b3f0 Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Tue, 13 Jan 2026 11:12:16 +0100 Subject: [PATCH 04/10] patterns/paa: Small improvements Added extra data description, and better indication of internally compressed data mipmap data. --- patterns/a3_paa.hexpat | 27 ++++++++++++++++++++++++--- 1 file changed, 24 insertions(+), 3 deletions(-) diff --git a/patterns/a3_paa.hexpat b/patterns/a3_paa.hexpat index 02ad904e..9bdb851f 100644 --- a/patterns/a3_paa.hexpat +++ b/patterns/a3_paa.hexpat @@ -5,6 +5,10 @@ #pragma MIME image/x.a3-paa +fn get_data_description() { + return "PAA texture files are the proprietary image format used for textures in Arma 3.\nSimilar to most other formats used in game engines, the PAA stores not only a single resolution, but a series of precomputed mipmaps.\nPAA supports multiple pixel encoding formats, such as DXT1, DXT5, RGBA5551, grayscale, and others. Mipmap data in DXT encoded files is optionally compressed with the LZO1X algorithm. All other types are unconditionally LZSS compressed."; +}; + import type.color; import std.mem; import std.sys; @@ -50,6 +54,12 @@ enum Swizzle: u8 { BLANK_BLACK = 9 }; +enum Compression: u8 { + NONE = 0, + LZO1X = 1, + LZSS = 2 +}; + struct Tagg { char signature[8]; u32 length; @@ -73,14 +83,25 @@ struct Mipmap { u16 width_and_lzo [[format("format_width_lzo")]]; u16 height; - u16 width = width_and_lzo & 0x7FFF; + u16 width = width_and_lzo; + Compression compression = Compression::NONE; + if ((u32(parent.format) & 0xFF00) == 0xFF00) { + width = width_and_lzo & 0x7FFF; + compression = width_and_lzo & 0x8000 ? Compression::LZO1X : Compression::NONE; + } else { + compression = Compression::LZSS; + } if (width == 0 && height == 0) { break; } u24 size; - u8 data[size]; + match (compression) { + (Compression::NONE): u8 encoded_data[size]; + (Compression::LZO1X): u8 lzo_compressed_data[size]; + (Compression::LZSS): u8 lzss_compressed_data[size]; + } } [[format("format_resolution")]]; struct PAA { @@ -100,7 +121,7 @@ fn format_resolution(ref auto mip) { fn format_width_lzo(u16 value) { u16 width = value & 0x7FFF; if (value & 0x8000) { - return std::format("{0:d} (+LZO)", width); + return std::format("{0:d} (+LZO flag)", width); } else { return std::format("{0:d}", width); } From 7ecc6f1493332d541f643e9cdcb901fdcba0943c Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Tue, 13 Jan 2026 23:50:09 +0100 Subject: [PATCH 05/10] patterns/a3: Moved Arma 3 patterns into common folder --- README.md | 8 ++++---- patterns/{ => a3}/a3_bmtr.hexpat | 0 patterns/{ => a3}/a3_paa.hexpat | 0 patterns/{ => a3}/a3_rtm.hexpat | 0 patterns/{ => a3}/a3_texheaders.hexpat | 0 5 files changed, 4 insertions(+), 4 deletions(-) rename patterns/{ => a3}/a3_bmtr.hexpat (100%) rename patterns/{ => a3}/a3_paa.hexpat (100%) rename patterns/{ => a3}/a3_rtm.hexpat (100%) rename patterns/{ => a3}/a3_texheaders.hexpat (100%) diff --git a/README.md b/README.md index fef624ae..fe8abca9 100644 --- a/README.md +++ b/README.md @@ -36,10 +36,10 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | ARC | | [`patterns/arc.hexpat`](patterns/arc.hexpat) | Minecraft Legacy Console Edition ARC files | | ARIA2 | | [`patterns/aria2.hexpat`](patterns/aria2.hexpat) | ARIA2 Download Manager Control files | | ARM VTOR | | [`patterns/arm_cm_vtor.hexpat`](patterns/arm_cm_vtor.hexpat) | ARM Cortex M Vector Table Layout | -| Arma 3 PAA | `image/x.a3-paa` | [`patterns/a3_paa.hexpat`](patterns/a3_paa.hexpat) | Arma 3 PAA texture file | -| Arma 3 RTM | `application/x.a3-rtm` | [`patterns/a3_rtm.hexpat`](patterns/a3_rtm.hexpat) | Arma 3 RTM animation file (plain) | -| Arma 3 RTM (binarized) | `application/x.a3-bmtr` | [`patterns/a3_bmtr.hexpat`](patterns/a3_bmtr.hexpat) | Arma 3 RTM animation file (binarized) | -| Arma 3 texHeaders.bin | `application/x.a3-texheaders` | [`patterns/a3_texheaders.hexpat`](patterns/a3_texheaders.hexpat) | Arma 3 texture index file | +| Arma 3 PAA | `image/x.a3-paa` | [`patterns/a3/a3_paa.hexpat`](patterns/a3/a3_paa.hexpat) | Arma 3 PAA texture file | +| Arma 3 RTM | `application/x.a3-rtm` | [`patterns/a3/a3_rtm.hexpat`](patterns/a3/a3_rtm.hexpat) | Arma 3 RTM animation file (plain) | +| Arma 3 RTM (binarized) | `application/x.a3-bmtr` | [`patterns/a3/a3_bmtr.hexpat`](patterns/a3/a3_bmtr.hexpat) | Arma 3 RTM animation file (binarized) | +| Arma 3 texHeaders.bin | `application/x.a3-texheaders` | [`patterns/a3/a3_texheaders.hexpat`](patterns/a3/a3_texheaders.hexpat) | Arma 3 texture index file | | Assassin's Creed: Unity | | [`patterns/AC Unity`](patterns/Assassin's Creed: Unity) | Assassin's Creed: Unity archive files -- .forge & .data (compressed and decompressed) -- | | Bastion | | [`patterns/bastion/*`](https://gitlab.com/EvelynTSMG/imhex-bastion-pats) | Various [Bastion](https://en.wikipedia.org/wiki/Bastion_(video_game)) files | | BeyondCompare BCSS | | [`patterns/bcss.hexpat`](patterns/bcss.hexpat) | BeyondCompare Snapshot (BCSS) file | diff --git a/patterns/a3_bmtr.hexpat b/patterns/a3/a3_bmtr.hexpat similarity index 100% rename from patterns/a3_bmtr.hexpat rename to patterns/a3/a3_bmtr.hexpat diff --git a/patterns/a3_paa.hexpat b/patterns/a3/a3_paa.hexpat similarity index 100% rename from patterns/a3_paa.hexpat rename to patterns/a3/a3_paa.hexpat diff --git a/patterns/a3_rtm.hexpat b/patterns/a3/a3_rtm.hexpat similarity index 100% rename from patterns/a3_rtm.hexpat rename to patterns/a3/a3_rtm.hexpat diff --git a/patterns/a3_texheaders.hexpat b/patterns/a3/a3_texheaders.hexpat similarity index 100% rename from patterns/a3_texheaders.hexpat rename to patterns/a3/a3_texheaders.hexpat From f4230ac1bf781a2cf99ef765218b92e854a98840 Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Thu, 15 Jan 2026 00:42:54 +0100 Subject: [PATCH 06/10] patterns/a3: Added pattern for MLOD P3D --- README.md | 1 + patterns/a3/a3_p3d_mlod.hexpat | 222 +++++++++++++++++++++++++++++++++ 2 files changed, 223 insertions(+) create mode 100644 patterns/a3/a3_p3d_mlod.hexpat diff --git a/README.md b/README.md index fe8abca9..b2ad9127 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | ARC | | [`patterns/arc.hexpat`](patterns/arc.hexpat) | Minecraft Legacy Console Edition ARC files | | ARIA2 | | [`patterns/aria2.hexpat`](patterns/aria2.hexpat) | ARIA2 Download Manager Control files | | ARM VTOR | | [`patterns/arm_cm_vtor.hexpat`](patterns/arm_cm_vtor.hexpat) | ARM Cortex M Vector Table Layout | +| Arma 3 P3D (MLOD) | `model/x.a3-p3d-mlod` | [`patterns/a3/a3_p3d_mlod.hexpat`](patterns/a3/a3_p3d_mlod.hexpat) | Arma 3 P3D model file (MLOD) | | Arma 3 PAA | `image/x.a3-paa` | [`patterns/a3/a3_paa.hexpat`](patterns/a3/a3_paa.hexpat) | Arma 3 PAA texture file | | Arma 3 RTM | `application/x.a3-rtm` | [`patterns/a3/a3_rtm.hexpat`](patterns/a3/a3_rtm.hexpat) | Arma 3 RTM animation file (plain) | | Arma 3 RTM (binarized) | `application/x.a3-bmtr` | [`patterns/a3/a3_bmtr.hexpat`](patterns/a3/a3_bmtr.hexpat) | Arma 3 RTM animation file (binarized) | diff --git a/patterns/a3/a3_p3d_mlod.hexpat b/patterns/a3/a3_p3d_mlod.hexpat new file mode 100644 index 00000000..91f192bd --- /dev/null +++ b/patterns/a3/a3_p3d_mlod.hexpat @@ -0,0 +1,222 @@ +#pragma author MrClock +#pragma description Arma 3 P3D model format (MLOD) + +#pragma endian little + +#pragma MIME model/x.a3-p3d-mlod + +fn get_data_description() { + return "MLOD type P3D files are used for authoring 3D models for Arma 3.\nThese files can be carated in and edited in the Object Builder application.\nAll data is stored uncompressed for ease of editing.\nDuring the PBO packing process they are further \"binarized\" into ODOL type P3D files, that are optimized for use by the game engine. (These are no longer editable, the conversion is irreversible.)\n\nP3D model files can by quite large, so by default only the 1st LOD is processed by this pattern. Processing of all LODs can be enabled in the pattern settings."; +}; + +import std.string; +import std.core; + +bool process_all_lods in; + +using asciiz = std::string::NullString; + +enum FaceType: u32 { + TRIANGLE = 3, + QUAD = 4 +}; + +struct Vector { + float x; + float y; + float z; +} [[static,format("formatVector")]]; + +struct UV { + float u; + float v; +} [[static,format("formatUV")]]; + +enum SurfaceFitting: u8 { + NORMAL = 0, + ON_SURFACE = 1, + ABOVE_SURFACE = 2, + UNDER_SURFACE = 4, + KEEP_HEIGHT = 8 +}; + +enum Lighting: u8 { + NORMAL = 0, + SHINING = 1, + SHADOWED = 2, + FULL_LIT = 4, + HALF_LIT = 8 +}; + +enum DecalMode: u8 { + NORMAL = 0, + DECAL = 1, + RADIO12 = 2 +}; + +enum Fog: u8 { + NORMAL = 0, + NONE = 1, + SKY = 2 +}; + +enum NormalCalculation: u8 { + FACE_AREA = 0, + HIDDE_VERTEX = 1, + FIXED = 2, + FACE_ANLGE = 4 +}; + +bitfield VertexFlags { + padding : 5; + NormalCalculation normals : 3; + u8 user; + padding : 2; + Fog fog : 2; + padding : 2; + DecalMode decal : 2; + Lighting lighting : 4; + SurfaceFitting surface : 4; +} [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 32)]]; + +struct Vertex { + Vector position; + VertexFlags flags; +}[[static,format("formatVertex")]]; + +struct FacePoint { + u32 vertex_index; + u32 normal_index; + UV uv; +} [[static]]; + +enum ZBiasFlag: u8 { + NONE = 0, + LOW = 1, + MIDDLE = 2, + HIGH = 3 +}; + +bitfield FaceFlags { + user : 7; + disable_texture_merging : 1; + padding : 2; + flat_lighting : 1; + reversed_face : 1; + padding : 10; + ZBiasFlag zbias : 2; + position : 1; + padding : 1; + double_sided_face : 1; + disable_shadow : 1; + padding : 4; +} [[bitfield_order(std::core::BitfieldOrder::MostToLeastSignificant, 32)]]; + +struct Face { + FaceType type; + FacePoint points[4]; + FaceFlags flags; + asciiz texture; + asciiz material; +} [[format("formatFace")]]; + +struct Edge { + u32 vertex_1; + u32 vertex_2; +} [[sealed,static,format("formatEdge")]]; + +struct Property { + char name[64] [[transform("std::string::to_string")]]; + char value[64] [[transform("std::string::to_string")]]; +} [[static,sealed,format("formatProperty")]]; + +struct Tagg { + bool active; + asciiz name; + u32 length; + + if (name == "#EndOfFile#") { + break; + } + + match (name) { + ("#SharpEdges#"): Edge edges[length/8]; + ("#Property#"): Property property; + ("#Mass#"): float masses[parent.count_verticies]; + ("#UVSet#"): { + u32 channel; + UV coordinates[(length - 4) / 8]; + } + (_): if (std::string::starts_with(name, "#") && std::string::ends_with(name, "#")) { + u8 unknown_data[length]; + } else { + u8 vertex_weights[parent.count_verticies]; + u8 face_weights[parent.count_faces]; + } + } +} [[format("formatTagg")]]; + +struct P3dmLod { + char signature[4]; + u32 version_major; + u32 version_minor; + u32 count_verticies; + u32 count_normals; + u32 count_faces; + u32; // Unknown data (might be unused model flags) + Vertex verticies[count_verticies]; + Vector normals[count_normals]; + Face faces[count_faces]; + char tagg_signature[4]; + Tagg taggs[while(true)]; + float resolution; +} [[format("formatP3dmLod")]]; + +struct P3D { + char signature[4]; + u32 version; + u32 count_lods; + if (!process_all_lods) { + P3dmLod lod_0; + } else { + P3dmLod lods[count_lods]; + } +}; + +fn formatVector(ref Vector pos) { + return std::format("[{0:.3f}, {1:.3f}, {1:.3f}]", pos.x, pos.y, pos.z); +}; + +fn formatUV(ref UV pos) { + return std::format("[{0:.3f}, {1:.3f}]", pos.u, pos.v); +}; + +fn formatVertex(ref Vertex vert) { + return formatVector(vert.position); +}; + +fn formatFace(ref Face face) { + return face.type == FaceType::TRIANGLE ? "triangle" : "quad"; +}; + +fn formatEdge(ref Edge edge) { + return std::format("{0:d} <-> {1:d}", edge.vertex_1, edge.vertex_2); +}; + +fn formatProperty(ref Property prop) { + return std::format("\"{0:s}\" = \"{1:s}\"", prop.name, prop.value); +}; + +fn formatTagg(ref Tagg tagg) { + if (std::core::has_member(tagg, "vertex_weights")) { + return std::format("\"{0:s}\" selection", tagg.name); + } else { + return std::format("\"{0:s}\"", tagg.name); + } +}; + +fn formatP3dmLod(ref P3dmLod lod) { + return std::format("Resolution: {0}", lod.resolution); +}; + +P3D file @ 0x0000; From 2b121896494fe448b6ff9bb2f8625e1ab5e6dc0e Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Thu, 15 Jan 2026 00:43:57 +0100 Subject: [PATCH 07/10] patterns/a3: Added pattern for RAP --- README.md | 1 + patterns/a3/a3_rap.hexpat | 165 ++++++++++++++++++++++++++++++++++++++ 2 files changed, 166 insertions(+) create mode 100644 patterns/a3/a3_rap.hexpat diff --git a/README.md b/README.md index b2ad9127..f21148ad 100644 --- a/README.md +++ b/README.md @@ -36,6 +36,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | ARC | | [`patterns/arc.hexpat`](patterns/arc.hexpat) | Minecraft Legacy Console Edition ARC files | | ARIA2 | | [`patterns/aria2.hexpat`](patterns/aria2.hexpat) | ARIA2 Download Manager Control files | | ARM VTOR | | [`patterns/arm_cm_vtor.hexpat`](patterns/arm_cm_vtor.hexpat) | ARM Cortex M Vector Table Layout | +| Arma 3 config | `application/x.a3-rap` | [`patterns/a3/a3_rap.hexpat`](patterns/a3/a3_rap.hexpat) | Arma 3 binary/rapified config | | Arma 3 P3D (MLOD) | `model/x.a3-p3d-mlod` | [`patterns/a3/a3_p3d_mlod.hexpat`](patterns/a3/a3_p3d_mlod.hexpat) | Arma 3 P3D model file (MLOD) | | Arma 3 PAA | `image/x.a3-paa` | [`patterns/a3/a3_paa.hexpat`](patterns/a3/a3_paa.hexpat) | Arma 3 PAA texture file | | Arma 3 RTM | `application/x.a3-rtm` | [`patterns/a3/a3_rtm.hexpat`](patterns/a3/a3_rtm.hexpat) | Arma 3 RTM animation file (plain) | diff --git a/patterns/a3/a3_rap.hexpat b/patterns/a3/a3_rap.hexpat new file mode 100644 index 00000000..31f35443 --- /dev/null +++ b/patterns/a3/a3_rap.hexpat @@ -0,0 +1,165 @@ +#pragma author MrClock +#pragma description Arma 3 binary configuration format + +#pragma endian little + +#pragma MIME application/x.a3-rap + +fn get_data_description() { + return "The RAP format is the binarized/\"rapified\" version of configuration files for Arma 3. Plain text configuration, material definition, scenario description and other files using the configuration syntax are rapified during the PBO packing process. The game can work with the plain text versions (they are actually rapified during boot), but properly converting them into the binary format ahead of time makes the booting easier."; +}; + +import std.mem; +import std.string; +import std.core; +import std.io; + +using asciiz = std::string::NullString [[format("formatAsciiz")]]; + +/* +Item counts are stored in 7-bit encoded integers. In each byte the top bit signals +if the next byte belongs to the number as well. +*/ +struct CompressedUint { + u8 extras[while(std::mem::read_unsigned($, 1) & 0x80)]; + u8 last; +} [[sealed,transform("transformCompressedUint"),format("formatCompressedUint")]]; + +enum MemberType: u8 { + CLASS = 0, + LITERAL = 1, + ARRAY = 2, + EXTERNAL = 3, + DELETE = 4, + ARRAY_EXTENSION = 5 +}; + +enum ValueType: u8 { + STRING = 0, + FLOAT = 1, + INTEGER = 2, + ARRAY = 3, + VARIABLE = 4 +}; + +using Array; + +struct ArrayItem { + ValueType type; + match (type) { + (ValueType::STRING): asciiz value; + (ValueType::FLOAT): float value; + (ValueType::INTEGER): s32 value; + (ValueType::ARRAY): Array value; + (ValueType::VARIABLE): asciiz value; + } +} [[format("formatArrayItem")]]; + +struct Array { + CompressedUint count_items; + ArrayItem items[count_items]; +} [[format("formatArray")]]; + +using ClassBody; + +struct Member { + MemberType type; + + match(type) { + (MemberType::CLASS): { + asciiz name; + ClassBody *body : u32; + } + (MemberType::LITERAL): { + ValueType subtype; + asciiz name; + match (subtype) { + (ValueType::STRING): asciiz value; + (ValueType::FLOAT): float value; + (ValueType::INTEGER): s32 value; + (_): std::error(std::format("Unexpected subtype for literal: {}", subtype)); + } + } + (MemberType::ARRAY | MemberType::ARRAY_EXTENSION): { + asciiz name; + Array value; + } + (MemberType::EXTERNAL): { + asciiz name; + } + (MemberType::DELETE): { + asciiz name; + } + } +} [[format("formatMember")]]; + +using Enums; + +struct ClassBody { + asciiz parent_name; + CompressedUint count_members; + + Member members[count_members]; + u8 *pointer : u32 [[comment("In the root body this points to the enum list.\nIn all others it points to the next body on the same level.")]]; +}; + +struct EnumItem { + asciiz name; + s32 value; +} [[format("formatEnumItem")]]; + +struct Enums { + u32 count_items; + EnumItem items[count_items]; +}; + +struct RAP { + char signature[4]; + u32; + u32; + Enums *enums : u32; + ClassBody root; +}; + +fn formatAsciiz(ref asciiz value) { + return std::format("\"{0:s}\"", value); +}; + +fn transformCompressedUint(ref CompressedUint value) { + u64 result = 0; + for (u8 i = 0, i < sizeof(value.extras), i += 1) { + result += (value.extras[i] & 0x7F) << (7*i); + } + + result += value.last << (sizeof(value.extras) * 7); + + return result; +}; + +fn formatCompressedUint(ref CompressedUint value) { + return value; +}; + +fn formatArrayItem(ref ArrayItem value) { + return value.value; +}; + +fn formatArray(ref Array value) { + return "{...}"; +}; + +fn formatMember(ref Member item) { + match (item.type) { + (MemberType::CLASS): return std::format("class {0:s} {{...}};", item.name); + (MemberType::LITERAL): return std::format("{0:s} = {1};", item.name, item.value); + (MemberType::ARRAY): return std::format("{0:s}[] = {{...}};", item.name); + (MemberType::EXTERNAL): return std::format("class {0:s};", item.name); + (MemberType::DELETE): return std::format("del {0:s};", item.name); + } +}; + +fn formatEnumItem(ref EnumItem value) { + return std::format("{0:s} = {1}", item.name, item.value); +}; + +RAP file @ 0x0000; From 21ed025f41ad684cb215afe2e213b95653aec6ca Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Thu, 15 Jan 2026 00:44:41 +0100 Subject: [PATCH 08/10] magic/arma3: Added P3D and RAP to magic file --- magic/arma3_magic | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/magic/arma3_magic b/magic/arma3_magic index 7c3eb0b7..33c2880e 100644 --- a/magic/arma3_magic +++ b/magic/arma3_magic @@ -20,7 +20,7 @@ !:ext rtm # Arma 3 binarized RTM animation -0 string BMTR Arma 3 binarized RTM animation file +0 string BMTR Arma 3 RTM animation file (binarized) !:mime application/x.a3-bmtr !:ext rtm @@ -28,3 +28,14 @@ 0 string 0DHT Arma 3 texture index file !:mime application/x.a3-texheaders !:ext bin + +# Arma 3 MLOD P3D model +0 string MLOD Arma 3 P3D model file (MLOD) +!:mime model/x.a3-p3d-mlod +!:ext p3d +>0x0c string P3DM P3DM LOD type + +# Arma 3 binarized config +0x01 string raP Arma 3 binary configuration file +!:mime application/x.a3-rap +!:ext bin From 5974d7be683c08e754ac49ba46a827781dbe0c7c Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Thu, 15 Jan 2026 00:44:55 +0100 Subject: [PATCH 09/10] patterns/a3: Added test files for P3D and RAP --- tests/patterns/test_data/a3_p3d_mlod.hexpad.p3d | Bin 0 -> 5537 bytes tests/patterns/test_data/a3_rap.hexpat.bin | Bin 0 -> 118 bytes 2 files changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/patterns/test_data/a3_p3d_mlod.hexpad.p3d create mode 100644 tests/patterns/test_data/a3_rap.hexpat.bin diff --git a/tests/patterns/test_data/a3_p3d_mlod.hexpad.p3d b/tests/patterns/test_data/a3_p3d_mlod.hexpad.p3d new file mode 100644 index 0000000000000000000000000000000000000000..fb055d679289ce27dcc7af113920fcd68b290653 GIT binary patch literal 5537 zcmeHKNpI9j5bgaNx=u;Xd;l z_!UTx8~lKwzN#*Fx$Q|}@{Y7*cU4!_SJhqZF8A#1JJ&lM=QcR!?q0q=dxCmL{1)nt z(6a+Pwefh|rS2;|qlizzQ=`5B&2q!5T*u>{<%B=9_J;TJHzOLGfur$wBAt@X7q2sc z9?y`y`fQ|Oj|M+NHPfrfjV=50m=!vji-|eJX>d3m>sK+6zqnBvkxFWu7tZli$rmZ; zU?(~3vLL}y_cf6j;@e4&*UGRiyGp)DfxMr2yetwdgAbQswtJu? zS{bOlcrh<4ewcY3Ll6A8VJyzozEY`8?wWEHW+NF|Tke{2b-=^~a&5V5%GE)GwD#OJ z){!yM2gi?YrbLqzD27s$Y) z*KZEFz&?UpzaG#ShYou3m_N_w`}soRkbzE9o1c5pyEIvxJpA{5-cQ_l4U}-C=?<6g zD2yqua+YLaudDoj9Val0tG?|R`_os}tnb!6-brpOF5G#1^V#z}@y?1gE}7>^VvZIZ z^#whnw`XUjs5EH_)e^u573B1wDX(%Jk9(FA-ijsj*TyT*E2tm+B%1oK82d5(t@~s8 z9{2xEe;aywXy`6%5D@eYJu-A^G3~g;nc%BybjW`hkBwnyFyXhHi=~Y%&1qGfNpq>J zkHt4JRMyST-OQyDgNdgyUyE;ahBye~hHq28Puci-Sm-9#14QhK^uq@CUXBz!PE?}D zidr_61?XrCNO}l)N$V0Gkl=GYB|IP@QuCd)F5v-by(K&#VSPf0mMlo2mBR|TvgLB+ z+}U!7b(5kdy8syprKn!QLrl=bZ@h2(yLFTqi3#K}FO;28E#UzPF&r;7U51^=mj!=3 zW0#&R<@MjMfP~zgDm7h(bvVtu#02Cs%;Q!6{R&9P-PuypWgyQnA67hJ9ij!91`1ACL3edmo;ru2R;)mo2NxV!z63AICR&yk5fp`FJxf;kb?H(PBzA zmqWv&9HCLq4q0w^mFsxivwXD@J{VjW4F-cQ^(a4OsmA!{!C)xz3n}S<))F;>E_iPX zo=TIHe2>Fp_|)Qt4;hap3_4RK-{$7gw&buA{vU2CIXHlPOni)m7$xD8ZysXF=6q`e zx+2B118TU@=~jYPm<_Du)+p=8T6~N_N095s7K?ax)zU1tMvQ4pqN#lS*b=$;4!WS? z*&uHc6Qpf&hcz=R?xKt^ACQ5L2LF*}W3E`4Uu|+1&o1m#K-Ei*c!5=%yRa_sFS(LE zE=aHmcVxQ@;@pW3u>w*+mND?5R4WpfoNK)oOTEQ>v43%;TiV^_-cr8Yf1S93pypon zd-G(HF9W`PyV%~OV$n%v=U&n!_Y9AIqQ#s1bpGBjd6hEk3wgln9&oksT6y~ZZtuh2 D;I6|A literal 0 HcmV?d00001 diff --git a/tests/patterns/test_data/a3_rap.hexpat.bin b/tests/patterns/test_data/a3_rap.hexpat.bin new file mode 100644 index 0000000000000000000000000000000000000000..649db87f73a695d8f498f9735070284c8b1b5ebe GIT binary patch literal 118 zcmZQ5N(^8C0uCT90x=jFoYT?+5=)XZQi~bX5TZdPxlWmR3>GjE7Dk4|(vpn)A_m_g s=bZfHYzC&H)WXutqSO?}l$89uVg?4L(!9(P5M7>{SO69Q+W<5H0BBqps{jB1 literal 0 HcmV?d00001 From 9b99860b2ccfc1d1658da60e42c55b06550aadce Mon Sep 17 00:00:00 2001 From: MrClock8163 <48679831+MrClock8163@users.noreply.github.com> Date: Wed, 4 Feb 2026 02:10:48 +0100 Subject: [PATCH 10/10] patterns/a3: Small correction to type names in TexHeaders format --- patterns/a3/a3_texheaders.hexpat | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/patterns/a3/a3_texheaders.hexpat b/patterns/a3/a3_texheaders.hexpat index 3d579104..6b7ad9f0 100644 --- a/patterns/a3/a3_texheaders.hexpat +++ b/patterns/a3/a3_texheaders.hexpat @@ -54,7 +54,7 @@ enum PixelFormat: u8 { }; enum Suffix: u32 { - DEFAULT = 0, + DIFFUSE = 0, DIFFUSE_LINEAR = 1, DETAIL = 2, NORMAL = 3, @@ -63,7 +63,7 @@ enum Suffix: u32 { TREECROWN = 6, MACRO = 7, SHADOW = 8, - SPECILAR = 9, + SPECULAR = 9, DITHERING = 10, DETAIL_SPECULAR = 11, MASK = 12,