Battlefield: Bad Company (Xbox 360 Recomp)
Battlefield: Bad Company is a Windows PC port of the Xbox 360 game with support for higher resolutions and mouse and keyboard controls.
- Type: PC port
- Game: Battlefield: Bad Company
- Runs on Windows
- By solidussnake95
- Latest release beta-1.0, 2026-11-07
- Source: https://github.com/solidussnake95/battlefield-bad-company-xbox360-recomp-
README
Battlefield: Bad Company — Xbox 360 static recompilation for Windows
A static recompilation of Battlefield: Bad Company (Xbox 360, Title ID 454107F9)
into a native Windows x64 application, built with ReXGlue SDK 0.10.0.
This repository contains project code only — no retail game content and no code
generated from it (game/ and generated/ are ignored; see below).
Contents
| Path | Description |
|---|---|
CMakeLists.txt | Builds bfbc.exe (WIN32) and stages the GPU plugin plus a linker map |
bfbc_manifest.toml | Recompilation manifest: entry point, setjmp/longjmp, hand-discovered function entries |
src/main.cpp | Registers the application (REX_DEFINE_APP) |
src/bfbc_app.h | App hooks: launch defaults, GPU plugin preload, startup graphics values, input factory |
src/bfbc_graphics.h | F5 overlay: display, resolution, VSync, frame cap, filtering, lighting/compatibility, mouse, key bindings |
src/bfbc_frame.h / .cpp | Measures the guest's per-frame VdSwap rate and logs it (Frame: 30.5 fps); no limiter |
src/bfbc_configzip.h | Reads and rewrites Render.ForceFov and PostProcess.FilmGrainEnable inside config.zz for the F5 sliders |
src/bfbc_mouse.h / .cpp | Mouse → right-stick shaping layer, and the input system that installs it |
src/bfbc_patches.cpp | Stubs for the SDK's XeKeys* licence-signature functions |
build.ps1 | Full build: configure → codegen → branch repair → Ninja |
build/fix_gen_branches.ps1 | Rewrites codegen branches that become REX_FATAL although their label exists |
build/add_funcs.ps1, build/analyze_image.ps1 | Tooling for adding missing function entries to the manifest |
build/NOTES_missing_functions.md | How missing functions were found without splitting existing ones |
build/run_bfbc.bat | Short launcher (no longer required) |
RELEASE.md | How the Windows package is assembled and published |
Requirements
- Windows x64.
- ReXGlue SDK 0.10.0 (
win-amd64) — providesrexglue.exe,rexruntime.dllandrexgpu-xenos.dll. - LLVM/Clang 19.1.5, CMake 3.31.6, Ninja 1.13.2.
- An
xwinsysroot with MSVC 14.44 and Windows SDK 10.0.26100 (no Visual Studio install needed).
The toolchain paths are set at the top of build.ps1; point them at your machine.
Game files (not included)
Place a copy you own in gamedump/ so that it looks like this:
game/default.xex # the title's executable image
game/Output/... # content data
game/dist/...
game/dvd-prb/...
Nothing can be generated or run without default.xex.
Build and run
powershell -ExecutionPolicy Bypass -File build.ps1
build\bfbc.exe
build.ps1 performs, in order:
- CMake configure with Clang and the dynamic runtime.
rexglue codegen bfbc_manifest.toml— emitsgenerated/default/*fromgame/default.xex.build/fix_gen_branches.ps1— repairs codegen branch output before it is compiled.- Ninja build →
build/bfbc.exe.
Running bfbc.exe on its own (double-click) is enough: it locates ..\game (or game\
next to the executable), loads the xenos plugin, allows the title to write to GAME:\,
and then creates the graphics system.
A prebuilt Windows package is published under Releases.
What this project adds on top of the SDK
- Launch defaults (
OnConfigurePaths): game data root,gpu_plugin=xenos, andallow_game_relative_writes— applied only when neither the command line norbfbc.tomldecides, so explicit settings always win. - Internal resolution actually works (
OnPreSetup): the GPU plugin registers its cvars when its DLL is loaded, but the SDK loads that DLL during presentation setup, afterbfbc.tomlhas been read. Saveddraw_resolution_scale_x/yvalues were therefore dropped and the render-target cache was always built at 1×. The DLL is now preloaded so the cvars exist, the saved values are pushed in, and only then is the graphics system created. The renderer switches the menu no longer edits (they stay inbfbc.toml) take the same path, so they are in place before any render target or resolve pipeline is built. - Mouse and keyboard (
OnPreSetup→CreateShapedInputSystem,src/bfbc_mouse.cpp): the SDK's default input system always registers its ownMnkInputDriver, and a second keyboard/mouse driver cannot take over —MnkInputDriverreports one constant device handle andInputSystem::DriverForDevice()resolves that handle to the first driver that reported it. A wrapper registered next to it was consequently never polled, sobfbc_mouse_gain/curve/deadzonenever applied. A customRuntimeConfig::input_factorynow builds the same system (SDL or XInput pads, NOP fallback) with the shaping driver in place of the SDK's: gain, response curve and deadzone are applied to the right stick after the driver, live, and the driver honours the runtime's active gate. The left mouse button fires and the right one aims out of the box: the SDK records mouse buttons as the virtual keysLMB/RMB, so they are bound to the pad's triggers (keybind_right_trigger/keybind_left_trigger) unlessbfbc.tomlalready stores a binding for them. The shipped layout is A=Space, B=F/X (exit a vehicle), X=R, LB=Q, RB=2, RT=LMB, LT=RMB, WASD on the left stick and the d-pad on the arrow keys. Bindings are registered by the input module after the SDK has readbfbc.toml, soOnConfigurePathspushes the file'skeybind_*values itself before filling in any that are missing; otherwise every launch silently used the SDK defaults. Binding rows in F5 capture the next press instead of taking typed text, so mouse buttons (LMB/RMB/MMB),Shift,Control,Altand the arrow keys can be bound; the names written are the ones the runtime's key parser accepts. - F5 overlay (
bfbc_graphics.h): display mode, output resolution, internal resolution, VSync, anisotropy, shader compilation, mouse gain/vertical gain/curve and everykeybind_*— all saved tobfbc.toml; the field-of-view slider and the film-grain switch are the exceptions, written intoconfig.zzbecause the engine only reads those values at boot. bfbc_patches.cpp:XeKeysConsolePrivateKeySignandXeKeysConsoleSignatureVerificationreport success instead of 0, otherwise the title aborts on its disc check.fix_gen_branches.ps1: turnsif (cond) REX_FATAL(...)back intoif (cond) goto loc_XXXX;where the emitter failed to classify a branch whose target label is present in the emitted body.
Known limitations
- The game runs at its own ~30 Hz, and there is no frame limiter: the engine's loop is
fixed at 30.0 frames per second regardless of what the host can do, so a cap can
only make it slower. The limiter was therefore removed, and
bfbc.tomlfiles that still containbfbc_fps_limitkeep working (the key is accepted and ignored).bfbc_frame.cpplogs the measured rate and the shortest/longest gap between swaps every two seconds tobin\logs\bfbc_NNN.logasFrame: 30.0 fps (gap 32.7-33.9 ms); earlier builds logged 30.5 here because the counter included the first frame of the window in the rate but not in the elapsed time. GPU driver overlays show a much higher number because the host presents the same image many times per second when VSync is off. The gap range is the useful number: a steady rate with a wide range is what reads as stutter on screen, so keep VSync on (a 30.0 fps source then occupies exactly two refreshes of a 60 Hz display). - A higher frame rate is not available, and frame rate equals game speed: the
engine advances one fixed simulation step per frame, so running the render loop
faster runs the whole game faster. Measured:
Game.MaxFps 60is quantised back to 30,Game.MaxFps 0removes the engine's pacing entirely (~500 fps), andvideo_mode_refresh_ratedoes not affect the rate. What does work is lifting the engine cap and pacing the swap inbfbc_frame.cpp:bfbc_pace_fps = 60holds a measured 60.0 fps (16.4-16.9 ms gaps) — but at double game speed, because the simulation step does not scale. A usable 60 fps mod therefore needs the per-frame simulation step itself patched to half, which is engine code work, not a setting.bfbc_pace_fps = 0(the default) leaves the engine's own 30 fps pacing in charge. - Field of view is a launch setting: the engine reads
Render.ForceFovfrom theGame.cfgoverride block at boot, so a running session cannot change it. F5 → Rendering is a 45–120° slider behind Override field of view; Apply & save rewritesconfig.zz(onlyGame.cfgis replaced, every other entry is copied byte for byte, and the new archive is re-parsed and compared before the file on disk is touched), reports "restart required", and the stored value is read back the next time the menu opens. The same value is reachable from the command line withpython tools\config_patch.py fov game\dist\xenon\config.zz 78(offremoves it). The key is from the engine's own setting table in the decompressed image, whereForceFovsits in the same class asRender.PerfOverlayEnable, which the config already drives. - Film grain is off by default and is a launch setting: F5 → Rendering has a
Film grain checkbox backed by
PostProcess.FilmGrainEnableinGame.cfg. The engine reads it at boot, so toggling it and pressing Apply & save rewritesconfig.zzand reports "restart required". The port ships with the valuefalse; the same switch is reachable throughpython tools\config_patch.py applyprofiles, which now keep it off. - Internal resolution changes are saved to
bfbc.tomland applied on the next launch, because the render-target cache is created once at startup. - The mouse is not raw input: the title only reads an analog stick, so mouse look
emulates the right stick and follows the game's own curve. The shaping layer makes that
usable: it maps movement into
[floor, 1]so slow aiming cannot fall inside the game's own deadzone (7849 of 32767, where the stick used to produce nothing at all), and it folds out the poll interval so the same hand speed turns the same amount at any frame rate.gain,curve,deadzone,floorand the frame-rate switch are all in F5, and thePC aimpreset sets them to a starting point (gain 2.00, curve 0.75). A shaped value that reaches full deflection is still capped there, and the game's own acceleration/smoothing sits on top - true 1:1 PC aiming would mean patching its look code, not feeding the stick. - Shift, Ctrl and Alt work as action keys: the SDK's MnK driver matches modifier
masks exactly, which means a bare
Shiftbinding can never fire (holding the key makes the live mask non-zero while the binding asks for zero). F5 and the startup config loader therefore store it asShift+Shift(Control+Control,Alt+Alt) and the row still displaysShift/Control/Alt. Combined bindings such as the defaultShift+Upd-pad mapping are unchanged, andCtrl/LeftCtrlvalues saved by older builds are rewritten to the working form on load. - In-game button prompts stay Xbox 360 icons: the title has no keyboard/mouse input
path at all (it never calls
XamInputGetKeystrokeor any keyboard query, andXamInputGetCapabilitiesis only used to detect a wheel), and its only prompt art is the 360 glyph set inside the Frostbite.fbrbUI bundles (menu/main,menu/hud). Swapping in PC-style icons means replacing textures in those bundles, not a cvar or a code switch. Because the prompts follow the pad buttons, they do track the binding: with the default mouse mapping, the fire prompt is the right-trigger icon. - The renderer/lighting switches are config-only now:
Lighting / CompatibilityandShadows / depthwere removed from F5 at the owner's request, but the values they set stay inbfbc.toml(draw_resolution_scaled_texture_offsets, the resolve half-pixel flags,d3d12_readback_resolve,depth_float24_*,gpu_3d_to_2d_texture,texture_cache_memory_limit_render_to_texture) and are still pushed before GPU init on every launch, so the shadow and gamma fixes keep working. Changing one means editing that file; they all take effect at startup. - Some optional assets produce
NtCreateFilewarnings (files absent from the disc image); they do not prevent startup.
Legal
This repository contains no game content and no generated code derived from it.
game/ and generated/ are listed in .gitignore. To use this project you must own
a copy of the game.