X-Wing Alliance Static Recompilation

August 24, 2026 · View on GitHub

A static recompilation of Star Wars: X-Wing Alliance (1999) by Totally Games / LucasArts, targeting modern Windows with native x86 execution and a modern graphics pipeline.

Project Status

PhaseStatusDescription
Phase 0CompleteBinary analysis, PE parsing, section mapping
Phase 1CompleteSafeDisc decryption, memory dump from runtime
Phase 2CompleteFunction discovery (2,674 functions, 443,224 instructions)
Phase 3Completex86-to-C code generation (2,701 functions, 606,424 lines of C)
Phase 4CompleteCompilation and linking (0 errors, 1 warning)
Phase 5CompleteRuntime execution — CRT init, import bridging, game startup
Phase 6CompleteWin32/DirectX HAL — COM mocks operational, main loop running
Phase 7CompleteD3D11 rendering backend — device, shaders, execute buffer parser, 2D surface pipeline
Phase 8CompleteFrontend + concourse rendering — pilot creation, the fully-rendered Azzameen concourse room (backdrop, Emkay droid, holo-globe, animated doors), mouse hover/click input
Phase 9CompleteMenu navigation + flight entry — pilot creation → concourse → Combat Simulator → skirmish setup → mission load → flight, with a real 20-flight-group mission and a crash-free flight loop presenting frames
Phase 10In ProgressVisible 3D flight — texture-mapped spacecraft rendered from the game's own OPT models, in a starfield, with perspective, per-face lighting and backface culling (see 3D Flight below). Remaining: transparency, full flight-group population, the engine's own camera, audio, game logic

Screenshots

Texture-mapped spacecraft in flight — rendered from the game's own OPT model data: real faces, per-face normals for lighting and backface culling, and the original 1999 textures point-sampled onto the hull. Every object in the world is drawn with its own model, loaded on demand from the game's craft list. The cyan flight HUD is the game's own:

Textured craft in flight

Getting there: one ship, then textures, then a populated world

Textured Y-wing

A Y-wing with its 1999 hull textures — the first craft rendered with real faces and materials.

Shaded ships

Correct geometry with flat shading, before textures — the same Y-wing plus a second craft.

First ships and starfield

Earlier: the first correctly-placed craft with a starfield, before the face-index stride was fixed.

First visible flight frame

And before that — the first flight frame that was not black at all.

The recompiled game boots, creates a pilot, and navigates the full frontend — each screen rendered from the original game's assets via the recompiled 2D pipeline and D3D11 backend.

Combat Simulator menu — reached by navigating from the concourse through the Combat Simulator door. Fully composited backdrop with live menu labels (highlighted Single Player, plus Multiplayer, Film Room, Back to Family Transport):

Combat Simulator menu

Concourse room — the Azzameen family-home concourse: room backdrop, the Emkay (MK-09) droid, the holographic galaxy globe, the animated doors, and the highlighted "Play Mission" label:

Concourse Room

Pilot creation — the entry screen: starfield nebula with lens flare, the Empire and Rebel faction crests, and the "Create a new pilot." / "Create Pilot" text and input field drawn from the fronttxt.txt string table:

Pilot creation

Earlier concourse captures

Concourse with Combat Simulator door

An earlier concourse capture showing the highlighted "Combat Simulator" door label. Reaching this required fixing a joystick-detection gate, the .lst resource-list parser, a test-after-reload codegen bug in the recompiler, and an unresolved jump-table in the .dat image decoder.

Concourse pilot-creation menu

Earlier pilot-creation capture — full background, holographic globe, Empire/Rebel faction symbols via RLE-decoded CBM surfaces.

Runtime Progress

