Open Citadel is an experimental native compatibility host for the original Epic Citadel 1.07 Android/UE3 build. The active target is a native 32-bit Windows application; Linux and other native hosts remain secondary targets.
It loads the original Android x86 game library directly on Windows instead of
running Android or an emulator. The Windows build currently starts UE3, reads
the donor assets, compiles the game's shaders, and renders the Epic Citadel
scene. Mouse clicks and drags are translated to the original touch controls,
Windows SDL audio handles the Java MP3-song/WAV-sound callbacks, and display
and mouse-look settings persist. A portable Windows ZIP now bundles the host,
runtime DLLs, donor importer, and dependency notices without proprietary game
data. WASD movement is confirmed working, and F2 opens an in-game settings
overlay for controls, performance, and display options. An SDL-backed OpenSL ES
PCM queue shim is implemented and tested; actual in-game use remains unverified.
Physical gamepad behavior is also still unverified.
The project began from the compatibility substrate developed in
i-port-katamari, but Open Citadel is now a standalone project and is not
limited to PortMaster.
The Windows x86 host currently reaches real UE3 startup and renders on an NVIDIA GeForce RTX 4090 through ANGLE. The bring-up has verified:
- Win32 ELF32 mapping/relocation and native import resolution
- fake Java/JNI activity and
JNI_OnLoadregistration - original UE3 OBB and cooked asset reads
- GLES through ANGLE, with 292 shaders and programs compiled/linked
- ATITC fallback using the texture caches shipped in the donor
- visible 3D scene rendering; mouse click/drag reaches the game's touch UI
- native SDL audio device started the donor's
town_renderMP3 callback - MP3/WAV and OpenSL PCM queue playback tests, plus 13 Windows loader/ABI/ input/settings/audio tests
This is a working bring-up, not a finished port. The Java audio callback path plays donor music and supports WAV sound callbacks. The OpenSL ES compatibility shim passes a dummy-device queue/callback/re-enqueue test, but the latest live scene-startup smoke run did not enter that path. WASD movement is confirmed; physical gamepad behavior is not. Clean-machine validation and an installer remain future work; the portable ZIP can be generated with CPack.
See OPEN_CITADEL.md for detailed donor-format notes, architecture, reverse-engineering findings, and the milestone tracker.
Use CMake 3.21 or newer and a vcpkg installation (set VCPKG_ROOT if it is
not already configured), then configure the 32-bit Windows host. The root
vcpkg.json supplies SDL2,
ANGLE, mpg123, pthreads, dirent, and zlib:
CMake also downloads and SHA-256 verifies the pinned Dear ImGui v1.92.9b sources used for the Windows GLES2 settings overlay.
cmake -S tools/windows -B build/windows-app-win32 `
-G "Visual Studio 17 2022" -A Win32 `
-DCMAKE_TOOLCHAIN_FILE="$env:VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake" `
-DVCPKG_TARGET_TRIPLET=x86-windows `
-DVCPKG_INSTALLED_DIR=build/vcpkg-installed `
-DOPEN_CITADEL_BUILD_APP=ON
cmake --build build/windows-app-win32 --config Release --parallel
ctest --test-dir build/windows-app-win32 -C Release --output-on-failure
cpack --config build/windows-app-win32/CPackConfig.cmake -C Release `
-G ZIP -B build/windows-app-win32/packageThe resulting ZIP includes a Windows quick start, the donor importer, and
third-party runtime notices; it deliberately excludes game data. See
WINDOWS_QUICKSTART.md after extracting it.
Import your own XAPK if the donor has not already been imported:
python tools/open_citadel_import.py EpicCitadel.xapk gamedata/epic-citadel-1.07Run from the repository root with no arguments when the imported directory is in that default location, or pass its path explicitly:
.\build\windows-app-win32\Release\open-citadel.exe
.\build\windows-app-win32\Release\open-citadel.exe .\gamedata\epic-citadel-1.07Clicking the ground walks to that destination. Holding and dragging either
mouse button looks around; W/A/S/D send a virtual movement-stick input. F3
captures the pointer for clickless relative camera look; press F3 or Escape to
release it. F2/settings and focus loss also release capture. While captured,
mouse motion feeds the game's right-stick axes; click-to-walk and
drag-look are available again when the pointer is released. On first launch,
the host creates
%APPDATA%\OpenCitadel\EpicCitadel\settings.ini. F2 opens an in-game Dear
ImGui settings overlay. Its Game tab shows live FPS/frame time and controls
mouse-look sensitivity, vertical inversion, VSync, the next-launch FPS cap,
uncapped benchmark mode, native relative mouse look (enabled by default),
capture-on-launch (off by default), and the game's 3D render scale (50%, 75%,
or 100%; 100% by default). The Controls
tab can rebind Forward, Backward, Left, and
Right; Escape cancels a rebind and Reset restores WASD. The Display tab accepts
custom width/height values and selects fullscreen for the next launch. Shift+F2
opens the legacy native dialog as a fallback. All options are saved in the
settings file.
OPEN_CITADEL_CONFIG can select a different settings file. Set
OPEN_CITADEL_NATIVE_MOUSE_LOOK=0 to disable the F3 capture option.
Enable Capture mouse on launch in the Game tab or set
capture_mouse_on_launch=true in the settings file to start captured; Escape
releases the pointer. OPEN_CITADEL_NATIVE_MOUSE_LOOK_CAPTURE overrides that
setting when set.
On Windows, the Game tab offers Disable the game's 60 FPS cap next launch.
The default is the game's 60 FPS cap; changing the option requires a restart.
The saved setting can also be overridden with OPEN_CITADEL_UNCAP_FPS=1.
VSync is independent:
when enabled it still synchronizes presentation to the display, while uncapped
mode with VSync off can use substantially more CPU/GPU. A 20-second uncapped
benchmark run on an RTX 4090 logged 1,264–1,395 frame submissions/s at
1280x720; actual rates depend on hardware and scene load.
Disabling only the game's 60 FPS cap can still leave presentation limited to
the monitor's refresh rate. The dedicated benchmark option disables both.
For a fully uncapped benchmark, choose Uncapped benchmark mode next launch
in the Game tab or set OPEN_CITADEL_UNCAPPED_BENCHMARK=1. This starts UE3 in
benchmark mode, removes its 60 FPS limit, and forces VSync off for that run;
the saved VSync preference is preserved. That validation run reported the game
cap disabled with VSync off; this host path is not capped at 250 or 299 FPS.
Any lower rate shown by a separate in-game benchmark counter would be a
different measurement and should be traced independently.
The settings file stores the startup window size and fullscreen preference;
the Display tab and environment variables accept arbitrary sizes/aspect ratios.
Render scale applies on the next launch and is independent of window size and
aspect ratio. OPEN_CITADEL_RESOLUTION_SCALE can override it with 0.50,
0.75, or 1.00.
On Windows the window can also be resized live (minimum 320x240). Each resize
updates UE3 with the GL drawable dimensions and scales mouse/touch coordinates
to match; the windowed size is saved after resizing settles. A live resize from
1280x720 to 1203x720 was visually checked with the scene still rendering.
Startup was also visually checked at 960x720 (4:3) and 1280x540 (2.37:1
ultrawide); both reported matching drawable dimensions and rendered without
letterboxing. Fullscreen startup was visually checked at 3440x1440; it filled
the display and reported a matching UE3 drawable. The live F11/Alt+Enter
transition still needs a separate visual validation pass.
Environment variables override matching settings-file values. Mouse sensitivity
defaults to 1.0 (accepted range 0.1–4.0):
$env:OPEN_CITADEL_MOUSE_SENSITIVITY = '1.5'
$env:OPEN_CITADEL_INVERT_MOUSE_Y = '1'On Windows, choose a starting window size before launch:
$env:OPEN_CITADEL_WIDTH = '1920'
$env:OPEN_CITADEL_HEIGHT = '1080'
.\build\windows-app-win32\Release\open-citadel.exeWidth and height are selected independently (320–7680 by 240–4320); there is no preset aspect-ratio list. Startup rendering has been verified at 1024x768 (4:3) and 2560x1080 (21:9 ultrawide), as well as 1920x1080. The Display tab's saved startup size applies on the next launch; a running window can be dragged to another size immediately.
For borderless desktop fullscreen, set $env:OPEN_CITADEL_FULLSCREEN = '1'
before launching. Fullscreen at the desktop's 3440×1440 size and windowed
1920×1080 startup have both rendered successfully. F1 shows help; Escape
sends Back to the game except while the settings overlay is open. WASD movement
through the guest joystick callback is confirmed working. Rebindable movement
controls are available from F2; gamepad behavior remains future work. VSync is
on by default and can be changed with
the Game tab or OPEN_CITADEL_VSYNC=0.
The host reports average FPS and frame time to its console once per second,
after the initial scene frames.
Windows audio uses SDL output with mpg123 for MP3 music and SDL decoding for
WAV effects. LOADER_TRACE=1 prints audio-device and callback diagnostics.
Android OpenSL ES engine/output-mix/player calls and mono/stereo PCM16 buffer
queues are routed to the SDL mixer. The dummy-device test covers queue
completion, callback re-enqueue, state, clear, volume, and teardown. Actual
in-game use of this path has not yet been observed, so game-side effects remain
to be verified.
This repository does not distribute Epic Citadel APK/XAPK/OBB files,
libUnrealEngine3.so, or other proprietary game data. Supply your own Epic
Citadel 1.07 donor.
Import a donor with:
python3 tools/open_citadel_import.py EpicCitadel.xapk game/The importer validates the expected package/version/native hash and extracts the runtime layout while preserving the original UE3 OBB.
x86 host:
make open-citadel-x86ARMHF / PortMaster-oriented target:
make open-citadel-armhfGitHub Actions builds and smoke-tests the i386 compatibility host and packages a portable runtime used for private-donor testing.
The loader/JNI/thunk compatibility substrate originated in
xXJSONDeruloXx/i-port-katamari.
Open Citadel carries that substrate in-tree so it is independently buildable.