Wes Ellis./ a personal notebook
Technology. Stories. Side projects.
A few things worth writing down.
← Back to Side Projects

Side Projects

Genesis ROM Workshop: Pulling Palettes, Tiles and Text Out of Sega Genesis ROMs

Hands holding a game controller in front of a Sega Mega Drive with a Sonic cartridge plugged in.

Part 6 of the thread The retro workbench

PROJECT AT A GLANCEOpen source · experimental
What it is
Open-source tool
My role
Creator
Year
2025
Built with
  • Python
  • Pillow
  • NumPy
  • Capstone
THE SHORT VERSION4 points
  • A Python toolkit for pulling palettes, text and tile graphics out of Sega Genesis / Mega Drive ROMs.
  • It decodes Genesis 4-bit planar tiles and renders them as PNG tile sheets and palette previews.
  • There's a hex viewer, ROM header and checksum analysis, and a text extraction and translation workflow too.
  • It's command-line only and a learning project: no compressed-data support yet, and results need checking by eye.

A Genesis cartridge is a few megabytes of bytes with no labels on them. Somewhere in there are the palettes, the tile graphics and the dialogue, but you can't just open it and look. You need something that knows how the hardware stores things and can turn raw bytes back into pictures and words.

genesis-rom-workshop, or GRW, is my Python toolkit for that. It's aimed at ROM hacking, translation projects, preservation research and plain curiosity about how 16-bit games were put together. Bring your own legally owned ROMs; none are included.

What's in the box

GRW is a set of small tools with one CLI, grw_pipeline.py, on top.

Tool What it does
palette_editor Extracts, edits and swaps color palettes; exports JSON or PNG previews
graphics_renderer Decodes 4-bit planar tiles and renders them to PNG
asset_extractor Pulls out graphics tiles, tilemaps and sprites, with rendering built in
text_extractor Finds text strings and handles a translation export and patch workflow
hex_editor Hex view with address, hex and ASCII columns; edit, search and compare ROMs
rom_analyzer Header parsing, checksum validation and entropy stats
asset_manager Organizes everything you extract into projects

How the graphics work

This is the interesting part. Genesis graphics are built from 8x8 tiles, 32 bytes each, at 4 bits per pixel, so every tile can use 16 colors from one palette. The pixel data is stored in planar bitplanes, which is why a raw dump looks like static until something decodes it.

GRW's renderer does that decoding and writes out PNGs at 1x up to 16x scale, with optional grid overlays for pixel-level work. Pair a palette with a stretch of tile data and you get a tile sheet you can open in any image viewer, instead of a .bin file and a JSON list of hex colors.

Note

Palette extraction assumes the standard VDP format, and GRW doesn't find palettes automatically yet. If a sheet comes out in strange colors, it's usually the wrong palette, not broken tiles.

Using it

Clone it and install the requirements. Pillow is the one that matters, since every image export depends on it.

git clone https://github.com/wesellis/genesis-rom-workshop.git
cd genesis-rom-workshop
pip install -r requirements.txt

Then point the pipeline at a ROM:

python grw_pipeline.py analyze game.bin --output-dir ./analysis
python grw_pipeline.py extract-palettes game.bin --format png
python grw_pipeline.py extract-text game.bin --output text.json

There's also a graphics demo in examples/ that renders palette previews, a tile sheet and individual tiles from one ROM in a single run. Every tool can be imported from Python as well if you'd rather script it.

Tip

Start with analyze. It checks the header and checksum and gives you a feel for the ROM before you go digging for graphics.

Rough edges

I'll be straight about where this stands, because the README is. It's a learning project, and some of what's described there is only partly built or still planned.

  • No compressed data. Graphics stored with Nemesis or Kosinski compression can't be unpacked yet, so they won't render.
  • Command line only. There's no GUI.
  • Genesis first. Master System, Game Gear and 32X files are recognized, but support is thin.
  • Text is ASCII. Japanese ROMs need Shift-JIS and other encodings that aren't there yet, and text detection can throw false positives.
  • Sprites stay as tiles. Assembling multi-tile sprites automatically isn't implemented.
  • Check your results. ROM layouts vary a lot between games, so treat the output as a starting point.

The roadmap puts compression support first, then sprite assembly, full tilemap rendering, a GUI, broader tests, and eventually YM2612 and PSG audio playback.

If retro hardware is your thing, the arcade cabinet build is the other end of the same hobby: playing these games rather than taking them apart.

Try it

It's on GitHub. Contributions are welcome, especially anyone who's written a Nemesis decompressor before.