The recompiled binary boots through full initialization, loads game assets, and renders the concourse menu:

  • Stable main loop: 29M+ PeekMessage calls per 15-second run, zero crashes
  • VC6 CRT initialization: heap, locks, stdio, atexit all functional
  • Native file I/O: All game CRT file functions replaced with host CRT (fopen/fgets/fclose/fseek/ftell/fread/fwrite) — original MSVC 6.0 FILE* struct layout incompatible with modern CRT
  • String tables loaded: strings.txt (184KB, ~1,500 game strings) and fronttxt.txt (47KB, menu labels) parsed correctly
  • Text rendering: GDI font glyph capture (.abp files), pixel-level text drawing on concourse surfaces
  • 39 .dat resource files + CBM concourse backgrounds loaded successfully
  • Window creation: "X-Wing Alliance" window at 1920x1080 (native resolution)
  • DirectX COM mocks: Full mock objects for IDirectDraw, IDirectDrawSurface, IDirect3D, IDirect3DDevice, IDirectInput, IDirectInputDevice, IDirectSound, IDirectSoundBuffer (178 vtable bridges)
  • 357 total bridges registered (179 Win32 API + 178 COM vtable)
  • 48 manual overrides including 6 callback functions missed by code generator
  • Callee-saved register protection: g_ebx/g_esi/g_edi automatically preserved across all calls
  • 6 SafeDisc-encrypted jump tables reconstructed with switch/goto
  • D3D11 rendering backend: Device (feature level 11.0), swap chain, HLSL shaders, execute buffer parser, texture manager, 2D surface blit (RGB565→BGRA8)
  • 2D rendering pipeline: CBM RLE decode → offscreen surface → BltFast → back buffer → D3D11 upload → present. ~32% surface coverage per frame
  • Source color keying: SetColorKey + BltFast DDBLTFAST_SRCCOLORKEY support for transparent sprite compositing
  • Concourse menu with text: Background nebula, holographic globe with lens flare, Empire/Rebel faction symbols, and text labels ("Create Pilot", "Fly Solo", etc.) all rendering correctly. 70% pixel coverage via RLE-decoded CBM surfaces with 16-bit palette lookup

Fixes Applied During Runtime Bringup

#FixImpact
1SafeDisc function stubs (4 encrypted functions)Bypass copy protection checks
2SafeDisc jump table reconstruction (6 switch statements)Correct control flow in CRT + game
3Null function pointer guard (ICALL(0) → no-op)Prevent crash on null callback
4Retry/fail dialog stub (sub_00433C50 → returns 'F')Avoid infinite retry loop
5SBH (small block heap) disabled, heap handle setUse system heap instead of VC6 SBH
636 CRT lock objects pre-initializedMulti-threaded CRT support
7_initstdio manual implementationstdin/stdout/stderr FILE structs
8VEH crash handler with ring buffer traceDiagnostics for runtime crashes
9TEST x,x; jge/jg/jle codegen fix (1,122 instances)Correct signed-negative checks
10repne scasb / repe cmpsb codegen fix (961 instances)Correct string operations
11TEST x,x; jbe/ja codegen fix (208 instances)Correct unsigned flag checks
12DEC x; jcc codegen fix (1,205 instances)Compare result vs 0, not 1
13COM mock infrastructure (IDirectDraw, IDirect3D, etc.)Game passes DirectX init
14Callee-saved register protection in RECOMP_CALLFixes systemic ebx/esi/edi corruption
156 callback function manual overridesMissed entry points for function pointers
16Watchdog uses TerminateProcess (avoids loader lock)Clean shutdown on hang
17D3D11 rendering backend (device, shaders, pipeline)Modern GPU rendering via execute buffer translation
18IDirect3DTexture mock with GetHandle/LoadTexture handle tracking for execute buffer rendering
19Native CRT file I/O (8 VFS functions replaced)Game's recompiled CRT was corrupted; host CRT works
20rep stosw implementation (32 instances)RLE pixel fill was silently no-op'd
21Display BPP global (0x9F700A) initializationAll 2D blit functions use this for 8/16bpp path selection
22test REG; mov REG; jcc codegen fixFlag evaluation used wrong (post-mov) register value
23BltFast surface copy implementationOffscreen→back buffer compositing for menu rendering
24Source color keying (SetColorKey + BltFast)Transparent sprite compositing via DDBLTFAST_SRCCOLORKEY
25Font file loading (.abp format)times0/2/5.abp loaded for concourse text rendering
26Inner timing loop + callback dispatchNested event loop at 24fps with registered menu callbacks
27AND reg, imm; jcc codegen fix (25 instances)RLE literal pixel copy was completely skipped — concourse went from 23% to 70% pixel coverage
28test reg; mov reg; jcc codegen fix (3 BPP instances)BPP dispatch used wrong register value after mov overwrote flags source
29_old_ variable scoping fix (lifter + generator)Block-scoped declarations broke across goto labels; moved to function scope
30Game CRT file I/O native replacements (7 functions)MSVC 6.0 FILE* layout differs from host CRT — ftell returned -1, crashing string parser
31String table loading (strings.txt + fronttxt.txt)184KB game strings + 47KB menu labels parsed and available for text rendering

