diff --git a/README.md b/README.md index a53c40b..c2c709b 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 PAA | | [`patterns/a3_paa.hexpat`](patterns/a3_paa.hexpat) | Arma 3 PAA texture 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 | @@ -266,6 +267,7 @@ Everything will immediately show up in ImHex's Content Store and gets bundled wi | Name | Path | Description | |------|------|-------------| +| Arma 3 | [`magic/arma3_magic`](magic/arma3_magic) | Identifies Arma 3 binary formats | | Nintendo Switch | [`magic/nintendo_switch_magic`](magic/nintendo_switch_magic) | Identifies common file types used on the Nintendo Switch | | Portable Executable | [`magic/portable_executable_magic`](magic/portable_executable_magic) | Identifies PE files used on Windows diff --git a/magic/arma3_magic b/magic/arma3_magic new file mode 100644 index 0000000..6df2c80 --- /dev/null +++ b/magic/arma3_magic @@ -0,0 +1,15 @@ +# A libmagic database containing definitions for files used by the Arma 3 game by Bohemia Interactive + +# Arma 3 PAA image +0x02 string GGAT Arma 3 PAA image file +!:mime image/x.a3-paa +!:ext paa +>0 leshort 0xff01 DXT1 compression +>0 leshort 0xff02 DXT2 compression +>0 leshort 0xff03 DXT3 compression +>0 leshort 0xff04 DXT4 compression +>0 leshort 0xff05 DXT5 compression +>0 leshort 0x4444 RGBA4 format +>0 leshort 0x1555 RGBA5 format +>0 leshort 0x8888 RGBA8 format +>0 leshort 0x8080 Grayscale format diff --git a/patterns/a3_paa.hexpat b/patterns/a3_paa.hexpat new file mode 100644 index 0000000..02ad904 --- /dev/null +++ b/patterns/a3_paa.hexpat @@ -0,0 +1,120 @@ +#pragma author MrClock +#pragma description Arma 3 PAA image format + +#pragma endian little + +#pragma MIME image/x.a3-paa + +import type.color; +import std.mem; +import std.sys; + +struct Color { + u8 b; + u8 g; + u8 r; + if (alpha) u8 a; +} [[sealed, format("type::impl::format_color"), color(std::format("{0:02X}{1:02X}{2:02X}", r, g, b))]]; + +using BGR8 = Color; +using BGRA8 = Color; + +enum PixelFormat: u16 { + DXT1 = 0xFF01, + DXT2 = 0xFF02, + DXT3 = 0xFF03, + DXT4 = 0xFF04, + DXT5 = 0xFF05, + RGBA4 = 0x4444, + RGBA5 = 0x1555, + RGBA8 = 0x8888, + GRAY = 0x8080 +}; + +enum AlphaMode: u32 { + NONE = 0, + INTERPOLATED = 1, + BINARY = 2 +}; + +enum Swizzle: u8 { + ALPHA = 0, + RED = 1, + GREEN = 2, + BLUE = 3, + INVERTED_ALPHA = 4, + INVERTED_RED = 5, + INVERTED_GREEN = 6, + INVERTED_BLUE = 7, + BLANK_WHITE = 8, + BLANK_BLACK = 9 +}; + +struct Tagg { + char signature[8]; + u32 length; + + match (signature) { + ("GGATCGVA"): BGRA8 color; + ("GGATCXAM"): BGRA8 color; + ("GGATGALF"): AlphaMode alpha; + ("GGATSFFO"): u32 offsets[16]; + ("GGATZIWS"): Swizzle copies[4]; + (_): u8 data[length]; + } +} [[format("format_tagg_name")]]; + +struct Palette { + u16 length; + BGR8 colors[length]; +}; + +struct Mipmap { + u16 width_and_lzo [[format("format_width_lzo")]]; + u16 height; + + u16 width = width_and_lzo & 0x7FFF; + + if (width == 0 && height == 0) { + break; + } + + u24 size; + u8 data[size]; +} [[format("format_resolution")]]; + +struct PAA { + PixelFormat format; + Tagg taggs[while(std::mem::read_string($, 4) == "GGAT")]; + Palette palette; + Mipmap mipmaps[while(true)]; + u16 EOF; + + std::assert_warn(EOF == 0, "Invalid EOF sentinel"); +}; + +fn format_resolution(ref auto mip) { + return std::format("{0:d} x {1:d}", mip.width, mip.height); +}; + +fn format_width_lzo(u16 value) { + u16 width = value & 0x7FFF; + if (value & 0x8000) { + return std::format("{0:d} (+LZO)", width); + } else { + return std::format("{0:d}", width); + } +}; + +fn format_tagg_name(Tagg data) { + match (data.signature) { + ("GGATCGVA"): return "Average color"; + ("GGATCXAM"): return "Max color"; + ("GGATGALF"): return "Alpha flag"; + ("GGATZIWS"): return "Swizzle"; + ("GGATSFFO"): return "Mipmap offsets"; + (_): return "Unknown"; + } +}; + +PAA file @ 0x0000; diff --git a/tests/patterns/test_data/a3_paa.hexpat.paa b/tests/patterns/test_data/a3_paa.hexpat.paa new file mode 100644 index 0000000..373a502 Binary files /dev/null and b/tests/patterns/test_data/a3_paa.hexpat.paa differ