Metadata-Version: 2.4
Name: cri-afs
Version: 0.1.0
Summary: Reader/writer for the CRI AFS archive format used in PS2/Dreamcast/GameCube games
Author: soyjxck
License-Expression: MIT
Keywords: cri,afs,ps2,dreamcast,gamecube,archive,rom-hacking
Classifier: Development Status :: 4 - Beta
Classifier: Topic :: System :: Archiving
Classifier: Programming Language :: Python :: 3
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"

# cri-afs

Reader/writer for the **CRI AFS** archive format used in PS2 / Dreamcast / GameCube games.

If this helped you, consider [buying me a coffee](https://ko-fi.com/soyjack)

CRI AFS is the bulk archive format CRI Middleware shipped with their authoring tools. It's a flat container of fixed-offset sub-files (typically 0x800-aligned to CD sectors), with an optional trailing filename TOC. Examples of games shipping `.AFS` archives:

- Magna Carta: Tears of Blood (`SHIP.AFS`, `MUSIC.AFS`, `LINEAR.AFS`, `FILE.AFS`)
- Burnout, Burnout 2/3
- Various Sega and CRI-licensed PS2/Dreamcast titles

Round-trips byte-identically when given the original TOC metadata.

## Install

```bash
pip install cri-afs
```

## Usage

### Command line

```bash
# Inspect an AFS file (entry table, type histogram, sample names)
cri-afs info SHIP.AFS

# Extract every entry to a directory
cri-afs extract SHIP.AFS --out ship_extracted/

# Pack a directory back into an AFS
cri-afs pack ship_extracted/ -o SHIP.AFS
```

### Python API

```python
from cri_afs import Afs, write_afs

# Read
afs = Afs.open("SHIP.AFS")
print(f"{len(afs.entries)} entries")
names = afs.read_filename_toc()

with open("SHIP.AFS", "rb") as fh:
    blob = afs.read_entry(0, fh)   # raw bytes of sub-file 0

# Write
entries = [(name, blob) for name, blob in zip(names, payloads)]
write_afs("OUT.AFS", entries, toc_metadata=afs.read_toc_metadata())
```

## Format

```
0x00  4   magic       "AFS\0"
0x04  4   entry_count
0x08  N*8 entries     (offset: u32, size: u32) per entry
...   8   toc_pointer (toc_offset: u32, toc_size: u32)
                      — pad to 0x800 alignment —
              per entry: blob, padded up to 0x800
              trailing filename TOC: 32-byte filename + 16-byte metadata per entry
```

The trailing filename TOC is optional in the spec but present in practice for every CRI-shipped AFS. `read_toc_metadata()` returns the per-entry 16-byte metadata (timestamp/size fields the engine ignores at runtime but expects to be present); pass it to `write_afs(toc_metadata=...)` for a byte-identical round-trip.

## License

MIT