3D Flight (Phase 10, in progress)

The recompiled game now flies: it drives the skirmish launch path into the flight engine, loads a real .tie mission, and renders texture-mapped craft in space.

How the geometry gets drawn. The lifted model renderer (sub_00442F70) turned out to be unreachable in this port — it is fed by a node-type dispatch that never sees a valid node, and nothing in the port had ever exercised that path (the concourse uses an entirely different renderer, so there was no working reference to diff against). Rather than keep chasing it, the port takes the standard recompilation fallback: replace the render path that cannot be lifted, using the game's own loaded data. A native D3D11 path walks each object's OPT model out of guest memory and submits it directly. It is env-gated (XWA_NATIVEDRAW); the default build is unchanged.

What renders today:

  • Craft built from the real OPT models — each object resolves its own model through the engine's own resource table, and every mesh root in that model is walked (a Y-wing is six).
  • Real faces, not a vertex-order wireframe: the OPT face record is decoded properly, including the quad/triangle distinction.
  • Per-face normals from the model, used for flat shading and backface culling.
  • The original textures, taken from the 16-bit surfaces the engine itself converted them into, and point-sampled — these are 8×8 to 128×64 textures stretched over whole hull panels, so bilinear filtering smears them into what looks like shiny lighting instead of panel detail.
  • Perspective projection matching the engine's own focal scale, with a depth buffer, plus a starfield so the scene reads as space.

The OPT runtime format, as decoded for this (byte-packed, 2-byte aligned — every node is +0x00 = 0, +0x04 = type, +0x08 = child count, +0x0C = child array, +0x10 = count, +0x14 = inline data):

NodeMeaningLayout notes
3Mesh verticescount xyz float triples
13Texture coordinates(u,v) float pairs
11Vertex normalsxyz float triples
1Face data+0x10 = face count, then 16 int32 per face — vertex[4], edge[4], texcoord[4], normal[4] (-1 = triangle) — then a 3-float face normal each, then 6 more floats: 100 bytes per face
21Face groupingchildren are LODs; each LOD's children are texture/faces pairs
27Texture (in memory)the file's type 20, rewritten on load; its descriptor leads to a converted 16-bit surface
24Node referenceindirection to a texture defined elsewhere

