Underground 2 Revived
Play Need for Speed: Underground 2 (2004) in Chrome and Firefox with WebGPU graphics, Web Audio, up to 120 fps widescreen, controller support, free roam and Online races with friends. Fan project, not
翻譯準備中
Halo: Combat Evolved running natively on the Anbernic RG35XX H (RG35XXH) and other Allwinner H700 handhelds with Knulli. ARM64 port of the halo-ce-universal decompilation for the Mali-G31: 60 fps menus, about 40–55 fps campaign. No emulation. Ready-to-install release; bring your own Xbox disc.
翻譯準備中
取自儲存庫 README
正在載入完整 README。 以下為已儲存的摘錄。
Halo: Combat Evolved on Anbernic RG35XX H — native port for Knulli (Allwinner H700)檢視完整 README ↗
Yes — Halo CE runs natively on the Anbernic RG35XX H (RG35XXH) under Knulli: 60 fps in the menus and about 40 to 55 fps in campaign levels at the default render scale, and it plays online (co-op and multiplayer, with PCs and other handhelds) and offline against bots. This repository is a native ARM64 (AArch64) port of the Halo: Combat Evolved decompilation, OpenCE (formerly halo-ce-universal), to the Allwinner H700 and its Mali-G31 GPU. There is no emulation: no xemu, no Box64, no Wine. You need your own copy of the original Xbox game. This repository has the source code, the documentation, and ready-made releases that need only your disc image: they hold no game data.
Captured on an Anbernic RG35XX H (640x480 screen).
MULTIPLAYER > BOT MATCH sets how many bots join (1 to 15), how well they play (Easy to Legendary) and, in Team Slayer, whose side they are on. Then you pick the map and the game type, as in the Xbox's split screen, and play them on the handheld, with no network (Install: Bot Match).
| BOT MATCH, under Create Game | The bots' settings |
|---|---|
| The lobby: you and seven bots on Blood Gulch | Two of the bots at the red base |
Hold SELECT and press D-pad Down in a game: the level's vehicles, weapons, equipment and characters, placed in front of you, a character on your side or against you, and a page of the game's cheats (Install: the spawn menu).
| The Silent Cartographer (b30): the vehicles page | A Pelican it spawned on the beach |
|---|---|
When a new release is out, the launcher offers it before the game starts. START downloads it from this repository's releases, checks it against the SHA-256 GitHub gives for it and installs it: your maps, saves and settings stay. Any other button plays the version you have, and that release isn't offered again (Install: Updating).
| Server browser: public internet games | Online on Blood Gulch, a 23-player public game |
|---|---|
| Main menu | The Silent Cartographer (b30), render scale 0.75 |
maps/ from your Xbox disc image on the handheld.halo/custom_maps/. Joining
a game on one you don't have downloads it from the game's players
(Install: Custom Edition maps).Frames per second on an Anbernic RG35XX H, Knulli Gladiator II, stock thermal
limits: the typical value and the range of HALO_FPS_LOG's 5-second
averages over the first minute of each level after it has loaded, with the
level already in the game's cache (the first time a level is entered, its
loading screen decompresses it there: Install).
Dynamic resolution is on, as it is by default. c10's opening holds 60 after
its first 45 seconds. The b30 battle varies from run to run by 2 or 3 fps.
Last updated: 2026-10-06 (release v2026.10.06).
| Scene | render_scale = 0.75 (480x360, default) | render_scale = 1.0 (640x480) |
|---|---|---|
| Main menu | 60 | 60 |
| c10, 343 Guilty Spark (swamp) | about 57 (46–60) | about 56 (23–60) |
| b30, The Silent Cartographer (beach battle) | about 43 (38–47) | about 38 (28–47) |
| a30, Halo (level opening) | about 59 (39–60) | about 40 (26–60) |
At 0.75 the limits are the game's own thread and the Mali driver's CPU time for each draw call on the GL thread. Where the GPU is the limit, as in c10, dynamic resolution lowers the render scale, down to 0.5, until it keeps up. At 640x480 the GPU is the limit in the a30 opening and the b30 battle too. The b30 battle steps down to 480x360 to 520x390 for much of the fight. In a30 the driver waits for the GPU where dynamic resolution does not yet see, so after its intro a30 stays at 640x480 (Roadmap). The kernel's thermal governor lowers the clocks at 70 °C. The history of the optimisations and the measurements behind them are in docs/PERFORMANCE.md.
The port is made for the Allwinner H700 (4x Cortex-A53 at 1.5 GHz, Mali-G31 MP2, 1 GB RAM), with Knulli or another firmware that has PortMaster. Halo moves and aims with two analog sticks; on a handheld with one or none, the D-pad takes the missing stick's place (Install: controls).
| Device | Screen | Status |
|---|---|---|
| Anbernic RG35XX H | 640x480 | Tested |
| Anbernic RG35XX Plus | 640x480 | No analog sticks: from v2026.10.21 the D-pad walks, turns and aims; not yet tried on one (#9) |
| Anbernic RG35XX SP | 640x480 | No analog sticks: from v2026.10.21 the D-pad walks, turns and aims; not yet tried on one (#9) |
| Anbernic RG35XX 2024 | 640x480 | No analog sticks: from v2026.10.21 the D-pad walks, turns and aims; not yet tried on one (#9) |
| Anbernic RG40XX H | 640x480 | Untested, expected to work |
| Anbernic RG40XX V | 640x480 | Untested. One analog stick: it moves, and from v2026.10.21 the D-pad aims |
| Anbernic RG CubeXX | 720x720 | Tested on Knulli Scarab, online co-op included. The 4:3 picture has bars above and below, black from v2026.10.26 |
| Anbernic RG34XX SP | 720x480 | Works, reported by a user (#1) |
| Anbernic RG34XX | 720x480 | No analog sticks: from v2026.10.21 the D-pad walks, turns and aims; not yet tried on one (#9) |
| Anbernic RG353M (RK3566, Mali-G52) | 640x480 | Works on ROCKNIX, reported by a user (#5) |
| R36S (RK3326, Mali-G31) | 640x480 | Works on dArkOSen from v2026.10.07, reported by a user (#4) |
| Firmware | How | Status |
|---|---|---|
| Knulli (Gladiator II) | the Knulli zip | Tested |
| Knulli (Gladiator II) | the PortMaster zip | Tested |
| Knulli (Scarab) | the Knulli zip | Tested (RG CubeXX) |
| muOS (2601 Jacaranda) | the PortMaster zip | Tested from v2026.10.07 (RG35XX H, offline and online). Earlier releases crashed there in menus, at respawn and in the pause menu |
| ArkOS and others with PortMaster | the PortMaster zip | Untested. The host needs glibc 2.30 or newer and SDL2 2.0.18 or newer |
| ROCKNIX | the PortMaster zip | Reported working by a user on an RG353M with Arm's Mali driver, with the default settings once ROCKNIX was up to date. On their older ROCKNIX (20260601) parts of the picture stopped updating unless debug.instance_models = false (#5). ROCKNIX can also use the open Panfrost driver, which the port has not been tried with |
| dArkOSen | the PortMaster zip | Reported working by a user on an R36S from v2026.10.07; earlier releases crashed there (#4) |
The port is made and tested for the H700's Mali-G31. Handhelds with other
Mali GPUs and Arm's driver can work too: users report the Rockchip ones
above working. TrimUI handhelds and other devices without a Mali GPU are not
supported. If you try another device or firmware, please
open an issue with the result and your halo/log.txt.
.iso
or .xiso), North American or European. The Xbox version's maps are
required; the PC version's files do not work.maps/ (1.8 GB) and the cache
the game sets up at its first start (0.8 GB), plus room for the disc image
until the maps are copied.halo-ce-knulli-<version>.zip, or
halo-ce-portmaster-<version>.zip for PortMaster), or the files built as
described in Build from source.halo-ce-knulli-<version>.zip from the
latest release.roms
folder (over the network, \\KNULLI\share). It adds
roms/ports/Halo.sh and the folder roms/ports/halo/..iso) into roms/ports/halo/.The first start copies maps/ out of the disc image, with its progress on
the screen (about four minutes); the image can be deleted afterwards. The
game's own first start then takes about a minute more with a black screen
while it sets up its cache (the screen says so first); later starts take
seconds. Instead of a disc image you can also copy an extracted Xbox maps/
folder into halo/. To quit, hold the hotkey (MENU, or SELECT) and press
START. New releases are offered by the launcher before the game starts
(START installs one); a newer release unzipped over the old files works
too: maps/, save/ and config.toml stay.
With PortMaster (on Knulli or another firmware) instead: put
halo-ce-portmaster-<version>.zip in PortMaster's autoinstall folder and
start PortMaster, then copy the disc image into ports/halo/
(Install).
On a Batocera PC (x86-64) instead: unzip
halo-ce-batocera-pc-<version>.zip into /userdata (over the network,
\\BATOCERA\share), put the disc image, or the Xbox game's maps/ folder,
in roms/ports/halo-pc/, and start Halo CE (PC build) from Ports. It is the
decompilation's own 32-bit Linux build with this port's additions (Bot
Match, the spawn menu), and it installs each new release by itself before
it starts (Batocera PCs).
The resulting layout:
/userdata/roms/ports/
├── Halo.sh
└── halo/
├── halo
├── halo_guest.elf
├── halo_extract.py
├── halo_screen.py
├── sdl_mapping.py
├── halo_update.py the updater the launcher runs
├── config.default.toml
├── VERSION the release installed
├── brokers.txt internet play's signalling brokers
├── config.toml written at the first launch
├── log.txt the log of the last launch
├── maps/ extracted from your disc image
└── save/ saved games and the shader cache
The first time each shader combination is used, the driver compiles it, on
a thread of its own: what it draws appears a moment late, rather than the
game stopping for it. The compiled programs are kept in halo/save/shaders,
so later launches load them instead.
The build runs on Linux x86-64. It was done on Ubuntu 24.04 under WSL.
python3, ninja-build, git, curl.arm64_32 (ILP32 AArch64) target.gcc-aarch64-linux-gnu (13.x): the host, an ordinary aarch64 glibc program.release-2.30.12 tag. build.sh downloads them.build.sh downloads them (port/knulli/glibc_sysroot.sh, which
checks the packages' checksums) and unpacks them with dpkg-deb.sudo apt install python3 ninja-build git curl gcc-aarch64-linux-gnu
wget https://apt.llvm.org/llvm.sh && sudo bash llvm.sh 22
curl -LO https://dl.google.com/android/repository/android-ndk-r28c-linux.zip
unzip -q android-ndk-r28c-linux.zip
The host links against the handheld's own libSDL2-2.0.so.0. Copy it from
the handheld's /usr/lib into a sysroot/ folder. It is used only at link
time and must not be committed. EGL and OpenGL ES are linked by their usual
names (libEGL.so.1, libGLESv2.so.2), against Debian 11's libglvnd.
mkdir -p sysroot
scp 'root@<handheld>:/usr/lib/libSDL2-2.0.so.0*' sysroot/
# or: adb pull /usr/lib/libSDL2-2.0.so.0 sysroot/
ANDROID_NDK=$PWD/android-ndk-r28c SYSROOT_LIB=$PWD/sysroot ./build.sh
build.sh:
work/halo-ce-universal and checks out the commit in
UPSTREAM_COMMIT;patches/halo-ce-universal-knulli.patch and copies port/knulli
into the upstream tree;work/;python3 configure.py --release --android-ndk <ndk> --android-guest-cc clang-22
(the first time, and again for another upstream commit; it downloads
musl and SDL3 for the guest), then
port/knulli/build.sh;halo, halo_guest.elf, Halo.sh, halo_extract.py,
halo_screen.py and sdl_mapping.py into dist/.The first build takes 10 to 30 minutes; later builds are incremental.
Optional variables: GUEST_CC (default clang-22), HOST_CC (default
aarch64-linux-gnu-gcc), WORK, DIST, JOBS.
The upstream project decompiled Halo CE's Xbox build to C and ported it to Linux, Windows and Android. Its Android port compiles the game as an ILP32 AArch64 "guest image" (32-bit pointers, as on the Xbox, in 64-bit ARM code) that a small host program loads and serves.
This port reuses that guest image and adds a new host for Knulli in
port/knulli/: an aarch64 glibc Linux program instead of an Android app. It
answers the guest's SDL3 calls with the firmware's SDL2, the only SDL with
the Mali framebuffer video driver, and runs the guest's OpenGL ES calls on a
GL thread. The renderer changes for the Mali-G31 are in the patch against
upstream (port/linux/src and source/).
Details: docs/HOW-IT-WORKS.md and port/knulli/README.md.
The documentation index describes each document and suggests a reading order for players, builders and developers.
HALO_*
variable, with its default and effect.The settings are in halo/config.toml, written at the first launch. The
launcher's defaults for the handheld:
| Setting | Default | Effect |
|---|---|---|
display.render_scale | 0.75 | The 3D picture's resolution as a fraction of the screen's, 0.5 to 1.0, scaled up at the end of the frame. Lower is faster. |
display.model_detail | 0.5 | How early objects switch to their simpler models (1.0 is the game's own switch point), multiplied by the render scale. |
display.fast_shaders | true | Colours and combiner arithmetic in half precision (fp16). |
display.fast_textures | true | DXT1 and 16-bit Xbox textures sent to the GPU as 16-bit texels. |
display.screen_width | 0 | The columns of the 480-line picture: 0 for the screen's shape (640 on 4:3 screens, 720 on 3:2), 640 for the Xbox's 4:3. |
input.dpad_controls | "auto" | On a handheld without a right stick, the D-pad aims or walks and turns (Install: controls); "off" keeps the game's own D-pad. |
display.interpolation | true | Draws a frame for every display refresh, blending between the game's 30 ticks a second; false keeps 30 fps. |
display.frame_pacing | true | Shows each frame at the display refresh it was drawn for, so that most frames show the world as it is when they are seen. |
display.vsync | true | Waits for the display between frames. |
update.auto | true | The launcher's updater: a new release of this port is offered before the game starts (SETTINGS > NETWORK SETUP > CHECK FOR UPDATES). |
network.online | true | Internet play: the server browser, hosting and joining. It connects only while you use them. |
bots.count | 7 | How many bots join a Bot Match, with bots.skill and bots.teams; BOT MATCH's screen sets them. |
Environment variables such as HALO_RENDER_SCALE=0.6 override a setting for
one run. Every setting is described in
docs/CONFIGURATION.md, and the debug.* settings and
the profiling variables also in
port/knulli/README.md.
A file halo/init.txt runs console commands at start-up, for example
map_name levels\b30\b30 to start a level directly.
halo/log.txt has the details. A start while the game runs already is
refused, and the log notes it.maps/ out of the disc image
takes about four minutes, with its progress on the screen; then the game
sets up its cache for about a minute with a black screen. A copy that was
stopped goes on where it stopped at the next start.maps/ folder.sdl_mapping.py builds the SDL mapping from
EmulationStation's controller configuration. Check that the handheld's
controls are configured in EmulationStation.halo/save/shaders; what
it draws is skipped until it is ready. Deleting that folder is safe; the
programs are compiled again.display.render_scale for more headroom.Halo.sh restores the CPU
governor and the GPU's minimum clock on exit; the next start, or a
reboot, restores them too.Yes. This port runs Halo CE natively on the RG35XX H under Knulli, as AArch64 code on the H700's Cortex-A53 cores with the Mali-G31 GPU. It is built from the halo-ce-universal decompilation; you supply the Xbox game's data.
At the default render scale of 0.75: 60 fps in the menus, about 57 fps on 343 Guilty Spark (c10), about 43 in the beach battle of The Silent Cartographer (b30) and about 59 at the opening of Halo (a30). At the full 640x480 it is about 38 to 56 fps. The ranges are in the performance table.
The Xbox version. The decompilation is of the Xbox build, and it reads the Xbox maps. The PC version (Gearbox's port) has different map files that this code cannot load. Use a disc image of the original Xbox game. Halo Custom Edition's maps run beside it (Install: Custom Edition maps).
The repository contains only source code and documentation, and its releases only the port's programs built from that source: the upstream decompilation's authors released their code under CC0, and so does this port. Neither contains game files, disc images or maps. You need your own copy of the Xbox game. Whether decompilation projects are lawful depends on where you live; this is not legal advice. See the legal notice.
Not on Knulli: the Knulli zip is a plain port, a launcher script in
roms/ports and a folder. Each release also has a PortMaster zip, for
PortMaster on Knulli or on other firmware
(Install). Both use the
firmware's own SDL2 and graphics driver and none of PortMaster's runtimes.
muOS works with the PortMaster zip from v2026.10.07, tested on an RG35XX
H with muOS 2601 Jacaranda, in the campaign and online. Earlier releases
crashed there in menus, at respawn and in the pause menu: muOS's SDL takes
over the crash handler the port uses to notice the game writing to its
textures. ROCKNIX is reported working on an RG353M with Arm's Mali driver,
once ROCKNIX was up to date: an older version (20260601) left parts of the
picture frozen unless debug.instance_models = false (#5). It can
also use the open Panfrost driver, which the port has not been tried with.
dArkOSen on an R36S is reported working from v2026.10.07 (#4). The host is built on glibc 2.31
and links EGL and OpenGL ES by their usual names, so that it loads on
firmware older than Knulli. Reports are welcome.
The PC version is a 32-bit x86 Windows program that draws with Direct3D 9. Running it on the H700 would need x86 translation, Wine, and a Direct3D to OpenGL ES translation layer, all on four Cortex-A53 cores and 1 GB of RAM. Each layer costs CPU time the device does not have. The native port runs the game's own logic as ARM64 code and draws with OpenGL ES directly.
xemu emulates the whole original Xbox, its Pentium III CPU and its NV2A GPU, and needs a fast desktop CPU and a desktop OpenGL or Vulkan GPU. The H700's Cortex-A53 cores and its OpenGL ES-only Mali driver are far below that. A native port of the decompiled code avoids the emulation entirely.
It should run on the other Allwinner H700 handhelds that run Knulli: the
Anbernic RG35XX Plus, SP and 2024, RG40XX H and V, RG CubeXX and RG34XX.
Halo moves and aims with two analog sticks. The Plus, SP, 2024 and RG34XX
have none and the RG40XX V one: from v2026.10.21 the D-pad takes their
place, not yet tried on those handhelds (#9). The RG35XX H is the one it is developed on, and a user reported the RG34XX
SP working, its 3:2 screen filled with screen_width = 0
(#1). TrimUI handhelds and other SoCs are not
supported.
Yes, offline: MULTIPLAYER > BOT MATCH plays Slayer or Team Slayer against up to 15 computer players on the handheld alone, at Easy, Normal, Heroic or Legendary (Install: Bot Match). The bots are BirchWoodGod's, from the PS Vita port, contributed to upstream OpenCE (in review there). They play in Bot Match only: not online, not on a LAN, and not in other game types.
This is an unofficial fan project. It is not affiliated with, endorsed by or sponsored by Microsoft, Xbox Game Studios, Halo Studios or Bungie. Halo, Halo: Combat Evolved and Xbox are trademarks of Microsoft Corporation.
The repository and its releases contain no Halo game files: no disc images, maps, XBE or extracted assets. They contain no Arm Mali driver binaries and no device libraries; the releases hold only the port's own programs, built from this source. To play, you need your own copy of the original Xbox game. Details: docs/LEGAL.md.
CC0 1.0 Universal, like upstream. Contributions are welcome; see CONTRIBUTING.md.