UrbanRecomp Enhanced

UrbanRecomp Enhanced is a Super Nintendo SimCity recompilation for Android, iOS, Linux, macOS, and Windows with enhanced features like adjustable development speeds and larger maps.

README

UrbanRecomp Enhanced

Urban Recomp

Urban Recomp is a static recompilation of the Super Nintendo release of SimCity (1991) onto snesrecomp, the same general-purpose 65816-to-C framework used by MegaManXSNESRecomp and the other snesrecomp game repositories. Everywhere else in this repository it is simply "the game".

Unofficial fan project. Urban Recomp is not affiliated with, endorsed by or sponsored by Electronic Arts, Maxis or Nintendo. SimCity is a trademark of Electronic Arts Inc., and Super Nintendo is a trademark of Nintendo; they are named only to say which cartridge this project works with. The repository contains no ROM data and no game artwork: you must supply your own legally obtained copy of the US cartridge. Any file name works -- the launcher lets you pick the file, and the tools recognise it by its contents.

Enhanced fork: this repository builds on blackerking/UrbanRecomp, preserving its history, credits and licenses. It adds per-city development speeds (1x, 3x, 5x, 10x, 20x, 50x), faster refresh of changed power networks, 64-bit population accounting up to 9,999,999,999,999, map choices through 3840x3200, Journey mode with population-triggered border expansions, Copy/Paste, zoom and mouse drag panning. Vulkan composition and native C simulation kernels accelerate large cities. Music runs on its own thread, with the restored 19-track set and 10M/100M milestone themes bundled in the portable release. Widescreen status and navigation controls align with the wider city view. See the changelog, credits and performance evidence and remaining limits.

UrbanRecomp Enhanced 1.0.2 includes fresh builds for all five platforms. Get the Windows x64 EXE, universal macOS app, Linux x86-64 package, signed Android APK or iPhone/iPad sideloading IPA. All five packages include the latest biome and lava-flood improvements. Android supports ARM64/x86-64; the iOS IPA requires user signing. See mobile controls and installation. The Windows package is one portable EXE; run it and select your own clean US ROM. Bundled files unpack into a versioned private cache. Saves/settings stay beside the portable EXE. --portable-docs opens the bundled credits and licenses. The enhancements require the verified US ROM and the supported host execution path. Ctrl+wheel anchors zoom to the actual mouse pointer in the city and map preview while retaining fixed HUD/menu sizes. Enhanced releases use compiled C game execution and omit the 65816 interpreter from the release executable. Register and save-state layouts, memory access order and device clocks remain compatible. The independent interpreter is available only in explicit reference builds for testing. See native execution and verification. For universal Mac app packaging and save locations, see the macOS build notes. Development is distributed across city districts at Normal speed as well; its time allowance keeps development work bounded per frame. See the enhancement notes for current behavior and performance limits. Native generation retains original 120x100 terrain and extends its generator to larger maps at native feature scale. It also adds map numbers 00000–99999, sharp previews with zoom and drag panning, larger vehicle fleets, responsive large construction, and zoom-out sufficient to show the complete 3840x3200 map. It also fixes title-click cursor corruption and tool-window backdrops. Version 1.0 consolidates Enhanced Betas 1–23; see features, controls, save compatibility and testing limits.

Adaptive Widescreen, the default renderer: the shared Mods launcher offers Fit to window, Fit height, Fit width, and 4:3 through 32:9 presets (21:9 out of the box) while keeping the full original view visible; switching it off there returns to the classic widescreen. See build/run instructions and renderer scope and the dependency audit. The branch retains current main's engine pin and host fixes; controls default to the top left.

Just want to build and play it? See SETUP.md for a Windows/Linux quick-start. Contributors should read CONTRIBUTING.md instead. For a consolidated map of everything reverse-engineered about this ROM so far (WRAM variables, named routines, patch sites, compressed data regions), see docs/ROM_MAP.md.

Open threads: docs/OPEN_QUESTIONS.md lists what is left to investigate, with the evidence already gathered for each.

How it runs

The map-selection screen offers nine GENERATION styles: Native, Procedural (the earlier generator), Islands, Lakes, Rivers, Fractal, Continent, Delta and Atolls. Native remains the default; the choice applies to newly generated maps. Five-digit map-number editing waits until the mouse leaves the arrow area before regenerating. Ctrl+wheel zooms around the mouse pointer, and fused native construction loops accelerate large successful rectangle fills. Setup-menu lettering changes together with its sprite list to avoid flashes of mismatched text between pages. Map previews stay sharp through NEXT, number editing, generation and setup transitions.

