- Download
age2_x1_fixed.tar.gzfrom the Releases page - Unpack it into your
age2_x1folder - From now on launch the game with
age2_x1_fixed.exeinstead ofage2_x1.exe - No patching of the game binary is required
- Black magic now removes all of the suffering
- ?????
- PROFIT
- If you want to use the fix with a different version of the game, just modify the
.inifile accordingly.
Sometimes when you tab out of the game, after tabbing back in the game will be stuck in a state where it is constantly scrolling in some direction.
This makes the game unplayable and requires a restart of the game. This is especially annoying in multiplayer games.
The scrollbug happens mostly when running the game in Wine, but has supposedly also been observed on Windows.
- Use Wine. I don't know if these steps work on Windows.
- Start a game, e.g. a standard single-player random map game
- Hold the left-arrow key for 3 seconds
- Release the left-arrow key
- Wait for one second
- Tab out of the game
- Wait for one second
- Tab back into the game
- Congratulations; you now have a scroll bug
Age of Empires II uses the GetKeyboardState() and GetKeyState() Win32 API functions to determine whether one of the scrolling keys is pressed.
In the result of these functions, the key stats are reported as a single byte.
According to the MSDN documentation,
- Bit 7 of the byte says whether the key is currently pressed
- Bit 0 of the byte toggles each time the key is pressed
- All other bits are not documented
Age of Empires II wants to check whether the key is currently pressed. To do this it checks whether the byte value is >= 1 instead of checking if Bit 7 is set (& 0x80).
This works as long as bits 1..6 are zero.
Unfortunately, wine seems to use bit 2 to provide some other metadata, obviously related to tabbing out of the window, so it doesn't guarantee that bits 1..6 are always 0.
Windows also seems to sometimes use some of the other bits.
Every single one, i.e.:
- AoK
- AoC
- Forgotten Empires
- UserPatch
- AoE2:HD
- AoE2:DE
This uses the Microsoft Research Detours library to inject replacements of GetKeyState() and GetKeyboardState() into the running Age of Empires II binary.
The replacement functions mask out bits 1..6 in the results.
The fix consists of two binaries:
age2_x1_fixed.exereads the configuration fromage2_x1_fixed.iniand calls theexethat is specified in theinifile with the injectordllfile that is specified in theinifile.fix_scrollbug32.dllinjects fixed versions ofGetKeyState()andGetKeyboardState()into any 32-bit Windows executable it is used with.
- Use Windows (please, somebody port this to winegcc or mingw or something that works on Linux)
- Install Visual C++ Build Tools 2015
- Get this source code (duh.)
- Get
detours.libanddetours.h, e.g. by building the Detours library from source by invokingnmake, or by downloading the binaries from this Github repo. Place these two files in a subfolderdetours/of this source code - Call
build.bat - You will now have a whole bunch of garbage files, plus
fix_scrollbug32.dlldll_inject.exedll_inject.ini
dll_inject.exe can be renamed to whatever name. It is recommended to rename it to {name_of_the_exe_you_want_to_fix}_fixed.exe.
When launched, dll_inject.exe will
- open the
.inifile with the same name - in that
.inifile in the section[dll_inject], read the keysexe_fileanddll_file - launch the specified
exefile and inject the specifieddllfile - wait until the exe file has finished
If you use the pre-compiled and pre-configured age2_x1_fixed.exe, age2_x1_fixed.ini, fix_scrollbug32.dll you can just place them in the age2_x1 folder and launch age2_x1_fixed.exe instead of age2_x1.exe.
Thanks to Sulix who posted an analysis of the bug on the Steam Forum.
Thanks to Microsoft Research for the Detours library and the excellent accompanying samples. dll_inject.cpp was derived directly from the withdll example, and fix_scrollbug.cpp was derived by modifying one of the other examples.