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. 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.
.upk untouched
Overrides are intercepted at Preload time, in memory. Nothing inside the game's shipped package files is ever modified on disk.
TOML mods
Each mod is a folder with a mod.toml plus binary export overrides. Drop it in; the loader discovers it at startup.
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.
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.
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.
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.
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.
Entry point. DirectInput8 proxy, background init thread, orchestrates every subsystem in order.
String-reference function discovery: finds a function by the literals it touches, then walks its cross-references.
Zydis micro-disassembly helpers — pulls struct displacements, immediates and RIP-relative globals from raw instruction streams.
Architecture-specific decode plumbing behind the disp_extract queries.
Ties anchor + disp_extract together to populate one UE3Layout describing the target build.
The resolved layout — FName tables, ULinkerLoad offsets, FArchive vtable slots, UObject field reads.
Locates and validates FArchive virtual slots (Serialize / Tell / Seek / Precache / SerializeName).
Classic byte-pattern / wildcard-mask scanning across a module, used as a fallback signature method.
Minimal inline hook manager — batches detours and commits them together once resolution succeeds.
Reads mod.toml manifests, registers content-path redirects, resolves load-order find/replace rules.
Indexes each mod's binary export overrides and name-maps, then serves them from the Preload hook.
TOML parsing into ModConfig/ LoadedMod, a rotating file logger, and small path/string helpers.
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.
- CMake 3.25+, targeting Windows (native MSVC or a MinGW cross toolchain)
- Bundles Zydis as a static subdirectory dependency for the disassembler
- Links against
psapifor module enumeration - Output is
dinput8.dll— nolibprefix, matching the expected proxy name
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.