Enhanced Windows releases run compatible ROM-generated C alongside connected native C simulation routines over snesrecomp's PPU, APU, DMA and cartridge device models. The host retains original H/V clocks, interrupts and bus order; Vulkan shares presentation and compute for supported graphics and derived city fields. Derived-field and graphics failures retain CPU fallbacks. Game instructions use compiled per-address C, including complete verified regional profiles; uncovered code modifications produce a diagnostic rather than being interpreted. PC enhancements still require the verified US ROM. Generated game code and the player's ROM remain private build inputs.

The earlier interpreter and optional AOT/fiber bring-up are documented in docs/AOT_LLE.md. Enhanced builds use SC_PROGRAM=ON and SC_AOT=OFF; contributor generation instructions are in CONTRIBUTING.md.

No full disassembly. This project will not produce a complete, labelled disassembly of the game. Another fan project already works on that: Vitor Vilela's SA-1 version. The original project developed its ROM research independently. This enhanced fork follows mouse behavior publicly described in Vitor Vilela's SA-1 Beta 2 post. No SA-1 patch bytes or third-party ROM data have been imported. Vitor Vilela, or anyone else, is welcome to bring the improvements made here into their own work, subject to the licenses of the relevant components.

Graphics/text export tool

tools/extract_graphics.py decompresses and exports the game's font tileset, scenario tileset, and every advisor/scenario dialog text block (Nintendo's LC_LZ5-style compression, used throughout this ROM) as PNG and raw .bin files, for mod support. Needs Pillow (pip install Pillow); run with python tools/extract_graphics.py from the repo root once your ROM is in the repository root (any file name). Output goes to extracted_assets/ (gitignored -- it's derived from the copyrighted ROM, so it's never committed). Verified against this project's own ROM: every dialog text block decompresses and renders as readable English text.

Scenario map export tool

tools/extract_maps.py decodes every scenario map out of the ROM to extracted_assets/maps/ — a raw 24000-byte .bin in exactly the layout the game keeps live at $7F0200 (120×100 cells, one little-endian 16-bit tile index each), plus a false-colour .png preview. No dependencies; run python tools/extract_maps.py from the repo root with your ROM in the repository root (any file name). The format is documented in docs/REFERENCE_map_format.md.

The last two entries are free play and the tutorial map. Both place zero buildings, where every real scenario stamps 200+ — the tell that they're maps you start on rather than cities you inherit. Free play's terrain is drawn as Mario's face, which is worth knowing before assuming a decode has gone wrong.

Post-load power dropout (stock-ROM bug, fixed)

After a load the whole city reads as unpowered for several seconds, and since the decline logic runs during that window, loading a game actively costs you population.

