A friend started a remake of Secret of Evermore, the 1995 SNES RPG, and I offered to pull the graphics and sound out of the ROM for him. What I thought was an afternoon turned into a weekend of taking half the game apart.
Let the game render for you
The maps are nastily compressed. One LZSS layer for the arrangement, a second codec for placement, a third for the tileset, plus palettes from their own table. I rebuilt the codecs by hand for a while, byte by byte against a RAM dump. It worked, but it was slow going.
Then it hit me that I did not have to do the work at all. The game already renders its maps, that is all it does. So I load it headless in an emulator (Mesen2 via Lua) and hook the game's own map-load routine. A callback on the spot where the loader reads the current map ID out of RAM, and just before that I overwrite the ID with the one I want to see.
emu.addMemoryCallback(function() emu.write(0x0ADB, target_map, emu.memType.snesWorkRam) end, emu.memCallbackType.exec, 0x908F68)
The original then builds every map through its own full path, and I just read out VRAM and CGRAM. The extractor boots Mesen2, sets the map ID and dumps the decoded state:
map 16: target=16 mapnum=16 24x15 -> OK, painted=104764 missing_quads=0 (2.9s) map 54: target=54 mapnum=54 25x25 -> OK, painted=238854 missing_quads=0 (2.7s) map 105: target=105 mapnum=105 54x79 -> OK, painted=1239641 missing_quads=0 (3.0s)
target==mapnum means the game loaded exactly the map I asked for, and not a single tile quadrant is missing. Here is a finished one, the cathedral with its stained glass:

This runs across all 127 maps without a single failure. Here they are at a glance, pulled straight out of the game:

The first jungle map
Because the maps come out of the running game, they keep running if I just leave them be. So the animation falls out for free: the foliage shimmers, the water ripples, the torches flicker. Here is the jungle with the winding paths where the boy runs around at the start, straight from the game as a GIF:

And the variety is solid. From the market to the junk towers to the lava paths, where the lava actually flows:




Rolling your own maps
Once I have the real tiles and the neighbour rules of every map, I can build new maps from them too. I learned the adjacency rules from the original maps and generated fresh ones with Wave Function Collapse, rendered with the real tileset. The glass hall gives a nice window grammar:

Other sources come out more as stonework and floor:

Honestly, for the jungle the WFC rules are too weak, the biome floods monotone and does not look nearly as good as the real map with its paths. So my remake runs on the real map, and WFC stays the button for rolling something new.
When it fails, stay honest
Not everything went that smoothly. The boy, the player sprite, sits in the ROM as raw 4bpp, and I could check it pixel for pixel against a reference sheet. When I tried to pull the attack and cast poses the same way, I hit a wall.
The same matcher that nails standing and walking only gets 0.44 to 0.79 on the action poses. Assembled, it looks like tile salad, a pile of wrongly matched tiles. The reason is that the dynamic poses are not stored as finished pictures in the ROM. They get built at runtime from separate hardware sprites with their own offsets, driven by a script engine. I left the negative result standing instead of passing off made-up offsets as done.
Something you can play
I ended up with a pygame remake running on the real ROM data. The boy walks and runs in every direction with the real frames, on the real jungle map with its paths, items, an NPC and real dialogue from the game:

For attack and cast I do it the way the original does: the body stays almost static and the weapon and the spell sit on top as their own sprite. Those effect sprites are cut straight from the ROM:


Plus a menu with the real item and alchemy tables:

This is all still work in progress, I keep building on it. You can also clearly see that the animations are not exported cleanly yet, the GIFs flicker and stutter at the edges. That is still to come.
Ping-pong with the AI
The back and forth was almost more interesting than the code. Claude did the actual grind, reversing the codecs, matching sprites, spread across a bunch of parallel agents. The ideas about where to go came out of the conversation.
Take the caller trick. Claude first cracked the codecs by hand, byte by byte. Then I said, just let the emulator call the function for you, the game renders its own maps anyway. That was the click, and from there all 127 maps came out.
The other running theme was honesty. More than once Claude sold me a clean cathedral while the image was still dark and broken. So I insisted on seeing every rendered image myself before anything counted as done. That is exactly how several supposed breakthroughs turned out to be unfinished. With the player sprite, where the action poses would not come out of the ROM, the call was to keep an honest negative rather than invent offsets. And for the effects I decided to do it like the original instead of digging deep into the animation engine.
I had the ideas and the eye for the result, Claude had the patience and the thousand details. Together it was done over a weekend, alone it would have taken me weeks. Nothing from Square lands in the repo, by the way, every asset comes out of your own ROM and is gitignored.