C3
CU 3 ML
CRAPPY UNREAL ENGINE 3 MOD LOADER
A dinput8.dll proxy that hijacks a UE3 game's load order, re-derives the engine's internal layout at runtime by disassembling the executable, and serves mod overrides without ever repacking a single .upk.
UNSTABLE MAYBE NOT WORKING x64 x86
Target win-x64
Proxy dinput8.dll
Disasm Zydis
cu3ml.log · init_thread()
what it does
01 · drop-in

Single DLL

Ships as dinput8.dll. Windows loads it in place of the system library, and it forwards every call to the real one once its own setup is done.

02 · non-destructive

.upk untouched

Overrides are intercepted at Preload time, in memory. Nothing inside the game's shipped package files is ever modified on disk.

03 · plain-text

TOML mods

Each mod is a folder with a mod.toml plus binary export overrides. Drop it in; the loader discovers it at startup.

boot sequence · dllmain → loaded mod
01

Hijack the load order dllmain.cpp

The game asks Windows for dinput8.dll and gets this one. DirectInput8Create is re-exported, stalls just long enough for the real system DLL to load, then forwards transparently.

02

Discover mods mod_loader::discover()

Scans the mods directory for folders with a mod.toml, parses name / author / version / content paths / spawn & replace patches, and builds the active list.

03

Re-derive the engine layout ue3_resolve()

No hardcoded offsets. Functions are found by the string constants they reference ( anchor::), then the surrounding x86-64 is walked with a Zydis-backed disassembler ( disp_extract::) to recover struct offsets, vtable slots and array bases from the live binary.

04

Install the hooks hook::install_all()

Patches StaticLoadObject to redirect mod-declared content paths, and Preload so export overrides can be swapped in before an object deserializes.

05

Serve overrides override_loader::

When the game preloads a matching export, the loader substitutes the mod's .bin payload — with its own name-map remap table — for the original serialized bytes.

module breakdown · 12 translation units
dllmain.cpp

Entry point. DirectInput8 proxy, background init thread, orchestrates every subsystem in order.

anchor.cpp / .hpp

String-reference function discovery: finds a function by the literals it touches, then walks its cross-references.

disp_extract.cpp / .hpp

Zydis micro-disassembly helpers — pulls struct displacements, immediates and RIP-relative globals from raw instruction streams.

disp_extract_arch.cpp

Architecture-specific decode plumbing behind the disp_extract queries.

ue3_resolve.cpp

Ties anchor + disp_extract together to populate one UE3Layout describing the target build.

ue3_layout.hpp

The resolved layout — FName tables, ULinkerLoad offsets, FArchive vtable slots, UObject field reads.

farchive_vtable.cpp / .hpp

Locates and validates FArchive virtual slots (Serialize / Tell / Seek / Precache / SerializeName).

pattern_scanner.cpp / .hpp

Classic byte-pattern / wildcard-mask scanning across a module, used as a fallback signature method.

hook.cpp / .hpp

Minimal inline hook manager — batches detours and commits them together once resolution succeeds.

mod_loader.cpp / .hpp

Reads mod.toml manifests, registers content-path redirects, resolves load-order find/replace rules.

override_loader.cpp / .hpp

Indexes each mod's binary export overrides and name-maps, then serves them from the Preload hook.

config · logs · util

TOML parsing into ModConfig/ LoadedMod, a rotating file logger, and small path/string helpers.

why disassemble, not hardcode
UE3 games ship with wildly different compiler settings and struct padding build to build. Instead of maintaining an offset table per game, the resolver reads the machine code around known anchor functions and extracts what it needs live — the same displacement work a human would do by hand in a disassembler.
first_neg_lea first_imul_imm rip_store_then_vcall nth_rip_global indexed_store_global field_disp_before_vcall imm_then_call
what a mod looks like
example_mod / mod.toml
name = "ExampleMod" version = "1.0.0" author = "deadYokai" description = "Replaces a UI font" # enabled, content_path, # spawn_patches, replace_patches # are all optional fields
example_mod / overrides/
overrides/ ├── Dishonored_MainMenu/ │ ├── Dishonored_MainMenu.namemap │ ├── …MainMenu_IA2.bin │ └── …MainMenu_IE9.bin └── UI_Loading_SF_LOC_INT/ ├── …ChaletComprime.bin ├── …PageA.bin └── …LOC_INT.namemap
build · windows target, cross-compiled
shell
# native MSVC cmake -B build cmake --build build --config Release # or cross-compiled with MinGW cmake -B build -DCMAKE_TOOLCHAIN_FILE=<mingw>.cmake cmake --build build # result: build/dinput8.dll # drop next to the game .exe, beside a mods/ folder
status
As stable as the README claims.
CU3ML resolves engine internals by disassembling the target at runtime instead of using known-good offsets, so behavior varies between game builds and isn't guaranteed to work at all. Keep backups, expect rough edges, and treat every hook as a controlled experiment against someone else's binary.
support · built by deadYokai
dinput8 proxy loader for unreal engine 3 · third-party: zydis, dear imgui