NFL Head Coach 09
OriginXbox 360
TechnologyStatic recompilation (native C/C++)
Available forWindows
View official projectAbout the project
NFL Head Coach 09 es un videojuego de acción y aventura publicado por EA Sports en 2008.
Official project information
NFL Head Coach 09 — static recompilation
Version 0.8.2
A native Windows port of NFL Head Coach 09 (Xbox 360, title ID 45410841), built by
statically recompiling the game's PowerPC executable to C++ with
XenonRecomp and running it on a host layer
that replaces the Xbox kernel, XAM, graphics, audio and input.
No game data from the disc is included. You need your own copy of the disc, and nothing under
iso/, build/ or ppc/ may be redistributed. The hc27 folder holds the modern rosters and
images HC27.exe plays with (see below), which may be redistributed.
Installing
Download HC09Setup.exe from the releases page
and run it. The wizard:
- checks for the build tools: Visual Studio 2022 or later (Community is fine) with Desktop development with C++, C++ Clang Compiler for Windows and C++ CMake tools for Windows, and Git for Windows;
- asks for an install folder (a short path such as
C:\Games\HC09Recompis best) and your NFL Head Coach 09 disc image (.iso), which it checks is the right game; - offers automatic update checks: when enabled, the game checks this repository's latest release each time it starts and, if there is a newer one, asks before downloading and rebuilding;
- downloads the source, XenonRecomp and FFmpeg, imports the game's executable from your disc
image, recompiles it and builds
HC09.exe(about 1 GB of downloads and 10 to 20 minutes), then adds a Start menu shortcut.
The game is built on your PC because the recompiled executable is derived from the game's own code,
which this project does not distribute. The disc image stays where it is: the game reads its data
from it while you play. Settings live in hc09.ini in the install folder (IsoPath, AutoUpdate).
Running setup again and choosing the same folder updates that installation to the latest release
(keeping your saves), and is also how to change the disc image; a development clone on a git branch
is built as it is instead. The setup log is %TEMP%\HC09Setup.log.
Playing with today's rosters (HC27.exe)
Setup builds a second game next to the original: HC27.exe, NFL Head Coach 09 with today's
league. It has the current players with their ratings and contracts, the current head coaches,
real player and coach portraits, updated team names and logos, and seasons that start in 2026.
Everything it needs comes with the download (the hc27 folder in the install folder), so there
is nothing to import or set up.
- Open your install folder (for example
C:\Games\HC09Recomp) and go intobuild\hc09. - Double-click HC27.exe. To start it more easily later, right-click it and choose Send to > Desktop (create shortcut). The Start menu shortcut setup made opens the original game, HC09.exe.
- Start a new career or a Coach Now game: the new rosters are already in place.
Good to know:
- HC27 uses the same disc image as the original game, and the two keep separate saves
(HC27's are in
build\userdata-hc27), so a career in one never touches the other. - The rosters are up to date through week 8 of the 2026 season: trades, waiver claims, signings and releases since the ratings were taken are applied. Players the ratings don't have (most practice squad call-ups) are missing.
- Updates (automatic, or running setup again) bring new versions of the rosters and images too.
- If HC27 says the disc's default roster is not where it expects it, your disc image is a different version of the game from the one the rosters were made from; HC09.exe still works.
- Draft picks get today's rookie contracts: when a career's draft ends, every pick is signed to the four-year deal his pick number pays, with no negotiating with agents. Rosters can then run past 53 players until the training camp cut day, as NFL offseason rosters do.
- First-round picks come with the fifth-year option. In the off-season before a first-round pick's
fourth year, he appears on the restricted free agent tender screen with two choices instead of
the tenders: decline the option, or exercise it for a fifth year at his position's option salary
(the average of the 3rd to 25th highest salaries at the position; the Pro Bowl tiers are not
applied). The computer's teams decide on their own. A career started before this release needs
its players' draft rounds set once:
python tools/roster_import/draft_rounds.py --save build/userdata-hc27/content/00000001/CAREER-<name>/CAREER-<name>. - The original game is unchanged: HC09.exe ignores the
hc27folder.
Status
| Stage | State |
|---|---|
| Disc extraction, XEX decryption/decompression | done |
| Recompiler config (ABI helpers, setjmp/longjmp, jump tables, leaf-function bounds, data skips) | done — recompiler log is clean |
| Missing PPC/VMX instructions in XenonRecomp (~40 opcodes), guarded integer division | implemented in patches/, not yet verified against hardware tests |
| Host executable builds and links | done |
| Memory, Rtl, TLS, time, strings/printf, XEX header, kernel variables | implemented |
| Threads, waits, events, semaphores, mutants, timers | implemented (no APCs, no forced suspend) |
File system (disc read straight from the ISO, writable build/userdata) | implemented |
| GPU command processor (ring buffer, fences, scratch write-back, per-CPU interrupts, vblank, swaps, shader/constant loads, predicated tiling) | implemented |
| Physical memory windows (0xA0000000 / 0xC0000000 / 0xE0000000 aliased) | implemented (0xE0000000's 4 KiB offset not reproduced) |
| Xenos shader microcode → HLSL translator | implemented; all 148 shaders captured from menus and gameplay translate and compile. Compiled with DXC (shader model 6.0, dxcompiler.dll and dxil.dll copied from the Windows SDK at build time) in the background and cached in build/shadercache: the 148 take about 5 s in all, at most 0.12 s each, against 52 s and up to 6 s with D3DCompile, which is used when DXC is missing or the GPU lacks shader model 6.0 |
| D3D12 renderer | draws, depth/stencil, culling, blending, alpha test, textures (linear/tiled, full mip chains incl. packed mip tails, render-to-texture mips, anisotropic filtering, border colours, cube maps, volumes, stacked arrays with normalized layer coordinates, common formats, resolved depth), resolves (incl. tiled colour and depth strips and the red/blue copy swap), present, fast clears through multisampled EDRAM aliases, 4x MSAA for the multisampled surfaces the 3D scenes render to (HC09_NO_MSAA=1 turns it off) |
| Vulkan renderer (default) | from NFL Head Coach 09 for PC (src/vk): draws the game's Direct3D calls with Vulkan; up to 240 fps (FrameRate) and 4x resolution (Scale); falls back to D3D12 without Vulkan 1.3 |
| Audio output | WASAPI shared mode, 5.1 mix downmixed to stereo |
| XMA decoder | FFmpeg's XMA2 decoder (LGPL build in thirdparty/ffmpeg), 16-bit PCM into the title's output rings |
| Music player (XMP) | no user soundtrack: always idle, controls accepted |
| Controller input (XInput) | implemented, plus a keyboard/scripted virtual pad for player 1 |
| Saves (XAM content packages) | implemented: packages are folders under build/userdata/content/<type>/ |
| Remaining kernel/XAM imports | 36 still stubbed (APCs, voice chat, Live UI, marketplace); none are called in normal play. System message boxes answer with their focused button at once |
The game boots, plays its intro movie in a window, loads, renders the title screen, and
reaches its menus (favourite-team prompt, main menu with its 3D background, team select) with
controller or keyboard input, and plays a game: weekly coaching agenda, coin toss, and the
in-game play-calling screen, where called plays run and the clock and downs advance. Career
Mode (new career, season goals, the career hub, training camp, gameplan, then a preseason game
in another stadium), the Xbox Live, My Coach and Media World menus, and a 21-minute unattended
match (score and quarters advancing, memory flat) all run without errors. Menus and in-game screens run at about 30-35 fps with music and sound effects, and the
profile saves (Quick Save, LS) to build/userdata/content and loads on the next start.
Controls
Player 1 is always connected. A real controller works; the keyboard also works while the game window has focus: Enter = Start, Esc = Back, Z/Space = A, X = B, C = X, V = Y, Q/E = LB/RB, 1/3 = LT/RT, F/H = left/right stick click, arrows = D-pad, WASD = left stick, IJKL = right stick.
F11 or Alt+Enter toggles borderless fullscreen. The window can be resized freely; the picture keeps its 16:9 shape, with black bars where the window's shape differs.
With the D3D12 renderer, set HC09_RESOLUTION_SCALE=2 (up to 4) renders at that multiple of the game's 1280x720, e.g.
2560x1440, for a sharper picture in fullscreen on a high-resolution display. Menus and gameplay
held 30-35 fps at 2x in testing; 1x is the default.
With both renderers (see below), the 3D scenes get two effects the original does not have: ambient occlusion (soft shading where
surfaces meet, such as a player's feet on the field or the folds of a coach's jacket) and
reflections on the field. The turf is no mirror, so a reflection is drawn dark and with strong
contrast, like a shadow cast by the player near where he stands, longest at low camera angles. Both work from the scene's depth buffer, so they cost little and need no change to the
game's own shaders. Set their strength in hc09.ini (0 turns an effect off, 1 is the default):
[Graphics]
AmbientOcclusion=1
Reflections=1
set HC09_SSAO=<strength> and set HC09_SSR=<strength> override the file for one run. The Vulkan renderer
also has post-processing (bloom, colour grading and depth of field; see its settings below).
Renderers: Vulkan (60 fps, 1440p) and D3D12
The game is drawn by a renderer built on Vulkan (it comes from the NFL Head Coach 09 for PC
project; see THIRD_PARTY_NOTICES.md), which can run the game at 60 frames per second and at
higher resolutions. By default it keeps the original game's pace (about 35 fps) at 720p. To get
60 fps at 1440p, open hc09.ini in your install folder with Notepad and add:
[Graphics]
FrameRate=60
Scale=2
(If the file already has a [Graphics] section, add the two lines under it.) The settings:
FrameRate: frames per second, 30 to 240. Leave it out (or use 35) for the game's own pace. The game's clock runs in real time at any setting: plays and game time are not sped up.Scale: the picture is drawn at this multiple of the game's 1280x720: 1 = 720p, 2 = 1440p, 3 = 4K. Higher looks sharper in fullscreen and needs a faster graphics card.VSync=offpresents without waiting for the display (may tear); on by default.Fullscreen=onstarts in borderless fullscreen (F11 or Alt+Enter still toggles it).MSAA: anti-aliasing,1as the game asks (4x in 3D scenes, the default),2at most 2x,0off. Lower settings help slower cards atScale=3.GPU: on a PC with two graphics chips,discrete,integrated, or part of the card's name.Anisotropy: texture filtering that keeps field lines and yard numbers sharp at the broadcast camera's low angles,16(the default) down to2, oroffas the original game draws them.Upscaling: how the picture is fitted to the window.fsr(the default) enlarges it with AMD FidelityFX Super Resolution 1, sharper than plain stretching on a screen larger than the picture (720p on a 1440p or 4K screen), and shrinks a larger picture (Scale=2on a 1080p screen) by averaging all the pixels each screen pixel covers.bilinearstretches it as before.Sharpening:0(off) to100,75by default; applied whenever the picture is scaled.SmoothMotion:onby default. The game draws players and the camera at the full frame rate, but the ball and a few objects only move 30 times a second; this draws them in even steps between those moves (about 1/30 s behind).offshows them as the game moves them.HDTextures:onbuilds an HD texture pack on your PC as you play. The game saves each texture it loads intobuild\hashed, and when you close it,HC09HDPack.exeupscales the new ones 4x with Real-ESRGAN on your graphics card and saves them intextures\hd, where the game loads them from the next start. The first run downloads the upscaler (45 MB) intothirdparty\realesrgan. Data textures (normal maps, masks) are left as the game has them. The pack is made from your own copy of the game and stays on your PC.offby default.Bloom,ColorGradingandDepthOfField: post-processing of the 3D scenes (never the menus or on-field graphics), each a strength where0is off and1the default. Bloom makes stadium lights and other near-white highlights glow softly. Colour grading adds a filmic roll-off into white (so the bloom never clips), a little contrast and colour, and a faint vignette. Depth of field softens what lies beyond the middle of the picture, as a TV camera's long lens does, focusing as the camera moves; it is0(off) by default, since it also softens the far side of the field during play. TryDepthOfField=1.HDR:onshows the game in HDR on an HDR monitor (turn HDR on in Windows' display settings first; without it the game stays in SDR). The picture keeps its look, with SDR white atHDRPaperWhitenits (200by default; raise it if the game looks dim next to the desktop), while the 3D scene's highlights (stadium lights, sky, sunlit white uniforms, field paint) go brighter, up toHDRPeaknits (1000by default; set it to your monitor's peak brightness). The menus and on-field graphics stay at paper white.offby default.Crowd:onby default. The game fills the stands with cards of 16 animated fans, so every fan of a kind moves in step with the others. This gives each seat its own moment of the fans' last second of movement, and mirrors and shades some of them, so the stands look less like a repeating pattern.offdraws the crowd as the game does.AspectRatio: for ultrawide screens,21:9,32:9, any<width>:<height>, orautofor your screen's shape;16:9(the game's own) by default. The 3D scenes fill the wider screen and show more at the sides, while the menus and on-field graphics keep their shape in the middle. Things just past the original picture's sides can occasionally pop in late, since the game still decides what to draw for 16:9.
The Vulkan renderer needs a graphics card and driver with Vulkan 1.3 (any card from the last
several years). On a PC without one, the game uses the older D3D12 renderer instead, which runs at
the game's own pace. To use the D3D12 renderer anyway, add Renderer=d3d12 under
[Graphics]. HC27's rosters, portraits and logos work with both.
set HC09_RENDERER=d3d12 (or vulkan) picks a renderer for one run, and HC09_FRAMERATE,
HC09_SCALE, HC09_VSYNC, HC09_FULLSCREEN, HC09_NATIVE_MSAA, HC09_GPU, HC09_ANISOTROPY, HC09_UPSCALE, HC09_SHARPEN, HC09_SMOOTH_MOTION and HC09_ASPECT (width / height) override the
settings above the same way. HC09_RESOLUTION_SCALE is the D3D12 renderer's resolution.
Rosters and replacement images
tools/roster_import builds a roster from a modern ratings export and current coaching staffs
(see its README), with player and coach portraits.
HC27.exe, built alongside HC09.exe, is the game with that roster: it reads hc27\ROSTER in
place of the disc's default roster (so new careers and the roster screens start with it, nothing
to load), lays the patches in hc27\disc (<archive>@<hex offset>.bin, such as the league's
finances that league_salaries.py writes) over the disc's files, draws the images in
hc27\textures before those in textures, and keeps its saves apart in build\userdata-hc27.
Its seasons start in 2026: the game's calendar still runs from 2008, so every year it shows (the
clipboard's date, the salary cap years, draft classes, important dates) is 18 years later. Without hc27\ROSTER it reports what to build and exits.
HC09.exe is unaffected by the hc27 folder.
The front end's "swappable" images (player and coach portraits, team logos, helmets and the
other headcoach/swappables/... libraries) can be replaced without touching the disc: a PNG at
textures/swappables/<library>/<id>.png in the install folder (for HC27, also
hc27/textures/swappables/...) is drawn instead of image <id> of that library, at its own resolution (images more than twice the game's size are halved until
they fit, since these are drawn without mip maps). A player's portrait id is PSXP in the
roster. HC09_NO_REPLACEMENTS=1 draws the game's own images; HC09_TRACE_SWAPPABLES=1 logs
each image the game loads ([swappable] PlayerPortraits/1650 -> texture 0x161CF000).
Any other 2D texture (a logo in a menu, a helmet decal, a sign at the career campus) can be
replaced by its contents: a PNG at textures/hashed/<content hash>.png (for HC27, also
hc27/textures/hashed/...) is drawn wherever the game uses that texture, with mip maps built for
it. HC09_DUMP_HASHED=1 saves every texture the game draws, once, as
build/hashed/<content hash>.png, in the colours it is drawn with, so the one to replace can be
found and edited; the log names the swappable image a dumped texture holds, if any. Both kinds of
replacement image hold the colours as drawn (a texture's channel order does not matter).
HC27 can also rename a team (TEAM_RENAMES in tools/roster_import/import_roster.py; the
Redskins are the Commanders): the game names a team's uniforms, helmets and career campus after
its name in the roster and finds them by a hash of that name, so HC27 hashes the old name for
them (kRenamedTeams in src/common/edition.cpp).
Fixes to the original game
- Injured players heal after Play Creator again. In the original game, opening Play Creator
in a career left the game in its practice mode after Play Creator closed, and the
injury system does nothing in practice mode: from then on no injury in the league counted down
or healed, and games recorded no new ones. The mode in force before Play Creator is now restored
when it closes (
src/game_fixes.cpp). A career saved while affected is repaired by opening and closing Play Creator once.
Known issues
- Rendering: real MSAA only for multisampled surfaces used by predicated tiling (the 3D
scenes); other multisampled surfaces render single-sampled. EDRAM aliasing is reproduced only
for clear rectangles drawn into a multisampled surface that covers a single-sampled one.
Alpha-to-coverage (
RB_COLORCONTROLbit 4) is not implemented; the title does not enable it in the menus or in gameplay. - Predicated tiling is approximated for screen-sized targets: the first tile pass renders the whole frame, later passes skip their draws, and tile clears are skipped (the per-draw bin masks the GPU derives from screen-extent queries are not reproduced).
- The video board and LED ribbon boards at the Colts' stadium (Lucas Oil) show a flat, flickering
white/cyan slab. This is the game's own data, not the port: the
JUMBOTRONeffects reference source-art paths that were never exported (.../textures/stadiums/scoreboards/jumbotron_COLOR.dds,jumbo_GRIME_color4.dds,jumbo_ads_color_COL, ...), so the engine substitutes its greyMissing.ddsfor the LED pixel and grime masks, and the shader's 9x brightness then saturates the board.HC09_ENGINE_LOG=1shows the engine's own "Asset missing, using default" messages for them. Stadiums whose boards use other materials (e.g. Texas Stadium) render normally.
Requirements
- Visual Studio 2022 with Desktop development with C++, C++ Clang Compiler for Windows and MSBuild support for LLVM (clang-cl)
- CMake ≥ 3.20 and Ninja
- Python 3.12 (a project venv is used:
python -m venv .venvthen.venv\Scripts\pip install pycryptodome capstone) - FFmpeg 8.1 LGPL shared build (BtbN
ffmpeg-n8.1-latest-win64-lgpl-shared-8.1.zip) extracted tothirdparty/ffmpeg(providesinclude/,lib/and the DLLs copied next toHC09.exe)
Pipeline
Run from the repository root. Put the disc image at iso/NFL Head Coach 09.iso.
# 1. Build XenonRecomp with the HC09 instruction patch
git clone --recursive https://github.com/hedge-dev/XenonRecomp thirdparty/XenonRecomp
git -C thirdparty/XenonRecomp checkout ddd128b
git -C thirdparty/XenonRecomp apply ../../patches/xenonrecomp.patch
tools\build_xenonrecomp.bat
# 2. Extract and decrypt the executable (the XEX key is read from the XenonRecomp checkout)
python -I tools/xdvdfs.py extract "iso/NFL Head Coach 09.iso" build/extracted default.xex
.venv/Scripts/python -I tools/xexdecrypt.py build/extracted/default.xex build/default.pe
.venv/Scripts/python -I tools/xeximports.py build/extracted/default.xex build/default.pe --csv build/imports.csv
# 3. Analyse and generate the recompiler config
build/xenonrecomp/XenonAnalyse/XenonAnalyse.exe build/extracted/default.xex config/HC09_switch_tables.toml
.venv/Scripts/python -I tools/gen_functions.py build/default.pe config/HC09_switch_tables.toml config/HC09_functions.toml build/imports.csv
# Jump tables XenonAnalyse misses (dispatches without a bounds check): check, then add
.venv/Scripts/python -I tools/check_switches.py build/default.pe config/HC09_switch_tables.toml
.venv/Scripts/python -I tools/add_missing_switches.py build/default.pe config/HC09_switch_tables.toml 0x824374DC 0x8254D164 0x82596A14 0x825B1874 0x82E575D4
.venv/Scripts/python -I tools/make_config.py
# 4. Recompile to C++ (into ppc/)
mkdir ppc
build/xenonrecomp/XenonRecomp/XenonRecomp.exe config/HC09.toml thirdparty/XenonRecomp/XenonUtils/ppc_context.h
# 5. Build and run
.venv/Scripts/python -I tools/gen_import_stubs.py build/imports.csv
tools\build.bat
build\hc09\HC09.exe
HC09.exe finds the project folder itself (it looks upward from its own location for
build\default.pe), so it can be double-clicked in build\hc09 or started from a shortcut.
If something it needs is missing, it says so in a message box. It takes the disc image from its
command line (HC09.exe [image.pe] [default.xex] [disc.iso]), else from hc09.ini, else from
iso/NFL Head Coach 09.iso.
The XenonRecomp commit is pinned in patches/xenonrecomp.commit (setup reads it from there).
build\installer\HC09Setup.exe --import <disc.iso> <project folder> does step 2's extraction and
decryption without Python (the configs and src/kernel/import_slots.inc from steps 2 and 3 are
committed, so the setup wizard skips those steps).
Releases
Setup and the in-game update check use the repository's latest GitHub release: tag it v<version>
with the version in CMakeLists.txt (project(HC09Recomp VERSION ...)), and attach
HC09Setup.exe, which contains no game code. Build it standalone with either compiler:
cmake -S tools/installer -B build/setup -G Ninja -DCMAKE_BUILD_TYPE=Release
cmake --build build/setup # -> build/installer/HC09Setup.exe
Installed copies offer the update the next time they start; accepting it checks out the release tag, re-patches XenonRecomp if the pin changed, and recompiles and rebuilds the game.
Layout
| Path | Contents |
|---|---|
config/HC09.base.toml | Hand-written recompiler settings (edit this one) |
config/HC09.toml | Generated: base + HC09_functions.toml |
config/HC09_switch_tables.toml | Generated by XenonAnalyse |
patches/ | XenonRecomp changes: new instructions, bdnzf fix |
src/ | Host runtime: memory map, thread block, kernel/XAM imports |
src/gpu/ | Command processor and the D3D12 renderer |
src/vk/ | The Vulkan renderer and frame pacing (MIT, src/vk/LICENSE) and their adapter (vk_adapter.cpp) |
src/common/ | Shared by the game and setup: hc09.ini settings, GitHub release check |
tools/installer/ | HC09Setup.exe: install/update wizard (disc import, build pipeline) |
tools/ | Extraction, decryption and analysis scripts |
Debugging
- Unimplemented imports log their first call:
[stub] Name (library) called from 0x.... - Access violations inside guest memory print the faulting guest address.
set HC09_DUMP_STACKS=20dumps every guest thread's call stack after 20 seconds (20,30dumps at each time). Recompiled functions are namedsub_<guest address>, so these read as guest stacks.set HC09_WATCH=830DD0E1puts a hardware write breakpoint on guest bytes (up to 4, comma-separated) and logs each write with the writer's stack.*83229400+164watches the 8 bytes at (pointer stored at 0x83229400) + 0x164, resolved 5 s after startup.A6713ED4:4watches 4 bytes (1, 2, 4 or 8, aligned);HC09_WATCH_VALUE=<hex>reports only writes that leave that value andHC09_WATCH_AFTER=<seconds>arms the watches that late. Each distinct writer stack is printed once.HC09_WATCH_READS=1catches reads as well (e.g. which code uses a string in the image).set HC09_SCAN_TDB=40,230lists, at those seconds after startup, the game's databases in guest memory that hold the tableHC09_SCAN_TABLE(defaultDRPK, the draft picks), with its address and record count.HC09_SCAN_WATCH=B4,BCputs write watches at those offsets from the table found;HC09_SCAN_BYTES=<text>lists where a text occurs in memory.set HC09_ENGINE_LOG=1prints the engine's own debug messages (compiled out in retail) as[engine] ..., e.g.Asset missing, using default: <path>for every texture a material references that no loaded package provides.set HC09_TRACE_SQL=INJY,PIRElogs each database query the game runs through its query call whose text names one of those tables or fields (written as the game spells them), as[sql] ...with its arguments, the values it returned and its result. Queries the game prepares ahead of time do not pass through it.set HC09_SAMPLE_THREAD=160samples guest thread 0x160's stack every 2 ms for its whole life and prints each distinct call path once.- Indirect calls to code that was not recompiled as a function stop with
indirect call to 0x..., which is not a recompiled function; access violations print the faulting stack withppc/*.cppline numbers. src/debug/hooks.cppoverrides individual recompiled functions (sub_<address>) to log or patch them; the original stays callable as__imp__sub_<address>.set HC09_GPU_STATS=1prints command-processor throughput, packet counts and its time split (draw / swap / idle) every 2 s, plus the share of wall time in each phase of a draw and texture-cache counters, and a[gpu] memoryline: video memory in use, cached textures and resolve targets (with how many were released after a minute unused), resolves dropped as stale (the title loaded other data over their memory) and fetches a resolve could not serve. Aflushes (heap exhausted)line counts the times a frame ran out of upload space, view descriptors or samplers, each of which makes the CPU wait for the GPU to finish. Aguest memory on the GPUline counts the memory copied to the GPU and the title's writes to watched pages. WithHC09_GPU_MEMORY=0it also logs a large texture whose change its sampled check missed (changed unseen by its samples).- The game's physical memory is mirrored in a 512 MiB GPU buffer: vertex shaders read their
vertices from it and byte-swap them, and a compute shader untiles textures from it. A page is
copied when a draw first needs it and again only after the game writes to it (copied pages are
write-protected, and the first write to one marks it stale), so unchanged geometry and textures
cost no CPU time per frame.
set HC09_GPU_MEMORY=0swaps vertices and untiles textures on the CPU instead, noticing texture changes by hashing; that is also the fallback when the buffer cannot be created. set HC09_USERDATA=C:\path\folderkeeps saves and the cache in another folder (e.g. a fresh profile forHC09_PRESSscripts, which assume the first-start prompts).set HC09_DUMP_FRAME=300traces every packet and draw of frame 300 (13500/150: frame 13500 and every 150th after it);HC09_FIND_DWORDS=<hex,...>searches guest memory for a dword sequence at that frame andHC09_FIND_STRING=<text>for that text (case-insensitive), printing the surrounding characters. Frame dumps also log each draw's shader bool constants ([frame] bools).set HC09_DUMP_SHADERS=1writes each new shader's microcode tobuild/shaders/;build\hc09\XenosDisasm.exe [--hlsl] build\shaders\*.bindisassembles, translates and compiles them.set HC09_SCREENSHOT=300,900saves frames 300 and 900 (counted asHC09_PRESScounts them) asbuild/screenshot_300.bmpand so on (python tools/bmp_to_png.py build/screenshot_300.bmp out.png). With either renderer: Vulkan saves the window as shown, D3D12 its back buffer.set HC09_TRACE_NAMES=1(HC27) prints each distinct asset name the game looks up in its archives (packages, models, textures), with the caller.set HC09_FXC=1compiles shaders with D3DCompile instead of DXC;HC09_SYNC_SHADERS=1compiles each when first drawn instead of in the background (withHC09_GPU_STATS=1, printing how long each took).set HC09_EFFECTS_DEBUG=1shows the ambient occlusion alone (white open, dark covered),2the reflections alone,3applies both effects to the left half of the screen only and4to every other frame, for comparisons. The first 3D scene logs the camera found ([effects] first scene).set HC09_D3D12_DEBUG=1enables the D3D12 debug layer and prints each distinct message;HC09_NO_RENDER=1disables rendering.set HC09_AUDIO_RECORD=C:\path\out.wavrecords everything sent to the speakers (48 kHz stereo).set HC09_AUDIO_STATS=1prints XMA decoder counters, the output peak level and the sound card queue depth (normally about 25 ms) every 5 s;HC09_NO_AUDIO=1disables audio output;HC09_XMA_DUMP=1saves one context's packets tobuild/xma_dump.binforbuild\hc09\XmaTest.exe.set HC09_PRESS=1800:start,1900:apresses buttons at those frames (held 6 frames).set HC09_STACK_ON_OPEN=campusprints the call stack of every file open whose path contains that text.set HC09_DRAW_SNAPSHOTS=1withHC09_DUMP_FRAMEsaves the render target after every visible draw of that frame (build/draw_<n>.bmp);HC09_DUMP_TEXTURES=<frame>saves the RGBA8 textures sampled from that frame on (HC09_DUMP_TEXTURES=dump: those sampled in the dump frame).HC09_DRAW_SNAPSHOTS=<first>-<last>limits the snapshots to those draws andHC09_DRAW_SNAPSHOTS=s<RB_SURFACE_INFO hex>to draws to that surface;HC09_STENCIL_SNAPSHOTS=1also saves the stencil plane (build/stencil_<n>.bmp: bit 0 red, bit 1 green). Snapshot<n>shows the result of the draw logged as<n-1>.set HC09_DUMP_AFTER_RESOLVE=512x512+2@2500dumps (asHC09_DUMP_FRAMEwould) the frame 2 frames after the first 512x512 colour resolve at or after frame 2500, for events whose frame number varies between runs.HC09_TRACE_RESOLVES=1logs every colour resolve;HC09_SAVE_RESOLVES=512x512saves each resolve of that size asbuild/resolve_<frame>_<addr>.bmp(512x512@2500-2510only in those frames).set HC09_DUMP_VS_CONSTS=152-155/HC09_DUMP_PS_CONSTS=200-255(with a frame dump) print that range of vertex or pixel shader constants for every draw. Frame dumps also log each draw's blend/depth/stencil registers, its index range and every bound texture (format, size, tiling, swizzle);HC09_DUMP_VERTEX_DATA=1adds the first 48 dwords of every vertex buffer (slow: it also prints texture slots read as vertex fetches).set HC09_TRACE_SURFACE=04020118logs every draw to that render surface (RB_SURFACE_INFO) in any frame, with its index/depth/clip registers, and saves the target and depth buffer after each one (build/surface_<n>.bmp,build/surface_<n>_depth.bmp). Debugging overrides:HC09_SAMPLER_TEST=lod0|wrap|point,HC09_NO_DEPTH_TEST=1,HC09_FORCE_INDEXED=1.- With a frame dump,
HC09_DUMP_TEXTURE_MEMORY=1saves every sampled texture's raw guest memory (build/texmem_<frame>_<base>_f<format>_<w>x<h>_p<pitch>.bin),HC09_DUMP_VERTEX_MEMORY=1every vertex buffer read (endian-swapped,build/vbmem_<address>_s<stride>.bin), andHC09_PROBE_PIXEL=<x>,<y>logs which draws change that render-target pixel ([probe]). set HC09_TRACE_RING=<seconds>logs ring-buffer batches from that time;HC09_TRACE_FENCES=<hex>logs fence writes (and ring indirect-buffer calls) once a fence value reaches that number;HC09_TRACE_D3D=1reports long D3D fence waits (with the D3D worker queues) and concurrent kickoffs;HC09_TRACE_KICKS=<seconds>logs ring kicks;HC09_TRACE_WAITS=1logs each distinctWAIT_REG_MEM. Critical-section waits over 3 s are always logged ([cs]).HC09_THREAD_STATS=1prints each thread's CPU share every 5 s (the command processor is 0xC0, the XMA decoder 0xA0).set HC09_DUMP_MEMORY=1AD1D000:8000(withHC09_DUMP_FRAME) saves raw physical memory asbuild/mem_<addr>.bin;HC09_NO_E_WINDOW=1leaves the 0xE0000000 view unmapped.set HC09_DUMP_MIPS=256x256f2saves every mip level of textures of that size and Xenos format, as stored by the game (untiled, byte-swapped), tobuild/mip_<frame>_<base>_<level>_<w>x<h>.bin(frame of the upload).HC09_ORIGINAL_MIPS=1keeps the stored mip levels of textures whose levels are not downscales of level 0 (such as the field's grass noise); by default those are rebuilt from level 0 ([gpu] texture ... rebuilding them).
Analysis tools
tools/peinfo.py— sections and.pdatastatstools/find_abi.py— locate__savegprlr_14and friendstools/xrefs.py— find branches to an addresstools/disasm.py— disassemble a rangetools/check_switches.py— cross-check jump-table coverage
