Repository navigation
Releases: seiunx-dev/cridecoder
Release list
v0.3.7
Speeds up USM extraction from unbuffered files by reading each chunk header in one call. Output is unchanged.
Performance
- Read the 0x2D-byte USM chunk prefix in one call instead of about 12 small reads and seeks plus three allocations per chunk. Extracting from an unbuffered
Filenow takes 3 syscalls per chunk in memory and 4 when writing to disk. On real USMs of 28, 38 and 93 MB this cuts about 50–57% of CPU cycles and 66–67% of instructions;Cursorinput and peak memory are unchanged. (#38)
Artifacts
- crates.io:
cridecoder0.3.7 - PyPI:
cridecoder0.3.7
Maintenance
- Add an
examples/perf_corpus.rsbenchmark harness with output digests for byte-identity checks. (#38) - Move CI and releases onto the shared ci-templates; pushing a tag now builds the wheels and publishes the GitHub Release, PyPI and crates.io. (#35)
- Skip the Sonar scan on Dependabot and fork pull requests. (#34)
- Bump thiserror from 2.0.20 to 2.0.21 and encoding_rs from 0.8.35 to 0.8.42. (#36)
- Bump the version to 0.3.7. (#39)
Full Changelog: v0.3.6...v0.3.7
v0.3.6
Fixes USM extraction for files that store audio metadata before the metadata end markers. Files without audio metadata take the unchanged path and extract byte-identically to 0.3.5.
Fixed
- Accept USM files that carry an audio seek table after the video one, followed by one end marker per stream (
@SFVmeta,@SFAmeta,@SFVend,@SFAend).read_metadataand the extractor failed on such files, for example Global kr/en loading movies, withexpected @SFV signature; these movies now yield VP9 video and HCA audio. (#30)
Artifacts
- crates.io:
cridecoder0.3.6 - PyPI:
cridecoder0.3.6
Maintenance
- Add SonarQube Cloud analysis, resolve its findings and raise test coverage. (c9dd31d, 4c4db41, 524c179, 165a576, e8f0cb4, 20e931e)
- Bump PyO3 from 0.29.0 to 0.29.2. (#21)
- Bump serde from 1.0.228 to 1.0.229. (#18)
- Bump serde_json from 1.0.150 to 1.0.151. (#19)
- Bump thiserror from 2.0.18 to 2.0.20. (#22)
- Bump actions/setup-python from 6 to 7. (#20)
- Point CLAUDE.md at AGENTS.md. (a4228fa)
Full Changelog: v0.3.5...v0.3.6
v0.3.5
Releases the GIL in every remaining Python binding, so a Python thread pool can run any mix of cridecoder calls in parallel. Signatures and outputs are unchanged; the only visible difference is that other Python threads keep running during cridecoder calls.
Performance
- Release the GIL while the Rust core runs in ACB extraction (
extract_acb,extract_acb_tracks,extract_acb_bytes,extract_acb_unique_bytes), HCA encoding (encode_hca,encode_hca_bytes), the builders (build_acb,build_acb_bytes,build_music_acb_bytes,build_usm,build_usm_bytes) and USM (extract_usm,extract_usm_bytes,read_usm_metadata). The decode functions already did this since 0.3.4. If Python threads gave you flat scaling before, this release fixes it for these calls. (c81bf7b)
Artifacts
- crates.io:
cridecoder0.3.5 - PyPI:
cridecoder0.3.5
Maintenance
Full Changelog: v0.3.4...v0.3.5
v0.3.4
Makes serial HCA decoding about 35% faster, adds opt-in multithreaded decoding for HCA and ACB, and removes redundant copies from USM extraction. Decoded PCM and extracted streams are byte-identical to 0.3.3, and existing functions keep their signatures and serial behaviour.
Added
- Decode HCA on several threads with
HcaDecoder::decode_to_wav_parallel(w, threads). Output is byte-identical to serial decoding; HCA v3 files with noise reconstruction andthreads <= 1use the serial path. On a 2:22 stereo HCA on an Apple M4, decoding goes from 52 to 9.8 ms with 10 threads. (#16) - Decode ACBs on several threads with
decode_acb_to_wav_to_memory_parallel,decode_acb_to_wav_parallelanddecode_acb_to_wav_from_file_parallel. Tracks are spread across workers and leftover threads decode blocks within a track, so both single-track music ACBs and many-track sound-effect banks use the whole budget. A 6-track ACB goes from 398 to 97 ms. (#16) - Pass
threadstodecode_hca,decode_hca_bytes,decode_acb_to_wavanddecode_acb_to_wav_bytesin Python:Nonedecodes serially as before,0uses all cores andNuses N threads. These four functions now release the GIL while decoding, serial mode included. (#16)
Performance
- Speed up serial HCA decoding by about 35% (72.0 to 53.5 ms on the benchmark file): dequantizer bit counts are computed instead of looked up, scale-factor deltas go through a register-resident bit accumulator, and file input is read about 1 MiB ahead instead of once per block. (#16)
- Extract USM streams in memory with one copy instead of three: 4.7 to 1.0 ms on a 28.5 MB USM. (#16)
- Write extracted USM streams through a 1 MiB buffer: 28.7 to 14.7 ms on the same file. (#16)
Upgrade notes
- Worker threads use their own 8 MiB stacks, named
hca-decodeandacb-decode, independent of the caller'sRUST_MIN_STACK. (#16)
Artifacts
- crates.io:
cridecoder0.3.4 - PyPI:
cridecoder0.3.4
Maintenance
Full Changelog: v0.3.3...v0.3.4
v0.3.3
Makes ACB extraction faster and adds de-duplicated waveform extraction for ACBs whose cues share audio. Per-cue output is byte-identical to 0.3.2.
Added
- Extract each distinct waveform of an ACB once, together with the cues that use it, with
extract_acb_unique_bytesin Python orextract_acb_unique_to_memoryin Rust. Each item is{"extension", "subkey", "data", "cues": [{"name", "cue_id"}, ...]}. On a 4.8 MB test ACB this turns 4 per-cue tracks (8.16 MB) into 2 waveforms (4.80 MB). (#13)
Performance
- Move the embedded AWB out of the parsed table instead of copying it, and drop a redundant input copy in the
*_bytesbindings. In-memoryextract_acb_byteson a 4.8 MB ACB goes from about 0.80 to 0.62 ms. (#13) - Read the whole file once in
extract_acb_from_file,extract_acb_tracks_from_fileanddecode_acb_to_wav_from_fileinstead of seeking and reading per field. File-basedextract_acbon the same ACB goes from about 6.5 to 2.3 ms at best. (#13)
Artifacts
- crates.io:
cridecoder0.3.3 - PyPI:
cridecoder0.3.3
Full Changelog: v0.3.2...v0.3.3
v0.3.2
Gives every extract and decode operation an in-memory variant, so an ACB or USM can go all the way to decoded audio and streams without temporary files. In-memory and file-based decoding produce byte-identical WAV output. Purely additive.
Added
- Extract ACB tracks in memory with
extract_acb_bytes(acb_data), which returns{name, cue_id, extension, subkey, data}per track. (#12) - Decode an ACB to WAV in memory with
decode_acb_to_wav_bytes(acb_data, key=None), which returns{name, cue_id, extension, data}with WAV bytes for HCA tracks. (#12) - Extract USM streams in memory with
extract_usm_bytes(usm_data, key=None, export_audio=False), which returns{name, extension, data}per stream. (#12) - Use ACB-to-WAV decoding from Rust:
decode_acb_to_wav,decode_acb_to_wav_to_memoryanddecode_acb_to_wav_from_file, withDecodedAcbTrackandDecodeAcbError. The Pythondecode_acb_to_wavnow wraps this code. (#12) - Carry the cue ID in
ExtractedAcbTrack(cue_id). (#12)
Upgrade notes
- The in-memory functions read only the AWB embedded in the ACB. ACBs that stream from an external
.awbfile still need the path-basedextract_acb. (#12)
Artifacts
- crates.io:
cridecoder0.3.2 - PyPI:
cridecoder0.3.2
Full Changelog: v0.3.1...v0.3.2
v0.3.1
Makes the per-AWB AFS2 subkey, needed to decrypt type-56 HCA, available from Python without re-parsing the ACB, and adds a one-call ACB-to-WAV decoder. Purely additive.
Added
- Extract ACB tracks together with their metadata with
extract_acb_tracks(acb_path, output_dir), which returns a list of{"path", "name", "cue_id", "subkey"}(orNonefor an invalid file). Pass thesubkeyand the global key todecode_hcato decode encrypted tracks. The Rust crate exposesextract_acb_tracksandextract_acb_tracks_from_file, returningExtractedTrackFile. (#11) - Decode an ACB straight to WAV files with
decode_acb_to_wav(acb_path, output_dir, key=None). Each AWB's subkey is applied automatically, so encrypted ACBs need only the globalkey; non-HCA tracks are written out unchanged. (#11)
Artifacts
- crates.io:
cridecoder0.3.1 - PyPI:
cridecoder0.3.1
Full Changelog: v0.3.0...v0.3.1
v0.3.0
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::constantsandUtfTable::rowsfields now holdrustc_hash::FxHashMap<String, Value>instead ofstd::collections::HashMap<String, Value>. Code that names the old map type needs to useFxHashMapfrom therustc-hashcrate or let the type be inferred. (#9)
Added
- Decode type-56 encrypted HCA by passing the optional
keyandsubkeyparameters todecode_hcaanddecode_hca_bytes. Unencrypted files are unaffected, and ACB extraction now reads each AWB's subkey from AFS2 automatically. (#10) - Ship Python type information:
cridecoder.pyistubs and a PEP 561py.typedmarker, 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
smplloop 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
.hcaextension. (#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,
@UTFversions 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
- crates.io:
cridecoder0.3.0 - PyPI:
cridecoder0.3.0
Maintenance
Full Changelog: v0.2.3...v0.3.0
v0.2.3
Brings back in-memory ACB and USM extraction in the Rust crate, on top of the faster HCA decoding and PCM16 chunk APIs from 0.2.2.
Added
- Extract ACB tracks and USM streams from data in memory (
extract_acb_to_memory,extract_usm_to_memory, returningExtractedAcbTrackandExtractedUsmStream). (8df6ff1)
Upgrade notes
- Coming from 0.2.1:
ExtractedUsmStreamnow has anamefield instead offilename. (8df6ff1) - The Python
extract_acb_bytesandextract_usm_bytesbindings and thecridecoder.pyistubs from 0.2.1 are still not included. The stubs return in 0.3.0 and Python in-memory extraction in 0.3.2. (8df6ff1)
Artifacts
- crates.io:
cridecoder0.2.3 - PyPI:
cridecoder0.2.3
Full Changelog: v0.2.2...v0.2.3
v0.2.2
Speeds up HCA decoding, AFS cue lookup and USM extraction with bit-identical output, and adds streaming 16-bit PCM decoding. This release is built from main and does not contain the in-memory extraction APIs from 0.2.1.
⚠️ Breaking changes
- 0.2.1 was tagged from a branch that was never merged, so this release does not have its additions: Rust
extract_acb_to_memory,extract_usm_to_memory,ExtractedAcbTrackandExtractedUsmStream, Pythonextract_acb_bytesandextract_usm_bytes, and thecridecoder.pyistubs. If you use them, stay on 0.2.1 or move to 0.2.3, which brings the Rust functions back (itsExtractedUsmStreamhas anamefield where 0.2.1 hadfilename). Python in-memory extraction returns, with different signatures, in 0.3.2, and the stubs in 0.3.0. (f776365)
Added
- Decode HCA straight to interleaved 16-bit PCM, one frame at a time (
decode_frame_i16) or as chunks passed to a callback (decode_to_pcm16_chunks). Chunks hold only valid samples after encoder-delay trimming, so audio can be piped into another encoder without an intermediate WAV file. (5d8a366)
Performance
- Speed up HCA decoding with bit-exact WAV output: specialized IMDCT stages with constant tables, a faster block decode and PCM conversion, and a reused WAV output buffer. (5d8a366, 16ec188, bce2891, a9a15d5, a822089, ee2cb3d, 8833391)
- Speed up AFS cue lookup and the extraction of unmasked USM chunks. (5d8a366)
Artifacts
- crates.io:
cridecoder0.2.2 - PyPI:
cridecoder0.2.2
Maintenance
Full Changelog: v0.2.1...v0.2.2