diff --git a/Dasm.png b/Dasm.png new file mode 100644 index 0000000..b76bdf8 Binary files /dev/null and b/Dasm.png differ diff --git a/Main.cpp b/Main.cpp new file mode 100644 index 0000000..09b9a7b --- /dev/null +++ b/Main.cpp @@ -0,0 +1,94 @@ +#include + +#include + +// Types + +typedef void(__thiscall* onplaytest_t)(void*, void*); +typedef void(__thiscall* cctouches_t)(void*, void*, void*); + +// Globals + +static bool g_hasPushStacked = 0; +static onplaytest_t g_onPlaytest = nullptr; +static cctouches_t g_onTouchBegan = nullptr; +static cctouches_t g_onTouchEnded = nullptr; + +// Helpers + +static bool writeMemory( + std::uintptr_t const address, + std::vector const& bytes) +{ + DWORD p; + VirtualProtect(reinterpret_cast(address), bytes.size(), PAGE_EXECUTE_READWRITE, &p); + + return ::WriteProcessMemory( + ::GetCurrentProcess(), + reinterpret_cast(address), + bytes.data(), + bytes.size(), + NULL) == TRUE; +} + +static bool writePtr( + std::uintptr_t const address, + std::uintptr_t const ptr) +{ + auto const p = reinterpret_cast(&ptr); + + return ::writeMemory( + address, + std::vector(p, p + sizeof(std::uintptr_t))); +} + +// Callback + +static void __fastcall onplaytestHook(void* self, void* edx, void* param) +{ + if (!g_hasPushStacked) + g_onPlaytest(self, param); +} + +static void __fastcall ontouchbeganHook(void* self, void* edx, void* param1, void* param2) +{ + g_hasPushStacked = true; + g_onTouchBegan(self, param1, param2); +} + +static void __fastcall ontouchendedHook(void* self, void* edx, void* param1, void* param2) +{ + g_hasPushStacked = false; + g_onTouchEnded(self, param1, param2); +} + +// Main + +DWORD WINAPI MainThread(LPVOID) +{ + auto base = reinterpret_cast(GetModuleHandleA(NULL)); + + g_onPlaytest = reinterpret_cast(base + 0x87600); + g_onTouchBegan = reinterpret_cast(base + 0x907B0); + g_onTouchEnded = reinterpret_cast(base + 0x911A0); + + auto const onPlaytestAddr1 = base + 0x76E53; + auto const onPlaytestAddr2 = base + 0x92109; + + writePtr(onPlaytestAddr1 + 1, reinterpret_cast(&onplaytestHook)); + writePtr(onPlaytestAddr2 + 1, reinterpret_cast(&onplaytestHook) - onPlaytestAddr2 - 5); + writePtr(base + 0x2997D0, reinterpret_cast(&ontouchbeganHook)); + writePtr(base + 0x2997D8, reinterpret_cast(&ontouchendedHook)); +} + +// Entrypoint + +BOOL WINAPI DllMain(HINSTANCE dll, DWORD const reason, LPVOID) +{ + DisableThreadLibraryCalls(dll); + + if (reason == DLL_PROCESS_ATTACH) + CreateThread(0, 0, &MainThread, 0, 0, 0); + + return TRUE; +} \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..b76e0ca --- /dev/null +++ b/README.md @@ -0,0 +1,15 @@ +# editorFreezeFix + +### The problem + +When using the editor, creators might accidentally hit the playtest key when placing objects: since the action of clicking is used by both normal mode and playtest mode, this causes a race condition where the editor switches to playtest mode (`EditorUI->0x1A4->0x3AC`) before the action of placing the object occurs. The state of the editor is then inconsistent, because it thinks we're constantly placing an object, and we cannot place more. + +![](Dasm.png) + +### The fix + +The fix is very simple: we check for the user click action, and we prevent the editor from entering playtest mode if the action is not done (for experts: if `ccTouchEnded()` is not done yet). + +### Credits + +Huge thanks to [night](https://twitter.com/uwunight_), who has asked me to fix this bug in the first place, and who told me a consistent way of reproducing it! \ No newline at end of file