Releases: playday3008/PatchFix
Release list
PatchFix Releases
Releases are published per game. Pick the latest release for your game:
| Game | Latest release |
|---|---|
| AC.Rogue.PatchFix | ac-rogue/v3.3.1 |
| AC.Syndicate.PatchFix | ac-syndicate/v3.3.1 |
Releases up to v3.3.0 bundled every game under one tag.
ac-syndicate/v3.3.1
What's Changed
- ci: derive the release version from CMakeLists.txt by @playday3008 in #20
- refactor: restructure repo for multiple games by @playday3008 in #21
Full Changelog: v3.3.0...ac-syndicate/v3.3.1
ac-rogue/v3.3.1
What's Changed
- ci: derive the release version from CMakeLists.txt by @playday3008 in #20
- refactor: restructure repo for multiple games by @playday3008 in #21
Full Changelog: v3.3.0...ac-rogue/v3.3.1
v3.3.0
Ultrawide renders correctly in Syndicate, and Rogue's frame rate no longer slows the game down or throws the camera around.
What's New
Syndicate ultrawide (AspectRatioFix)
The engine computes the correct aspect ratio for the selected mode and stores it at +0x95C/+0x960, but the viewport code reads +0x92C/+0x930. Only the AMD SLS paths write that second block, and both are gated off on a single monitor, so it keeps the 16:9 value it was initialised with and every ultrawide render is stretched -- in fullscreen and in borderless alike.
The new AspectRatioFix hook mirrors the computed ratio and its gate bytes into the block the consumer actually reads. Confirmed at 3440x1440: fullscreen and borderless both render correctly.
[Video]
; Aspect ratio override. Auto(0) = derive from the resolution the game applies.
; Accepts ratio like 21:9, 32:9, or decimal like 2.389
AspectRatio=0
[Hooks]
AspectRatioFix=trueLeaving AspectRatio=0 derives the ratio from whatever resolution the game applies, which is what you want unless the display reports something the game misreads.
Rogue frame pacing (FPSUnlock rework)
Setting a cap above the display's own ceiling ran the game in slow motion: at Target=120 against a 60 Hz present, the simulation advanced at half speed.
Two separate causes, both in how the plugin drove the engine's timing mode:
- Mode 3 was never an uncap.
UpdateFrameTimingre-arms the frame deadline for modes 0, 1 and 3, so mode 3 paced each frame to its own 15-frame trailing average and then fed that lagging average back as the frame delta. - Mode 0 ignored wall time. It stepped the simulation by
1/fixed_rateregardless of how long the frame actually took, so any external limiter below the cap slowed the game in proportion.
Both uncapped and capped now go through mode 2, which keeps the measured delta and the engine's 1/15 s clamp. Its deadline period is a float with a single reference in the binary: 0 leaves the deadline at the current time so the wait loop never runs, 1000/target paces to the cap.
While a cap is set the plugin also raises the process timer resolution to 1 ms. The game never calls timeBeginPeriod itself, so Sleep(1) rounds up to roughly 15.6 ms and nothing above about 64 FPS is reachable. Uncapped there is no wait at all, so the resolution is left alone.
Measured under Proton with DXVK holding Present at 60, simulation time taken from FrameTiming.accumulated_ticks against QPC:
| Build | Target | Present | Sim speed |
|---|---|---|---|
| v3.2.0 | 120 | 60 | 0.50x |
| v3.2.0 | 0 | 60 | 1.00x |
| v3.3.0 | 120 | 60 | 1.00x |
Refs #9.
Rogue camera lean (CameraLean)
The sprint camera sway is an additive sequence track on the CameraFXInterface roll angle, applied once per frame with no time step, so it scales directly with frame rate: a gentle lean at 30 fps becomes a 30-40 degree roll at 200 that snaps back when the sequence ends. Every camera modifier upstream of the FX pass is level, which is why this took a hardware watchpoint on the roll field to find.
The new CameraLean hook scales the track weight by frame_dt * ReferenceFPS at the sequence dispatcher, and only for additive tracks bound to that object. Measured at about 215 fps over 30 s of sprint-tapping: peak roll 24.7 deg without the hook, 6.0 deg with it at ReferenceFPS=60.
Fixes #8.
Rogue mouse smoothing (MouseSmoothing)
The counterpart to Syndicate's CameraSmoothing, but it cannot work the same way. Syndicate has a boolean gate choosing between a direct copy and a damped lerp of the look axes, so its hook flips one jnz to jmp. Rogue has no such gate -- tracing the look input end to end, the mouse delta is written raw into the virtual right stick and reaches all five consumers of that axis unfiltered. The lag that keeps the camera drifting after the mouse stops is applied further downstream, in the camera's own damping, which in this engine is data-driven. There is no branch to flip and no constant in the exe to change.
So MouseSmoothing hooks the input update where the delta is live -- after the cursor-mode filter, before the event consumers -- and pre-emphasises it to invert a first-order lag. Feeding estimate + (raw - estimate) / factor makes the downstream filter land on raw.
It is gated on is_in_game(), because menus drive the hardware cursor from the same DIMOUSESTATE buffer and amplifying deltas there makes the cursor unusable.
Debuggers can attach to Syndicate
VMProtect's debugger check redirects ntdll!DbgUiRemoteBreakin to a stub whose entire body is TerminateProcess(0xDEADC0DE), so the thread the OS injects on DebugActiveProcess killed the game instead of raising the breakpoint a debugger expects.
The plugin now restores the function's 5-byte prologue from the copy of ntdll on disk, putting it back to what the loader mapped. Verified live: Cheat Engine attaches, the game keeps running, and the bytes still read clean afterwards, so the packer does not reapply its patch. Useful if you want to poke at the game yourself.
Codebase audit
A full audit against the real binaries through the IDA databases, rather than inferred from source. The parts that affect you:
- ResolutionFix could crash. Its early return used
trampoline_rsp = rsp + 8, which points at shadow space rather than the return address, so filtering a mode jumped to whatever that slot held. Only reachable on a display that publishes a non-standard mode. - A VMProtect race. The
CreateThreadhook was armed before its result was stored, so a thread entering that window called through an unset hook and never reached the realCreateThread. - Config parsing was too permissive.
AspectRatio = 1.7abcparsed as1.7, andnanandinfwere accepted. Values are now trimmed, fully consumed and checked finite, falling back to the declared default. - Deleting an INI key did nothing until restart -- the field kept its old value. It now restores its default on reload.
- Silent partial hooks. An optional signature that stopped matching left part of a hook inactive with no way to tell that from a hook working as intended. The registry now warns per unresolved signature, by name, after install.
Builds are warnings-as-errors in CI, and the test suite runs under CTest with 37 passing tests in both configurations.
Configuration
New keys, both games. An existing INI works unchanged -- missing keys fall back to the defaults -- but copy the shipped INI if you want the new entries documented in place.
AC.Rogue.PatchFix.ini:
[CameraLean]
; Frame rate the lean was tuned for. At this rate the fix changes nothing; the
; game shipped capped at 30, so 30 reproduces the sway as designed and 60 keeps
; the stronger sway an unlocked 60 fps machine had. 0 removes the lean entirely.
ReferenceFPS=30
[Input]
; The engine's blend coefficient, measured by feel. 1.0 disables the
; compensation entirely. Lower it until the camera stops when the mouse stops;
; too low overshoots and the camera snaps back. Values below 0.02 are treated
; as off and logged. This is a per-frame coefficient: if the feel changes after
; changing [FPS] Target, retune it.
SmoothingFactor=0.1
[Hooks]
CameraLean=true
MouseSmoothing=trueAC.Syndicate.PatchFix.ini:
[Video]
; Aspect ratio override. Auto(0) = derive from the resolution the game applies.
; Accepts ratio like 21:9, 32:9, or decimal like 2.389
AspectRatio=0
[Hooks]
AspectRatioFix=trueAssets
| File | Description |
|---|---|
AC.Rogue.PatchFix.asi |
Rogue plugin (Windows) |
AC.Rogue.PatchFix-wine.asi |
Rogue plugin (Wine/Proton cross-compiled) |
AC.Rogue.PatchFix.ini |
Rogue default configuration |
AC.Syndicate.PatchFix.asi |
Syndicate plugin (Windows) |
AC.Syndicate.PatchFix-wine.asi |
Syndicate plugin (Wine/Proton cross-compiled) |
AC.Syndicate.PatchFix.ini |
Syndicate default configuration |
What's Changed
- fix(rogue): scale camera sway by frame time by @playday3008 in #14
- fix(rogue): pace FPS through the engine's vsync mode by @playday3008 in #10
- feat(rogue): add MouseSmoothing hook by @playday3008 in #13
- fix: audit the codebase and fix what it turned up by @playday3008 in #16
- feat(vmp): restore DbgUiRemoteBreakin so debuggers can attach by @playday3008 in #17
- feat(syndicate): add AspectRatioFix hook by @playday3008 in #19
Full Changelog: v3.2.0...v3.3.0
v3.2.0
Syndicate camera smoothing can be turned off, so the camera follows the mouse directly.
What's New
Syndicate camera smoothing toggle
The camera lerps toward its target orientation instead of snapping to it, which reads as mouse smoothing and adds input latency to every look movement. The engine already carries a flag that bypasses the interpolation, checked by a jnz immediately before it:
mov rcx, [r13+0xA20]
cmp byte [rcx+0x104], 0
jnz skip_smoothing ; patched to jmp
...
movss xmm0, [rbx+0xA0]
call <lerp>
movss [rax+0xA0], xmm0
The new CameraSmoothing hook rewrites that opcode from 0x75 to 0xEB, keeping the displacement, so the smoothing block is always skipped and the target orientation is written straight through. Restoring 0x75 re-enables it, which makes the setting hot-reloadable - edit the INI while the game runs and the change applies without a restart.
install() refuses to patch unless the byte actually reads 0x75, so a signature that drifts on a future game build fails loudly instead of corrupting an instruction.
[Input]
; CameraSmoothing: Disable the camera smoothing the game applies to look
; input, so the camera follows the mouse directly instead of lerping toward
; it. Hot-reloadable - unlike the [Hooks] toggle below, which only decides
; whether the hook installs at startup, this key takes effect immediately.
DisableCameraSmoothing=true
[Hooks]
CameraSmoothing=trueBoth default to true. An existing AC.Syndicate.PatchFix.ini works unchanged - missing keys fall back to the defaults - but copy the shipped INI if you want the new entries documented in place.
Rogue was checked for the same mechanism and does not have it: its camera path has no equivalent bypass flag, so there is no Rogue-side counterpart in this release.
Credit: the branch was identified by @lnx00 in game-patches.
Assets
| File | Description |
|---|---|
AC.Rogue.PatchFix.asi |
Rogue plugin (Windows) |
AC.Rogue.PatchFix-wine.asi |
Rogue plugin (Wine/Proton cross-compiled) |
AC.Rogue.PatchFix.ini |
Rogue default configuration |
AC.Syndicate.PatchFix.asi |
Syndicate plugin (Windows) |
AC.Syndicate.PatchFix-wine.asi |
Syndicate plugin (Wine/Proton cross-compiled) |
AC.Syndicate.PatchFix.ini |
Syndicate default configuration |
What's Changed
- feat(syndicate): add CameraSmoothing hook by @playday3008 in #11
Full Changelog: v3.1.0...v3.2.0
v3.1.0
Rogue display fixes - every refresh rate is selectable, and the settings menu no longer crashes.
What's New
Rogue display mode fixes
The game built its resolution list by keeping at most one entry per resolution, preferring whichever mode sat within 1 Hz of 60. On a high refresh rate display that dropped every native-refresh entry, so the saved mode no longer resolved to a list index. The menu then indexed its mode array with -1 and read roughly 4 GB past the end of it.
Symptoms: the resolution selector showed undefined, the game rewrote RefreshRate back to 60 on every save, and any settings interaction that refreshed the apply button (switching away from fullscreen, for one) crashed at ACC.exe+0x6D8C39.
Two new hooks, both toggleable in [Hooks]:
| Hook | What it does |
|---|---|
| FullModeList | Replaces the list builder. Keeps every mode the monitor reports, sorted by (width, height, refresh) as the game's own binary search expects. Same output format, same DXGI_ENUM_MODES_SCALING flag, same 800x600 floor as the engine. |
| ModeIndexGuard | Clamps the display mode index the settings menu reads, mirroring the clamp the mode setter already does, and returns zeroed output when the list is empty. Also covers a stale index left in an existing INI. |
[Hooks]
; Clamps the display mode index the settings menu reads, which the game
; does not bounds check.
ModeIndexGuard=true
; Lists every display mode the monitor reports instead of one per
; resolution at 60 Hz.
FullModeList=trueBoth default to true. An existing AC.Rogue.PatchFix.ini works unchanged - missing keys fall back to the defaults - but copy the shipped INI if you want the new entries documented in place.
Crash diagnostics
The exception handler previously logged only faults whose instruction pointer landed inside the plugin or a registered patch, so a crash anywhere else in the game left no record at all. It now also logs a bounded number of faults from outside plugin code, which is what located the bug above.
Assets
| File | Description |
|---|---|
AC.Rogue.PatchFix.asi |
Rogue plugin (Windows) |
AC.Rogue.PatchFix-wine.asi |
Rogue plugin (Wine/Proton cross-compiled) |
AC.Rogue.PatchFix.ini |
Rogue default configuration |
AC.Syndicate.PatchFix.asi |
Syndicate plugin (Windows) |
AC.Syndicate.PatchFix-wine.asi |
Syndicate plugin (Wine/Proton cross-compiled) |
AC.Syndicate.PatchFix.ini |
Syndicate default configuration |
What's Changed
- build(deps): bump actions/cache from 5 to 6 by @dependabot[bot] in #6
- build(deps): bump actions/checkout from 6 to 7 by @dependabot[bot] in #5
- fix(rogue): show every display mode and stop the settings menu crashing by @playday3008 in #12
Full Changelog: v3.0.0...v3.1.0
v3.0.0
Multi-game support -- AC Syndicate joins the family.
What's New
Assassin's Creed Syndicate support
Six hooks, all configurable via AC.Syndicate.PatchFix.ini:
| Hook | What it does |
|---|---|
| PlatformSpecsFix | Stubs DxDiag COM initialization to prevent startup freeze/deadlock |
| DS4v2Fix | Recognizes DualShock 4 v2 controllers (PID 0x09CC) for correct PlayStation prompts |
| PromptOverride | Force Xbox or PlayStation button prompts regardless of connected controller |
| ResolutionFix | Filters non-standard aspect ratio resolutions (e.g., 4096×2160 / 17:9) from the display mode list |
| FPSUnlock | Configurable frame rate cap -- uncap or set a target (e.g., Target=60 for London Drift perk) |
| LanguageUnlock | All languages available regardless of purchase region (enabled by default) |
Rogue improvements
- UI language override -- set
UILanguagein the INI to force a specific language (requires game restart to take effect) - FrameTiming struct writes -- FPS unlock now writes directly to the game's timing struct instead of patching code, with timestamp priming to prevent first-frame hangs
Crash diagnostics
Built-in diagnostics for all games:
- Crash reports with register dump and stack traces -- written to the game directory as
.logfiles - Minidump generation (
.dmp) for post-mortem analysis - Crash attribution -- identifies which hook was active when a crash occurred
- Crash journal -- tracks hook installation state across the session
If you experience a crash, check the game directory for .log and .dmp files and include them in bug reports.
Build & infrastructure
- Embedded version number (check file properties or log output)
- Security hardening flags (CFG, DEP, ASLR, GS)
- Unit tests via Catch2
- Ninja Multi-Config presets (debug + release coexist)
Installation
- Install an ASI loader (x64) into the game directory
- Place the
.asiand.inifor your game into<game>/plugins/:- Rogue:
AC.Rogue.PatchFix.asi+AC.Rogue.PatchFix.ini - Syndicate:
AC.Syndicate.PatchFix.asi+AC.Syndicate.PatchFix.ini
- Rogue:
- Edit the INI to configure
- Launch the game
Steam Deck / Proton: Add WINEDLLOVERRIDES="version.dll=n,b" %command% to launch options. Use the -wine.asi variant if the standard build doesn't load.
Assets
| File | Description |
|---|---|
AC.Rogue.PatchFix.asi |
Rogue plugin (Windows) |
AC.Rogue.PatchFix-wine.asi |
Rogue plugin (Wine/Proton cross-compiled) |
AC.Rogue.PatchFix.ini |
Rogue default configuration |
AC.Syndicate.PatchFix.asi |
Syndicate plugin (Windows) |
AC.Syndicate.PatchFix-wine.asi |
Syndicate plugin (Wine/Proton cross-compiled) |
AC.Syndicate.PatchFix.ini |
Syndicate default configuration |
Full Changelog: v2.1.0...v3.0.0
v2.1.0
What's New
FPS Unlock
Removes the game's ~63.8 FPS cap at runtime -- no patched exe needed. Replaces the Nexus mod with a configurable, hot-reloadable INI setting.
[FPS]
; 0 = uncapped (removes frame limiter entirely)
; Any positive value = cap to that FPS (e.g. 60, 120, 144)
Target=0- Uncapped (
Target=0): bypasses the game's sleep-loop frame limiter - Capped (
Target=N): rewrites the internal frame time constant to match the target FPS, using the game's own frame pacer - Hot-reload: change the INI while the game is running -- no restart needed
Full Changelog: v2.0.0...v2.1.0
v2.0.0
Complete rewrite -- new architecture, new features, new build system.
What's New
Language Unlock
- All 22 languages available regardless of purchase region -- no more region-locked audio/subtitles
- Game ID detection preserved so DLC entitlements stay valid
- Optional UI language override (force any language independent of registry)
FOV Correction
- Three modes: Auto, Vert+, Hor+
- Adjustable multiplier on top of the correction
Multi-Monitor / Triple-Screen
- Automatic detection (aspect ratio ≥ 4.0 threshold)
- Manual override:
Single,Multi/Triple
UI Scaling
- Configurable horizontal/vertical stretch (
0.0-1.0) - Fill pillarbox (ultrawide) or letterbox (narrow) areas
Hot-Reload & Per-Hook Toggles
- Edit
AC.Rogue.PatchFix.iniwhile the game is running -- changes apply immediately - Enable/disable individual hooks at runtime via
[Hooks]section
Installation
- Install an ASI loader into the game directory
- Place
AC.Rogue.PatchFix.asiandAC.Rogue.PatchFix.iniinto<game>/plugins/ - Edit the INI to taste
- Launch the game
Steam Deck / Proton: Add WINEDLLOVERRIDES="version.dll=n,b" %command% to launch options.
Assets
| File | Description |
|---|---|
AC.Rogue.PatchFix.asi |
Plugin (Windows build) |
AC.Rogue.PatchFix-linux.asi |
Plugin (Linux cross-compiled) |
AC.Rogue.PatchFix.ini |
Default configuration |
Read description
Partially fixed target position, disable black bars when not in game
If you know hot to fix it properly, you're welcome to open a pull request