A from-scratch modding stack for Alice: Madness Returns (Unreal Engine 3, D3D9). Zero hardcoded addresses, zero heavy dependencies — everything is discovered at runtime. It runs natively on Windows and under CrossOver/Wine on macOS.
Three pieces:
hysteria/— the framework: adinput8.dllproxy that hooks into the running game, exposes its whole UnrealScript object model, and loads mods.mods/— mods are tiny DLLs (C, C++, or Rust) built on the framework's API.studio/— Hysteria Studio: a cross-platform desktop app (Rust + egui) to open, inspect, edit, preview and export the game's.upkassets (textures, sounds, properties).
- Read and write any property on any live object, by name, through the class hierarchy.
- Intercept, modify, block or override the return of any of the game's ~8000 functions.
- Call any function, spawn/destroy actors, run console commands, draw an in-game overlay.
- A proper keyboard + mouse fix (Alice was built for a gamepad — see below).
- A standalone asset tool that loads all ~1700 cooked packages and lets you edit them.
The full game surface — 1861 classes, ~13900 properties, 8272 functions — is mapped in
hysteria_reference.txt (generated). Nothing is out of reach.
Alice: Madness Returns is gamepad-first; its keyboard+mouse aim has acceleration, smoothing
and a deadzone that eats small movements. The input mod fixes it the clean way — it keeps
the game's native camera and just neutralizes the bad parts: it forces the camera-input accel
timers (aTurnElapsedTime / aLookUpElapsedTime) flat and disables bEnableMouseSmoothing.
No input bypass, no camera fighting, menus still work.
hysteria/ the framework (compiles to the injected dinput8.dll)
proxy.c dll proxy + D3D9 EndScene/Reset hooks + raw-input plumbing + logmsg
mem.c GNames/GObjects discovery, name/class/property reflection
props.c runtime offset calibration (UProperty tables)
mod_hook.c ProcessEvent finder + inline detour + dispatch + per-frame ticks
mod_api.c the HysteriaAPI implementation + mod loader
hysteria_api.h the PUBLIC header mods compile against
hysteria.hpp C++ wrapper (lambdas, std::string, HYSTERIA_MOD macro)
hysteria_sdk.h GENERATED typed offset accessors (tools/gen_sdk.py) — not committed
console.c in-game log console; ui.c Nuklear overlay
camera.c render.c entities.c dump.c built-in overlay tools (freecam/ESP/actors/dump)
frame.c per-frame orchestrator; build.sh cross-compile + deploy
mods/
trainer/ god / fly / noclip / freeze / teleport / savestates / world / heal
alice/ Alice abilities, hysteria, shrink-sense
input/ the keyboard+mouse fix
examples/ reference mods in C, C++ and Rust (+ older demos)
build.sh builds every top-level mod -> Win32/Mods/<name>.dll
studio/ Hysteria Studio (Rust + egui) — the asset tool
tools/ gen_sdk.py, gen_reference.py, gen_ccmds.py, upk.py, lzo1x.py
hysteria/build.sh # build the framework, auto-deploy dinput8.dll to the game folder
mods/build.sh # build the mods, deploy to Win32/Mods/
bash play.command # launch windowed (RES=1920x1080 bash play.command to resize)
Toolchain: i686-w64-mingw32-gcc/g++ (Homebrew mingw-w64); Rust mods need nightly +
rustup target add i686-pc-windows-gnu + rustup component add rust-src. On macOS the game
runs from the CrossOver bottle via cxstart.
In game: ` toggles the overlay. The ProcessEvent hook installs itself; mods load from
Mods/ (hit "Reload mods" in the Modding tab to hot-reload).
C — mods/<name>/<name>.c:
#include <windows.h>
#include "hysteria_api.h"
static HysteriaAPI *A;
static void on_damage(AEvent *e){ if(e->self == A->player_pawn()) e->block = 1; } // godmode
__declspec(dllexport) void ModMain(HysteriaAPI *api){
A = api;
api->log("my mod loaded");
api->on("TakeDamage", on_damage);
api->set_bool(A->player_pawn(), "bCanFly", 1);
}C++ (hysteria.hpp — lambdas + std::string):
#include "hysteria.hpp"
using namespace hysteria;
HYSTERIA_MOD() {
log("my mod loaded");
on("TakeDamage", [](Event &e){ if (e.self() == player_pawn()) e.block(); });
on_tick([]{ if (key_pressed(VK_F11)) console("God"); });
}Rust (closures, no unsafe in your mod):
mod hysteria_api; mod hysteria; use hysteria::*;
#[no_mangle] pub extern "C" fn ModMain(api: *const hysteria_api::HysteriaAPI) {
hysteria::init(api);
on("TakeDamage", |e| if e.this() == player_pawn() { e.block(); });
on_tick(|| if key_pressed(0x7A) { console("God"); });
}See MODDING.md for the full guide. The API (hysteria_api.h, version 7) covers:
reflection, typed property get/set (int/float/bool/byte/vector/rotator), event hooks with
param + return editing, a generic typed call builder (call_begin → call_arg_* → call_invoke → call_out_*), spawn/destroy/console, per-frame on_tick + key_down/key_pressed,
overlay panels (ui_panel + widgets), world_info(), raw memory access, and raw mouse.
A standalone, cross-platform (Windows/macOS/Linux) asset tool — no game required.
cd studio && cargo run --release
It ships its own from-scratch LZO1X decompressor, UE3 package parser and DXT (BC1/BC2/BC3)
codec. It loads every cooked .upk, browses objects, edits int/float/bool/byte properties
in place, decodes struct values (Vector/Rotator/Color/...), previews and exports textures to
PNG (DXT1/DXT3/DXT5/G8/A8R8G8B8, reading streamed mips from .tfc), replaces textures from
PNG, exports/replaces sounds (OGG/WAV), bulk-exports all textures/sounds, shows a raw hex
view of any object, and writes a game-loadable package back out.
3D mesh view is wired to UModel (UE Viewer): select a SkeletalMesh/StaticMesh and click
"View in 3D (UModel)" to open its interactive 3D viewer (and export OBJ/glTF/textures).
Drop umodel_64.exe into the CrossOver bottle at C:\umodel\ (from gildor.org); umodel.command
launches the browser standalone.
It also has a Localization (text) mode: browse and edit every Localization/<LANG>/*
string file (menus, subtitles, objectives, tutorials...) with byte-exact saves (UTF-16 and
Latin-1 preserved). Editing these is the most reliable way to mod game text — it takes effect
in-game immediately, no package rebuild needed.
- Everything is discovered at runtime — GNames/GObjects, property/bitmask/super-class offsets, the ProcessEvent vtable index. No build-specific addresses are baked in.
tools/gen_sdk.pyturns an in-game F2 dump into typed offset accessors;gen_reference.pyturns it into the browsablehysteria_reference.txtAPI map.