A fix for the scrolling bug in Age of Empires.
It works by using a proxy dll that modifies the return values of the GetKeyboardState, GetKeyState and, GetAsyncKeystate functions when they are called.
All other functions are simply forward exports to the original functions from user32.dll
.
Change the string USER32.DLL
in your Age of Empires exe to USER33.DLL
and drop the user33.dll from the release page into the same folder as your game exe.
Note that you don't need a hex editor to find and change this string (Notepad++ works, regular Notepad might mess up the encoding and corrupt the exe)
You'll know if it's loaded when you start the game and get a message box like this:
Use MinGW:
i686-w64-mingw32-gcc -s user33.c user33.def -shared -o user33.dll
(you might need to replace i686-w64-mingw32-gcc
with the name/path of gcc
on your system)
As noted in the documentation for GetKeyboardState
, GetKeyState
, and GetAsyncKeystate
: only the high bit should be checked if you want to know if a key is pressed.
Age of Empires II HD (and likely other versions) simply check if the return value is > 0.
This breaks when Windows or Wine uses the middle bits for internal stuff,
the latter happens when switching workspaces or putting the game in the scratchpad in i3wm.
Credit goes to Steam user Sulix
for discovering that the problem is with how the game handles the return value of the functions:
thread
Note that Sulix only patched the GetKeyboardState function, but the game calls all three of the functions in several places
(which would explain why the tech tree still bugs with his fix):
(screenshots from radare2)
Instead of patching the calls in the exe as he outlines, this project patches the function exports to make the game's incorrect logic always work.
This means it easily works across different versions of the game and requires less effort to use and develop.
Because it makes the fix more portable:
Windows lets you redirect LoadLibrary calls of "Known DLLs" using a manifest file. More information can be found here.
However I couldn't get this to work on windows 7 (it might have something to do with cache, which I couldn't get to clear), additionally Wine doesn't seem to follow manifest files, instead it uses this section in winecfg to override a library version.
This works, however it means that I would have to do runtime function forwarding by
jumping to the entry point of the original functions (under the "Background" section)
(because the linker will try to link to our functions, since we have the same name as the the original dll),
which is slower and would require an absolute path (since loading by name would load our dll first) to the real user32.dll
which is different on 64-bit and 32-bit systems.
- Automated patching of the exe (though, how hard is it to change one byte in a hex editor?)
- Tests across more versions of the game