Static recompilation of Microsoft Hover! (1995), the capture-the-flag maze racer from the Windows 95 CD, from its shipping Win32 executable to native C.
Built on the pcrecomp toolchain and following its shared house style (layout, CLI, conformance harness, headless mode).
Status: v0.1.0-dev, alpha. Boots, starts a game, and plays level 1 to the end, recompiled throughout.
| Stage | State |
|---|---|
| P0: identify the binary | done: HOVER.EXE, MSVC 2.x + static MFC 3, no protection; DASHRES.DLL is resources only |
Function catalog (disasm32) |
5,090 functions, 96.6% of the code bytes |
Lift (run_lift.py) |
whole program: 5,135 functions, 0 lift errors, 426K lines of C |
Host (build/hover.exe, 32-bit, pcrecomp native32) |
350 imports: 336 bound to real Windows, 14 shimmed (17 with the presenter). Nothing native from the original runs |
| Graphics | The game draws with GDI as it always did; the presenter shows it in a resizable window of its own on Direct3D 11: sharp/smooth/nearest/integer scaling, borderless fullscreen (F11), CRT, retro dithering, vivid colour (docs/presenter.md). --classic for the original window |
| Title / attract screen | runs |
| Gameplay | F2 starts level 1; driving, turning, the radar, flags, pods and the opponents all run. A two-minute soak ran to the opponents winning, with no fault |
| Sound and music | waveOut and MIDI go to the real devices. Played in a windowed run; not checked note by note |
| Windowed mode | plays (keyboard and the original's menus), plus a Recomp menu for the port's own settings |
| Split screen | 2 to 4 players on one PC, each with their own view and dashboard, powerups, and a side to play on (--teams: co-op or versus); player 1's hovercraft is visible to the others (docs/multiplayer.md) |
| Online play | lockstep over UDP, up to 16 seats (each PC 1-4 of them, split screen), robots in every empty seat, players joining and leaving while it runs, join codes, and a desync check. Direct connection (LAN, Tailscale, or port forwarding) (docs/multiplayer.md) |
| Level seeds | pinned, shown in the title, saved, and shared as codes like L3-1789123456; same seed, same level, pixel for pixel (docs/levels.md) |
| Xbox controllers | XInput pads press the game's own bound keys; layout, deadzone and slot in hover.ini and the Recomp menu (docs/controller.md). Checked by a self-test on synthetic pad states; not yet tried with a real pad |
| Headless mode | --headless --record out.mp4, with --key for scripted input. Never shows or activates a window (docs/host.md) |
| Conformance harness | 22/22 milestones (boot, start, drive, 1,000 frames on a pinned seed, controller mapping, share codes, Recomp menu, a 2-player split screen, and online on this PC: in step through tick 400, a deliberate desync caught, and a late joiner replaying, taking its seat and leaving), 0 lift errors, 0 unresolvable tail calls (tools/conformance.py) |
One toolkit fix was needed, pcrecomp#24, open until it merges (docs/toolkit.md).
Real output of build\hover.exe --headless --record, recompiled code
throughout:
![]() |
![]() |
![]() |
![]() |
Things the 1995 game never had. All of it is host code beside the
recompiled game (src/runtime/) plus patches applied while lifting
(run_lift.py); none of it changes how a single-player level plays.
Split screen, 2 to 4 players on one PC. Each player gets their own view
and dashboard: radar (showing only what that player has explored), speed,
pods and gauges, score and flags. Each player picks up and uses their own
jump, wall and cloak pods, hears their own pickups and hits, and plays on a
side (--teams): co-op with player 1, or on the robots' side. A cloaked
player is hidden from the other views. Player 2 drives with I/J/K/L and uses
pods with U/O/P, or uses a pad.
Online play. Lockstep over UDP for up to 16 seats, with 1 to 4 per PC.
Robots drive every empty seat. Players join and leave while the game runs:
a joiner replays the session (a minute of play takes under a second), then
takes its seat. Games are shared with a join code (HOVER-XXXXX-XXXXX), and
every 20 ticks the PCs compare a hash of the world to catch a desync.
Connections are direct (LAN, Tailscale, or a forwarded port).
The presenter. The game still draws with GDI. The port shows the result in a resizable Direct3D 11 window: sharp, smooth, nearest or integer scaling, borderless fullscreen (F11 or Alt+Enter), a CRT effect with curvature, 16-bit or 8-bit retro dithering, and vivid colour. The "256 colour" warning at every start is gone.
Level seeds. Every level comes from a seed. You can pin a seed, save a
level you liked, and share it as a code like L3-1789123456. The same seed
gives the same level, pixel for pixel.
Xbox controllers. XInput pads press the game's own keys. Layout,
deadzone and controller slot are set in hover.ini or the Recomp menu.
Headless and recording. --headless --record out.mp4 --key ... plays a
scripted run without showing a window. The GIFs above were made this way.
Details: docs/multiplayer.md, docs/presenter.md, docs/levels.md, docs/controller.md, docs/host.md. What is next: ROADMAP.md.
Nothing from the game: no executable, no data, no disc image, and no
generated source. The recompiled C is produced on your machine from your
own copy by run_lift.py, into src/recomp/gen/, which is gitignored. The
tool ships; its output never does.
You need your own copy of Hover!: the Windows 95 CD (it is in
FUNSTUFF\HOVER), the Hover! CD, an ISO or BIN/CUE of either, or a zip of
the game folder. Nothing is downloaded for you.
- Download this repository (the green Code button, then Download ZIP) and unzip it somewhere with 1 GB free.
- Double-click
Setup.cmd.
It checks for Python 3.10+, the pefile and capstone packages, the pcrecomp
toolkit and the Visual Studio 2022 C++ compiler, and asks before
installing any of them, saying what and how big. It finds Hover! in your
drives, or asks where it is (a drive, a folder, or a .zip/.iso/.cue/
.bin). Then it copies the game into game\hover, finds every function,
recompiles HOVER.EXE and builds build\hover.exe. A rerun skips the
finished steps. If it stops, it says why in one sentence, and the details are
in setup.log.
It ends with a Hover! shortcut in the folder. Double-click it to play.
Prerequisites: Windows 10/11, Python 3.10+ (py -3 --version), git,
Visual Studio 2022 (or its Build Tools) with Desktop development with
C++, ffmpeg on PATH for --record only, and the pcrecomp toolkit
cloned beside this repository as tools:
some-folder\
tools\ <- git clone https://github.com/sp00nznet/pcrecomp tools
hover\ <- this repository
Until the jump-table fix (pcrecomp#24) merges, use its branch:
git -C tools checkout fix/generate-jump-table-self-arm (docs/toolkit.md).
- Python packages:
py -3 -m pip install --user pefile capstone - Your copy's files into
game\hover(from a drive, a folder, a zip or an image):Expected:py -3 tools\gamefiles.py E:\ py -3 tools\gamefiles.py hoverwindows95.zipreading 85 files from hoverwindows95.zip!hover.zip!HOVER!/(orcopying ...for a folder) thendone: ...\game\hover. - The function catalog (under a minute):
Expected, at the end:
py -3 tools\catalog.py[*] Functions: 5090 (thunks=12, leaves=1238). - Recompile to C:
Expected:
py -3 run_lift.py[*] HOVER.EXE: base=0x00400000 code=0x00401000-0x0045F600 IAT=350 catalog=5090 [*] 45 outside branch targets added ============================================================ lifted 5135 errors 0 no terminator 8 - Build (the 64-bit-hosted MSVC targeting x86, CMake and Ninja; all three come
with the C++ workload):
Expected:
build.cmdLinking C executable hover.exe. - Check it without opening a window:
Expected:
build\hover.exe[bind] 0x00400000: 336 native, 0 guest, 14 shimmed, 0 unresolvedand(dry run: image mapped and bound; --run enters 0x0043FB73). - Play:
build\hover.exe --run.
The usual trip-ups: python opening the Microsoft Store (that is Windows' alias
placeholder; use py -3, or turn the alias off in Settings > Apps > Advanced
app settings > App execution aliases); a freshly installed Python not being on
PATH until you open a new window; and a pcrecomp without the jump-table fix
(Setup.cmd names it; the lifted game then faults when level 1 starts).
build\hover.exe --run # play, in the original's window
build\hover.exe --run --headless --record out.mp4 --key F2@2000 --key UP@8000+6000 --frames 600
build\hover.exe --run --headless --watchdog 60 --native-trace # every Windows call
py -3 tools\conformance.py # milestones and lift health, vs the baseline
In the game: F2 starts, the arrow keys drive, F3 pauses, and the object is to collect your opponents' blue flags before they collect your red ones. With an Xbox controller: RT/LT or the left stick drive, A jumps, X drops a wall, B cloaks, Start pauses, Back starts a new game.
Settings live in build\hover.ini (written with its defaults on first
run) and in the game's Recomp menu: Level (seeds, saved levels, share
codes), Video (filter, window size, fullscreen, CRT, dithering) and
Controller, and Multiplayer (1-4 players split screen; host or join an online game). F11 or Alt+Enter toggles fullscreen. Switching to another
window pauses the game, as the original did; F3 continues.
| Flag | |
|---|---|
--run |
enter the game (without it: map, bind and stop) |
--headless |
a cloaked window that never reaches a screen or takes focus; message boxes go to stderr, dialogs answer OK, and a fresh settings profile each run |
--record out.mp4 |
pipe the window to ffmpeg (30 fps, wall-clock paced) |
--frames N |
exit after N frames of the 3D view |
--diff A,B |
print how much of the window changed between frames A and B |
--key NAME@MS[+HOLD] |
press a key MS milliseconds after entry, for HOLD ms (default 100): UP, LEFT, F2, SPACE, a letter, or a VK code |
--game DIR |
the game folder (default game\hover) |
--watchdog S |
stop after S seconds and say where every thread was |
--native-trace, --callbacks |
one line per call into Windows, or back from it |
--seed N |
pin the level seed for this run (the ini's [levels] seed otherwise) |
--players N |
split screen for N players (1-4) |
--host [PORT], --seats N, --clients N |
host an online game (UDP 7795) with N seats, starting now (or once N PCs join) |
--join CODE, --join HOST[:PORT], --local N |
join one, bringing N local players |
--teams hr... |
each seat's side, h (player 1's) or r (the robots') |
--classic |
the game's own window instead of the presenter |
--shot N:file.bmp |
save frame N exactly as the game drew it |
--pad-selftest, --levels-selftest |
check the controller mapping or the share codes, then exit |
py -3 tools\addr2line.py ADDR ... names host addresses from a fault report
or a watchdog dump.
Steps 3 to 5 above. PCRECOMP (Python) and -DPCRECOMP= (CMake, through
set CMAKE_ARGS=... for build.cmd) point at a toolkit checkout other than
..\tools; the lifter and the runtime must come from the same tree. How the
pieces fit is docs/architecture.md; what the host
does and why is docs/host.md.
MIT, for the code in this repository (LICENSE; what it does not cover is in NOTICE). Hover! itself is © 1995 Microsoft Corporation; none of it is here.






