Skip to content

Releases: seiunx-dev/cridecoder

v0.3.7

Choose a tag to compare

@github-actions github-actions released this 08 Oct 01:36
v0.3.7
ebdc14d

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 File now 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; Cursor input and peak memory are unchanged. (#38)

Artifacts

Maintenance
  • Add an examples/perf_corpus.rs benchmark 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

Choose a tag to compare

@MejiroRina MejiroRina released this 27 Sep 07:10

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 (@SFV meta, @SFA meta, @SFV end, @SFA end). read_metadata and the extractor failed on such files, for example Global kr/en loading movies, with expected @SFV signature; these movies now yield VP9 video and HCA audio. (#30)

Artifacts

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

Choose a tag to compare

@MejiroRina MejiroRina released this 27 Aug 20:31
ee3ff3d

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

Maintenance
  • Adapt fixed-width loops to the current Clippy. (02ef582)
  • Point the crate's repository and homepage at seiunx-dev/cridecoder. (8da3661)

Full Changelog: v0.3.4...v0.3.5

v0.3.4

Choose a tag to compare

@MejiroRina MejiroRina released this 07 Jul 04:46
3e6a07b

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 and threads <= 1 use 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_parallel and decode_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 threads to decode_hca, decode_hca_bytes, decode_acb_to_wav and decode_acb_to_wav_bytes in Python: None decodes serially as before, 0 uses all cores and N uses 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-decode and acb-decode, independent of the caller's RUST_MIN_STACK. (#16)

Artifacts

Maintenance
  • Update the API docs and README for 0.3.x and credit PyCriCodecs. (#14)
  • Bump rustc-hash from 2.1.2 to 2.1.3. (#15)

Full Changelog: v0.3.3...v0.3.4

v0.3.3

Choose a tag to compare

@MejiroRina MejiroRina released this 27 Jun 15:47
bf66b7e

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_bytes in Python or extract_acb_unique_to_memory in 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 *_bytes bindings. In-memory extract_acb_bytes on 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_file and decode_acb_to_wav_from_file instead of seeking and reading per field. File-based extract_acb on the same ACB goes from about 6.5 to 2.3 ms at best. (#13)

Artifacts

Full Changelog: v0.3.2...v0.3.3

v0.3.2

Choose a tag to compare

@MejiroRina MejiroRina released this 27 Jun 07:50
378950e

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_memory and decode_acb_to_wav_from_file, with DecodedAcbTrack and DecodeAcbError. The Python decode_acb_to_wav now 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 .awb file still need the path-based extract_acb. (#12)

Artifacts

Full Changelog: v0.3.1...v0.3.2

v0.3.1

Choose a tag to compare

@MejiroRina MejiroRina released this 27 Jun 07:14
acc4c8d

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"} (or None for an invalid file). Pass the subkey and the global key to decode_hca to decode encrypted tracks. The Rust crate exposes extract_acb_tracks and extract_acb_tracks_from_file, returning ExtractedTrackFile. (#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 global key; non-HCA tracks are written out unchanged. (#11)

Artifacts

Full Changelog: v0.3.0...v0.3.1

v0.3.0

Choose a tag to compare

@MejiroRina MejiroRina released this 27 Jun 06:17
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

v0.2.3

Choose a tag to compare

@MejiroRina MejiroRina released this 02 Jun 13:02
8df6ff1

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, returning ExtractedAcbTrack and ExtractedUsmStream). (8df6ff1)

Upgrade notes

  • Coming from 0.2.1: ExtractedUsmStream now has a name field instead of filename. (8df6ff1)
  • The Python extract_acb_bytes and extract_usm_bytes bindings and the cridecoder.pyi stubs 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

Full Changelog: v0.2.2...v0.2.3

v0.2.2

Choose a tag to compare

@MejiroRina MejiroRina released this 02 Jun 12:27
5d8a366

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, ExtractedAcbTrack and ExtractedUsmStream, Python extract_acb_bytes and extract_usm_bytes, and the cridecoder.pyi stubs. If you use them, stay on 0.2.1 or move to 0.2.3, which brings the Rust functions back (its ExtractedUsmStream has a name field where 0.2.1 had filename). 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

Maintenance
  • Bump PyO3 from 0.28.2 to 0.28.3. (#4)
  • Bump serde_json from 1.0.149 to 1.0.150. (#3)
  • Bump actions/setup-python from 5 to 6. (#2)
  • Exclude editor and build leftovers from the published crate. (8833391)

Full Changelog: v0.2.1...v0.2.2