# PS1 Save and Memory-Card Formats This guide describes the formats JAPS1ME currently reads or writes. The notes were informed by the [MemcardRex project](https://github.com/ShendoXT/memcardrex), which is also credited in the main README. Corresponding JAPS1ME implementations: `src/gme.rs` and `src/ps1/`. ## DexDrive `.gme` A `.gme` file is a 3904-byte header followed by a 131072-byte raw card image (134976 bytes total). JAPS1ME identifies the format by length plus the `MC` signature at offset 3904. ### Header | Offset | Size | Meaning | | --- | ---: | --- | | `0x00` | 11 | Magic: `123-456-STD` (canonical files) | | `0x12`–`0x15` | — | Version/status bytes written by MemcardRex (`0x01`, `0x01`/`M`) | | `0x16`–`0x25` | 15 | Per-slot type bytes (copies of each directory frame's type) | | `0x26`–`0x35` | 15 | Per-slot next-pointer bytes | | `0x40`–`0xF3F` | 15 × 256 | Per-slot comments (system-codepage text, NUL-terminated) | Some files in the wild have a zeroed or otherwise non-canonical header. Like MemcardRex, JAPS1ME accepts those when the size is 134976 and bytes at 3904 are `MC`, and reports a `missing DexDrive header magic` warning. Comments are decoded as Windows-1252 and are informational only; `convert` never modifies them. ## Raw card image (`.srm`/`.mcd`/`.mcr`) The raw image is 131072 bytes (1024 frames of 128 bytes). RetroArch `.srm`, DuckStation `.mcd`, ePSXe `.mcr`, and similar formats share this layout; only the file extension differs. JAPS1ME reads all of these as the same raw card image. `convert` writes exactly the `.gme` payload bytes `3904..` with no modification, so directory checksums and save data are preserved verbatim. ### Per-game filenames New-card install accepts a source named `GAMENAME (REGIONCODE)` with an optional trailing DuckStation slot suffix. It writes `GAMENAME (REGIONCODE).srm` for RetroArch and `GAMENAME (REGIONCODE)_SLOT.mcd` for DuckStation (default slot `1`). For example, `Crash Bandicoot (USA).srm` becomes `Crash Bandicoot (USA)_1.mcd` in a DuckStation directory. Multi-disc title labels are retained; RetroArch users must manually copy and rename a card for the next disc, while DuckStation uses the same per-game card across discs. ### Geometry | Raw offset | Size | Meaning | | --- | ---: | --- | | `0x00` | 128 | Card header frame (`MC` magic) | | `0x80`–`0x7FF` | 15 × 128 | Directory frames (slots 1–15) | | `0x2000`– | 15 × 8192 | Save data blocks | ### Directory frames Each 128-byte frame describes one slot. Relevant fields: | Offset | Size | Meaning | | --- | ---: | --- | | `0x00` | 1 | Slot type: `0xA0` formatted, `0x51` initial, `0x52`/`0x53` middle/end link, `0xA1`–`0xA3` deleted variants | | `0x04` | 3 | Payload size, little-endian | | `0x08` | 1 | Next-slot pointer (`0xFF` = end of chain) | | `0x0A` | 2 | Region (`BA` America, `BE` Europe, `BI` Japan) | | `0x0C` | 10 | Product code | | `0x16` | 8 | Save identifier | | `0x7F` | 1 | XOR checksum over bytes `0x00..0x7F` | A save is an initial slot plus its link chain (up to 15 slots). Links not reachable from any initial slot are orphaned; `list` reports them and counts them as free, matching MemcardRex's broken-link handling. ### Save titles Each data block carries a 64-byte Shift-JIS title at block offset 4, NUL-terminated on an even byte boundary. JAPS1ME decodes it with a malformed-sequence replacement and falls back to the save name (`region + product code + identifier`) when no usable title exists. ### Save icons Each initial save block carries a 16x16, 4bpp icon (the grid shown on the PS1 BIOS memory-card screen). JAPS1ME reads it for `export-icon` only and never writes it back. | Block offset | Size | Meaning | | --- | ---: | --- | | `0x60` | 32 | Palette: 16 x 16-bit entries (`R` bits 0–4, `G` bits 5–9, `B` bits 10–14, bit 15 flag) | | `0x80` | 128 | Primary icon bitmap: 16 rows x 8 bytes, low nibble = left pixel, high nibble = right pixel | Two further frames follow at `0x100`/`0x180` for animated icons; JAPS1ME exports only the primary frame. Any palette entry whose red, green, blue, and bit-15 flag are all zero is treated as transparent, matching MemcardRex's `loadPalette`/`loadIcons`. Channel values are expanded to 8-bit by shifting the 5-bit components left by 3. `export-icon --palette NAME` can replace the HSL hue of colored palette entries while preserving saturation and lightness; `NAME` is a preset (`RED`, `GREEN`, `BLUE`, `YELLOW`, `PURPLE`, `ORANGE`) or a saturated `#RRGGBB` hex color, and transparent entries, exact black/white, and achromatic grays remain unchanged. Its optional `--frame circle` scales the icon down so the whole square fits inside the canvas's inscribed circle, centers it on transparent padding, and then applies a binary circular alpha mask, so no icon pixel is cropped and no border is drawn. ### Imports `import` copies a save's directory frames and data blocks from a source card into the target's first free slots in slot order. Link bytes are relinked (`0x51` initial, `0x52` middle, `0x53` end, `0xFF`-terminated), the initial frame's size field is recomputed as `blocks * 8192`, and each touched frame's XOR checksum is regenerated. Imports from a deleted source slot are normalized to active link types. The updated card is re-parsed before it replaces the target. ## References - [MemcardRex](https://github.com/ShendoXT/memcardrex): `MemcardRex.Core/ps1card.cs` (`getGmeHeader`, `OpenMemoryCard`, `calculateXOR`, `loadStringData`, `FindSaveLinks`, `loadPalette`, `loadIcons`) and `MemcardRex.Core/Hardware/DexDrive.cs` (128-byte frame protocol and XOR framing on the wire). - The corresponding JAPS1ME implementations: `src/gme.rs`, `src/ps1/`.