Reverse engineering Seven Cities of Gold (Ozark Softscape / Electronic Arts, 1984) from the original Commodore 64 disks, and rebuilding it for macOS in Swift.
The goal is fidelity underneath and a modern surface on top: the simulation's rules, stats and randomness recovered from the original binaries and verified against them, presented at native resolution with modern conveniences. Not smooth scrolling, as it turns out — the original moves the map by whole tiles and never between them, and tweening it looks wrong.
Status: milestone one — exploration. You can start an expedition, sail west out of Spain,
make landfall, walk the continent and get back aboard, on the classic map or on a world the
original's own World Maker builds. The game's rules — trade, natives, the court, scoring — are
not started. See TODO.md.
The classic map and the terrain art above both came out of the original disks — the art is not a screenshot of the game, but the game's own tile data, read out of its main program and redrawn. See What the app does.
Everything on that screen came out of the original disks and none of it ships here: the map,
the terrain tiles, the frame around the viewport, and the charset every word is set in — MEN
and DEPTH included. As with the shot above, it is not a photograph of the game but the game's
own data, read off disks you supply and redrawn.
The whole of it is navigation: pick a world, arrive at sea off the eastern edge as the original does, sail west, run the ship aground to send the party ashore, explore, and walk back to the ship to leave. There is no crew, no cargo, no supplies and no clock — the four panels and the pace line hold dashes because nothing stands behind them yet, and they are drawn rather than omitted because that is where those numbers go when they arrive.
The screen is the original's, rebuilt rather than emulated. The charset, the viewport frame and the palette all come off your own disks; the layout, the colors, the compass and the wave stipple were each measured from the game running under an emulator rather than eyeballed. Two things it does that the C64 could not: the window can be as wide as your screen, with fog of war supplying the constraint the original's six-tile porthole used to — and if you would rather have the porthole, Game ▸ Aperture ▸ Classic gives you exactly six tiles, drawn much larger than a C64 ever could.
No game data ships here, and none ever will. The code, graphics, text and maps are copyright Electronic Arts / Ozark Softscape. This repository contains only original engine code plus tools that read data from disk images you already own.
Almost all of the code, the reverse engineering and the notes here were written by
Claude, working from my direction. I am saying so plainly because it changes how you
should read the claims in NOTES.md.
What that means in practice:
- Findings are verified where they can be. Ported routines are asserted against output captured from the original 6502 running under an emulator; the map decode was cross-checked against an independent community dump; the terrain art was checked against the game's own rendering. Where something is a guess or a simplification, it says so.
NOTES.mdrecords the wrong turns too, deliberately — a cipher that was declared absent and then found, a variant formula rejected twice on bad evidence, several confident conclusions drawn from measurements that could not support them. That record is more useful than a clean narrative, and it is a fair sample of how the work went.- Commit messages are long on purpose. They carry the reasoning and the corrections, so the history is readable as an account of the investigation rather than a list of changes.
Treat the reverse-engineering conclusions as well-evidenced but not peer-reviewed. If you know this game's internals and something here is wrong, I would like to know.
You do not need the game to try this. Clone it and run:
make run # opens the app on its New Game screenWithout disk images the classic map is unavailable, so pick Random World and the World Maker builds you one.
To get the classic map and the original terrain art, add images of disks you own:
mkdir -p d64
# d64/7CITIES1.D64 program disk, side 1 — the terrain art
# d64/7CITIES2.D64 program disk, side 2 — the historical map
./extract.sh # decodes both into assets/
make runOr skip all of that and use the app: File ▸ Import Disk Images… does the same thing from a file picker.
make on its own lists the other targets. Side 2 carries the historical map; side 1 carries
the terrain art, the charset and the World Maker. Either can be supplied without the other.
To build and run the port — one dependency, and it is the toolchain:
| Requirement | Version | Notes |
|---|---|---|
| macOS | 14+ | declared in Package.swift |
| Swift | 6.0+ | Xcode 16, or the standalone toolchain |
There are no Swift package dependencies — Package.swift has an empty dependency list, so
swift build fetches nothing. No emulator, no Python, no network access. AppKit and SpriteKit
are system frameworks.
To run the research tools in tools/ — optional, and needed by nobody who just wants to
build or play:
| Requirement | Needed by |
|---|---|
| Python 3.9+ | all of them |
| Pillow | the 9 that render or compare images (pip install pillow) |
| vice-mcp | the 14 that drive the emulator |
vice-mcp is a fork of VICE with an MCP server built in — Homebrew's VICE will not work.
Nothing on the build, extract or run path. Everything a user does is static. It is needed for three things:
- Differential verification.
rng_reference.pyandarith_reference.pyexecute the original 6502 and capture its output as test fixtures, which the Swift tests then assert against. Those fixtures are committed, so you only need the emulator to regenerate or extend them — which is how the World Maker phases and the game rules will be verified as they are ported. - Re-deriving the cipher. The substitution table in
GameCipherwas recovered from a known-plaintext pair captured live bycatch_decrypt.py. Its closed form was never found and the generating routine is not in RAM, so the table cannot currently be re-derived statically. If you would rather not take it on trust, that is the tool that reproduces it. - Measuring what is not on the disks. The exploration screen's colors, its compass sprites
and its wave stipple exist only at runtime — built into RAM by the game as it draws. They were
read off a live session rather than extracted, which is why those parts of the screen are
redrawn to measurements instead of loaded.
TODO.mdlists what is still guessed.
- New Game (Cmd-N) — the classic map of the Americas, or a world the original's World Maker builds fresh, generated off the main thread behind a progress bar
- Game ▸ Aperture — Classic, the original's six-tile window with no fog, or Explorer, a window as wide as the screen with fog of war and a three-tile sight radius
- Palette — three models of the C64's colors; Tiles — original C64 art, or custom tiles drawn for this port
- Steer with the arrow keys, the numpad, the
QWE/ASD/ZXCcluster, or vi'sYUI/HJKL/BNM— all four are live at once, so vi is there without being the default - Sail into land to go ashore; the ship moors where it was and waits. Walk back onto it to re-embark
- In Explorer only: drag or scroll to pan,
=/-to zoom,0to fit the whole world,fto re-center. Classic pins the aperture, so it offers none of them
The world is 256 x 400 tiles at roughly three miles each. The sight radius is three, which is the original's six-tile window expressed as a radius — so nothing is ever revealed that the C64 would not have revealed, and the extra room on a modern screen buys memory rather than vision.
Everything the screen is made of comes from your own disks, with no emulator: the classic map,
the terrain tiles, the charset the text is set in and the four glyphs of the viewport frame.
None of it is a screenshot. The game draws terrain as redefined characters and sets text in a
charset of its own, and both turned out to be static data — the tiles inside the main program,
the font in disk 1's raw sectors — so extract.sh reads them directly. If no art has been
extracted the app falls back to custom tiles and says so in its title bar.
Two things on that screen are not from the disks, because they are nowhere on them: the
compass and the wave stipple. Both are hardware sprites or runtime-composed characters, so they
are redrawn here to measurements taken from the game running under an emulator — shape,
thickness, color and density. NOTES.md and TODO.md say which parts are measured and which
are still a guess.
open app/SevenCities.xcodeprojBuild and run. There is nothing to configure: no scheme arguments, no paths. The app keeps its
assets in ~/Library/Application Support/SevenCities/assets, generates a world if none are
there, and has File ▸ Import Disk Images… for pulling the classic map and the original art
off disks you own. You never need a terminal.
The project is a deliberately thin wrapper: it owns the bundle, the menu bar and the launch,
and depends on SevenCitiesCore as a local Swift package. All the behavior lives in the
package's ViewerKit library, which the MapViewer command-line front end uses too, so the
two cannot drift apart. Because the app target references the package rather than duplicating
it, adding a file to the package needs no project edit.
Prefer the package on its own? open SevenCitiesCore/Package.swift works as well, and
swift build / swift test behave as usual.
extract.sh decode your disks into assets/
Makefile extract, run, test, build, app, clean
app/ thin Xcode app wrapper — bundle and launch only
SevenCities.xcodeproj depends on the package below, duplicates nothing
SevenCitiesCore/ the Swift package, where everything actually lives
Sources/SevenCitiesCore simulation, decoding and extraction, no UI
Sources/ViewerKit the SpriteKit viewer, shared by the app and the CLI
Sources/Extract the command-line extractor
Sources/MapViewer command-line front end for the viewer
images/ screenshots used by this README
tools/ Python research tools (not needed to build or run)
NOTES.md the reverse engineering record
TODO.md what is still missing
d64/ your disk images — gitignored
assets/ extracted data — gitignored
tools/ holds the archaeology: a D64 reader, a recursive-descent 6502 disassembler, harnesses
that drive VICE to verify ported code against the original, and the map and tile extractors
that came first. None of it is needed to build or play; it is how the format was worked out,
kept so the findings can be reproduced and checked.
Ported code is not trusted until it matches the original. tools/rng_reference.py loads the
original 6502 routine into an emulator, runs it, and captures the output as a test fixture; the
Swift tests then assert the port reproduces it exactly.
| Component | Status |
|---|---|
| Fastloader / disk sector order | Solved from the loader's own command string |
| Map format | Solved — blocked sectors, nibble tiles, 256x400 |
| Terrain vocabulary | Solved — from the game's own name table |
game cipher |
Solved — fixed byte substitution, verified exactly |
| Terrain tiles | Solved — read from the program, no emulator |
| Charset and viewport frame | Solved — static on disk 1, inverted at load |
| World Maker RNG | Ported and verified against the original 6502 |
| Multiply / divide helpers | Ported and verified against the original 6502 |
| World generation | Ported and verified — both bands byte for byte, and the draw order |
| Exploration screen | Measured from a live game — layout, colors, compass, wave stipple |
| Expedition position in memory | Not found — see TODO.md |
| The game's rules | Not started |
The map decode was cross-checked against an independent community dump of the historical map and agrees on every continent, island chain, river course and mountain range.
The World Maker is the largest thing verified this way. It is a plate-tectonics model and a cultural-diffusion model in 18 KB of 6502, and the port reproduces the original's output byte for byte over both bands of the map — every landmass, river course, lake and village — and finishes with its random generator in the state the original leaves it in, which is a claim about every draw of every phase in order, not just about the result.
NOTES.md records how each of these was established, including the approaches that failed.
Engine code here is original work, released under the MIT license (see LICENSE). It is not
affiliated with or endorsed by Electronic Arts. Seven Cities of Gold and all original game
assets remain the property of their respective rights holders.