Found and fixed by Truttle1 (https://www.youtube.com/@Truttle1), whose patch is what identified bit 15 ($8000) of each 16-bit map cell as the power bit. Measured here to confirm it: loading a scenario and sampling every 60 frames, powered cells sit at 0 for the first ~400 frames, then jump to 2888 and settle at 3043 — a ~6.7-second window with nothing powered.

Power repair always rebuilds the actual network after loading. It respects plant capacity and disconnected zones across the entire active map, before development can treat the save codec's missing power flags as an outage.

Implemented host-side in C rather than by porting Truttle1's bytes — this repo doesn't vendor third-party work, and doing it from C needs no free ROM space. It also avoids a quirk of that patch: because it replaces STZ $003a ; RTS with a 4-byte JSL, its RTL lands on 03:c8e1 and runs the SRAM loader a second time (harmless — an idempotent copy that doesn't touch $7F0200, so the power bits survive).

The trigger point 03:c8dd is confirmed: a bsnes exec breakpoint there fires on loading a saved game, reached via 00:c845 → 03:c8a0. This project's own headless harness still cannot drive that path (the mode-freeze technique doesn't reach it), so the confirmation is from bsnes, not from --qualify.

Root cause, found by a bsnes write breakpoint on $7F0200: the routine that makes bit 15 mean "powered" is 03:b152, which walks all 12000 cells applying a packed power bitmap held at $7FA598 (one bit per cell, AND #$7fff then conditionally ORA #$8000). That bitmap is not part of the SRAM save block — the load path restores $7F5FC0 and $7F6560 but nothing at $7FA598 — so after a load it has to be recomputed from scratch, and until it is, every cell reads unpowered. Hence the dropout, and hence why "assume powered until the real scan says otherwise" is the right shape of fix: 03:b152 corrects it on its next pass either way.

Launcher and settings

Started without a ROM argument, the game opens the shared recomp-ui launcher first (the recomp-ui submodule): ROM choice, window size, fullscreen, filtering, audio, Widescreen (Display), Language -- English, Deutsch, Francais (Localization), the Sylt scenario (Mods), and the keyboard bindings (Controller). The choices are saved to sc-settings.ini, the bindings to keybinds.ini, both next to where the game runs.

  • The ROM is found by its contents, never by its name. Without a ROM argument and without the launcher, the game uses the one saved in sc-settings.ini, else the first .sfc/.smc file in the working directory that is the US image; SC_LANG=E|F|G|J picks another region's image the same way.
  • --launcher shows the launcher even with a ROM argument, or after "skip the launcher" was ticked.
  • Every run that is not --qualify applies sc-settings.ini: widescreen, language and Sylt become SC_WIDESCREEN, SC_TRANSLATION / SC_PACKET_PATCH and SC_NINTH, each only where the variable is not already set by hand. --no-settings ignores the file; --qualify runs always do, so tools and comparisons keep their meaning.
  • A language needs translation_<de|fr>.bin and translation_<de|fr>_selector.scpk next to the game; without them it starts in English. They carry text and artwork from the German or French cartridge, so they are never shipped: with the US ROM and your own German or French ROM in the folder, python tools/make_translations.py de (or fr) builds them, byte for byte the files this project was tested with.
  • The launcher draws with OpenGL 3.3. Where there is none -- a Hyper-V VM without a GPU reports only "GDI Generic" OpenGL 1.1 -- the game skips it and starts with the saved settings. (recomp-ui itself does not check and crashed there; src/sc_launcher.c asks the context for its version first.)

Saving

Cities saved in the game, and the scenario win marks, live in the cartridge's battery-backed memory (SRAM). The game keeps it in urbanrecomp-us.srm in the folder it runs from (-eu, -fr, -de or -jp with another region's ROM; German and French run on the US ROM and share its file).

  • The file is written half a second after the game stores something, and again when the game closes, always through a temporary file, so a crash cannot leave half a save. The file as it was at start is kept as urbanrecomp-us.srm.bak.
  • It is the raw 32 KB SRAM image, the format SNES emulators use for .srm files.
  • Save states (Shift+digit) are separate. Loading one does not change the saved cities in the file.
  • SC_SRAM_PATH=<file> uses another file, SC_SRAM=0 turns saving off. --qualify runs never read or write it.

Controls (windowed mode)

The keys below are the defaults keybinds.ini gets on the first run; the launcher's Controller page changes them. keybinds.ini stores key POSITIONS (SDL scancode names), so on a German keyboard SNES Y reads Z there -- the key labelled Y.

SNES buttonKey(s)
D-padArrow keys, or U/H/J/K (up/left/down/right)
AS, or right mouse button
BX, or left mouse button
XA
YY
LQ
RW
StartEnter
SelectB

The letter bindings follow the labels on the keyboard, not QWERTY key positions, so they are the same keys on a German layout as on a US one. They are resolved through SDL_GetScancodeFromKey, because SNES Y and SNES B now sit on the keys labelled Y and X — which on QWERTZ are not where a positional binding would put them.

The right mouse button also pans the map while it is held and moved. The camera allows extra space above and below the map at every zoom level, so edge tiles can move clear of the HUD.

Keys that are not SNES buttons:

ActionKey
Pan the free city cameraX + arrow keys
Scroll faster (hold)Ctrl: 3x; Ctrl+Shift: 10x
Fast-forward (hold)Tab targets up to 6 frames; Shift+Tab 24; Ctrl+Shift+Tab 96, yielding busy batches for input (music keeps normal tempo)
Open Save City during play; Back/Close in menusEscape
Back during city setup (including map size and development speed)Controller X
Close full-screen information / confirm selected loan choiceEnter or normal Confirm
Reveal hidden test City 3 on Load CityCtrl+Shift+tilde
Zoom the map in / out (city view)+ / -
Save state to slot 1-9/0Shift+1 .. Shift+9, Shift+0
Load state from slot 1-9/01 .. 9, 0
Debug-menu code (controller 2, one shot)F2
Mouse cursor control (toggle)F3
Dump WRAM now (wram_snapshot.bin)F4
Debug cheats: No Disasters / Needless Money / Valve Max / Water ReclaimF5 / F6 / F7 / F8
Fast D-pad cursor (toggle)F9
Settings menu (toggle)F10

Map 31337 generates dry land with forests at every selectable size.

  • F2 enters the game's hidden debug menu. It plays the documented entry code (Left, A, Right, Y, Up, B, Down, X, Select, Start, Start, Select, R, R, L, L) on controller 2 in one go, timed automatically, instead of 16 hand-timed presses. Press it once on the "Goodbye! See you soon" quit-confirmation screen (Load/Save/Exit menu -> END). Confirmed working in play. Headless: SC_DEBUG_CODE_AT=<frame>.
  • F3 lets the host mouse move the game's cursor, ported from the community mouse patch (https://github.com/Selicre/simcity-mouse): it drives the same WRAM bytes ($7e01eb/$7e01ed) that patch identified, directly from real mouse movement instead of emulating an SNES mouse. Off by default. With the mouse buttons as B and A, the mouse alone can point, select and pan. It carries the same caveats the patch documents: some jank in menus, occasional resets to the origin.
  • F5-F8 flip the game's own debug-menu cheat flags in $0425 directly -- the same bits the hidden debug menu sets.
  • F9 makes the cursor move at host speed while a direction is held. The game paces its cursor with its own scheduler (the city simulation takes whole frames in which the cursor does not move, see docs/INVESTIGATION_cursor_cadence.md), which no ROM patch can remove. The normal D-pad input still reaches the game, so menus work as before. Off by default.
The F10 menu

F10 opens a settings overlay drawn by the host, not by the game. The game is paused while it is open. Up/Down select a row, Left/Right/Enter switch or step a value, or run an action; F10 closes it. Keys pressed in the menu are not passed to the game.

QOL

RowWhat it does
MOUSE CURSORSame as F3: the host mouse moves the game's cursor.
FAST TICKSOn by default. Removes the short delay the game puts between repeated steps while a button is held, so bulldozing and drawing roads continue smoothly instead of step by step.
DRAG TURBO1 (off), 2, 3, 4 or 6 game frames per displayed frame while the left or right mouse button is held. Speeds up building and the whole simulation, including the calendar.
PAN SPEEDHow many map tiles right-button panning may move per frame (1-8, default 1).
MOUSE SPEEDMouse sensitivity, 50-200 % (default 100).
FAST CURSORSame as F9. Adds host cursor movement for keyboard arrows/UHJK, independently of the calendar.
CURSOR SPEEDHow far FAST CURSOR moves per frame (2, 4, 8 or 16 pixels; default 4).
REPLAY MENUOn by default. When you pick a scenario you have already won, offers STANDARD (the scenario again, with its goal and disasters) or FREE (its city as free play).

CHEATS

RowWhat it does
ALL SCENARIO WONMarks every scenario as won -- the six, the hidden Las Vegas one, and Sylt when the ninth scenario is on -- so the select screen opens its fourth column (Las Vegas and free play) and shows a win mark on every card. It writes what the game's own save path writes, and only once the game has set up its save memory. It is stored in the save file like a saved city (see Saving), and switching it off does not take the wins back. Headless: SC_UNLOCK_ALL=1.
CHEAT NO DISASTER, CHEAT MONEY, CHEAT VALVE MAX, CHEAT WATERThe game's own debug-menu cheats, the same bits as F5-F8. Money and Valve Max are confirmed from the code that reads them; the other two bits are not yet.
SET POPHolds the population at a fixed value (off, 0, 2000, 10000, 50000, 100000, 500000, 600000), for testing the milestone messages.
SET CLASSForces the city rank (off, Village, Town, City, Capital, Metropolis, Megalopolis), independently of population. Milestones test this rank.
CLR MILESTONEResets the one-time population announcements so they can appear again.

Generation belongs to the map-selection screen, development speed belongs to city setup and each saved city, and disaster activation belongs to the game's Disasters panel. Power repair is always enabled.

STATE