{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreihdsfoxzogadzc5raghvwbl57r2mgszjbhznsoirppyaaa6un2rny",
"uri": "at://did:plc:xrpvi727ccnv4bnwaedgs3gd/app.bsky.feed.post/3me2v3j4czzi2"
},
"path": "/dumping-nintendo-ereader-card-roms?utm_campaign=rss",
"publishedAt": "2026-02-04T00:00:00.000Z",
"site": "https://sethmlarson.dev",
"tags": [
"Nintendo e‑Reader",
"play mini-games",
"GB Operator",
"Retro Dot Codes",
"Matt Greer",
"regularly posts",
"Series 1",
"“long” dotcode strips",
"Solitaire",
"Space Cadet Pinball",
"steps I documented for Ubuntu",
"format of e‑Reader save files",
"caitsith2.com",
"VPK0 compression algorithm",
"e‑Reader development tools repository",
"source code",
"printing the program onto physical cards",
"Analogue Pocket",
"FPGA",
"impossible to insert oddly-shaped cartridges",
"E-Reader Extender",
"Brian Hargrove",
"Delta Emulator"
],
"textContent": "The Nintendo e‑Reader was a peripheral released for the Game Boy Advance in 2001. The Nintendo e‑Reader allowed scanning “dotcode strips” to access extra content within games or to play mini-games. Today I'll show you how to use the GB Operator, a Game Boy ROM dumping tool, in order to access the ROM encoded onto e‑Reader card dotcodes.\n\nI'll be demonstrating using a new entrant to e‑Reader game development for the venerable platform: Retro Dot Codes by Matt Greer. Matt regularly posts about his process developing and printing e‑Reader cards and games in 2026. I was a recipient for one of his free e‑Reader card giveaways and purchased Retro Dot Cards “Series 1” pack which I'm planning to open and play for the blog.\n\n## Dumping a Nintendo e-Reader card contents\n\nThe process is straightforward but requires a working GBA or equivalent (GBA, GBA SP, Game Boy Player, DS, or Analogue Pocket *), a Nintendo e-Reader cartridge, and a GBA ROM dumper like GB Operator. Launch the e‑Reader cartridge using a Game Boy Advance, Analogue Pocket, or Game Boy Player. The e-Reader software prompts you to scan the dotcodes.\n\nThe Solitaire card stores its program data on two “long” dotcode strips consisting of 28 “blocks” per-strip encoding 104 bytes-per-block for a total of 5824 bytes on two strips (2×28×104=5824). If you want to see approximately how large a dotcode strip is, open this page in a desktop browser. After scanning each side of the Solitaire card you can play Solitaire on your console:\n\nI'll be honest, I was never into Solitaire as a kid, I was more “Space Cadet Pinball” on Windows... Anyways, let's archive the “ROM” of this game so even if we lose the physical card we can still play.\n\nTurn off your device and connect the e‑Reader cartridge to the GB Operator. Following the steps I documented for Ubuntu, start “Epilogue Playback” software and dump the e‑Reader ROM and critically: the save data. The Nintendo e‑Reader supports saving your already scanned game in the save data of the cartridge so you can play again next time you boot without needing to re-scan the cards.\n\nNow we have a `.sav` file. This file works as an archive of the program, as we can load our e-Reader ROM and this save into a GBA emulator to play again. **Success!**\n\n## Examining e-Reader Card ROMs\n\nNow that we have the `.sav` file for the Solitaire ROM, let's see what we can find inside. The file itself is mostly empty, consisting almost entirely of `0xFF` and `0x00` bytes:\n\n\n >>> data = open(\"Solitaire.sav\", \"rb\").read() >>> len(data), hex(len(data)) (131072, '0x20000') >>> sum(b == 0xFF for b in data) 118549 >>> sum(b == 0x00 for b in data) 8200\n\nWe know from the data limits of 2 dotcode strips that there's only 5824 bytes maximum for program data. If we look at the format of e‑Reader save files documented at caitsith2.com we can see what this data means. I've duplicated the page below, just in case:\n\n`ereader save format.txt`\n\n\n US E-reader save format Base Address = 0x00000 (Bank 0, address 0x0000) Offset Size Description 0x0000 0x6FFC Bank 0 continuation of save data. 0x6FFD 0x6004 All 0xFF 0xD000 0x0053 43617264 2D452052 65616465 72203230 30310000 67B72B2E 32333333 2F282D2E 31323332 302B2B30 31323433 322F2A2C 30333333 312F282C 30333233 3230292D 30303131 2F2D2320 61050000 80FD7700 000001 0xD053 0x0FAD All 0x00s 0xE000 0x1000 Repeat 0xD000-0xDFFF 0xF000 0x0F80 All 0xFFs 0xFF80 0x0080 All 0x00s Base Address = 0x10000 (Bank 1, address 0x0000) Offset Size Description 0x0000 0x04 CRC (calculated starting from 0x0004, and amount of data to calculate is 0x30 + [0x002C] + [0x0030].) 0x0004 0x24 Program Title (Null terminated) - US = Straight Ascii, Jap = Shift JIS 0x0028 0x04 Program Type 0x0204 = ARM/THUMB Code/data (able to use the GBA hardware directly, Linked to 0x02000000) 0x0C05 = 6502 code/data (NES limitations, 1 16K program rom + 1-2 8K CHR rom, mapper 0 and 1) 0x0400 = Z80 code/data (Linked to 0x0100) 0x002C 0x04 Program Size = First 2 bytes of Program data, + 2 0x0030 0x04 Unknown 0x0034 Program Size Program Data (vpk compressed) First 2 bytes = Size of vpk compressed data 0xEFFF 0x01 End of save area in bank 1. Resume save data in bank 0. The CRC is calculated on Beginning of Program Title, to End of Program Data. If the First byte of Program Title is 0xFF, then there is no save present. If the CRC calculation does not match stored CRC, then the ereader comes up with an ereader memory error. CRC calculation Details CRC table is calculated from polynomial 0x04C11DB7 (standard CRC32 polynomial) with Reflection In. (Table entry 0 is 0, and 1 is 0x77073096...) CRC calculation routine uses Initial value of 0xAA478422. The Calculation routine is not a standard CRC32 routine, but a custom made one, Look in \"crc calc.c\" for the complete calculation algorithm. Revision history v1.0 - First release V1.1 - Updated/Corrected info about program type. v1.2 - Updated info on Japanese text encoding v1.3 - Info on large 60K+ vpk files.\n\nFrom this format specification we can see that the program data starts around offset `0x10000` with the CRC, the program title, type, size, and the program data which is compressed using the VPK0 compression algorithm. Searching through our save data, sure enough we see some data at the offsets we expect like the program title and the VPK0 magic bytes `vpk0`:\n\n\n >>> hex(data.index(b\"Solitaire\\x00\")) '0x10004' >>> hex(data.index(b\"vpk0\")) '0x10036'\n\nWe know that the VPK0-compressed blob length is encoded in the two bytes before the magic header, little-endian. Let's grab that value and write the VPK0-compressed blob to a new file:\n\n\n >>> vpk_idx = data.index(b\"vpk0\") >>> vpk_len = int.from_bytes( ... data[vpk_idx-2:vpk_idx], \"little\") >>> with open(\"Solitaire.vpk\", \"wb\") as f: ... f.write(data[vpk_idx:vpk_idx+vpk_len])\n\nIn order to decompress the program data we'll need a tool that can decompress VPK0. The e‑Reader development tools repository points to `nevpk`. You can download the source code for multi-platform support and compile using `cmake`:\n\n\n curl -L https://github.com/breadbored/nedclib/archive/749391c049756dc776b313c87da24b7f47b78eea.zip \\ -o nedclib.zip unzip nedclib.zip cmake . && make # Now we can use nevpk to decompress the program. nevpk -d -i Solitaire.vpk -o Solitaire.bin md5sum Solitaire.bin 3a898e8e1aedc091acbf037db6683a41 Solitaire.bin\n\nThis `Solitaire.bin` file is the original binary that Matt compiled before compressing, adding headers, and printing the program onto physical cards. Pretty cool that we can reverse the process this far!\n\n## Nintendo e-Reader and Analogue Pocket\n\nThe Analogue Pocket is a hardware emulator that uses an FPGA to emulate multiple retro gaming consoles, including the GBA. One of the prominent features of this device is its cartridge slot, allowing you to play cartridges without dumping them to ROM files first.\n\nBut there's just one problem with using the Analogue Pocket with the Nintendo e-Reader. The cartridge slot is placed low on the device, making it impossible to insert oddly-shaped cartridges like the Nintendo e-Reader. Enter the _E-Reader Extender!_ This project by Brian Hargrove extends the cartridge slot while giving your Analogue Pocket a big hug.\n\n## Playing Nintendo e-Reader games on Delta Emulator\n\nThe best retro emulator is the one you bring with you, for this reason the Delta Emulator is my emulator of choice as it runs on iOS. However, there are challenges to running e-Reader games on Delta: specifically that Delta _only allows one save file per GBA ROM_. This means to change games you'd need to import a new e-Reader save file. Delta stores ROMs and saves by their ROM checksum (MD5).\n\n_One of two dotcode strips for the Solitaire e‑Reader card_\n\n\n\n\n* * *\n\nThanks for keeping RSS alive! ♥",
"title": "Dumping Nintendo e‑Reader Card “ROMs”",
"updatedAt": "2026-02-04T00:00:00.000Z"
}