Repository navigation
Releases: abilliontokens/urbz-recomp
Release list
v0.6.0 - Linux, natively
The port runs natively on Linux now - Vulkan for the picture, SDL3 for the window, input and
audio - from the same recompiled code as the Windows build. Two downloads:
Urbz-linux-x64.tar.zst |
x86-64 Linux, Vulkan 1.3 + the Vulkan loader. glslang travels in lib/ beside the binary; SDL3 and Dear ImGui are linked in. Wayland and X11 both work. |
Urbz.zip |
Windows 10/11 x64, Direct3D 12, as before. |
Unpack either one, put your own disc image (or default.xbe with its DATA and movies folders)
in assets/, and run it. Nothing from the game is shipped here.
What is actually new
The recompiler's output is platform-neutral, so both targets are built from one generated/ tree
and neither recompiles the XBE for the other. What had to change was the host side:
- The SDK was fast-forwarded onto its Linux runtime - the SDL3 host, the Vulkan backend, the
HLSL-to-SPIR-V path and the POSIX kernel paths - which also brings a set of D3D8/D3D12 fidelity
fixes to the Windows build. - Keyboard and mouse grew the platform split they never needed. The logical key set and the
per-poll snapshot are shared; Windows fills it from the window messages it already read, Linux
fromSDL_GetKeyboardStateplus a mouse sample taken on the window thread, which is the only
thread SDL allows it on. Bindings, the circle clamp on diagonals, the wheel's zoom pulse and
OR-only merging with a real controller are unchanged on both. - HDR stays a Windows feature. On Linux there is no HDR output path in this build, so the F1
panel says that and greys the choice out instead of offering a switch that does nothing. - One
Releasepreset now means one thing. Targeting Linux, CMake's own defaults carry
-DNDEBUG, and the runtime's log verbosity keys off it - so the same preset gave a rotating
verbose log on Windows and, on Linux, a single file with the boot preamble and nothing after it:
XBE_PERF=1printed nothing, the renderer logged no present geometry, and the pad hook's own
lines never appeared, which reads exactly like a dead input path.NDEBUGis now stripped on
both.
Measured before release, on an AMD BC-250 (RADV, GFX1013), KDE Plasma on Wayland
Driven from the title with the scripted pad, the Linux build walks the same path the Windows build
does - title, main menu, New Game, every Create-an-Urb page, name entry - and the draw counts follow
it: 1 on the title, 54 on the main menu, 528-585 in Create-an-Urb, 341 on the name keyboard.
Create-an-Urb is the one worth looking at: two skinned Sims, the store interior, the mirror's render
target, the translucent option panel and the game's own text, at 60.0 fps / 16.67 ms against the
default cap, 0 draws skipped, 0 pipelines missing, 0 errors in the log. Audio opens an SDL
stream at 48 kHz and the game's audiostr.xwb with it.
The renderer's own present line, on a 3440x1440 screen:
guest 640x480 target 1920x1440 scale 3.000 srcAspect 1.778 window 3440x1440
srcPx (0,48 1920x1344) dst (440,0 2560x1440)
The 16-row guest letterbox is cropped exactly as on Windows, and the title's 16:9 picture is
presented at 2560x1440 centred - not stretched to fill an ultrawide.
Both SDL3 video backends were exercised: the numbers above are the Wayland run, and with
WAYLAND_DISPLAY unset and DISPLAY=:0 the same binary logs Display: X11 (DISPLAY=:0) and
presents 1980 frames at 59.9 fps, 303 draws, zero errors. The X server on this machine is Xwayland,
so that proves SDL3's X11 backend and the Vulkan xlib/xcb surface path - not a session on a native
X server.
The published tarball was extracted somewhere else and run from there: every library resolves
inside the package (DT_RPATH $ORIGIN/lib, nothing falls through to the system), and it reached the
same 60 fps with zero errors.
About the Windows build in this release
It was cross-compiled on Linux with clang-cl and lld-link against an xwin MSVC/Windows SDK
cache, and it has not been run on Windows. It has run here under Wine 11.20, whose Direct3D 12
is vkd3d over Vulkan: it boots from a disc folder, matches the image by SHA-1, takes the scripted
pad, and renders a 493-draw scene at 60 fps with zero errors and no failed shader compiles. That
proves the build and that the Win32/D3D12/XAudio2 paths execute. It does not prove Windows draws
them correctly. If you want the version with the most hours on it, v0.5.0's exe was built and
tested on Windows.
It imports MSVCP140.dll and VCRUNTIME140*.dll, so a clean Windows needs the
Visual C++ 2015-2022 redistributable; everything
else it needs ships with Windows 10 and later. READ_ME_FIRST.txt in the zip says so too.
Everything from v0.5.0 is still here
HDR output on Windows, the pause menu working with the modern HUD, the 26% faster city districts,
the cheat codes in the F1 panel, XBE_MUTE for silent test runs, and the read-back and allocation
guards.
Unchanged and still true
Saving to a device and reloading, districts past the starting rooftop, jobs and minigames are not
proven - on either platform, and nothing here was measured in a city district on Linux. 108 of 238
XDK functions are unmatched and trap if called; five DirectSound exports still run their original
guest code. A SIGTERM delivered to the Linux build while a scene is up logs signal 11 from the
teardown path and writes no crash dump; the sibling Bustin' Out port does the same, so it predates
this work and only happens when the process is killed.
v0.5.0 - HDR
HDR output, off by default and switched on in the F1 panel.
The game draws an 8-bit SDR picture, so HDR here is about putting that picture on an HDR display at
the right luminance instead of letting Windows stretch it. There is no invented detail: nothing is
expanded, no inverse tone mapping, no saturation lift.
What it does. The swap chain becomes R16G16B16A16_FLOAT in scRGB. That was chosen over
HDR10/PQ because the source is 8-bit BT.709: the conversion is a decode and a multiply, with no
primaries matrix and no PQ encode to get wrong, and a window dragged onto an SDR display still looks
right because scRGB's 0 to 1 is plain sRGB. The picture is decoded with a 2.2 power rather than the
piecewise sRGB curve, which lifts the darkest steps about four times (code 10: 0.0030 against
0.0008) and is what makes an SDR image look washed out in HDR. White then lands on the paper-white
setting, and only if paper white exceeds the display's peak does the brightest channel roll off
along BT.2390, with the other two following so hue is kept.
The overlay and the modern HUD are drawn into an 8-bit intermediate and converted with the same
transform, so ImGui's blending, anti-aliasing and text are exactly what they are in SDR, and the HUD
sits at the game's own white.
In F1 → Display: HDR Off / Auto / On; Paper white, 80 to 480 nits, 200 by default, which is what
the game's white is drawn at; Peak brightness, whose far left is the display's own maximum. The
status line reports what the swap chain actually did rather than what the setting says - including
"forced on, but this display is SDR or has HDR off in Windows" - so a setting that cannot take
effect says so.
Measured across four displays on one machine:
| Window on | Log |
|---|---|
| 4K primary, HDR on in Windows, 1499 nit peak | HDR on (setting auto): R16G16B16A16_FLOAT scRGB swap chain, game white 200 nits, peak 1499.0 nits |
| second display, HDR on, 417.7 nit peak | re-armed within a frame at peak 417.7 nits |
| SDR ultrawide | HDR off (setting auto): this display is SDR or has HDR off in Windows, B8G8R8A8_UNORM swap chain |
any, hdr = "off" |
HDR off (setting off): turned off in settings, B8G8R8A8_UNORM swap chain |
The display is re-read every present through MonitorFromWindow and IDXGIFactory1::IsCurrent, so
moving the window between monitors or toggling HDR in Windows takes effect within a frame. With
hdr = off, which is the default, the swap chain, the blit and the per-frame work are exactly what
they were in v0.4.0.
Everything from v0.4.0 is still here: the pause menu working with the modern HUD, the 26% faster
city districts, the cheat codes in the F1 panel, XBE_MUTE for silent test runs, and the read-back
and allocation guards.
Unchanged and still true: jobs and minigames are not proven; 108 of 238 XDK functions are
unmatched and trap if called; five DirectSound exports still run their original guest code.
Verified before release: a clean folder holding only Urbz.exe and a disc image booted from that
image (sha1 5712b6a3...), on the discrete GPU, with HDR reported off by default, the EA logo movie
playing, zero errors in the log, no failed pipelines and no crash dump.
Download Urbz.zip, unzip it, and put your own disc image (or default.xbe with its DATA and
movies folders) in assets/. Nothing from the game is shipped here.
v0.4.0 - the pause menu, the districts and the cheats
Start works again, the districts are faster, and the cheat codes are in the F1 panel.
Fixed: Start did nothing with the modern HUD on
Bit 0x8000 on a clip is not _visible - it marks the clip as live on the stage, and the game's
Flash runtime treats a clip without it as deleted: its animation stops and script queued on it is
dropped. The host cleared that bit on the old HUD's corner clips and never put it back, which froze
mc_meters before its show animation finished. That animation is what turns the pad on, so with the
modern HUD - the shipping default - Start opened nothing: no pause menu, no Save, no Quit, and
the game kept running. The same freeze stopped the HUD's poll clip, so the reputation number and its
bar never moved, and saving left the old HUD on screen in place of the modern one.
The bit is now cleared only while the game draws its UI and put back the moment drawing ends, and the
clips are looked up by name every frame instead of from a list captured once. Verified in Diamond
Heights: Start opens Paused / Save / Quit / Options, the scene freezes, the modern HUD hides with it
and returns on resume, the clock advances, and saving no longer brings the old HUD back.
That same cached list was writing into freed memory. The game rebuilds its HUD movie on
Create-an-Urb, on quit to menu and on district travel, and the old code went on clearing bits at
addresses the game had freed and reused - silent heap corruption that surfaced later as a crash
somewhere unrelated. Looking the clips up each frame ends it.
26% faster in the city districts
Draw state was compared in full - all 14,232 bytes - on every draw. State now carries one mark bit
per 64-byte block and only marked blocks are compared; the state the renderer holds at each draw is
identical. Measured in Diamond Heights, same save, same settings, 1870 draws per frame:
| before | after | |
|---|---|---|
| frame rate | 75.8 fps | 95.9 fps |
| frame time | 13.19 ms | 10.42 ms |
| state compare | 2.42 ms | 0.88 ms |
| title thread in draws | 3.44 ms | 1.88 ms |
XBE_NO_STATE_DIRTY=1 restores the full compare, XBE_STATE_VERIFY=1 cross-checks the marks - zero
misses over a district load and 95 s of play. Diamond Heights submits 1870 draws a frame against
Central Station's 940, which is why it was the district that felt slow: the engine has no level of
detail and submits every visible object as its own draw.
Cheats in the F1 panel
The console cheat codes are built into a table at startup, and entering one just toggles a bit the
cheat gnome reads. The F1 panel now sets those same bits, so every code on the published PS2 list is
there - cheat gnome, all socials, skill objects, fill motives, max physical / mental / artistic,
power socials, team photo. Tick one, then use the gnome's menu as you would after typing the code.
They are off by default, never saved, and greyed out outside the city.
Also in this build
- Audio can be silenced for automated runs with
XBE_MUTE=1; the game ships audible as always. - A surface read-back could write past a smaller surface that had taken a freed address, and two
resource allocations were unchecked, which faulted on the render thread instead of failing. Both
guarded and logged. XBE_CAPTURE_PROBE=x,ynames the draw that wrote a pixel during an F3 capture. It settled the one
complaint that turned out not to be a defect: the black face in Diamond Heights is the elevator
shaft, drawn with the game's own 16x16 texture namedblack, exactly as the console drew it.- The README no longer advertises diagnostics that were removed with the renderer revert.
Unchanged and still true: jobs and minigames are not proven; 108 of 238 XDK functions are
unmatched and trap if called; five DirectSound exports still run their original guest code. Saving,
reloading and district travel are now exercised regularly - this build was driven through repeated
load, play, quit cycles and a 50 minute session in Diamond Heights without a crash or a crash dump.
Verified before release: a clean folder holding only Urbz.exe and a disc image booted from that
image (sha1 5712b6a3...), on the discrete GPU, with 0 errors in its log, no failed pipelines and no
crash dump.
Download Urbz.zip, unzip it, and put your own disc image (or default.xbe with its DATA and
movies folders) in assets/. Nothing from the game is shipped here.
v0.3.2 - the picture fills the window
The picture fills the window: no more black bars above and below it.
The game draws 640x448 inside its 640x480 frame - its own 2D screen matrix is 640 by 448 with a
16-row y offset in every captured draw, and the intro movie is 640x448 too. A TV overscanned those
rows away and no console ever showed them; a PC window has no overscan, so a 1920x1080 window
showed exactly 36 black rows (16 guest rows) at the top and the same at the bottom. The host now
presents rows 16 to 463 and nothing else, scaled to the same viewport the whole frame used, so the
picture fills the output. Measured after the change at 1920x1080: 0 black rows top and bottom, in
gameplay, on the menus and over the intro movie.
The modern HUD was anchored to those bars - its top row started below one, and its floor sat where
the game's own HUD ended, 0.894 of the uncropped height. Over the cropped picture that row is
0.922, so the reputation badge, clock, mood dial and motive bars keep the positions they had
relative to the picture.
Everything in v0.3.1 is still here: the modern HUD on by default (F1 turns it off), 60 fps in the
city at every camera distance, real game speed at any frame rate, the keyboard-and-mouse pad, and
the shader cache that keeps first entry into a district from stalling.
Unchanged and still true: districts past the starting rooftop, jobs and minigames are not
proven; 108 of 238 XDK functions are unmatched and trap if called; five DirectSound exports still
run their original guest code. The shader cache starts cold for a new player, so the first sight of
an effect compiles inline. Saving and reloading now has evidence behind it: this build was driven
from the title screen through Load Game into a saved Urb repeatedly while capturing footage.
Verified before release: a clean folder holding only Urbz.exe and a disc image booted from that
image (sha1 5712b6a3...), on the discrete GPU, with 0 errors in its log, no failed pipelines and
no crash dump.
Download Urbz.zip, unzip it, and put your own disc image (or default.xbe with its DATA and
movies folders) in assets/. Nothing from the game is shipped here.
v0.3.1 - the modern HUD (v0.2.4 renumbered)
Same build as v0.2.4, renumbered. v0.3.0 (60 fps in the city) was cut before v0.2.2, v0.2.3 and
v0.2.4, so the newest build carried the lowest number and a version sort put v0.3.0 on top. This
release is the v0.2.4 build - byte-identical Urbz.exe, SHA-1 39ca9504a25029347192e1596b66b87dc15b276b -
under a number that is actually the newest. It contains everything in v0.3.0 (the city frame rate
work) plus the menu-repeat fix, the city-load crash fix and the modern HUD. Take this one; v0.2.4
and v0.3.0 are superseded.
The game's HUD is a 640x480 movie stretched over whatever you run at. This build redraws it as
vectors at the output's own resolution and hides the original, so the corners stay sharp at 4K.
The modern HUD (on by default; F1 → Modern HUD turns it off and gives the original back)
- Reputation number, the bar towards the next unlock and the clock along the top left; the mood
dial and the five motive bars along the bottom left, in the game's own order and in the place the
original sat. - The NPC dialogue box is drawn as a card, keeping the game's own coloured words.
- The bottom-right action queue is redrawn around the game's own pictures of what the sim is doing.
- Every value is the game's: motive percents,
HUD_repand its base/next-unlock twins, and the
clock globals, all read from the live HUD object the game itself passes to its variable getter.
Hiding the old one uses the game's own mechanism. The display-list walk renders a child only
when bit 0x8000 of its flags is set - the bit ActionScript's _visible writes - so that bit is
cleared each frame on the status clips, which the game then skips along with their subtrees.
Nothing is drawn and then covered up. The pie menu, the lot name and the queue's rendered pictures
stay, because they are interaction, not status.
Two defects behind that, both fixed here:
- Guest pointers were range-checked against
0x8000000, but every clip lives in the physical
window at0x80000000. Every clip lookup and the reputation table walk silently returned
nothing. The check now matches the runtime's own map and additionally requires committed pages,
so a value that merely looks like an address cannot fault the host. - The motive percentages are a cache the game fills only while its own bars are on screen, so
hiding them froze the readout at zero. The host now makes the same six refresh calls per frame
that the game makes.
From v0.3.0, still in here: the city districts hold 60 fps at every camera distance (43.4 fps
zoomed out before), 183-278 fps uncapped, and first entry into a district no longer stalls on
shader compilation once shadercache\ is warm.
Unchanged and still true: saving and reloading, districts past the starting rooftop, jobs and
minigames are not proven; 108 of 238 XDK functions are unmatched and trap if called; five
DirectSound exports still run their original guest code. The shader cache starts cold for a new
player, so the first sight of an effect compiles inline.
Download Urbz.zip, unzip it, and put your own disc image (or default.xbe with its DATA and
movies folders) in assets/. Nothing from the game is shipped here.
v0.3.0 - 60 fps in the city
Superseded by v0.3.1 - it contains everything here plus the menu-repeat fix, the city-load crash fix and the modern HUD. This tag was cut before v0.2.2-v0.2.4, so its number is higher than its contents. Download v0.3.1.
The city districts now hold 60 fps at every camera distance, and run 183-278 fps uncapped.
Fixed: the frame rate drop in town, worst when zoomed out
The profile came before the fixes, and it overturned the obvious theory. In a district at 929
draws per frame the title thread was the critical path at every camera distance - it never
waited on the renderer (0.00 ms, 0 waits) while the render thread sat idle 11-18 ms of every
frame. Zooming out costs more only because the engine has no level of detail and a fixed far
plane: between the closest and furthest camera the field of view widens 55 to 65 degrees and the
pitch goes 13 to 58.5 degrees, which covers 3.2-3.7x the ground at the same 47 vertices per draw.
So the draw count rises, and the host side of each draw is what had to get cheaper.
| camera | before (60 cap) | after | before uncapped | after uncapped |
|---|---|---|---|---|
| zoomed in | 56.7 fps | 60.0 (cap) | 64.0 fps | 278.3 fps |
| middle | 47.8 fps | 60.0 (cap) | 47.4 fps | 206.1 fps |
| zoomed out | 43.4 fps | 60.0 (cap) | 41.6 fps | 183.2 fps |
Draw-normalised at 850 draws per frame, uncapped, the frame went from 20.43 ms to 4.59 ms.
What changed:
- The host runtime had been compiled unoptimised.
CMAKE_CXX_FLAGS_RELEASEwas empty for
clang targeting the MSVC ABI, so only the recompiled game code carried-O2. The renderer, the
command recorder and the XDK layer were all built at-O0. - The command recorder compared all 14,424 bytes of draw state on every draw. It now tracks
which 64-byte blocks changed and compares only those: 5.19 ms per frame down to 0.39 ms. - Per-draw redundancy in the D3D12 backend is skipped when an exact byte compare says nothing
changed - descriptor tables, samplers, texture stages, pipeline keys and constant buffers.
Upload traffic fell 36%. - Shader and pipeline compilation left the critical path: it runs on worker threads and is
cached on disk inshadercache\beside the exe, pinned to your adapter, driver and compiler.
First entry into a district - the worst of the stutter - used to spend 452 ms inside a single
frame compiling shaders, at 1.9 fps. Cold cache now: worst second 40.9 fps. Warm: no compiling at
all, worst second 53.8 fps.
A driver-level hazard was found and fixed on the way: re-binding an already-presented back buffer
at the head of the next command list removes the device on NVIDIA. It only became reachable once
the host was fast enough to get there before the flip.
Camera and cursor at high frame rates
Measured at 60 fps against 254-292 fps, sampling the game's own state at 100 Hz:
| at 60 fps | uncapped | ratio | |
|---|---|---|---|
| held zoom | 77.98 units/s | 77.24 units/s | 0.99 |
| idle auto-return | 0.84 s / 2.15 s | 0.85 s / 2.12 s | ~1.00 |
| selection cursor | 4.611 units/s | 4.722 units/s | 1.02 |
| walking | 4.639 units/s | 4.663 units/s | 1.01 |
Two genuine per-call bugs were fixed: the zoom's out-of-bounds dolly was being frame-time scaled
twice, and the shot-mode camera glide (0.6 per call) was not scaled at all. The camera easing back
out after you stop touching it is the game's own design - two seconds after the last camera input
it returns to its home distance, at the same rate on any hardware.
Also in this release
XBE_MUTE=1silences the output for a recording or benchmark run without changing any audio
timing.- Diagnostics for anyone chasing a visual oddity:
XBE_NO_DRAW_REUSE=1,XBE_NO_STATE_DIRTY=1,
XBE_SHADER_THREADS=0,XBE_NO_SHADER_CACHE=1, andXBE_STATE_VERIFY=1, which cross-checks
the new state fast path against a full comparison and logs anything it misses. - Keyboard and mouse play, and the real-time frame step from v0.2.x, unchanged.
Verified
XBE_STATE_VERIFY=1 over a full session - boot, district load, camera sweeps, walking, menus -
logs zero missed state changes. Scenery is pixel-identical to the pre-change screenshots. The
in-game clock advances 0.793 game-minutes per wall second at both 60 fps and ~200 fps, so the
higher frame rate does not change game speed. Every measured run: zero errors, no crash dumps.
Still not proven
Saving and reloading, districts past the starting rooftop, jobs and minigames. 108 of 238 XDK
functions still run the image's original code. See docs/status.md.
Running it
Unzip anywhere, put your own disc image (or extracted default.xbe with its DATA and movies
folders) in assets\ beside Urbz.exe, and run it. Windows 10/11 x64 and a Direct3D 12 GPU; no
game data is included. The exe only runs the USA retail XBE, SHA-1
5712b6a3e2eaaacfcae228cf6154ade499a0033c.
v0.2.4 - the modern HUD
Superseded by v0.3.1 - the same build, byte-identical
Urbz.exe, renumbered so the newest release also carries the highest version. Download that one.
The game's HUD is a 640x480 movie stretched over whatever you run at. This build redraws it as
vectors at the output's own resolution and hides the original, so the corners stay sharp at 4K.
The modern HUD (on by default; F1 → Modern HUD turns it off and gives the original back)
- Reputation number, the bar towards the next unlock and the clock along the top left; the mood
dial and the five motive bars along the bottom left, in the game's own order and in the place the
original sat. - The NPC dialogue box is drawn as a card, keeping the game's own coloured words.
- The bottom-right action queue is redrawn around the game's own pictures of what the sim is doing.
- Every value is the game's: motive percents,
HUD_repand its base/next-unlock twins, and the
clock globals, all read from the live HUD object the game itself passes to its variable getter.
Hiding the old one uses the game's own mechanism. The display-list walk renders a child only
when bit 0x8000 of its flags is set - the bit ActionScript's _visible writes - so that bit is
cleared each frame on the status clips, which the game then skips along with their subtrees.
Nothing is drawn and then covered up. The pie menu, the lot name and the queue's rendered pictures
stay, because they are interaction, not status.
Two defects behind that, both fixed here:
- Guest pointers were range-checked against
0x8000000, but every clip lives in the physical
window at0x80000000. Every clip lookup and the reputation table walk silently returned
nothing. The check now matches the runtime's own map and additionally requires committed pages,
so a value that merely looks like an address cannot fault the host. - The motive percentages are a cache the game fills only while its own bars are on screen, so
hiding them froze the readout at zero. The host now makes the same six refresh calls per frame
that the game makes.
Unchanged and still true: saving and reloading, districts past the starting rooftop, jobs and
minigames are not proven; 108 of 238 XDK functions are unmatched and trap if called; five
DirectSound exports still run their original guest code. The shader cache starts cold for a new
player, so the first sight of an effect compiles inline.
Download Urbz.zip, unzip it, and put your own disc image (or default.xbe with its DATA and
movies folders) in assets/. Nothing from the game is shipped here.
v0.2.3 - fixes the crash entering the city
Fixes the crash at the Create-an-Urb to city transition.
Fixed: the game died when you entered the city
Every attempt to finish making a sim and enter the city killed the process. Five minidumps, all
the same fault: inside nvwgf2umx.dll (NVIDIA's D3D12 driver), reading a null pointer, on a
driver-owned thread, during the burst of new shaders a city load creates.
It was bisected by elimination, each suspect killed by an actual run rather than by argument:
shader content (the same shaders compiled fine in sessions that reached the city), the on-disk
shader cache (the first crash ran with an empty one), concurrent pipeline creation (serialising
it still crashed), anisotropic filtering (crashed at 0 and at 16), the sampler-heap wrap (traced,
never fired), vsync and the tearing present path (crashed with vsync on), and every per-call
input wrapper (all disabled together, still crashed).
That left one change: the renderer rework that landed after v0.2.1 ("cut the city frame"). Its
six source files are reverted here, and the transition works again.
The -O2 host build from that same work is kept - it was the critical-path win, and the city
frame rate holds without the rest of the rework. What goes away until the defect inside it is
found and it can be re-landed: the shader cache between runs, parallel shader compiling (so the
first sight of a new effect compiles inline), and some per-draw headroom.
Also since v0.2.2
- Menus and the Create-an-Urb name keyboard repeat at console speed at any frame rate.
- Every frame-rate wrapper can now be switched off for a run, which is what made the bisect
above cheap:URBZ_NO_STEP,URBZ_NO_ZOOM,URBZ_NO_REPEAT,URBZ_NO_UIGATE,
URBZ_NO_IDLECAM,URBZ_NO_BINDINGS,URBZ_NO_SHOTCAM.
Carried over from v0.2.1 and v0.2.2
The game runs at its real speed at any frame rate, capped or uncapped, on a high-refresh display:
the engine quantised its frame step to whole 60ths of a second, which ran it 2.4x fast at 144 fps.
Camera zoom, the idle auto-return, the selection cursor and menu repeat are all time-based now.
Keyboard and mouse play alongside a controller: WASD walks, the arrows are the D-pad, the right
mouse button drag plus Q/E/R/F move the camera, the wheel zooms, space is A, escape is B, enter
is Start, tab is Back.
Still not proven
Saving and reloading, districts past the first, jobs and minigames. 108 of 238 XDK functions are
still unmatched and run the image's original code. See docs/status.md, which also records the
crash bisect in full and where to look to re-land the renderer work.
Running it
Unzip anywhere, put your own disc image (or extracted default.xbe with its DATA and movies
folders) in assets\ beside Urbz.exe, and run it. Windows 10/11 x64 and a Direct3D 12 GPU; no
game data is included. The exe only runs the USA retail XBE, SHA-1
5712b6a3e2eaaacfcae228cf6154ade499a0033c.
v0.2.2 - menu repeat and camera rate fixes
Menu and name-entry repeat now run at console speed at any frame rate, plus the camera/cursor rate fixes.
Fixed: menus and typing repeat too fast at high frame rates
Menus and the Create-an-Urb name keyboard do not use the controller's auto-repeat. They move on
UI-router events, gated by sub_0001D530 - a throttle that advances a global timer by the real
clock delta but then adds the cumulative tick count into each direction's accumulator, so its
repeat rate is quadratic in the frame rate: measured by simulation of the guest algorithm,
~8.8 repeats/s at 60 fps, 15.8/s at 144, 27.8/s at 240. That is why holding a direction to move
across the name keyboard raced.
The host now gates that throttle per direction to console cadence (src/game/hooks.cpp): the
original logic runs once per 1/60 s with its clock delta pinned to exactly 1/60, so it sees
precisely what it saw on the console at any frame rate. The pad auto-repeat used by one widget
was also corrected to the same timing, and the camera-zoom wrapper no longer writes anywhere if
the camera's parameter block is not valid.
Also since v0.2.1
- The blue selection cursor and camera-bound stick actions no longer run fast above 60 fps: the
script-binding executor handed raw stick values to handlers as per-frame displacements, and
those are now scaled by the real frame time. This also fixes "zoom does not hold" - holding
only the left stick was ramping the camera distance at full stick rate. - The camera's idle auto-return no longer accelerates harder at high frame rates, and the
shot-mode camera glide is time-based too. - City frame rate: renderer fixes and an optimised host build (see
docs/status.md).
Known issue: crash after selecting an Urbie
One crash reported on 2026-10-16 after selecting an Urbie. The minidump faults inside
nvwgf2umx.dll (the NVIDIA D3D12 driver) reading address 0x28, on a host rendering thread,
during a burst of fresh shader compilations in the character-select transition - it is not at a
guest address and not in the input code. It is recorded in docs/status.md. If it happens to
you, keep the .dmp file that appears in CrashDumps\ beside the exe together with the log in
logs\; that pair is what a driver-side triage needs.
Still not proven
Saving and reloading, districts past the starting rooftop, jobs and minigames. 108 of 238 XDK
functions are still unmatched and run the image's original code, including the DirectSound
exports 0xB00A3, 0xB00A5, 0xB00A4, 0xB0080, 0xB00A9 and IDirectSoundBuffer_Lock.
See docs/status.md.
Running it
Unzip anywhere, put your own disc image (or extracted default.xbe with its DATA and movies
folders) in assets\ beside Urbz.exe, and run it. Windows 10/11 x64 and a Direct3D 12 GPU; no
game data is included. The exe only runs the USA retail XBE, SHA-1
5712b6a3e2eaaacfcae228cf6154ade499a0033c.
v0.2.1 - any frame rate, real speed
The game now runs at its real speed at any frame rate, including uncapped on a high-refresh display.
Fixed: above 60 fps the game ran fast
Reported, and measured at 2.4x with a 144 fps cap. The host pacer was not at fault: the engine
measures real time (rdtsc through XAPI QueryPerformanceCounter), then quantises it in
sub_0018E360 to
ticks = clamp((int)(elapsed / [0x2F024C] + 0.5), 1, 15) // 0x2F024C = 1/60 s
dt = ticks * [0x2F024C] // -> [0x2E2A80]
[0x2E2A80] is the single delta the rest of the engine multiplies by - 97 reads in 63 functions -
so with a 1/60 s tick and a floor of one tick, every frame shorter than 25 ms still advanced the
game by a whole 1/60 s: 144 fps x 1/60 = 2.4 seconds of game time per real second.
The host now replaces only the quantiser (src/game/hooks.cpp): ticks of 1 ms, with a
seconds-per-tick written back so that ticks * [0x2F024C] is exactly the time that really passed.
The engine's own 0.25 s ceiling is kept, so a stall still cannot advance the world in one jump.
Two subsystems stepped per call rather than per delta and are wrapped the same way:
- Camera zoom (
sub_0001FC20): its 1.3-per-call rate and 0.1-per-call out-of-bounds dolly are
scaled by the frame's real time for the duration of the call. - Button auto-repeat (
sub_0018D6D0): the pad's repeat delay and rate counts are rescaled to
the call rate, so menus scroll at console timing instead of 2.4x fast at 144 fps.
Everything else was checked and already scales by the delta: animation channels and blend weights,
camera rotate/follow/recenter, needs decay, the game clock. Movie playback is paced by the
vertical-blank count and the audio clock, and the menus and title idle timer are wall-clock.
The F1 frame-rate cap keeps its full range - 30, 60, 72, 120, 144, 165, 240 and unlimited.
Verified: the engine's own frame delta summed against the wall clock reads 1.000 game seconds
per real second at caps of 60, 144 and unlimited, measured at 60, 138 and 2474 frames per second,
with no exceptions in any run.
Also in this release
Keyboard and mouse play, from v0.2.0: WASD walks, the arrows are the D-pad, the right mouse button
drag plus Q/E/R/F move the camera, the wheel zooms, space is A, escape is B, enter is Start. A
controller and the keyboard work at the same time, and the game opens a pad at boot on a PC with no
controller plugged in. The full table is in README.md.
Still not proven
Saving and reloading, districts past the starting rooftop, jobs and minigames. 108 of 238 XDK
functions are still unmatched and run the image's original code, including the DirectSound exports
0xB00A3, 0xB00A5, 0xB00A4, 0xB0080, 0xB00A9 and IDirectSoundBuffer_Lock. See
docs/status.md.
Running it
Unzip anywhere, put your own disc image (or extracted default.xbe with its DATA and movies
folders) in assets\ beside Urbz.exe, and run it. Windows 10/11 x64 and a Direct3D 12 GPU; no
game data is included. The exe only runs the USA retail XBE, SHA-1
5712b6a3e2eaaacfcae228cf6154ade499a0033c.