| Crates.io | amv_decoder |
| lib.rs | amv_decoder |
| version | 0.1.0 |
| created_at | 2025-12-16 06:45:14.166064+00 |
| updated_at | 2025-12-16 06:45:14.166064+00 |
| description | Experimental AMV parser and decoder for KiriKiri2 / KiriKiriZ engine videos. |
| homepage | |
| repository | https://github.com/xmoezzz/amv_decoder |
| max_upload_size | |
| id | 1987338 |
| size | 3,184,286 |
amv_decoder is an experimental Rust project that parses and (partially) decodes the AJPM / “Alpha Movie” video
format used by some KiriKiri2 and KiriKiriZ (krkrz) engine titles.
This repository is the product of reverse engineering work. It is not affiliated with, endorsed by, or supported by any engine or game vendor.
0x4D504A41 / AJPM).header_size.Prerequisites
Build
cargo build
Dump packets (recommended first step for new samples)
cargo run -- dump testcase/1.amv --out out_dump
This writes:
out_dump/index.jsonout_dump/frames/frame_000000.packet.binout_dump/frames/frame_000000.seg0.binout_dump/frames/frame_000000.seg1.binDecode frames (experimental)
cargo run -- decode testcase/1.amv --out out_decode --ppm true
This writes per-frame outputs:
out_decode/frame_000000.rgba (raw RGBA bytes)out_decode/frame_000000.ppm (RGB-only PPM, if --ppm true)Sample-based decode tests are marked #[ignore] because testcase/1.amv is not committed.
Run ignored tests locally:
cargo test -- --ignored --nocapture
All integers are little-endian.
A minimal working layout based on observed loader behavior:
u32 magic = 0x4D504A41 (AJPM)u32 unknown_04u32 revision (expected 0)u32 header_size (observed 168 or 232)u32 unknown_10u32 frame_countu32 fps_numu32 fps_denu16 widthu16 heightu8 attru8[3] reservedImmediately after the 40-byte header, the file contains global quantization tables whose size is
header_size - 40:
128 bytes in packet type A mode192 bytes in packet type B modeThe engine-side flag that selects the seek path (stream+29 in the IDA output) is derived from attr.
In practice, this correlates with whether alpha data is present.
sub_10016D20)This packet type begins with a 24-byte header and is split into two payload segments. Conceptually:
u32 tagu32 chunk_size (bytes after the first 8 bytes)u32 frame_idi16 p0i16 p1u16 w_aligned (multiple of 16)u16 h_aligned (multiple of 16)u32 seg0_lenu8 seg0[seg0_len]u8 seg1[chunk_size - 16 - seg0_len]Observed behavior suggests:
seg0 is zlib-compressed and inflates to a w_aligned * h_aligned byte plane.seg1 is an entropy-coded DCT coefficient stream.sub_10017570)This packet type begins with a 20-byte header and contains a single payload blob:
u32 tagu32 chunk_size (bytes after the first 8 bytes)u32 frame_idi16 p0i16 p1u16 w_aligned (multiple of 16)u16 h_aligned (multiple of 16)u8 payload[chunk_size - 12]Observed behavior suggests:
Both packet types use a JPEG-like coding model for 8×8 blocks:
Dual-licensed under MPL-2.0 (see Cargo.toml).