JAPS2ME/AGENTS.md

7 KiB

Agent and Contributor Guide

Project overview

JAPS2ME (Just A PS2 Memory Card Editor) is a Rust CLI and library for managing PCSX2 PlayStation 2 memory-card images. Current CLI commands:

  • list <CARD.ps2> — list saves as a Unicode table with game titles, file counts, payload sizes, and free space.
  • delete <CARD.ps2> <SAVE-DIRECTORY> — remove a save directory.
  • add <CARD.ps2> <SAVE.cbs|SAVE.max> — import a Codebreaker or Datel MAX Drive save, with interactive progress reporting.
  • export <CARD.ps2> <SAVE-DIRECTORY> <OUTPUT.cbs> — export a save as a Codebreaker file.
  • --version / -V — print the package version and exit.

The current package version is 0.2.0; keep the root package versions in Cargo.toml and Cargo.lock in sync.

The project is licensed under GPL-3.0-or-later. mymcplusplus is a technical reference for PS2 memory-card filesystem behavior and save formats; see its attribution in README.md.

Repository map

  • src/main.rs — Clap CLI, command dispatch, and terminal-aware indicatif progress rendering for add.
  • src/commands/list.rs — Unicode table rendering and byte-size formatting for list.
  • src/memory_card/icon_sys.rs — icon.sys title parsing with Shift-JIS decoding.
  • src/lib.rs — crate modules and public exports.
  • src/save.rs — shared imported-save model and format detection.
  • src/codebreaker/format.rs — Codebreaker RC4/zlib parser and encoder.
  • src/max_drive.rs — MAX Drive header/CRC parser, LZARI decoder, and file-record parser.
  • src/memory_card/card.rs — memory-card access, directory operations, allocation, FAT chains, save insertion/removal, summaries, and save export.
  • src/memory_card/superblock.rs — superblock parsing and card geometry.
  • src/memory_card/directory.rs — 512-byte directory-entry encoding and mode bits.
  • src/memory_card/fat.rs — FAT marker constants and helpers.
  • src/memory_card/ecc.rs — PS2 page spare-area ECC generation.
  • src/memory_card/transaction.rs — staged, validated, copy-on-write save imports with copy/write/verify/commit progress events.
  • save-games/README.md — inventory and contribution guidance for community-shared .cbs and .max saves in save-games/.
  • save-games/ — curated save containers, including the PES3allunlocked.max fixture used by include_bytes! tests; preserve these user-provided files and do not use them as writable integration-test targets.
  • mymcplusplus-reference/ — cloned Python reference implementation; do not modify it as part of normal JAPS2ME work.

Build and checks

Run these from the repository root:

cargo fmt --check
cargo test
cargo clippy --all-targets -- -D warnings
cargo build --release

After changing code, rebuild the executable being invoked. For example, cargo build refreshes target/debug/japs2me; cargo build --release refreshes target/release/japs2me. A stale binary can continue exhibiting behavior fixed in the source.

Memory-card safety

  • Never use the user's only memory-card image as a test target. Do not test writes against Mcd001.ps2, Mcd001edited.ps2, or another supplied .ps2 fixture.
  • Make a temporary copy under /tmp/opencode/ for integration tests. The add command already stages imports on a temporary image and replaces the target only after validation; delete currently writes directly to its target.
  • Keep PCSX2 closed while changing a card image, and recommend an independent backup before destructive operations.
  • For external compatibility checks, use mymcplusplus read-only commands such as check and ls against the temporary image. check should report no filesystem errors; verify imported file bytes against the source save when possible.

Filesystem and directory invariants

  • PS2 memory-card directory entries are 512 bytes. Directory length is an entry count; regular-file length is a byte count.
  • FAT links use the allocated high bit. A file or directory may occupy multiple clusters; every link and tail marker must be updated when allocating or freeing a chain.
  • Logical allocatable clusters are located after the metadata region. FAT and indirect FAT clusters are addressed as raw card clusters; file and directory clusters are addressed relative to allocatable_cluster_offset.
  • The root directory can span multiple FAT clusters. Root-slot lookup, listing, insertion, and deletion must traverse the full chain rather than assume a single cluster.
  • A save directory's . and .. entries have length zero. The save-directory entry in its parent (normally the root) stores the number of directory entries, including . and ...
  • When changing page data on ECC-bearing cards, regenerate the page's spare-area ECC. Do not write only page data while leaving stale ECC bytes.
  • MAX and Codebreaker inputs should be fully validated before the staged card replaces the original. Preserve file names, payload bytes, supported modes, and timestamps as appropriate for their source format.
  • Keep import progress callbacks reporting actual copy, payload-write, and verification work; terminal rendering belongs in the CLI and should remain hidden when stderr is not a terminal.
  • list summaries count regular files only and sum their stored byte lengths; directory entries such as . and .. are excluded. Free space is unallocated FAT clusters times the cluster size and is distinct from on-card allocated space.

Save-format details

  • Codebreaker files begin with CFU\0. Their body is RC4-decrypted using the fixed Codebreaker state, then zlib-decompressed. Header and per-file lengths must be bounds-checked. The encoder mirrors this layout: 64-byte file records plus raw data, zlib-compressed and RC4-encrypted, with stored_len set to the total file length and overlong headers grown past the 124-byte minimum for long titles.
  • MAX Drive files begin with Ps2PowerSave and have a 92-byte header. The CRC is computed over the header with its CRC field zeroed followed by the compressed payload. The LZARI output contains dirlen records: little-endian data length, 32-byte name, file bytes, and the format's alignment padding.
  • The supplied PES3allunlocked.max fixture should parse as directory BESLES-51912PES3OPT, with icon.sys (964 bytes), icon_view (33,400 bytes), and BESLES-51912PES3OPT (773,120 bytes). Tests use CRCs of the decoded files to catch LZARI regressions.

Change guidelines

  • Keep parsing separate from memory-card mutation; both save formats should produce the shared SaveImport model.
  • Prefer focused tests for parser boundaries, FAT/directory invariants, and failed-import rollback.
  • Do not weaken MAX CRC, decompressed-size, or record-bound checks to accept malformed input without a documented compatibility reason and a regression test.
  • Save files in save-games/ are separate assets, not automatically covered by the project's GPL license. Do not claim redistribution rights or assign a license without source/permission information; follow the contribution guidance in that directory's README.
  • Update README.md when user-facing commands, supported formats, build steps, or safety behavior change.