A loaded model image begins at file offset 8 (the loader overwrites the file's global-pointer field with the load address), with the mesh-root count at img+6 and the root pointer array at img+0x0E. Reading that image as if it were a node is what made earlier sessions report "garbage models": the value they read as a node type was the file's size field.

Populating the world. Getting more than a couple of craft on screen took three separate fixes:

  • The object walk was looking at the wrong slice of the table. Objects live at MEM32(0x7B33C4) + i*0x27 and the render walk runs [MEM32(0x7CA3B4), MEM32(0x917E64)) — a partition window that sub_004154A0 computes from a region index. Under the force-launch path that index is garbage, so the window landed on empty slots (632..760) while the mission's real objects sat at 0..6. The walk already skips dead entries, so it is widened to the whole table.
  • The engine's own dispatch only handles categories 8..15, and the mission's craft are categories 1/3/5/17, drawn by a different path entirely — so hooking after that switch only ever saw a fraction of the world. The native draw now hooks right after the "is this object alive" test.
  • Most craft types had no model loaded at all. WORD[0x7CA6E0 + type*2] was 0 for everything but the one or two types the player path happened to load, because the force-launch path never runs the mission's model preload. FLIGHTMODELS/SPACECRAFT0.LST is the game's own list of OPT paths in craft-type order — exactly the index an object's +0x00 field holds — so any missing model is loaded on demand through sub_004CC940 (the same path the concourse uses) and registered in the type map. 25 models load this way in a typical run.

Honest limits. Alpha is forced opaque, so cockpit glass and engine glow have no transparency yet. Arrivals: the cause is now known, confirmed against the retail game. The object/craft table is partitioned by mission region, and this mission has two of them — 18 flight groups in region 0 (the player's own Sabra, Xi, Selu, Azzameen, Norge) and 14 in region 1 (Harlequin Station, Pi, Chi, Psi). The partition index is a per-flight-group byte at FGrecord + 0xDAA, and sub_00417580 applies it once per flight group, so the last one wins. Retail ends up in region 0 because only region-0 groups are active at mission start; the recompiled game's forced launch has region-1 groups active too, so it lands in region 1 — where the craft slice is empty (base == count), leaving the create loop nowhere to put an arriving craft.

Single-player flight does not use DirectPlay at all. Call counters inside the COM mocks show Send, SendEx, Receive and GetMessageCount all at zero during flight, with the loopback session active — so the sim tick does not arrive as a network message on this path, and the engine's sub_004F6510 (whose only callers are the DirectPlay handler and the outer frame function) never runs. An older code comment describing that handler as the thing that ticks the simulation is describing the multiplayer route.

The flight-group activation walk that would arrive new craft, sub_00417580, is called only from the outer frame function — the one whose per-frame body this port never re-enters — so arrivals are evaluated once at setup and never again. Driving that walk from the flight loop does create craft, but they are exact duplicates: the same groups at identical coordinates in fresh slots on every pass. Retail re-runs the same walk every frame without duplicating, so it associates each flight group with the object it already created; the recompiled game's hand-built world has no such association. That is the same wall every arrival route reaches, and the reason the honest next move is to make the mission load through the engine's own path rather than to extend the forced-launch shortcuts further.

The simulation that does run lives under sub_004598E0, the per-frame update called by the flight loop: craft measurably move under it. Finding where mission logic and arrival evaluation sit inside that subtree is the remaining gap between "craft move" and "flight groups arrive".

The real flight loop is inside sub_00457C20: a frame-timer accumulator that waits for eight ticks, then calls the per-frame update sub_004598E0, then stamps the sim's last-simulated time to the frame timer and repeats. That stamp is why ticks injected from outside can never win — the loop marks the sim as current every frame regardless of whether it ran.

The activation loop itself is sub_00417580: it walks the flight groups and, for each one passing a runtime "already handled" test, sets the partition to that group's region and calls sub_004195E0 to activate it — so the partition flips back and forth as it walks and ends wherever the last group left it. That makes the failure exact: region 0's craft slice is [0,252) and region 1's is [380,632); the mission's real craft sit at indices 0–6 in region 0, but the forced launch ends in region 1 and has filled [380,399] with fabricated entries. The create loop iterates region 1's slice, finds junk, creates nothing, and never visits the real craft.

The partition cannot simply be swapped at a choke point, and that is measured rather than assumed: forcing it at the create loop's entry reads past the object table (the loop's bounds then disagree with the table allocated for the other layout), and forcing every call faults through a stub scene pointer instead. It has to be consistent from world-build onward. The single change that would fix this and the missing sim tick is getting sub_0050FCB0's per-frame body to run — it sets the partition from the player's own region and calls the sim update, and the forced launch reaches neither.

tools/peek_partition.py reads these globals out of the running retail game (read-only, no debugger, so it does not trip the SteamStub anti-debug). Retail in flight measures partition 0, base 0, count 252, walk [252,380), with every live object inside [0,252). Re-applying the player's region in the port reproduces those numbers exactly — but the engine re-partitions back continuously, so the fix is upstream: stop the forced launch from marking region-1 groups active, rather than clamping the partition.

Earlier framing of this problem

Arrivals are the current frontier, and are not working yet. The mission's 32 flight groups are parsed and present in memory (the table sits at 0x80DC80 with a 0xE42 stride, names at offset 0), but only the seven objects the initial build creates ever exist. The reason is now measured rather than guessed: the engine's frame function sub_0050FCB0 calls the per-frame sim update sub_004F6510(tick), but under force-launch it never reaches that call — it enters, stops at block 0x510668, and flight itself runs inside that call, down sub_00457C20 -> sub_004596C0. So the update can only be driven manually, which XWA_SIMDRIVE=N now does from the present path (N steps per frame, since flight presents only about twenty frames per run). Its gate turns out to be time-based: the update returns immediately unless the tick it is handed is past the time it last simulated, which it keeps at 0x8B94D4. Driving it from that value makes it do real work — the pending-create cursor advances from 8 to 236 of 632 — but no new craft appear yet, so a further gate remains in the create path itself.

Objects with junk coordinates are filtered out — the forced-launch path fabricates flight-group entries whose position fields hold float bit patterns. More importantly, the mission's own spawn logic is not running: this mission defines 32 flight groups, and the world contains about ten real objects, so what renders is everything that exists, not everything the mission calls for. Arrivals over time are the next piece. The camera uses the engine's real orientation, but in this force-built world the wingmen sit 40–80° off that axis, so the screenshots use a spectator camera (XWA_NLOOKAT) that aims at the nearest craft. The model→view transform is the port's own, not the engine's.

Cross-validated against X-Wing vs. TIE Fighter. XWA's engine is the successor to the 1997 X-Wing vs. TIE Fighter engine. Running the same unmodified toolchain on Z_XVT__.EXE recompiles it cleanly (1,787 functions), which is a useful check on the toolchain itself.

Binary Analysis

PropertyValue
Targetxwingalliance.exe (2.44 MB)
CompilerVisual C++ 6.0 (Visual Studio 98)
Build Date1999-06-15
Architecturex86-32, PE32, fixed base 0x00400000
Code (.text)0x00401000 - 0x005A8B20 (1.7 MB, 1,735,456 bytes)
Read-only Data (.rdata)0x005A9000 - 0x005ADA24 (18 KB)
Read/Write Data (.data)0x005AE000 - 0x00B0F974 (5.4 MB)
Copy ProtectionSafeDisc v1 (.bind section, runtime decryption)
ASLRNone (fixed image base, no relocations)

Recompilation Statistics

MetricValue
Functions recompiled2,702
Total lines of C606,424
Generated code size33.4 MB
Source files6 + header + dispatch table
Code generation time~7 seconds
Compilation errors0
Link errors0
Warnings1 (harmless shift count)

DirectX API Surface

The game uses DirectX 5/6 era APIs:

APIDLLUsage
DirectDrawddraw.dllSurface management, 2D rendering
Direct3D Immediate Mode(via DirectDraw)3D rendering with execute buffers (pre-DrawPrimitive)
DirectSounddsound.dllSound effects and audio
DirectInputdinput.dllJoystick and keyboard input
DirectPlaydplayx.dllMultiplayer networking
iMUSE(statically linked)LucasArts interactive music engine
SMUSHtgsmush.dllFMV video playback (5 exports)

Import Summary

DLLFunctionsPurpose
KERNEL32.dll100Core Win32 APIs
USER32.dll34Window management, input
GDI32.dll14Font/text rendering
WINMM.dll13Joystick, timers, CD audio
tgsmush.dll5SMUSH video playback
ADVAPI32.dll4Registry (settings)
ole32.dll3COM initialization
DDRAW.dll2DirectDraw
DINPUT.dll1DirectInput
DSOUND.dll1DirectSound
DPLAYX.dll1DirectPlay (multiplayer)
SHELL32.dll1ShellExecute

Architecture

xwa-recomp/
├── tools/                      # Python toolchain
│   ├── pe_analyze.py           # PE header/section/import parser
│   ├── disasm.py               # Capstone-based x86 disassembler + function finder
│   ├── lifter.py               # x86 instruction → C code lifter
│   ├── generate.py             # Linear sweep code generator (fast)
│   ├── translator.py           # Full pipeline orchestrator (legacy)
│   ├── dump_memory.py          # SafeDisc runtime decryption dumper
│   ├── fix_test_cond.py        # Fix TEST codegen bug (same-register CMP → TEST_NS/G/LE)
│   ├── fix_test_flags.py       # Fix TEST+JBE/JA codegen bug in generated output
│   ├── fix_string_ops.py       # Fix repne scasb / repe cmpsb codegen
│   ├── disasm_jmptbl.py        # Reconstruct unresolved switch/jump tables
│   ├── insert_func.py          # Lift a single missing function and splice it into the gen files
│   ├── read_real.py / poll_real.py / dbg_real.py  # Live guest-memory read/poll/debug helpers
│   ├── regen_0000.py           # Targeted regeneration of recomp_0000.c + dispatch/header
│   └── relift_func.py          # Re-lift a single function in place (codegen-bug iteration)
├── src/
│   ├── game/
│   │   ├── main.c              # Entry point, VEH handler, memory setup, manual overrides
│   │   ├── imports.c           # Win32/DirectX import bridges (179 functions)
│   │   ├── com_mocks.c         # COM mock objects (DirectDraw, Direct3D, DirectInput, etc.)
│   │   ├── com_mocks.h         # COM mock types and creation APIs
│   │   └── recomp/
│   │       ├── recomp_types.h  # Register model, memory macros, dispatch
│   │       └── gen/            # Auto-generated code (gitignored)
│   └── hal/
│       ├── d3d11_renderer.c    # D3D11 backend: device, execute buffer parser, textures
│       ├── d3d11_renderer.h    # D3D5 structures, render state enums, renderer API
│       └── shaders.h           # HLSL shader source (compiled at runtime)
├── config/
│   ├── pe_analysis.json        # PE metadata
│   └── functions.json          # Function list with addresses/sizes
├── CMakeLists.txt              # MSVC 2022 x86 build
├── CLAUDE.md                   # AI assistant project context
└── README.md                   # This file

Recompilation Approach

Global Register Model

x86 registers are mapped to C global variables following the burnout3 pattern:

/* Volatile (caller-saved) */
uint32_t g_eax, g_ecx, g_edx, g_esp;

/* Callee-saved (auto-preserved by RECOMP_CALL/RECOMP_ICALL macros) */
uint32_t g_ebx, g_esi, g_edi;

/* ebp is local per-function (FPO) */

Callee-saved registers (ebx, esi, edi) are automatically saved/restored around every RECOMP_CALL and RECOMP_ICALL, enforcing the x86 calling convention even when recompiled functions have stack imbalances.

Memory Access

Original data sections are mapped at their original virtual addresses. Memory access uses macros that translate through a base offset:

#define MEM32(addr) (*(volatile uint32_t *)ADDR(addr))

Indirect Call Dispatch

Three-tier lookup for indirect calls (vtables, function pointers):

  1. Manual overrides — hand-implemented replacements
  2. Auto dispatch table — binary search over 2,674 recompiled functions
  3. Import bridges — Win32/DirectX API translations

Condition Generation

Flags are pattern-matched from setter (cmp/test/sub) to consumer (jcc/setcc/cmovcc):

/* cmp eax, 5; jb target → */
if (CMP_B(eax, 5u)) goto L_target;

Build Requirements

  • CMake 3.20+
  • Visual Studio 2022 (MSVC, x86/Win32 target)
  • Python 3.10+ with capstone, pefile

Building

# Generate recompiled code (requires decrypted binary)
python -m tools config/xwingalliance_decrypted.exe --all -o src/game/recomp/gen

# Configure and build
cmake -B build -G "Visual Studio 17 2022" -A Win32
cmake --build build --config Release

SafeDisc Note

The Steam distribution of X-Wing Alliance encrypts the .text section with SafeDisc v1. The code is decrypted at runtime by the .bind section stub. Use tools/dump_memory.py to launch the game via Steam and dump the decrypted code:

# Launch game through Steam first, then:
python tools/dump_memory.py --pid <PID> "path/to/xwingalliance.exe" config/xwingalliance_decrypted.exe

Known Engine Details

  • Developer: Totally Games (dev path: K:\XWA\dev\)
  • Debug system: Deus debugging enabled, type %d, mask 0x%.8x
  • Console: XWing Alliance Console (debug build feature)
  • Sound engine: Aldraw_Init_Sound_Engine
  • 3D renderer: std3D_CacheTextureSurface, D3D execute buffer model
  • Hardware detection: 3dfx Voodoo / Glide checks
  • Registry: SOFTWARE\LucasArts Entertainment Company LLC\X-Wing Alliance\V2.0
  • Command line: XwingAlliance.exe %d skipintro

Generated-code hooks

src/game/recomp/gen/ is produced from the PE and is gitignored, so the env-gated hooks added to it do not survive a regeneration. The ones worth keeping live in tools/hooks/*.hook, anchored to a line of generated code rather than a line number:

python -m tools.apply_hooks --check   # report status, change nothing
python -m tools.apply_hooks           # re-apply anything missing

tools/run_tests.sh 6 fails if an anchor stops matching, which is the signal that the generator's output moved and a hook needs re-deriving.

License

The code in this repository is released under the MIT License.

That covers this project's own source — the recompilation toolchain, the HAL, the COM mocks and the native render path. It does not cover Star Wars: X-Wing Alliance itself: the game's binary, assets and data remain the property of their respective owners and are not distributed here.

This project is for game preservation purposes. You must own a legal copy of Star Wars: X-Wing Alliance to use this tool. No copyrighted game assets are included in this repository.

Part of the sp00nznet recompilation collection. See also:

  • burnout3 — Original Xbox x86 recomp (reference for x86-to-C lifter)
  • bw — Black & White Win32 recomp (reference for Win32 game patterns)
  • civ — Civilization DOS recomp (reference for 16-bit x86 lifting)