# AGENTS.md

## Project Overview

**cridecoder** is a pure Rust library for decoding CRI Middleware formats, with optional Python bindings via pyo3/maturin.

### Credits

CRI format implementation is based on [vgmstream](https://github.com/vgmstream/vgmstream).

## Architecture

```
src/
├── lib.rs          # Crate root, re-exports public API, #[pymodule] (behind "python" feature)
├── reader.rs       # Binary reader utilities (endianness, primitive types, alignment)
├── acb.rs          # ACB module root (re-exports submodules)
├── acb/
│   ├── consts.rs   # ACB/UTF constants and helpers
│   ├── utf.rs      # CRI UTF table parser
│   ├── afs.rs      # AFS2 archive parser
│   ├── track.rs    # Track list extraction from ACB
│   └── extractor.rs # ACB extraction logic
├── hca.rs          # HCA module root (re-exports submodules)
├── hca/
│   ├── decoder.rs  # Core HCA decoder (ClHca, header parsing, block decoding)
│   ├── hca_file.rs # High-level HcaDecoder with streaming, WAV output, key testing
│   ├── tables.rs   # Lookup tables for HCA decoding
│   ├── cipher.rs   # HCA encryption/decryption cipher
│   ├── ath.rs      # ATH (Absolute Threshold of Hearing) tables
│   ├── bitreader.rs # Bit-level reader/writer
│   └── imdct.rs    # Inverse MDCT transform
├── usm.rs          # USM module root (re-exports submodules)
├── usm/
│   ├── extractor.rs # USM extraction (video/audio stream demuxing)
│   └── metadata.rs  # USM metadata reading and JSON export
└── python.rs       # Python bindings (behind "python" feature)
```

## Key Types

- `extract_acb_from_file()` / `extract_acb()` — ACB extraction entry points
- `HcaDecoder` — High-level HCA to WAV/PCM decoder
- `extract_usm_file()` / `extract_usm()` — USM extraction entry points
- `ClHca` — Low-level HCA decoder state machine

## Building

```bash
# Pure Rust
cargo build
cargo test                              # Unit tests only
RUST_MIN_STACK=16777216 cargo test       # Unit + integration tests (HCA needs larger stack)

# Python extension
python3 -m maturin build --release

# crates.io dry run
cargo publish --dry-run --allow-dirty
```

## Testing

- **Unit tests**: Inline `#[cfg(test)] mod tests` in most modules
- **Integration tests**: `tests/integration_tests.rs` — requires `se_0126_01.acb` and `0703.usm` test fixtures in project root
- Integration tests need `RUST_MIN_STACK=16777216` due to large `ClHca` struct

## Conventions

- Use `thiserror` for error types
- Use `byteorder` for binary reading via the `reader.rs` wrapper
- Use `encoding_rs` for Shift-JIS text decoding (CRI uses Shift-JIS strings)
- Public API lives in module root files (`acb.rs`, `hca.rs`, `usm.rs`); internals are `mod` (private)
- Python bindings are behind `#[cfg(feature = "python")]` so they're opt-in

## Git commits

All commit subjects must follow:

```text
[Type] Short description starting with capital letter
```

Allowed types:

| Type      | Usage                                                 |
|-----------|-------------------------------------------------------|
| `[Feat]`  | New feature or capability                             |
| `[Fix]`   | Bug fix                                               |
| `[Chore]` | Maintenance, refactoring, dependency or build changes |
| `[Docs]`  | Documentation-only changes                            |

Rules:

- Description starts with a capital letter.
- Use imperative mood: `Add ...`, not `Added ...`.
- No trailing period.
- Keep the subject at or below roughly 70 characters.
- **Agent attribution uses the standard Git `Co-authored-by:` trailer in the commit body, not a free-form `Agent:` line.** This makes GitHub render the co-author avatar on the commit page. The trailer must be on its own line, separated from the subject by a blank line, in the form `Co-authored-by: <Display Name> <email>`. Suggested values per agent:
  - Claude (any 4.x): `Co-authored-by: Claude Opus 4.7 <noreply@anthropic.com>` (substitute the actual model, e.g. `Claude Sonnet 4.6`, `Claude Haiku 4.5`)
  - Codex: `Co-authored-by: Codex <noreply@openai.com>`
  - Copilot: `Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>`

Examples from this repo's history:

```text
[Feat] Add encoding Python bindings
[Fix] Resolve check and clippy warnings
[Chore] Configure Dependabot updates
[Feat] Add encoding support but not tested in game
```

## GitHub Actions workflows

Use the standardized workflow layout in `.github/workflows`:

- `ci.yml` runs on `main` pushes, pull requests targeting `main`, and manual dispatch.
- Rust CI order: `cargo fmt --all -- --check`, `cargo check --locked --all-targets`, `cargo clippy --locked --all-targets -- -D warnings`, then `cargo test --locked`.
- `release-crate.yml` publishes the Rust crate and keeps its package-specific release flow.
- `release-python.yml` builds and publishes Python artifacts and keeps its package-specific release flow.

Workflow maintenance rules:

- Keep workflow filenames and top-level names aligned: `CI`, `Release`, `Docker`, and optional package-specific names.
- Use `actions/checkout@v6`, `actions/setup-go@v6`, `actions/upload-artifact@v7`, `actions/download-artifact@v8`, `softprops/action-gh-release@v3`, and current Docker actions (`setup-buildx@v4`, `login@v4`, `metadata@v6`, `build-push@v7`).
- Keep `permissions` minimal: `contents: read` for CI/Docker build-only work, `contents: write` for release publishing, and `packages: write` only when pushing container images.
- Use workflow `concurrency` keyed by workflow name and ref, with release jobs using `release-${{ github.ref_name }}` and `cancel-in-progress: false`.
- Do not reintroduce legacy workflow names such as `rust-ci.yml`, `build.yml`, `release-build.yml`, `docker-build.yml`, or `docker-release.yml` unless a package-specific workflow already exists and is intentionally preserved.
