Skip to content

v0.3.0

Choose a tag to compare

@MejiroRina MejiroRina released this 27 Jun 06:17
· 33 commits to main since this release
025e47e

A correctness and performance release. HCA, ACB and USM decoding now matches the reference implementations (vgmstream's clHCA, PyCriCodecs and WannaCRI) on real game assets, HCA decoding and USM decryption are SIMD-accelerated, and encrypted HCA can be decoded with keys. Existing calls keep working and the new parameters are optional; Rust code that names the type of UtfTable's map fields needs a small change.

⚠️ Breaking changes

  • Rust only: the public UtfTable::constants and UtfTable::rows fields now hold rustc_hash::FxHashMap<String, Value> instead of std::collections::HashMap<String, Value>. Code that names the old map type needs to use FxHashMap from the rustc-hash crate or let the type be inferred. (#9)

Added

  • Decode type-56 encrypted HCA by passing the optional key and subkey parameters to decode_hca and decode_hca_bytes. Unencrypted files are unaffected, and ACB extraction now reads each AWB's subkey from AFS2 automatically. (#10)
  • Ship Python type information: cridecoder.pyi stubs and a PEP 561 py.typed marker, so editors and type checkers see typed signatures for all 12 exported functions. (#10)
  • Pick the USM output extension from the stream's codec: VP9 video is written as .ivf (otherwise .m2v) and HCA audio as .hca (otherwise .adx). (#10)
  • Write a smpl loop chunk into WAV files decoded from looping HCA. (#10)

Fixed

  • Compute the HCA resolution with an unsigned shift, as clHCA does, which fixes subtly wrong coefficients. (#10)
  • Trim the trailing encoder padding of HCA streams, so decoded audio has the true sample count. (#10)
  • Name ACB tracks after the cue they belong to; names were bound to the reference index and could be mismatched. (#10)
  • Give tracks from HCA-MX cue sheets the .hca extension. (#10)
  • Keep reading USM streams past the first #CONTENTS END, which cut longer streams short. (#10)
  • Apply the audio XOR mask to ADX audio only. (#10)
  • Fix the order of the video-mask passes in UsmBuilder, so a USM it builds extracts back to the original data. (#10)
  • Skip ACB cues with unhandled reference types instead of failing the whole extraction. (#10)
  • Validate VBR, @UTF versions and EOF bounds, fall back from UTF-8 to Shift-JIS and UTF-16 when decoding CRI strings, and align AFS2 next offsets. (#10)

Performance

  • Vectorize the HCA inverse MDCT with NEON on aarch64 and SSE2 or runtime-detected AVX2 on x86_64. Output stays bit-identical (no FMA), and decoding is about 1.3–1.5× faster than clHCA at -O3. (#10)
  • Decrypt USM packets in place with auto-vectorized code. The de-mask itself goes from about 3 to 40 GB/s, and end-to-end extraction of an encrypted USM is about 3.2–3.4× faster. (#10)
  • Compute HCA frame CRC16 with slicing-by-8; it was about 12% of decode time. (#10)
  • Dequantize HCA with a register-resident bit accumulator: the decode core goes from 106.1 to 78.4 ms (about 26% faster) on a 4.5 MB stereo HCA. (#8)
  • Speed up the HCA bit reader and turn the decode lookup tables into compile-time constants: the decode core goes from 120.6 to 113.1 ms (about 6% faster) on a 4.5 MB stereo HCA. (#6)
  • Speed up ACB track extraction: duplicate-name checks are now linear and parsed UTF rows use FxHash. A synthetic 2000-track ACB extracts about 2.7× faster (6.15 to 2.26 ms). (#9)

Artifacts

Maintenance
  • Bump PyO3 from 0.28.3 to 0.29.0. (#5)
  • Bump actions/checkout from 6 to 7. (#7)
  • Run fmt, clippy -D warnings, check and test in CI. (#10)
  • Box the HCA decoder state to keep it off the stack. (#10)
  • Document deferred, non-blocking items in KNOWN_GAPS.md. (#10)

Full Changelog: v0.2.3...v0.3.0