From 5364a577125f8ad8534b08dd2ffcab4d011952d1 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 14 Jan 2024 04:05:00 +0100 Subject: [PATCH 01/55] Initial rewrite --- .gitmodules | 6 + Camera.cpp | 162 ---- Camera.hpp | 11 - Config.cpp | 48 -- ControlHooks.cpp | 53 -- ControlHooks.hpp | 3 - Game.cpp | 424 ---------- Game.hpp | 400 ---------- Hooking.cpp | 806 ------------------- Hooking.hpp | 76 -- Menu.cpp | 1030 ------------------------- Menu.hpp | 94 --- dllmain.cpp | 87 --- game/README.md | 9 - game/d3d/d3dterrain.cpp | 27 - game/d3d/d3dterrain.hpp | 51 -- game/event.hpp | 17 - game/filesystem.hpp | 31 - game/font.cpp | 87 --- game/font.hpp | 19 - game/hookfilesystem.cpp | 147 ---- game/markup.hpp | 58 -- game/msfilesystem.hpp | 23 - game/multifilesystem.cpp | 91 --- game/obtable.hpp | 26 - game/reloc.cpp | 70 -- game/reloc.hpp | 20 - game/script/script.cpp | 77 -- game/script/script.hpp | 13 - game/stream/stream.hpp | 23 - premake5.lua | 82 +- src/Hook.cpp | 44 ++ src/Hook.h | 20 + src/Main.cpp | 20 + src/cdc/render/PCDeviceManager.cpp | 30 + src/cdc/render/PCDeviceManager.h | 42 + src/cdc/render/PCInternalResource.cpp | 21 + src/cdc/render/PCInternalResource.h | 21 + src/menu/Menu.cpp | 67 ++ src/menu/Menu.h | 20 + src/render/RenderContext.cpp | 27 + src/render/RenderContext.h | 10 + util.cpp | 36 - util.hpp | 4 - vendor/MinHook.h | 186 ----- vendor/imgui | 2 +- vendor/json | 2 +- vendor/minhook | 1 + vendor/patterns | 1 + 49 files changed, 362 insertions(+), 4263 deletions(-) delete mode 100644 Camera.cpp delete mode 100644 Camera.hpp delete mode 100644 Config.cpp delete mode 100644 ControlHooks.cpp delete mode 100644 ControlHooks.hpp delete mode 100644 Game.cpp delete mode 100644 Game.hpp delete mode 100644 Hooking.cpp delete mode 100644 Hooking.hpp delete mode 100644 Menu.cpp delete mode 100644 Menu.hpp delete mode 100644 dllmain.cpp delete mode 100644 game/README.md delete mode 100644 game/d3d/d3dterrain.cpp delete mode 100644 game/d3d/d3dterrain.hpp delete mode 100644 game/event.hpp delete mode 100644 game/filesystem.hpp delete mode 100644 game/font.cpp delete mode 100644 game/font.hpp delete mode 100644 game/hookfilesystem.cpp delete mode 100644 game/markup.hpp delete mode 100644 game/msfilesystem.hpp delete mode 100644 game/multifilesystem.cpp delete mode 100644 game/obtable.hpp delete mode 100644 game/reloc.cpp delete mode 100644 game/reloc.hpp delete mode 100644 game/script/script.cpp delete mode 100644 game/script/script.hpp delete mode 100644 game/stream/stream.hpp create mode 100644 src/Hook.cpp create mode 100644 src/Hook.h create mode 100644 src/Main.cpp create mode 100644 src/cdc/render/PCDeviceManager.cpp create mode 100644 src/cdc/render/PCDeviceManager.h create mode 100644 src/cdc/render/PCInternalResource.cpp create mode 100644 src/cdc/render/PCInternalResource.h create mode 100644 src/menu/Menu.cpp create mode 100644 src/menu/Menu.h create mode 100644 src/render/RenderContext.cpp create mode 100644 src/render/RenderContext.h delete mode 100644 util.cpp delete mode 100644 util.hpp delete mode 100644 vendor/MinHook.h create mode 160000 vendor/minhook create mode 160000 vendor/patterns diff --git a/.gitmodules b/.gitmodules index b960e73..e3123c1 100644 --- a/.gitmodules +++ b/.gitmodules @@ -4,3 +4,9 @@ [submodule "vendor/json"] path = vendor/json url = https://github.com/nlohmann/json +[submodule "vendor/patterns"] + path = vendor/patterns + url = https://github.com/ThirteenAG/Hooking.Patterns +[submodule "vendor/minhook"] + path = vendor/minhook + url = https://github.com/TsudaKageyu/minhook diff --git a/Camera.cpp b/Camera.cpp deleted file mode 100644 index 6a2b07b..0000000 --- a/Camera.cpp +++ /dev/null @@ -1,162 +0,0 @@ -#define _USE_MATH_DEFINES -#include - -#include "Hooking.hpp" -#include "Camera.hpp" - -void(__cdecl* origCAMERA_Process)(Camera* camera); - -#if TR8 -void(__cdecl* origGAMELOOP_Process)(int a1); -void(__thiscall* _setCameraRotation)(int _this, cdc::Vector* a2); -#endif - -bool(__cdecl* EVENT_InputActionOn)(int action); -void(__cdecl* CAMERA_CalcPosition)(cdc::Vector* position, cdc::Vector* base, cdc::Vector* rotation, float distance); -double(__thiscall* InputSystem_GetAxisValue)(int _this, int a2); - -void CAMERA_Fly(Camera* camera) -{ - if (Hooking::GetInstance().GetMenu()->GetFreeCamMode() == FreeCameraMode::nocontrol) - { - return; - } - - auto speed = EVENT_InputActionOn(18); - - if (EVENT_InputActionOn(1)) // forward - { - CAMERA_CalcPosition(&camera->position, &camera->position, &camera->rotation, speed ? -50.f : -20.f); - } - - if (EVENT_InputActionOn(2)) // backward - { - CAMERA_CalcPosition(&camera->position, &camera->position, &camera->rotation, speed ? 50.f : 20.f); - } - -#if TRAE - auto a = InputSystem_GetAxisValue(*(int*)0x008551A0, 17); - auto b = InputSystem_GetAxisValue(*(int*)0x008551A0, 16); -#else //(TR7 && RETAIL_VERSION) - auto a = InputSystem_GetAxisValue(*(int*)0x1101680, 17); - auto b = InputSystem_GetAxisValue(*(int*)0x1101680, 16); -#endif - - camera->rotation.x -= static_cast(a); - camera->rotation.z -= static_cast(b); -} - -void __cdecl CAMERA_Process(Camera* camera) -{ - origCAMERA_Process(camera); - -#if TRAE - if (*(__int16*)0x850984 == 7) -#elif (TR7 && RETAIL_VERSION) - if (*(__int16*)0x10FC974 == 7) -#endif - { - CAMERA_Fly(camera); - } -} - -#if TR8 -cdc::Vector RotationToDirection(cdc::Vector rotation) -{ - auto x = rotation.x; - - // thanks to sunbeam906 - // thanks to Xwilarg - auto xx = -sin(rotation.z); - auto yy = cos(rotation.z); - auto zz = (-sin(x) * cos(x)); // FIXME better calculation so you can go straight down/up - - return cdc::Vector{ static_cast(xx), static_cast(yy), static_cast(zz) }; -} - -cdc::Vector CameraForward(cdc::Vector position, cdc::Vector rotation, float distance) -{ - auto direction = RotationToDirection(rotation); - - auto x = position.x + direction.x * distance; - auto y = position.y + direction.y * distance; - auto z = position.z + direction.z * distance; - - return cdc::Vector{ x, y, z }; -} - -// our own TR8 implementation of EVENT_InputActionOn -bool IsActionPressed(int action) -{ - auto input = *(int*)0x00A02B68; /* INPUT_instances */ - auto actions = **(int**)(input + 0x18); - - // im not responsible if this overflows - return (*(int*)(actions + (24 * action)) & 4) == 4; -} - -void UpdateCamera(int camera, cdc::Vector position, cdc::Vector rotation) -{ - auto ptr = camera + 0x10; - - _setCameraRotation(ptr, &rotation); - *reinterpret_cast(camera + 0x40) = position; -} - -void __cdecl GAMELOOP_Process(int a1) -{ - origGAMELOOP_Process(a1); - - if (Hooking::GetInstance().GetMenu()->GetFreeCamMode() == FreeCameraMode::enabled) - { - auto speed = IsActionPressed(34); - auto camera = *reinterpret_cast(0xE80534); - - static cdc::Vector rotation{ 0.0f, 0.0f, 0.0f, 0.f }; - - auto position = *reinterpret_cast(camera + 0x40); - - if (IsActionPressed(2)) - { - position = CameraForward(position, rotation, speed ? 50.f : 20.f); - } - - if (IsActionPressed(4)) - { - position = CameraForward(position, rotation, speed ? -50.f : -20.f); - } - - auto a = InputSystem_GetAxisValue(*(int*)0x00A02B68, 17); - auto b = InputSystem_GetAxisValue(*(int*)0x00A02B68, 16); - - rotation.x -= static_cast(a); - rotation.z -= static_cast(b); - UpdateCamera(camera, position, rotation); - } -} -#endif - -void InstallCameraHooks() -{ -#if TRAE - MH_CreateHook((void*)0x481D70, CAMERA_Process, (void**)&origCAMERA_Process); - - EVENT_InputActionOn = reinterpret_cast(0x42F740); - CAMERA_CalcPosition = reinterpret_cast(0x00491320); - InputSystem_GetAxisValue = reinterpret_cast(0x004E38C0); -#elif TR7 && RETAIL_VERSION - MH_CreateHook((void*)0x47EBE0, CAMERA_Process, (void**)&origCAMERA_Process); - - EVENT_InputActionOn = reinterpret_cast(0x42E3B0); - CAMERA_CalcPosition = reinterpret_cast(0x48D8B0); - InputSystem_GetAxisValue = reinterpret_cast(0x4E3FC0); -#elif TR8 - // no known CAMERA_Process in Underworld so hook GAMELOOP_Process - MH_CreateHook((void*)0x005DFBE0, GAMELOOP_Process, (void**)&origGAMELOOP_Process); - - InputSystem_GetAxisValue = reinterpret_cast(0x00480E50); - _setCameraRotation = reinterpret_cast(0x0049DD70); -#endif - - MH_EnableHook(MH_ALL_HOOKS); -} \ No newline at end of file diff --git a/Camera.hpp b/Camera.hpp deleted file mode 100644 index 82d41e6..0000000 --- a/Camera.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once -#include "Game.hpp" - -void InstallCameraHooks(); - -struct Camera -{ - cdc::Vector position; - char padding[608]; - cdc::Vector rotation; -}; \ No newline at end of file diff --git a/Config.cpp b/Config.cpp deleted file mode 100644 index c47ffa6..0000000 --- a/Config.cpp +++ /dev/null @@ -1,48 +0,0 @@ -#include -#include -#include -#include "Hooking.hpp" - -namespace mh -{ - NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT(Config, - remove_legal_screen, - remove_quit_message, - hide_menu_on_start); -}; - -mh::Config& Hooking::GetConfig() -{ - return m_config; -} - -void Hooking::LoadConfig() -{ - nlohmann::json j; - - try - { - std::ifstream ifs("config.json"); - - //ifs >> j; - j = nlohmann::json::parse(ifs, nullptr, true, true); - - ifs.close(); - } - catch (std::exception&) - { - return; - } - - try - { - m_config = j.get(); - } - catch (std::exception& e) - { - std::ostringstream text; - text << e.what() << "\nThe default configuration will be used, Press 'OK' to continue."; - - MessageBoxA(nullptr, text.str().c_str(), "Could not load configuration", MB_OK | MB_ICONWARNING); - } -} \ No newline at end of file diff --git a/ControlHooks.cpp b/ControlHooks.cpp deleted file mode 100644 index 9b037b6..0000000 --- a/ControlHooks.cpp +++ /dev/null @@ -1,53 +0,0 @@ -#include "Hooking.hpp" - -//int GetNumButtons() -//{ -// return 21 /* game default */; -//} - -char(__thiscall* UIDataList__AllocateRunData)(DWORD _this, int a2, int a3, int a4, int a5, int a6); - -char(__thiscall* UIDataList__AddItem)(DWORD _this, int processFunc, void* data, int getDataFunc, int a5, const char* a6); - -const char* GetControlText(void* data, int index, int textIndex) -{ - if (textIndex) - { - return "DEL"; - } - - return "Binoculars"; -} - -char UIControllerConfigDataList(int a1, int a2) -{ - // since the game controller config is complex and hardcoded array size - // we construct the controller config data list ourself and add our own control - - if (!UIDataList__AllocateRunData(a1, 22, a2, 0x4EC5E0, 0x4EC5F0, 0x4ECB10)) - return 1; - - for (int i = 0; i < 21; i++) - { - UIDataList__AddItem(a1, 0x4ECB30, (void*)i, 0x4ECBE0, 0, 0); - } - - // hardcoded for now since internal controls array is a fixed size - UIDataList__AddItem(a1, 0, 0, (int)GetControlText, 0, 0); - - return 1; -} - -void InstallControlHooks() -{ - //MH_CreateHook((void*)0x004EC280, GetNumButtons, nullptr); - -#if TRAE - UIDataList__AllocateRunData = reinterpret_cast(0x004ED600); - UIDataList__AddItem = reinterpret_cast(0x004ED6C0); - - MH_CreateHook((void*)0x004ECCB0, UIControllerConfigDataList, nullptr); - - MH_EnableHook(MH_ALL_HOOKS); -#endif -} \ No newline at end of file diff --git a/ControlHooks.hpp b/ControlHooks.hpp deleted file mode 100644 index f14dade..0000000 --- a/ControlHooks.hpp +++ /dev/null @@ -1,3 +0,0 @@ -#pragma once - -void InstallControlHooks(); \ No newline at end of file diff --git a/Game.cpp b/Game.cpp deleted file mode 100644 index af3b994..0000000 --- a/Game.cpp +++ /dev/null @@ -1,424 +0,0 @@ -#include "Game.hpp" -#include "Hooking.hpp" - -bool Game::m_binoculars = false; - -void(__cdecl* GAMELOOP_ExitGame)(int a1); -char(__cdecl* UIScreenManager_PushScreen)(int screenId, int a2); -int(__cdecl* UIScreenManager_GetTopScreenID)(); -int(__cdecl* UIScreenManager_PushOkDialog)(const char* text, int a2, int a3, int a4); -int(__cdecl* UIScreenManager_PopScreen)(); - -#if TRAE || TR7 -void(__cdecl* PLAYER_DebugSwitchPlayerCharacter)(); -#elif TR8 -// has an extra arg in Underworld -int(__cdecl* PLAYER_DebugSwitchPlayerCharacter)(DWORD a1); -#endif - -DWORD(__cdecl* sub_C64D3F)(int a1, int a2, int a3); -void(__cdecl* G2EmulationInstanceSetEventAnimPlaying)(Instance* instance, int a2); - -void(__cdecl* INSTANCE_Post)(Instance*, DWORD, int); -int(__cdecl* INSTANCE_Query)(Instance*, int); -Instance*(__cdecl* INSTANCE_Find)(int); - -void(__cdecl* IncrHealth)(float amount); -void(__cdecl* UIFadeGroupTrigger)(int group); -void(__cdecl* game_SetGameValue)(int a1, float a2, char a3); -double(__cdecl* game_GetGameValue)(int a1); - -void(__cdecl* PLAYER_SetLookAround)(Instance* instance); -void(__cdecl* CAMERA_StartLookaroundMode)(DWORD camera); -int(__cdecl* SteerSwitchMode)(Instance* instance, int mode); -void(__cdecl* PLAYER_ReSetLookAround)(Instance* instance); -__int16(__cdecl* CAMERA_ForceEndLookaroundMode)(DWORD camera); - -void(__cdecl* sub_456B50)(); -void(__cdecl* EVENT_PlayerTurnGold)(); - -int(__cdecl* OBTABLE_GetObjectID)(char* name); - -char* (__cdecl* game_localstr_get)(int a1); - -Instance* (__cdecl* INSTANCE_BirthObjectNoParent)(int unitId, cdc::Vector* position, cdc::Vector* rotation, DWORD* introData, DWORD* object, int modelnum, int); -ObjectTracker*(__cdecl* STREAM_GetObjectTrackerByName)(char*); -bool(__cdecl* STREAM_PollLoadQueue)(); - -bool(__cdecl* GAMELOOP_IsWipeDone)(int type); -void(__cdecl* GAMELOOP_SetScreenWipe)(int type, int target, int time); - -// deathstate stuff -int(__stdcall* DeathState_Entry)(int a1, int a2); -void(__stdcall* DeathState_Process)(int a1, int a2); - -int __stdcall DeathStateEntry(int a1, int a2) -{ - auto ret = DeathState_Entry(a1, a2); - - if (!Hooking::GetInstance().GetMenu()->m_drawSettings.noRespawn) - { - // we NOPed the orginal screenwipe code so call it manually - if (GAMELOOP_IsWipeDone(10)) - { - GAMELOOP_SetScreenWipe(10, 100, 90); - } - } - - return ret; -} - -void __stdcall DeathStateProcess(int a1, int a2) -{ - if (!Hooking::GetInstance().GetMenu()->m_drawSettings.noRespawn) - { - DeathState_Process(a1, a2); - } -} - -bool(__cdecl* UIScreenManager_WantsNormalFading)(); - -bool HookedWipe11_WantsNormalFading() -{ - if (Hooking::GetInstance().GetMenu()->m_drawSettings.noMovieBars) - { - return false; - } - - return UIScreenManager_WantsNormalFading(); -} - -void(__cdecl* RenderG2_MotionBlur)(int blur, int time, int timeout); - -void HookedRenderG2_MotionBlur(int blur, int time, int timeout) -{ - if (Hooking::GetInstance().GetMenu()->m_drawSettings.noMotionBlur) - { - RenderG2_MotionBlur(0, 0, 0); - - return; - } - - RenderG2_MotionBlur(blur, time, timeout); -} - -void Game::Initialize() -{ -#if TRAE - GAMELOOP_ExitGame = reinterpret_cast(0x4542B0); - - GAMELOOP_SetScreenWipe = reinterpret_cast(0x00452A30); - GAMELOOP_IsWipeDone = reinterpret_cast(0x00452970); -#elif TR8 - GAMELOOP_ExitGame = reinterpret_cast(0x5DF760); - - GAMELOOP_SetScreenWipe = reinterpret_cast(0x0052E8B0); - GAMELOOP_IsWipeDone = reinterpret_cast(0x0052E880); -#elif TR7 - GAMELOOP_ExitGame = reinterpret_cast(ADDR(0x454550, 0x451870)); - - GAMELOOP_SetScreenWipe = reinterpret_cast(ADDR(0x452F40, 0x4503D0)); - GAMELOOP_IsWipeDone = reinterpret_cast(ADDR(0x452E80, 0x450310)); -#endif - - UIScreenManager_PushScreen = reinterpret_cast(0x4FCB60); - UIScreenManager_GetTopScreenID = reinterpret_cast(0x4FC210); - UIScreenManager_PushOkDialog = reinterpret_cast(0x4FD100); - UIScreenManager_PopScreen = reinterpret_cast(0x4FCD20); - -#if TRAE - INSTANCE_Post = reinterpret_cast(0x4580B0); -#elif TR7 - INSTANCE_Post = reinterpret_cast(ADDR(0x458250, 0x455510)); -#elif TR8 - INSTANCE_Post = reinterpret_cast(0x5B3750); -#endif - -#if TRAE - INSTANCE_Query = reinterpret_cast(0x00458060); -#elif TR7 - INSTANCE_Query = reinterpret_cast(ADDR(0x458200, 0x4554C0)); -#endif - - INSTANCE_Find = reinterpret_cast(0x004582D0); - -#if TRAE - PLAYER_DebugSwitchPlayerCharacter = reinterpret_cast(0x005A39A0); -#elif TR8 - PLAYER_DebugSwitchPlayerCharacter = reinterpret_cast(0x0079DB50); -#elif TR7 - PLAYER_DebugSwitchPlayerCharacter = reinterpret_cast(ADDR(0x5A9A90, 0x5A40B0)); -#endif - - G2EmulationInstanceSetEventAnimPlaying = reinterpret_cast(0x4DE940); - - IncrHealth = reinterpret_cast(0x005715E0); - UIFadeGroupTrigger = reinterpret_cast(0x004EE580); - game_SetGameValue = reinterpret_cast(0x004551A0); - game_GetGameValue = reinterpret_cast(0x004551E0); - -#if TRAE - MH_CreateHook((void*)0x4E3C80, localstr_get, (void**)&game_localstr_get); - - NOP((void*)0x005584DC, 5); - MH_CreateHook((void*)0x005699C0, DeathStateProcess, (void**)&DeathState_Process); - MH_CreateHook((void*)0x005581D0, DeathStateEntry, (void**)&DeathState_Entry); - MH_CreateHook((void*)0x0043AB40, HookedRenderG2_MotionBlur, (void**)&RenderG2_MotionBlur); - - UIScreenManager_WantsNormalFading = reinterpret_cast(0x004FC1F0); - - auto call = int{ (int)HookedWipe11_WantsNormalFading - 0x00452F92 - 4}; - memcpyProtectedSection((void*)0x00452F92, &call, sizeof(call)); - - //MH_CreateHook((void*)0x004FC1F0, UIScreenManager_WantsNormalFading, nullptr); -#elif TR8 - NOP((void*)0x0075AEDE, 5); - MH_CreateHook((void*)0x0075AF90, DeathStateProcess, (void**)&DeathState_Process); - MH_CreateHook((void*)0x0075AA50, DeathStateEntry, (void**)&DeathState_Entry); -#elif TR7 - MH_CreateHook((void*)ADDR(0x4E7690, 0x4E43C0), localstr_get, (void**)&game_localstr_get); - - NOP((void*)ADDR(0x563478, 0x55E188), 5); - MH_CreateHook((void*)ADDR(0x574320, 0x56EC70), DeathStateProcess, (void**)&DeathState_Process); - MH_CreateHook((void*)ADDR(0x5631B0, 0x55DEC0), DeathStateEntry, (void**)&DeathState_Entry); - MH_CreateHook((void*)ADDR(0x40D060, 0x40CA80), HookedRenderG2_MotionBlur, (void**)&RenderG2_MotionBlur); - - UIScreenManager_WantsNormalFading = reinterpret_cast(ADDR(0x4FC130, 0x4F7480)); - - auto patchaddr = ADDR(0x453499, 0x450929); - - // .text section in nextgen debug exe is read only - auto call = int{ (int)HookedWipe11_WantsNormalFading - patchaddr - 4 }; - memcpyProtectedSection((void*)patchaddr, &call, sizeof(call)); -#endif - - PLAYER_SetLookAround = reinterpret_cast(0x005600F0); - CAMERA_StartLookaroundMode = reinterpret_cast(0x0048A300); - SteerSwitchMode = reinterpret_cast(0x005BAE60); - PLAYER_ReSetLookAround = reinterpret_cast(0x00560110); - CAMERA_ForceEndLookaroundMode = reinterpret_cast<__int16(__cdecl*)(DWORD)>(0x0048A5E0); - - sub_456B50 = reinterpret_cast(0x456B50); - EVENT_PlayerTurnGold = reinterpret_cast(0x0044E290); - -#if TRAE - OBTABLE_GetObjectID = reinterpret_cast(0x4660C0); - - INSTANCE_BirthObjectNoParent = reinterpret_cast(0x0045BA90); - STREAM_GetObjectTrackerByName = reinterpret_cast(0x5DA260); - STREAM_PollLoadQueue = reinterpret_cast(0x005DB190); -#elif TR7 - OBTABLE_GetObjectID = reinterpret_cast(ADDR(0x4655B0, 0x462590)); - - INSTANCE_BirthObjectNoParent = reinterpret_cast(ADDR(0x45B710, 0x458990)); - STREAM_GetObjectTrackerByName = reinterpret_cast(ADDR(0x5DA400, 0x5D4270)); - STREAM_PollLoadQueue = reinterpret_cast(ADDR(0x5DB350, 0x5D51C0)); -#elif TR8 - OBTABLE_GetObjectID = reinterpret_cast(0x005BF770); - - INSTANCE_BirthObjectNoParent = reinterpret_cast(0x005BD0F0); - STREAM_GetObjectTrackerByName = reinterpret_cast(0x005C17D0); - STREAM_PollLoadQueue = reinterpret_cast(0x005C1DA0); -#endif -} - -void memcpyProtectedSection(void* dst, const void* src, size_t size) -{ - DWORD lpflOldProtect, _; - VirtualProtect((void*)dst, size, PAGE_EXECUTE_READWRITE, &lpflOldProtect); - - memcpy(dst, src, size); - - VirtualProtect((void*)dst, size, lpflOldProtect, &_); -} - -char* localstr_get(int a1) -{ - return game_localstr_get(a1); -} - -void Game::ResetGame(int unk1) -{ - GAMELOOP_ExitGame(unk1); -} - -void Game::PushScreen(int screenId, int unk2) -{ - UIScreenManager_PushScreen(screenId, unk2); -} - -int Game::GetTopScreenID() -{ - return UIScreenManager_GetTopScreenID(); -} - -void Game::PushOkDialog(const char* text, void(__cdecl* fn)(), int a3, int a4) -{ - UIScreenManager_PushOkDialog(text, (int)fn, a3, a4); -} - -void Game::PopScreen() -{ - UIScreenManager_PopScreen(); -} - -// Instance post, query and find are mostly used to interact with instances from a level e.g. doors, levers, traps -// for example to query a lever Game::InstanceQuery(leverInstance, 233) == 1 // lever is switched on -// more can be found here https://theindra.eu/projects/TRAE-Research/research.html#_example -void Game::InstancePost(Instance* instance, int a2, int data) -{ - if (instance == nullptr) - { - Hooking::GetInstance().GetMenu()->Log(__FUNCTION__ " ignored, no instance.\n"); - return; - } - - return INSTANCE_Post(instance, a2, data); -} - -int Game::InstanceQuery(Instance* instance, int a2) -{ - return INSTANCE_Query(instance, a2); -} - -Instance* Game::InstanceFind(int intro) -{ - return INSTANCE_Find(intro); -} - -bool Game::CheckChapter(char* chapter) -{ - auto base = *reinterpret_cast(0x812C68); - int chapters = *reinterpret_cast(base + 24); - - int i = 0; - int offset = 0; - while (std::strcmp(chapter, - reinterpret_cast( - *reinterpret_cast(base + 28) + offset))) - { - i++; - offset += 400; - - if (i >= chapters) - { - return false; - } - } - - return true; -} - -void Game::SwitchPlayerCharacter() -{ - if (*(int*)PLAYERINSTANCE == NULL) - { - Hooking::GetInstance().GetMenu()->Log(__FUNCTION__ " ignored, no player instance.\n"); - return; - } - -#if TRAE || TR7 - PLAYER_DebugSwitchPlayerCharacter(); -#elif TR8 - // patch numPlayerObjects to exclude missing DLC outfits - auto ptr = *(int*)0xE7EE50; - auto ptr2 = *(int*)(ptr + 4); - auto numPlayerObjects = (__int16*)ptr2; - *numPlayerObjects = 10; - - auto playerObjects = *(int*)(ptr2 + 4); - *(__int16*)(playerObjects + 6) = 12; // patch the entire list to skip lara_doppelganger since that one is missing, dammit CD - *(__int16*)(playerObjects + 8) = 13; - *(__int16*)(playerObjects + 10) = 14; - *(__int16*)(playerObjects + 12) = 16; // lara_mom_thrall also doesn't exist, skip too - *(__int16*)(playerObjects + 14) = 17; - *(__int16*)(playerObjects + 16) = 18; - *(__int16*)(playerObjects + 18) = 19; - - int a1 = 12; // im not sure what this value is, passing 12 works - PLAYER_DebugSwitchPlayerCharacter((DWORD)&a1); -#endif -} - -void Game::InstanceSetEventAnimPlaying(Instance* instance, int a2) -{ - G2EmulationInstanceSetEventAnimPlaying(instance, a2); -} - -void Game::IncreaseHealth(float amount) -{ - IncrHealth(amount); -} - -void Game::TriggerUiFadeGroup(int group) -{ - UIFadeGroupTrigger(group); -} - -void Game::SetGameValue(int key, float val, bool apply) -{ - game_SetGameValue(key, val, apply); -} - -double Game::GetGameValue(int key) -{ - return game_GetGameValue(key); -} - -void Game::ToggleBinoculars() -{ - auto player = (Instance*)PLAYERINSTANCE; - - if (!m_binoculars) - { - SteerSwitchMode(player, 0); - - *(__int16*)0x850CAC = 1; - *(bool*)0x850418 = 1; - *(int*)0x86C908 = 72; - *(int*)0x86C818 = 3; - - PLAYER_SetLookAround(player); - Game::InstancePost(player, 262265, 0); - Game::InstancePost(player, 262264, 8); - CAMERA_StartLookaroundMode(0x850670); - sub_456B50(); - } - else - { - *(int*)0x6926FC = 0; - *(int*)0x86CD90 = 0; - - PLAYER_ReSetLookAround(player); - Game::InstancePost(player, 262266, 0); - CAMERA_ForceEndLookaroundMode(0x850670); - } - - m_binoculars = !m_binoculars; -} - -// Activates the gold wet effect, calling this outside unit gr18 crashes the game -void Game::PlayerTurnGold() -{ - EVENT_PlayerTurnGold(); -} - -Instance* Game::BirthObjectNoParent(int unitId, cdc::Vector* position, cdc::Vector* rotation, DWORD* introData, DWORD* object, int modelnum, int initEffects) -{ - return INSTANCE_BirthObjectNoParent(unitId, position, rotation, introData, object, modelnum, initEffects); -} - -ObjectTracker* Stream::GetObjectTrackerByName(char* name) -{ - return STREAM_GetObjectTrackerByName(name); -} - -bool Stream::PollLoadQueue() -{ - return STREAM_PollLoadQueue(); -} - -int Game::GetObjectID(char* name) -{ - return OBTABLE_GetObjectID(name); -} \ No newline at end of file diff --git a/Game.hpp b/Game.hpp deleted file mode 100644 index 448f13f..0000000 --- a/Game.hpp +++ /dev/null @@ -1,400 +0,0 @@ -#pragma once - -#include -#include - -// hack until everything is patterns -#if TR7 && RETAIL_VERSION - #define ADDR(debug, retail) retail -#elif TR7 - #define ADDR(debug, retail) debug -#endif - -#if TRAE - #define GAMETRACKER 0x838330 - #define GAMETRACKER_BASE_AREA (GAMETRACKER + 204) - #define MAX_UNIT_LEN 16 - #define PLAYERINSTANCE 0x83833C - - #define DISKFS 0x838890 - #define ARCHIVEFS 0x83888C - #define CONFIGNAME "TRAE" - #define INSTANCELIST 0x817D64 - #define GLOBALDATA 0x007C8A50 - #define CAMERAMODE 0x850984 -#elif TR8 - #define GAMETRACKER 0x00E7F088 - #define GAMETRACKER_BASE_AREA (GAMETRACKER + 56) - #define MAX_UNIT_LEN 128 - #define PLAYERINSTANCE 0xE7F094 - - #define DISKFS 0x9CE27C - #define ARCHIVEFS 0x9CE278 - #define CONFIGNAME "TR8" - #define INSTANCELIST 0xD98D54 - #define GLOBALDATA 0 -#elif TR7 - #define GAMETRACKER ADDR(0x10EE730, 0x10E5370) - #define GAMETRACKER_BASE_AREA (GAMETRACKER + 204) - - #define MAX_UNIT_LEN 16 - #define PLAYERINSTANCE (GAMETRACKER + 12) - - #define DISKFS ADDR(0x10EEC80, 0x10E58C0) - #define ARCHIVEFS ADDR(0x10EEC7C, 0x10E58BC) - #define CONFIGNAME "TR7" - #define INSTANCELIST ADDR(0x10CEE64, 0x10C5AA4) - #define GLOBALDATA ADDR(0x107F920, 0x1076980) - #define CAMERAMODE ADDR(0, 0x10FC974) -#else - #error "No game specified, set TRAE for Anniversary, TR7 for Legend or TR8 for Underworld" -#endif - -namespace cdc -{ - struct Vector - { - float x, y, z, w; - }; -} - -struct ObjectTracker -{ - __int32 resolveObj; - char* objectName; - DWORD* object; - __int16 id; - __int16 status; -}; - -#if TRAE || TR7 -struct Model; - -struct AnimListEntry -{ - __int16 animationID; - __int16 animationNumber; -}; - -struct Object -{ - char pad1[24]; - __int16 numModels; - __int16 numAnims; - __int16 numAnimPatterns; - __int16 pad2; - Model** modelList; - AnimListEntry* animList; - char pad3[32]; - char* name; -}; - -struct Instance -{ - __int64 node; - Instance* next; - Instance* prev; - cdc::Vector position; - cdc::Vector oldPosition; - cdc::Vector rotation; - cdc::Vector oldRotation; - cdc::Vector scale; - cdc::Vector shadowPosition; - cdc::Vector centerOfMass; - __int32 matrix; - __int32 oldMatrix; - __int16 unk7; - __int8 unk8; - __int8 unk9; - __int16 introNum; - int unk10; - Object* object; - char padding[312]; - int introUniqueID; -}; -#elif TR8 -struct Object -{ - char padding[96]; - char* name; -}; - -struct Instance -{ - __int64 node; - Instance* next; - __int32 prev; - Object* object; - char padding[12]; - cdc::Vector position; - cdc::Vector rotation; - char padding2[24]; - int introUniqueID; -}; -#endif - -struct Level; -struct StreamUnit; -struct Signal; - -struct MeshVertex; -struct MeshVertex32; - -struct StreamUnitPortal -{ - char tolevelname[30]; - __int16 toSignalID; - __int16 MSignalID; - __int16 streamID; - unsigned __int16* closeVertList; - float activeDistance; - StreamUnit* toStreamUnit; - cdc::Vector min; - cdc::Vector max; - cdc::Vector portalquad[4]; - cdc::Vector normal; -}; - -struct Mesh -{ - char m_box[0x20]; - cdc::Vector m_position; - void* m_vertices; - void* m_faces; - int m_root; - void* m_clientData; - unsigned __int16 m_vertexType; - unsigned __int16 m_numNodes; - unsigned __int16 m_numFaces; - unsigned __int16 m_numVertices; -}; - -struct TerrainGroup -{ - cdc::Vector globalOffset; - cdc::Vector localOffset; - int flags; - int ID; - int uniqueID; - int splineID; - Instance* instanceSpline; - Level* level; - Mesh* mesh; - char pad[0x74]; -}; - -struct Terrain -{ - __int16 UnitChangeFlags; - __int16 spad; - int numIntros; - int introList; - int numStreamUnitPortals; - - // all portals - StreamUnitPortal* streamUnitPortals; - - int numTerrainGroups; - TerrainGroup* terrainGroups; - TerrainGroup* signalTerrainGroup; - Signal* signals; -}; - -struct Level -{ - Terrain* terrain; - char pad[140]; - Signal* SignalListStart; - __int16* SignalIDList; - void* splineCameraData; - int reloc; // void* to script PE -}; - -struct StreamUnit -{ - int id; - __int8 used; - __int8 unitHidden; - __int16 flags; - Level* level; -}; - -struct Model -{ - int version; - int numSegments; - int numVirtSegments; - int segmentList; - cdc::Vector modelScale; - int numVertices; - int vertexList; - int numNormals; - int normalList; - int numFaces; - int faceList; -}; - -struct MeshVertex -{ - __int16 x; - __int16 y; - __int16 z; -}; - -struct MeshVertex32 -{ - float x; - float y; - float z; - float w; -}; - -struct BBox -{ - cdc::Vector bMin; - cdc::Vector bMax; -}; - -struct SignalFace -{ - unsigned __int16 i0; - unsigned __int16 i1; - unsigned __int16 i2; - char adjacencyFlags; - char collisionFlags; - unsigned __int16 id; -}; - -struct IndexedFace -{ - unsigned __int16 i0; - unsigned __int16 i1; - unsigned __int16 i2; - char adjacencyFlags; - char collisionFlags; - char clientFlags; - char materialType; -}; - -struct DRAWVERTEX -{ - float x; - float y; - float z; - int color; - float u; - float v; - __int64 anPad; -}; - -#define RGBA(r, g, b, a) r | (g << 8) | (b << 16) | (a << 24) - -// taken from TRAE.pdb -// should be compatible with TR7 -enum MUD_FLAGS -{ - MUD_FLAG_NONE = 0, - MUD_FLAG_POLYLINE = 1, - MUD_FLAG_INSTANCE = 2, - MUD_FLAG_WRAPS = 4, - MUD_FLAG_PATTERN_INTERACT = 0x2000, - MUD_FLAG_ENEMY_JUMP_ACROSS = 0x4000, - MUD_FLAG_ENEMY_JUMP_DOWN = 0x8000, - MUD_FLAG_ENEMY_JUMP_UP = 0x10000, - MUD_FLAG_ATTACHPOINTS = 0x20000, - MUD_FLAG_PERCH = 0x40000, - MUD_FLAG_ROPE = 0x80000, - MUD_FLAG_WATER = 0x100000, - MUD_FLAG_CLOSEJUMPTARGET = 0x200000, - MUD_FLAG_LADDER = 0x400000, - MUD_FLAG_MANTLEDOWN = 0x800000, - MUD_FLAG_ZIPLINE = 0x1000000, - MUD_FLAG_HORIZPOLE = 0x2000000, - MUD_FLAG_VERTPOLE = 0x4000000, - MUD_FLAG_WALLVERTPOLE = 0x8000000, - MUD_FLAG_ENEMYDROPDOWN = 0x10000000, - MUD_FLAG_JUMPLANDING = 0x20000000, - MUD_FLAG_LEDGEGRAB = 0x40000000, - MUD_FLAG_DISABLED = 0x80000000, - MUD_FLAG_ALL_TYPES = 0x0FFFDC000, - MUD_FLAG_ALL = 0x0FFFFFFFF -}; - -struct MudFlag -{ - int flag; - const char* name; -}; - -constexpr MudFlag mudFlags[22] -{ - MudFlag { MUD_FLAG_NONE, "NONE | " }, - MudFlag { MUD_FLAG_INSTANCE, "INSTANCE | " }, - MudFlag { MUD_FLAG_WRAPS, "WRAPS | " }, - MudFlag { MUD_FLAG_PATTERN_INTERACT, "PATTERN_INTERACT | " }, - MudFlag { MUD_FLAG_ENEMY_JUMP_ACROSS, "ENEMY_JUMP_ACROSS | " }, - MudFlag { MUD_FLAG_ENEMY_JUMP_DOWN, "ENEMY_JUMP_DOWN | " }, - MudFlag { MUD_FLAG_ENEMY_JUMP_UP, "ENEMY_JUMP_UP | " }, - MudFlag { MUD_FLAG_ATTACHPOINTS, "ATTACHPOINTS | " }, - MudFlag { MUD_FLAG_PERCH, "PERCH | " }, - MudFlag { MUD_FLAG_ROPE, "ROPE | " }, - MudFlag { MUD_FLAG_WATER, "WATER | " }, - MudFlag { MUD_FLAG_CLOSEJUMPTARGET, "CLOSEJUMPTARGET | " }, - MudFlag { MUD_FLAG_LADDER, "LADDER | " }, - MudFlag { MUD_FLAG_MANTLEDOWN, "MANTLEDOWN | " }, - MudFlag { MUD_FLAG_ZIPLINE, "ZIPLINE | " }, - MudFlag { MUD_FLAG_HORIZPOLE, "HORIZPOLE | " }, - MudFlag { MUD_FLAG_VERTPOLE, "VERTPOLE | " }, - MudFlag { MUD_FLAG_WALLVERTPOLE, "WALLVERTPOLE | " }, - MudFlag { MUD_FLAG_ENEMYDROPDOWN, "ENEMYDROPDOWN | " }, - MudFlag { MUD_FLAG_JUMPLANDING, "JUMPLANDING | " }, - MudFlag { MUD_FLAG_LEDGEGRAB, "LEDGEGRAB | " }, - MudFlag { MUD_FLAG_DISABLED, "DISABLED | " }, -}; - -class Game -{ -public: - static void Initialize(); - - static void ResetGame(int a1); - - static void PushScreen(int screenId, int a2); - static int GetTopScreenID(); - static void PushOkDialog(const char* text, void(__cdecl* fn)(), int a3, int a4); - static void PopScreen(); - - static void InstancePost(Instance* instance, int a2, int data); - static int InstanceQuery(Instance* instance, int a2); - static Instance* InstanceFind(int intro); - - static bool CheckChapter(char* chapter); - static void SwitchPlayerCharacter(); - static DWORD AnimDataSomething(int a1, int a2, int a3); - static void InstanceSetEventAnimPlaying(Instance* instance, int a2); - - static void IncreaseHealth(float amount); - static void TriggerUiFadeGroup(int group); - - static void SetGameValue(int key, float val, bool apply); - static double GetGameValue(int key); - - static bool m_binoculars; - static void ToggleBinoculars(); - - static void PlayerTurnGold(); - static Instance* BirthObjectNoParent(int unitId, cdc::Vector* position, cdc::Vector* rotation, DWORD* introData, DWORD* object, int modelnum, int initEffects); - - static int GetObjectID(char* name); -}; - -class Stream -{ -public: - static ObjectTracker* GetObjectTrackerByName(char* name); - static bool PollLoadQueue(); -}; - -char* localstr_get(int a1); - -void memcpyProtectedSection(void* dst, const void* src, size_t size); diff --git a/Hooking.cpp b/Hooking.cpp deleted file mode 100644 index ddd1d73..0000000 --- a/Hooking.cpp +++ /dev/null @@ -1,806 +0,0 @@ -#include -#include "Hooking.hpp" -#include "Game.hpp" -#include "ControlHooks.hpp" -#include "Camera.hpp" - -#include "game/reloc.hpp" -#include "game/d3d/d3dterrain.hpp" -#include "game/script/script.hpp" -#include "game/font.hpp" -#include "game/msfilesystem.hpp" -#include "game/markup.hpp" - -LPDIRECT3DDEVICE9 pDevice; -HWND pHwnd; - -Hooking::Hooking() - : m_menu(nullptr) -{ - // load config.json into m_config - LoadConfig(); - - // hook into d3d9 creation function and wait for a device -#if TRAE - auto pFound = FindPattern((PBYTE)"\xE8\x00\x00\x00\x00\x85\xC0\x75\x00\xE8\x00\x00\x00\x00\x6A\x00\x6A\x22\xE8", "x????xxx?x????xxxxx"); - auto D3D_Init = GetAddress(pFound, 1, 5); - - MH_CreateHook(reinterpret_cast(D3D_Init), hooked_Direct3DInit, reinterpret_cast(&original_Direct3DInit)); -#elif TR8 - MH_CreateHook(reinterpret_cast(0x478640), hooked_Direct3DInit, reinterpret_cast(&original_Direct3DInit)); -#elif TR7 - auto pFound = FindPattern((PBYTE)"\xE8\x00\x00\x00\x00\x8B\x00\x89\x00\xD4\x85\x00", "x????x?x?xx?"); - assert(pFound); - - auto D3D_Init = GetAddress(pFound, 1, 5); - MH_CreateHook(reinterpret_cast(D3D_Init), hooked_Direct3DInit, reinterpret_cast(&original_Direct3DInit)); -#endif - - // remove intros - // TODO this code make quite a lot of assumptions, refactor - if (m_config.remove_legal_screen) - { -#if TRAE - NOP((void*)0x0045FDBA, 10); - NOP((void*)0x0045FDCE, 6); - NOP((void*)0x0045FD3F, 6); - - *(int*)0x838838 = 3; -#elif TR7 && RETAIL_VERSION - NOP((void*)0x45D043, 10); - NOP((void*)0x45D057, 6); - NOP((void*)0x45CFC9, 6); - - *(int*)0x10E5868 = 3; -#endif - } - - InstallControlHooks(); - InstallCameraHooks(); - InsertTerrainDrawableHooks(); - - FONT_Init(); - -#if TRAE - MH_CreateHook((void*)0x467E60, MakePeHandle, nullptr); -#elif TR7 - MH_CreateHook((void*)ADDR(0x467310, 0x4642F0), MakePeHandle, nullptr); -#endif - -#if TR8 - MultiFileSystem_Add = reinterpret_cast(0x472C50); -#endif - - Game::Initialize(); - - MH_EnableHook(MH_ALL_HOOKS); -} - -Hooking::~Hooking() -{ - MH_Uninitialize(); -} - -std::shared_ptr& Hooking::GetMenu() noexcept -{ - return m_menu; -} - -char(__thiscall* original__PCDeviceManager__CreateDevice)(DWORD* _this, DWORD a2); - -char __fastcall PCDeviceManager__CreateDevice(DWORD* _this, DWORD _, DWORD a2) -{ - auto val = original__PCDeviceManager__CreateDevice(_this, a2); - - if (!val) - { - return val; - } - -#if TRAE - auto address = *reinterpret_cast(0xA6669C); -#elif TR8 - auto address = *reinterpret_cast(0xAD75E4); -#elif TR7 - auto address = *reinterpret_cast(ADDR(0x139C758, 0x1392E18)); -#endif - pDevice = *reinterpret_cast(address + 0x20); - - Hooking::GetInstance().GetMenu()->SetDevice(pDevice); - - return val; -} - -void(__thiscall* orginal_PCDeviceManager__ReleaseDevice)(DWORD* _this, int status); - -void __fastcall PCDeviceManager__ReleaseDevice(DWORD* _this, DWORD _, int status) -{ - Hooking::GetInstance().GetMenu()->OnDeviceReleased(); - ImGui_ImplDX9_InvalidateDeviceObjects(); - - pDevice = nullptr; - - orginal_PCDeviceManager__ReleaseDevice(_this, status); -} - -float* (__cdecl* TRANS_RotTransPersVectorf)(DWORD a1, DWORD a2); -void(__cdecl* org_Font__Flush)(); - -cdc::FileSystem* GetFS() -{ -#ifndef TR8 - static cdc::FileSystem* pFS = CreateMultiFileSystem(*(cdc::FileSystem**)ARCHIVEFS, *(cdc::FileSystem**)DISKFS); - - return pFS; -#else - // return regular FS in Underworld since this function is used by our code too - return g_pFS; -#endif -} - -bool(__cdecl* orgInitPatchArchive)(const char* name); -void(__thiscall* MultiFileSystem_Add)(void* _this, cdc::FileSystem* filesystem, bool unk, bool insertFirst); - -bool InitPatchArchive(const char* name) -{ - // init the orginal patch archive, let the game add it to the multi filesystem - auto ret = orgInitPatchArchive(name); - - // create the hook filesystem and add it front to the multi filesystem - auto pFS = CreateHookFileSystem(*(cdc::FileSystem**)DISKFS); - MultiFileSystem_Add(GetFS(), pFS, false, true); - - return ret; -} - -#if TRAE || (TR7 && RETAIL_VERSION) -void __cdecl EVENT_DisplayString(char* str, int time) -{ - Hooking::GetInstance().GetMenu()->Log("%s\n", str); -} - -void __cdecl EVENT_DisplayStringXY(char* str, int time, int x, int y) -{ - if (!Hooking::GetInstance().GetMenu()->m_drawSettings.drawDebug) return; - - FONT_SetCursor((float)x, (float)y); - FONT_PrintFormatted(str); -} - -void __cdecl EVENT_FontPrint(char* fmt, ...) -{ - if (!Hooking::GetInstance().GetMenu()->m_drawSettings.drawDebug) return; - - va_list vl; - va_start(vl, fmt); - char str[1024]; // size same as game buffer - vsprintf_s(str, fmt, vl); - va_end(vl); - - FONT_PrintFormatted(str); -} - -void __cdecl EVENT_PrintScalarExpression(int val, int time) -{ - if (!Hooking::GetInstance().GetMenu()->m_drawSettings.drawDebug) return; - - FONT_Print("%d", val); -} -#endif - -bool(__cdecl* objCheckFamily)(Instance* instance, unsigned __int16 family); - -#if TRAE || TR7 -void(__cdecl* TRANS_TransToDrawVertexV4f)(DRAWVERTEX* v, cdc::Vector* vec); - -void(__cdecl* DRAW_DrawQuads)(int flags, int tpage, DRAWVERTEX* verts, int numquads); - -void DrawQuads(cdc::Vector* v0, cdc::Vector* v1, int color = RGBA(255, 0, 0, 255)) -{ - DRAWVERTEX vertex[4]; - - cdc::Vector v3 = *v1; - cdc::Vector v4 = *v0; - v3.z += 20.f; - v4.z += 20.f; - v3.y += 20.f; - v4.y += 20.f; - - TRANS_TransToDrawVertexV4f(vertex, v0); - TRANS_TransToDrawVertexV4f(&vertex[1], v1); - TRANS_TransToDrawVertexV4f(&vertex[2], &v3); - TRANS_TransToDrawVertexV4f(&vertex[3], &v4); - - vertex[0].color = color; - vertex[1].color = color; - vertex[2].color = color; - vertex[3].color = color; - - DRAW_DrawQuads(2, 0, vertex, 1); -} - -void DrawBoundingBox(cdc::Vector* v0, cdc::Vector* v1, int color = RGBA(0, 0, 255, 255)) -{ - auto a1 = cdc::Vector{ v0->x, v0->y, v0->z }; - auto a2 = cdc::Vector{ v1->x, v0->y, v0->z }; - auto a3 = cdc::Vector{ v1->x, v1->y, v0->z }; - auto a4 = cdc::Vector{ v0->x, v1->y, v0->z }; - - auto b1 = cdc::Vector{ v0->x, v0->y, v1->z }; - auto b2 = cdc::Vector{ v1->x, v0->y, v1->z }; - auto b3 = cdc::Vector{ v1->x, v1->y, v1->z }; - auto b4 = cdc::Vector{ v0->x, v1->y, v1->z }; - - DrawQuads(&a1, &a2, color); - DrawQuads(&a2, &a3, color); - DrawQuads(&a3, &a4, color); - DrawQuads(&a4, &a1, color); - - DrawQuads(&b1, &b2, color); - DrawQuads(&b2, &b3, color); - DrawQuads(&b3, &b4, color); - DrawQuads(&b4, &b1, color); - - DrawQuads(&b1, &a1, color); - DrawQuads(&b2, &a2, color); - DrawQuads(&b3, &a3, color); - DrawQuads(&b4, &a4, color); -} - -void DrawQuads(cdc::Vector* v0, cdc::Vector* v1, cdc::Vector* v2, cdc::Vector* v3, int color) -{ - DRAWVERTEX vertex[4]; - - TRANS_TransToDrawVertexV4f(vertex, v0); - TRANS_TransToDrawVertexV4f(&vertex[1], v1); - TRANS_TransToDrawVertexV4f(&vertex[2], v2); - TRANS_TransToDrawVertexV4f(&vertex[3], v3); - - vertex[0].color = color; - vertex[1].color = color; - vertex[2].color = color; - vertex[3].color = color; - - DRAW_DrawQuads(2, 0, vertex, 1); -} - -void DrawQuads(int flags, int tpage, DRAWVERTEX* verts, int numquads) -{ - DRAW_DrawQuads(flags, tpage, verts, numquads); -} - -std::string FlagToFlags(int flag) noexcept -{ - std::string name; - - for (int i = 0; i < 22; i++) - { - if (flag & mudFlags[i].flag) - { - name += mudFlags[i].name; - } - } - - // somewhat stolen from https://stackoverflow.com/a/37795988/9398242 - if (name.empty()) - name = "NONE"; - else - name.erase(name.end() - 3, name.end()); - - return name; -} - -template -cdc::Vector GetVertice(unsigned int vertice, Mesh* mesh, cdc::Vector mapposition) -{ - auto vertex = ((T*)mesh->m_vertices)[vertice]; - - return cdc::Vector{ - mapposition.x + vertex.x, - mapposition.y + vertex.y, - mapposition.z + vertex.z }; -} - -// TODO move this to font.cpp and add some sort of function to subcribe before Font::Flush -void __cdecl Font__Flush() -{ - auto level = *(Level**)(GAMETRACKER + 8); - auto drawSettings = Hooking::GetInstance().GetMenu()->m_drawSettings; - - // prints queued file requests - if (drawSettings.printFileRequests && g_pDiskFS) - { - auto queue = ((cdc::MSFileSystem*)g_pDiskFS)->m_queue; - - auto y = 15.f; - - for (auto request = queue; request != nullptr; request = request->m_next) - { - FONT_SetCursor(15.f, y); - FONT_Print("%s (%d/%d KB)\n", request->m_pFileName, request->m_bytesRead / 1000, request->m_size / 1000); - - y += FONT_GetHeight(); - } - } - - // draws collision mesh - if (level && (drawSettings.drawCollision || drawSettings.drawEnemyCollision)) - { - auto terrain = (Terrain*)level->terrain; - - for (int group = 0; group < terrain->numTerrainGroups; group++) - { - auto terraingroup = terrain->terrainGroups[group]; - auto mesh = terraingroup.mesh; - - if (mesh) - { - // loop trough all collision faces - for (int i = 0; i < mesh->m_numFaces; i++) - { - auto face = ((IndexedFace*)mesh->m_faces)[i]; - - // get the position of every vertice for this face - auto x = GetVertice(face.i0, mesh, mesh->m_position); - auto y = GetVertice(face.i1, mesh, mesh->m_position); - auto z = GetVertice(face.i2, mesh, mesh->m_position); - - if ((terraingroup.flags & 0x4000) != 0) - { - if (drawSettings.drawEnemyCollision) - { - DrawQuads(&x, &y); - DrawQuads(&y, &z); - DrawQuads(&z, &x); - - DrawQuads(&x, &y, &z, &x, RGBA(255, 69, 0, 20)); - } - } - else if (drawSettings.drawCollision) - { - auto color = (terraingroup.flags & 0x42) != 0 ? RGBA(255, 0, 255, 20) : RGBA(0, 255, 0, 20); - - DrawQuads(&x, &y); - DrawQuads(&y, &z); - DrawQuads(&z, &x); - - DrawQuads(&x, &y, &z, &x, color); - } - } - } - } - } - - // draws signal mesh - if (level && drawSettings.drawSignals) - { - auto terrain = (Terrain*)level->terrain; - auto signalTerrainGroups = terrain->signalTerrainGroup; - - if (signalTerrainGroups) - { - auto mesh = signalTerrainGroups->mesh; - - if (mesh) - { - // loop trough all faces - for (int i = 0; i < mesh->m_numFaces; i++) - { - auto face = ((SignalFace*)mesh->m_faces)[i]; - - // get the position of every vertice for this face - auto x = GetVertice(face.i0, mesh, mesh->m_position); - auto y = GetVertice(face.i1, mesh, mesh->m_position); - auto z = GetVertice(face.i2, mesh, mesh->m_position); - - DrawQuads(&x, &y, &z, &x, RGBA(255, 0, 0, 10)); - } - } - } - } - - // draws portals - if (drawSettings.drawPortals && level) - { - auto terrain = *(Terrain*)level->terrain; - - // every portal - for (int i = 0; i < terrain.numStreamUnitPortals; i++) - { - auto portal = terrain.streamUnitPortals[i]; - - auto srcVector = cdc::Vector{}; - - // TODO vector functions? - srcVector = cdc::Vector{ (portal.min.x + portal.max.x) / 2, (portal.min.y + portal.max.y) / 2 , (portal.min.z + portal.max.z) / 2 }; - - TRANS_RotTransPersVectorf((DWORD)&srcVector, (DWORD)&srcVector); - - // draw text if visible on screen - if (srcVector.z > 16.f) - { - // draw portal id and destination - FONT_SetCursor(srcVector.x, srcVector.y); - FONT_Print("portal %d", i); - - srcVector.y += 15.f; - FONT_SetCursor(srcVector.x, srcVector.y); - - FONT_Print("to %s", portal.tolevelname); - - // draw portal bounds - auto v2 = cdc::Vector{ portal.min.x, portal.min.y, portal.max.z }; - auto v4 = cdc::Vector{ portal.max.x, portal.max.y, portal.min.z }; - - DrawQuads(&portal.min, &v2, &portal.max, &portal.min, RGBA(0, 0, 255, 10)); - DrawQuads(&portal.min, &v4, &portal.max, &portal.min, RGBA(0, 0, 255, 10)); - } - } - } - - // draw markup - if (Hooking::GetInstance().GetMenu()->m_drawSettings.drawMarkup) - { -#if TR7 - auto markUpManager = *(int*)ADDR(0x1120DC8, 0x1117544); -#elif TRAE - auto markUpManager = *(int*)0x86CD14; -#endif - - for (auto box = *(MarkUpBox**)(markUpManager + 0x18); box != nullptr; box = box->next) - { - auto markup = box->markup; - - // filter our instance markup except for water - if (markup && !(box->instance && (box->flags & MUD_FLAG_WATER) == 0)) - { - auto polyLine = markup->polyLine; - auto position = markup->position; - - auto srcVector = cdc::Vector{ position[0], position[1], position[2] }; - TRANS_RotTransPersVectorf((DWORD)&srcVector, (DWORD)&srcVector); - - if (srcVector.z > 16.f) - { - // display the markup flags - FONT_SetCursor(srcVector.x, srcVector.y); - FONT_Print("%s", FlagToFlags(box->flags).c_str()); - } - - if (polyLine) - { - // draw the polyline - auto x = &polyLine->points[0]; - for (int i = 0; i < polyLine->numPoints - 1; i++) - { - auto y = &polyLine->points[i + 1]; - - DrawQuads(x, y); - - x = y; - } - } - - // draw the bounding sphere for water and perch markup - if ((box->flags & (MUD_FLAG_WATER | MUD_FLAG_PERCH)) != 0) - { - auto bbox = markup->bbox; - - auto srcVector1 = cdc::Vector{ markup->position[0] + static_cast(bbox[0]), markup->position[1] + static_cast(bbox[1]), markup->position[2] + static_cast(bbox[2]) }; - auto srcVector2 = cdc::Vector{ markup->position[0] + static_cast(bbox[3]), markup->position[1] + static_cast(bbox[4]), markup->position[2] + static_cast(bbox[5]) }; - - DrawBoundingBox(&srcVector1, &srcVector2); - } - } - } - } - - auto settings = Hooking::GetInstance().GetMenu()->m_drawSettings; - - // draw instances - if (settings.draw || settings.drawPath) - { - // loop trough all instances - for (auto instance = *(Instance**)INSTANCELIST; instance != nullptr; instance = instance->next) - { - auto object = instance->object; - auto data = *(DWORD*)((DWORD)instance + 448); - auto extraData = *(DWORD*)((DWORD)instance + 572); - - if (settings.drawPath) - { - if (extraData && *(unsigned __int16*)(data + 2) == 56048) - { -#if TR7 - auto routing = extraData + 0x1060; -#elif TRAE - auto routing = extraData + 4432; -#endif - auto path = routing + 0x90; - auto length = *(int*)(path + 0x1A8); - - auto x = (cdc::Vector*)(path); - for (int j = 0; j < length - 1; j++) - { - auto y = (cdc::Vector*)(path + 16 * (j + 1)); - - DrawQuads(x, y); - - x = y; - } - } - } - - // TODO filter only pickups - auto show = [](DrawSettings settings, Instance* instance, DWORD data) - { - if (strlen(settings.filterName) > 0) - { - return strstr(instance->object->name, settings.filterName) != 0; - } - - if (!settings.filter) return true; - - // if selected 'draw enemy health' and instance is an enemy continue - if (settings.drawHealth && data && *(unsigned __int16*)(data + 2) == 56048) return true; - - return objCheckFamily(instance, 35) /* keys, healthpacks stuff */ || objCheckFamily(instance, 39) /* ammo */; - }; - - auto srcVector = cdc::Vector{}; - srcVector = instance->position; - TRANS_RotTransPersVectorf((DWORD)&srcVector, (DWORD)&srcVector); - - if (settings.draw && show(settings, instance, data) && srcVector.z > 16.f /* only draw when on screen */) - { - FONT_SetCursor(srcVector.x, srcVector.y); - FONT_Print("%s", object->name); - - if (settings.drawHealth && extraData && data && *(unsigned __int16*)(data + 2) == 56048) - { -#if TRAE - auto health = *(float*)(extraData + 5280); -#elif TR7 - auto health = *(float*)(extraData + 5040); -#endif - srcVector.y += 15.f; - FONT_SetCursor(srcVector.x, srcVector.y); - FONT_Print("%8.2f", health); - } - - if (settings.drawIntro) - { - srcVector.y += 15.f; - FONT_SetCursor(srcVector.x, srcVector.y); - FONT_Print("Intro %d", instance->introUniqueID); - } - - if (settings.drawAddress) - { - srcVector.y += 15.f; - FONT_SetCursor(srcVector.x, srcVector.y); - FONT_Print("%p", instance); - } - - if (settings.drawFamily && data) - { - srcVector.y += 15.f; - FONT_SetCursor(srcVector.x, srcVector.y); - FONT_Print("Family %d", *(unsigned __int16*)(data + 2)); - } - - if (settings.drawAnim && instance->object->numAnims > 0) - { - auto anim = G2EmulationInstanceQueryAnimation(instance, 0); - - srcVector.y += 15.f; - FONT_SetCursor(srcVector.x, srcVector.y); - FONT_Print("Anim %d (%X)", anim, instance->object->animList[anim].animationID); - } - } - } - } - - org_Font__Flush(); -} -#endif - -void Hooking::GotDevice() -{ - this->m_menu = std::make_unique(pDevice, pHwnd); - this->m_menu->SetVisibility(!m_config.hide_menu_on_start); - - // hook game's d3d9 present function and wndproc function -#if TRAE - MH_CreateHook(reinterpret_cast(0x61BB80), hooked_PCRenderContext_Present, reinterpret_cast(&original_PCRenderContext_Present)); - - MH_CreateHook(reinterpret_cast(0x4040B0), hooked_RegularWndProc, reinterpret_cast(&original_RegularWndProc)); -#elif TR8 - MH_CreateHook(reinterpret_cast(0x519360), hooked_PCRenderContext_Present, reinterpret_cast(&original_PCRenderContext_Present)); - - MH_CreateHook(reinterpret_cast(0x478BC0), hooked_RegularWndProc, reinterpret_cast(&original_RegularWndProc)); -#elif TR7 - auto present = FindPattern((PBYTE)"\x8B\x41\x14\x85\xC0\x74\x19\x8B\x54\x24\x0C", "xxxxxxxxxxx"); - MH_CreateHook(reinterpret_cast(present), hooked_PCRenderContext_Present, reinterpret_cast(&original_PCRenderContext_Present)); - - auto wndProc = FindPattern((PBYTE)"\x83\xEC\x68\x55", "xxxx"); - MH_CreateHook(reinterpret_cast(wndProc), hooked_RegularWndProc, reinterpret_cast(&original_RegularWndProc)); -#endif - - // hook SetCursorPos to prevent the game from resetting the cursor position - MH_CreateHookApi(L"user32", "SetCursorPos", hooked_SetCursorPos, reinterpret_cast(&original_SetCursorPos)); - -#if TRAE - MH_CreateHook((void*)0x00617F50, PCDeviceManager__ReleaseDevice, (void**)&orginal_PCDeviceManager__ReleaseDevice); - MH_CreateHook((void*)0x00617BE0, PCDeviceManager__CreateDevice, (void**)&original__PCDeviceManager__CreateDevice); -#elif TR8 - MH_CreateHook((void*)0x005223F0, PCDeviceManager__ReleaseDevice, (void**)&orginal_PCDeviceManager__ReleaseDevice); - MH_CreateHook((void*)0x00522580, PCDeviceManager__CreateDevice, (void**)&original__PCDeviceManager__CreateDevice); -#elif TR7 - MH_CreateHook((void*)ADDR(0xECCC20, 0xEC6550), PCDeviceManager__ReleaseDevice, (void**)&orginal_PCDeviceManager__ReleaseDevice); - MH_CreateHook((void*)ADDR(0xECC8F0, 0xEC62A0), PCDeviceManager__CreateDevice, (void**)&original__PCDeviceManager__CreateDevice); -#endif - -#if TRAE - // patch debug print nullsubs to our functions - *(DWORD*)(GLOBALDATA + 528) = (DWORD)EVENT_DisplayString; - *(DWORD*)(GLOBALDATA + 304) = (DWORD)EVENT_DisplayString; - - // draw debug - *(DWORD*)(GLOBALDATA + 1400) = (DWORD)EVENT_DisplayStringXY; - *(DWORD*)(GLOBALDATA + 464) = (DWORD)EVENT_FontPrint; - *(DWORD*)(GLOBALDATA + 1292) = (DWORD)EVENT_PrintScalarExpression; -#elif (TR7 && RETAIL_VERSION) - *(DWORD*)(GLOBALDATA + 504) = (DWORD)EVENT_DisplayString; - *(DWORD*)(GLOBALDATA + 304) = (DWORD)EVENT_DisplayString; - - *(DWORD*)(GLOBALDATA + 1336) = (DWORD)EVENT_DisplayStringXY; - *(DWORD*)(GLOBALDATA + 444) = (DWORD)EVENT_FontPrint; - *(DWORD*)(GLOBALDATA + 1232) = (DWORD)EVENT_PrintScalarExpression; -#endif - -#if TRAE - objCheckFamily = reinterpret_cast(0x534660); - - MH_CreateHook((void*)0x00434C40, Font__Flush, (void**)&org_Font__Flush); - - TRANS_RotTransPersVectorf = reinterpret_cast(0x00402B50); - - TRANS_TransToDrawVertexV4f = reinterpret_cast(0x00402F20); - - DRAW_DrawQuads = reinterpret_cast(0x00406D70); -#elif TR7 - MH_CreateHook((void*)ADDR(0x435050, 0x432570), Font__Flush, (void**)&org_Font__Flush); - - TRANS_RotTransPersVectorf = reinterpret_cast(ADDR(0x402D00, 0x402B20)); - - TRANS_TransToDrawVertexV4f = reinterpret_cast(ADDR(0x4030D0, 0x402EF0)); - - DRAW_DrawQuads = reinterpret_cast(ADDR(0x406720, 0x406240)); - - objCheckFamily = reinterpret_cast(ADDR(0x5369C0, 0x531B10)); - - #if !RETAIL_VERSION - // nop out useless F3 mouse toggle to be replaced by our F3 - NOP((void*)0x405559, 5); - #endif -#endif - -#if TR8 - MH_CreateHook((void*)0x4A3280, ScriptPrintInt, nullptr); - MH_CreateHook((void*)0x4A32C0, ScriptPrintFloat, nullptr); - MH_CreateHook((void*)0x795DB0, NsCoreBase_PrintString, nullptr); - - MH_CreateHook((void*)0x4A3200, ScriptLogInt, nullptr); - MH_CreateHook((void*)0x4A3240, ScriptLogFloat, nullptr); - MH_CreateHook((void*)0x795E30, NsCoreBase_PrintString, nullptr); -#endif - - MH_EnableHook(MH_ALL_HOOKS); -} - -int hooked_Direct3DInit() -{ - // call orginal game function to init d3direct device - auto val = original_Direct3DInit(); - - // get the d3d9 device and hwnd -#if TRAE - pHwnd = *reinterpret_cast(0x6926C8); -#elif TR8 - pHwnd = *reinterpret_cast(0x9EEDE8); -#elif TR7 - auto pFound = FindPattern((PBYTE)"\x8B\x0D\x00\x00\x00\x00\x56\x51\xE8\x00\x00\x00\x00\xE8", "xx????xxx????x"); - - pHwnd = **(HWND**)(pFound + 2); -#endif - - // (IDirect3DDevice*)devicemanager->d3device -#if TRAE - auto address = *reinterpret_cast(0xA6669C); -#elif TR8 - auto address = *reinterpret_cast(0xAD75E4); -#elif TR7 - pFound = FindPattern((PBYTE)"\x8B\x0D\x00\x00\x00\x00\x68\x00\x00\x00\x00\x50\xE8", "xx????x????xx"); - auto address = **(int**)(pFound + 2); -#endif - auto device = *reinterpret_cast(address + 0x20); - pDevice = reinterpret_cast(device); - - // if direct3d initialisation fails using the device will crash the game instead of showing game dx9 error - if (pDevice != nullptr) - { - Hooking::GetInstance().GotDevice(); - } - - return val; -} - -#if TRAE || TR7 -void __fastcall hooked_PCRenderContext_Present(DWORD* _this, void* _, int a2, int a3, int a4) -{ - if (pDevice) - { - Hooking::GetInstance().GetMenu()->OnPresent(); - } - - // call orginal game present function to draw on the screen - original_PCRenderContext_Present(_this, a2, a3, a4); -} -#elif TR8 -void __fastcall hooked_PCRenderContext_Present(DWORD* _this, void* _, int a2) -{ - - // call orginal game present function to draw on the screen - original_PCRenderContext_Present(_this, a2); - - Hooking::GetInstance().GetMenu()->OnPresent(); -} -#endif - -LRESULT hooked_RegularWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -{ - auto menu = Hooking::GetInstance().GetMenu(); - - if (msg == WM_KEYUP && wparam == VK_F8) - { - auto focus = menu->IsFocus(); - - // set menu focus - menu->SetFocus(!focus); - menu->SetVisibility(!focus); - } - -#if TRAE - if (msg == WM_CLOSE - && Hooking::GetInstance().GetConfig().remove_quit_message) - { - PostQuitMessage(0); - } -#endif - - // pass input to menu - menu->Process(hwnd, msg, wparam, lparam); - - // pass input to orginal game wndproc - return original_RegularWndProc(hwnd, msg, wparam, lparam); -} - -BOOL WINAPI hooked_SetCursorPos(int x, int y) -{ - // prevent game from reseting cursor position - if (Hooking::GetInstance().GetMenu()->IsFocus()) - { - return 1; - } - - return original_SetCursorPos(x, y); -} - -void NOP(void* address, int num) -{ - DWORD lpflOldProtect, _; - VirtualProtect(address, num, PAGE_EXECUTE_READWRITE, &lpflOldProtect); - memset(address, 0x90, num); - VirtualProtect(address, num, lpflOldProtect, &_); -} \ No newline at end of file diff --git a/Hooking.hpp b/Hooking.hpp deleted file mode 100644 index f514ffc..0000000 --- a/Hooking.hpp +++ /dev/null @@ -1,76 +0,0 @@ -#pragma once - -#include -#include "vendor/MinHook.h" -#include "Game.hpp" -#include "Menu.hpp" - -#include "game/filesystem.hpp" - -namespace mh -{ - struct Config - { - bool remove_legal_screen = true; - bool remove_quit_message = true; - bool hide_menu_on_start = false; - }; -} - -class Hooking -{ -public: - static Hooking& GetInstance() - { - static Hooking instance; - return instance; - } - - Hooking(); - ~Hooking(); - - Hooking(Hooking const&) = delete; - void operator=(Hooking const&) = delete; - - void GotDevice(); - std::shared_ptr& GetMenu() noexcept; - - mh::Config& GetConfig(); - void LoadConfig(); - -private: - std::shared_ptr m_menu; - - mh::Config m_config; -}; - -static int(*original_Direct3DInit)(); -int hooked_Direct3DInit(); - -#if TRAE || TR7 -static void(__thiscall* original_PCRenderContext_Present)(DWORD*, int, int, int); -void __fastcall hooked_PCRenderContext_Present(DWORD* _this, void* _, int a2, int a3, int a4); -#elif TR8 -static void(__thiscall* original_PCRenderContext_Present)(DWORD*, int); -void __fastcall hooked_PCRenderContext_Present(DWORD* _this, void* _, int a2); -#endif - -static LRESULT(__stdcall* original_RegularWndProc)(HWND, UINT, WPARAM, LPARAM); -static LRESULT hooked_RegularWndProc(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - -static BOOL(WINAPI* original_SetCursorPos)(int, int); -static BOOL WINAPI hooked_SetCursorPos(int x, int y); - -void NOP(void* address, int num); - -uint8_t __declspec(noinline)* FindPattern(BYTE* bMask, char* szMask); -uint8_t __declspec(noinline)* GetAddress(uint8_t* ptr, uint8_t offset, uint8_t instr_size); - -void DrawQuads(int flags, int tpage, DRAWVERTEX* verts, int numquads); - -extern int(__cdecl* G2EmulationInstanceQueryAnimation)(Instance* instance, int section); - -bool InitPatchArchive(const char* name); - -extern bool(__cdecl* orgInitPatchArchive)(const char* name); -extern void(__thiscall* MultiFileSystem_Add)(void* _this, cdc::FileSystem* filesystem, bool unk, bool insertFirst); \ No newline at end of file diff --git a/Menu.cpp b/Menu.cpp deleted file mode 100644 index 018364e..0000000 --- a/Menu.cpp +++ /dev/null @@ -1,1030 +0,0 @@ -#include "Menu.hpp" -#include "Game.hpp" -#include "Hooking.hpp" - -#include "game/event.hpp" -#include "game/stream/stream.hpp" -#include "game/obtable.hpp" - -static bool shouldReloc = true; - -char IsPs2() -{ - // somewhat the zoom code for binoculars is inside an 'if IsPs2()' in TRAE so we have to hook that function - return Game::m_binoculars; -} - -bool switchPlayerNextFrame = false; - -void(__cdecl* origSTREAM_FinishLoad)(StreamUnit* unit); -void STREAM_FinishLoad(StreamUnit* unit) -{ - if (!shouldReloc) - { - Level* level = unit->level; - level->reloc = 0; // set reloc module ptr to 0 - } - - origSTREAM_FinishLoad(unit); -} - -int(__cdecl* INSTANCE_ReallyRemoveInstance)(Instance* instance, int a2, char a3); -void(__cdecl* INSTANCE_SetModel)(Instance* instance, int model); - -void(__cdecl* G2EmulationInstanceSetAnimation)(Instance* instance, int section, int anim, int frame, int frames); -void(__cdecl* G2EmulationInstanceSetMode)(Instance* instance, int section, int mode); -int(__cdecl* G2EmulationInstanceQueryAnimation)(Instance* a1, int section); - -void(__cdecl* INSTANCE_HideUnhideDrawGroup)(Instance*, int, int); - -void(__thiscall* origCinematicHandlerImpl_NextFrame)(int _this); -void __fastcall CinematicHandlerImpl_NextFrame(int _this, int) -{ - auto streamFlags = (int*)0x8383F4; - if (*streamFlags & 0x1000) - { - // dont progress cine - return; - } - - origCinematicHandlerImpl_NextFrame(_this); -} - -#if TR8 -void(__cdecl* CAMERA_SetMode)(int mode); -int(__thiscall* _setToGameplayCamera)(int _this); -#endif - -int(__cdecl* RELOC_GetProcAddress)(int reloc, const char* symbol); - -void(__cdecl* LOAD_UnitFileName)(char* name, char* unit, char* extension); - -Menu::Menu(LPDIRECT3DDEVICE9 pd3dDevice, HWND hwnd) -{ - m_pd3dDevice = pd3dDevice; - m_hwnd = hwnd; - - OnLayoutChange(); - LoadSettings(); - - ImGui::CreateContext(); - - ImGui_ImplWin32_Init(m_hwnd); - ImGui_ImplDX9_Init(m_pd3dDevice); - - -#if TRAE - MH_CreateHook((void*)0x004E6EC0, IsPs2, nullptr); - MH_CreateHook((void*)0x005DB680, STREAM_FinishLoad, (void**)&origSTREAM_FinishLoad); - - MH_CreateHook((void*)0x00424FE0, CinematicHandlerImpl_NextFrame, (void**)&origCinematicHandlerImpl_NextFrame); -#endif - -#if TRAE - INSTANCE_ReallyRemoveInstance = reinterpret_cast(0x0045A3A0); - INSTANCE_SetModel = reinterpret_cast(0x00458A90); - - G2EmulationInstanceSetAnimation = reinterpret_cast(0x004DE690); - G2EmulationInstanceSetMode = reinterpret_cast(0x004DE7F0); - G2EmulationInstanceQueryAnimation = reinterpret_cast(0x004DE8B0); - - INSTANCE_HideUnhideDrawGroup = reinterpret_cast(0x004319B0); - - RELOC_GetProcAddress = reinterpret_cast(0x4680C0); - LOAD_UnitFileName = reinterpret_cast(0x45F650); -#elif TR7 - MH_CreateHook((void*)ADDR(0x5DB800, 0x5D5640), STREAM_FinishLoad, (void**)&origSTREAM_FinishLoad); - - INSTANCE_ReallyRemoveInstance = reinterpret_cast(ADDR(0x45A330, 0x4575B0)); - INSTANCE_SetModel = reinterpret_cast(ADDR(0x458C80, 0x455F40)); - - G2EmulationInstanceSetAnimation = reinterpret_cast(ADDR(0x4E1F00, 0x4DEC30)); - G2EmulationInstanceSetMode = reinterpret_cast(ADDR(0x4E2060, 0x4DED90)); - G2EmulationInstanceQueryAnimation = reinterpret_cast(ADDR(0x4E2120, 0x4DEE50)); - - INSTANCE_HideUnhideDrawGroup = reinterpret_cast(ADDR(0x458FB0, 0x456230)); - - RELOC_GetProcAddress = reinterpret_cast(ADDR(0x467570, 0x464550)); - LOAD_UnitFileName = reinterpret_cast(ADDR(0x45F4D0, 0x45C730)); -#elif TR8 - INSTANCE_ReallyRemoveInstance = reinterpret_cast(0x005BC4E0); - INSTANCE_SetModel = reinterpret_cast(0x005B9170); - - G2EmulationInstanceSetAnimation = reinterpret_cast(0x005B1EA0); - G2EmulationInstanceSetMode = reinterpret_cast(0x005B1F50); - - CAMERA_SetMode = reinterpret_cast(0x005F39F0); - _setToGameplayCamera = reinterpret_cast(0x5EDF50); - - LOAD_UnitFileName = reinterpret_cast(0x477970); -#endif -} - -void Menu::OnDeviceReleased() -{ - m_pd3dDevice->Release(); -} - -void Menu::SetDevice(LPDIRECT3DDEVICE9 pd3dDevice) -{ - m_pd3dDevice = pd3dDevice; - ImGui_ImplDX9_Init(m_pd3dDevice); -} - -void Menu::OnPresent() -{ - ImGui::GetIO().MouseDrawCursor = m_focus; - - ImGui_ImplDX9_NewFrame(); - ImGui_ImplWin32_NewFrame(); - - ImGui::NewFrame(); - - // draw menu - if (m_visible) - { - this->Draw(); - } - - ImGui::EndFrame(); - - ImGui::Render(); - ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); -} - -void Menu::Process(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam) -{ - auto shift = (GetKeyState(VK_SHIFT) & 0x8000) != 0; - - if (msg == WM_KEYUP && wparam == VK_F2) - { - ToggleFlight(!m_flight); - } - - if (msg == WM_KEYUP && wparam == VK_F9) - { - Game::SwitchPlayerCharacter(); - } - - if (msg == WM_KEYUP && wparam == VK_F11) - { -#if TRAE || TR7 - Game::InstancePost(*(Instance**)PLAYERINSTANCE, 262167, 3); -#elif TR8 - // instance message types were changed in Underworld - Game::InstancePost(*(Instance**)PLAYERINSTANCE, 4, 3); -#endif - } - - if (msg == WM_KEYUP && wparam == VK_F7) - { - m_visible = !m_visible; - - // if menu is focussed and being hidden also stop focus - if (m_focus && !m_visible) - { - SetFocus(false); - } - } - - if (msg == WM_KEYUP && wparam == VK_F5) - { -#if TR8 - auto gPlayerControl_Enabled = (bool*)0x8AB4E6; -#elif TRAE - auto gPlayerControl_Enabled = (bool*)0x666C34; -#elif TR7 - auto gPlayerControl_Enabled = (bool*)ADDR(0xF1E40C, 0xF15AB4); -#endif - - *gPlayerControl_Enabled = !*gPlayerControl_Enabled; - } - - if (msg == WM_KEYUP && wparam == VK_F3 && shift) - { -#if TRAE || TR7 - auto debugTimeMult = (float*)(GAMETRACKER + 0x13C); -#elif TR8 - auto debugTimeMult = (float*)(GAMETRACKER + 0x148); -#endif - - if (*debugTimeMult < 0.3f) - { - *debugTimeMult = 1.0f; - } - else if (*debugTimeMult < 0.5f) - { - *debugTimeMult = 0.2f; - } - else - { - *debugTimeMult = 0.4f; - } - } - - if (msg == WM_KEYUP && wparam == VK_F4) - { - if (m_freeCamMode == FreeCameraMode::disabled) - { - // enable free camera - m_freeCamMode = FreeCameraMode::enabled; - - // switch to camera -#if TR8 - CAMERA_SetMode(11); - - auto camera = *(int*)0xE80534; - *(cdc::Vector*)(camera + 0x40) = (*(Instance**)PLAYERINSTANCE)->position; -#elif TRAE || (TR7 && RETAIL_VERSION) - *(__int16*)CAMERAMODE = 7; -#endif - } - else if (m_freeCamMode == FreeCameraMode::enabled) - { - // disable free camera but keep camera active - m_freeCamMode = FreeCameraMode::nocontrol; - } - else if (m_freeCamMode == FreeCameraMode::nocontrol) - { - m_freeCamMode = FreeCameraMode::disabled; - - // switch back to gameplay camera -#if TR8 - _setToGameplayCamera(0xE804F0 /* AVCameraManager */); -#elif TRAE || (TR7 && RETAIL_VERSION) - *(__int16*)CAMERAMODE = 2; -#endif - } - } - - // pause the game with F3 - if (msg == WM_KEYUP && wparam == VK_F3 && !shift) - { -#if TRAE || TR7 - auto streamFlags = (int*)(GAMETRACKER + 0xC4); -#elif TR8 - auto streamFlags = (int*)(GAMETRACKER + 0x30); -#endif - if (*streamFlags & 0x1000) - { - *streamFlags &= 0xFFFFEFFF; - } - else - { - *streamFlags |= 0x1000u; - } - } - -#if TRAE - if (msg == WM_KEYUP && wparam == VK_DELETE) - { - Game::ToggleBinoculars(); - } -#endif - - if (switchPlayerNextFrame) - { - switchPlayerNextFrame = false; - - Game::SwitchPlayerCharacter(); - } - -#if TRAE || TR7 - if (msg == WM_KEYUP && wparam == VK_INSERT) - { - *(int*)(GAMETRACKER + 0x1C) ^= 0x8000; // hide hud - } -#endif - - if (msg == WM_INPUTLANGCHANGE) - { - OnLayoutChange(); - } - - if (m_flight) - { - ProcessFlight(msg, wparam); - } - - if (m_focus) - { - ImGui_ImplWin32_WndProcHandler(hwnd, msg, wparam, lparam); - } -} - -void Menu::ToggleFlight(bool flight) -{ - m_flight = flight; - -#if TRAE || TR7 - auto ptr = reinterpret_cast(GAMETRACKER + 227); -#elif TR8 - auto ptr = reinterpret_cast(0xE7F143); -#endif - - if (m_flight) - { - //*ptr |= 256; - *ptr |= 256; - } - else - { - //*ptr ^= 256; - *ptr = 0; - } - -#if TRAE || TR7 - Game::InstancePost(*reinterpret_cast(PLAYERINSTANCE), 1048592, m_flight); -#else - Game::InstancePost(*reinterpret_cast(PLAYERINSTANCE), 12, m_flight); -#endif -} - -void Menu::ProcessFlight(UINT msg, WPARAM wparam) -{ - auto base = *reinterpret_cast(PLAYERINSTANCE); - -#if TRAE || TR7 - auto z = reinterpret_cast(base + 24); -#elif TR8 - auto z = reinterpret_cast(base + 40); -#endif - - auto upKey = m_isAzertyLayout ? 0x41 /* A */ : 0x51 /* Q */; - auto downKey = m_isAzertyLayout ? 0x57 /* W */ : 0x5A /* Z */; - - if (msg == WM_KEYDOWN && wparam == upKey) - { - *z += m_flightSpeed; - } - - if (msg == WM_KEYDOWN && wparam == downKey) - { - *z -= m_flightSpeed; - } -} - -void Menu::Draw() -{ - static bool show_instance_viewer = false; - static bool show_event_debug_viewer = false; - - if (show_instance_viewer) DrawInstanceViewer(&show_instance_viewer); - -#if TRAE || TR7 // event system doesnt exist in Underworld - if (show_event_debug_viewer) DrawEventDebugViewer(&show_event_debug_viewer); -#endif - - ImGui::Begin("Menu", nullptr, ImGuiWindowFlags_MenuBar); - - if (ImGui::BeginMenuBar()) - { - if (ImGui::BeginMenu("Tools")) - { - ImGui::MenuItem("Instance viewer", NULL, &show_instance_viewer); - -#if TRAE || TR7 - ImGui::MenuItem("Event debug", NULL, &show_event_debug_viewer); -#endif - ImGui::EndMenu(); - } - - if (ImGui::BeginMenu("Help")) - { - if (ImGui::MenuItem("GitHub")) - { - ShellExecuteA(0, 0, "https://github.com/TheIndra55/TRAE-menu-hook", 0, 0, SW_SHOW); - } - - ImGui::EndMenu(); - } - - ImGui::EndMenuBar(); - } - - ImGui::Text("Unit = %s, Flight = %s", (char*)GAMETRACKER_BASE_AREA, m_flight ? "true" : "false"); - ImGui::SliderFloat("Z speed", &m_flightSpeed, 0, 500); - - ImGui::Checkbox("No deathfade", &m_drawSettings.noRespawn); -#if TRAE || TR7 - if (ImGui::Checkbox("No cinematic bars", &m_drawSettings.noMovieBars)) - { - SaveSettings(); - } - - if (ImGui::Checkbox("No motion blur", &m_drawSettings.noMotionBlur)) - { - SaveSettings(); - } -#endif - -#if TRAE - if (ImGui::Button("Fill 'er Up")) - { - // pointers everywhere! - auto missingHealth = *(float*)(*(DWORD*)((*(DWORD*)PLAYERINSTANCE) + 448) + 164); - - Game::IncreaseHealth(missingHealth); - Game::TriggerUiFadeGroup(1); - // TODO ammo - } -#endif - -#if TRAE || TR7 - ImGui::Checkbox("Load unit script", &shouldReloc); - - if (ImGui::Checkbox("Enable debug draw", &m_drawSettings.drawDebug)) - { - *(int*)(GAMETRACKER + 0x18) /*debugFlags2*/ |= 0x100; - } - - ImGui::Checkbox("Show load queue", &m_drawSettings.printFileRequests); -#endif - -#if TRAE - ImGui::Checkbox("Enable debug keypad", (bool*)0x7C8A3C); - ImGui::Checkbox("Wireframe", (bool*)0x7C7CD4); -#elif TR7 - ImGui::Checkbox("Enable debug keypad", (bool*)ADDR(0x107F90C, 0x107696C)); - ImGui::Checkbox("Wireframe", (bool*)ADDR(0x107E580, 0x1075BD4)); -#endif - -#if TRAE || TR7 - ImGui::Checkbox("Draw instances", &m_drawSettings.draw); - ImGui::Checkbox("Draw markup", &m_drawSettings.drawMarkup); - ImGui::Checkbox("Draw enemy path", &m_drawSettings.drawPath); - ImGui::Checkbox("Draw collision", &m_drawSettings.drawCollision); - ImGui::Checkbox("Draw non-player collision", &m_drawSettings.drawEnemyCollision); - ImGui::Checkbox("Draw portals", &m_drawSettings.drawPortals); - ImGui::Checkbox("Draw signals", &m_drawSettings.drawSignals); - - if (ImGui::CollapsingHeader("Draw settings")) - { - ImGui::InputText("filter", m_drawSettings.filterName, 100); - - ImGui::Checkbox("Filter family", &m_drawSettings.filter); - ImGui::Checkbox("Draw intro id", &m_drawSettings.drawIntro); - ImGui::Checkbox("Draw animation", &m_drawSettings.drawAnim); - ImGui::Checkbox("Draw pointer address", &m_drawSettings.drawAddress); - ImGui::Checkbox("Draw 'family'", &m_drawSettings.drawFamily); - ImGui::Checkbox("Draw enemy health", &m_drawSettings.drawHealth); - } -#endif - - // unit switch - static char unit[MAX_UNIT_LEN] = ""; - ImGui::InputText("##unit", unit, MAX_UNIT_LEN); - ImGui::SameLine(); - - if (ImGui::BeginCombo("##Unit", nullptr, ImGuiComboFlags_NoPreview)) - { - auto unitList = gUnitList; - - for (int i = 0; i < unitList->numUnits; i++) - { - auto x = &unitList->units[i]; - - if (strlen(x->name) > 0) - { - bool selected = unit == x->name; - - if (ImGui::Selectable(x->name, selected)) - strcpy_s(unit, x->name); - - if (selected) - ImGui::SetItemDefaultFocus(); - } - } - - ImGui::EndCombo(); - } - - if (ImGui::Button("Load unit")) - { - // check if any file exists, this does not check if file is actually a unit - char drmName[256]; - LOAD_UnitFileName(drmName, unit, "drm"); - - if (unit && GetFS()->FileExists(drmName)) - { - // change current unit - strcpy_s((char*)GAMETRACKER_BASE_AREA, MAX_UNIT_LEN, unit); - Game::ResetGame(4); - } - } - -#if TRAE - ImGui::Text("screen: %d", Game::GetTopScreenID()); - - static char screenId[8] = ""; - ImGui::InputText("screen Id", screenId, 8); - if (ImGui::Button("Push screen")) - { - int screen = std::atoi(screenId); - - Game::PushScreen(screen, 0); - } -#endif - - if (ImGui::Button("Clear")) { - this->logBuffer.clear(); - } - -#if TR8 - if (ImGui::Button("Unhide all instances")) - { - // loop trough all instances - for (auto instance = *(Instance**)INSTANCELIST; instance != nullptr; instance = instance->next) - { - // send post message to toggle hide - Game::InstancePost(instance, 7, 0); - } - } -#endif - - if (ImGui::Button("List instances")) - { - // loop trough all instances and log to console - for (auto instance = *(Instance**)INSTANCELIST; instance != nullptr; instance = instance->next) - { - Log("%s - %d\n", instance->object->name, instance->introUniqueID); - } - - // copy console buffer to clipboard - ImGui::SetClipboardText(this->logBuffer.begin()); - } - - auto player = *reinterpret_cast(PLAYERINSTANCE); -#if TRAE - if (ImGui::Button("Real gold lara")) - { - Game::PlayerTurnGold(); - } - - if (ImGui::Button("Give all weapons")) - { - for (int i = 0; i < 3; i++) - { - Game::InstancePost(player, 262256, i); - } - } - - static int item; - ImGui::InputInt("itemId", &item); - if (ImGui::Button("Give item")) - { - Game::InstancePost(player, 262256, item ); - } -#endif - -#if TR7 && RETAIL_VERSION - static int weapon; - ImGui::InputInt("weapon", &weapon); - if (ImGui::Button("Give weapon")) - { - Game::InstancePost(player, 262257, weapon); - } -#endif - - // object select for birthing - static char name[100] = ""; - ImGui::InputText("##object", name, 100); - ImGui::SameLine(); - - if (ImGui::BeginCombo("##Instance", nullptr, ImGuiComboFlags_NoPreview)) - { - auto objectList = gObjectList; - - for (int i = 0; i < objectList->numObjects; i++) - { - auto object = &objectList->objects[i]; - auto selected = object->name == name; - - if (ImGui::Selectable(object->name, selected)) - strcpy_s(name, object->name); - - if (selected) - ImGui::SetItemDefaultFocus(); - } - - ImGui::EndCombo(); - } - - if (ImGui::Button("Birth instance")) - { - // check if object actually exists as file - char drmName[256]; - LOAD_UnitFileName(drmName, name, "drm"); - - if (name && GetFS()->FileExists(drmName)) - { - cdc::Vector position; - cdc::Vector rotation; - if (player != nullptr) - { - position = player->position; - rotation = player->rotation; - } - else - { - // player not found, try first instance - position = (*(Instance**)INSTANCELIST)->position; - } -#if TRAE - auto unitId = *(int*)0x838418; -#elif TR7 - auto unitId = *(int*)(GAMETRACKER + 0xE8); -#elif TR8 - auto unitId = *(int*)((*(int*)PLAYERINSTANCE) + 224); -#endif - - auto tracker = Stream::GetObjectTrackerByName(name); - while (tracker->status != 2 && Stream::PollLoadQueue()); - - Game::BirthObjectNoParent(unitId, &position, &rotation, nullptr, tracker->object, 0, 1); - } - } - -#if TRAE || TR7 - static char outfit[100] = ""; - ImGui::InputText("outfit", outfit, 100); - if (ImGui::Button("Load outfit")) - { - switchPlayerNextFrame = true; - - auto obj = Game::GetObjectID(outfit); - *(int*)(GAMETRACKER + 0x438) /* alt player object */ = obj; - } -#endif - - ImGui::End(); - - ImGui::SetNextWindowSize(ImVec2(500, 100), ImGuiCond_FirstUseEver); - ImGui::Begin("Log", nullptr); - - // log window (based on imgui_demo log window) - ImGui::BeginChild("LogRegion", ImVec2(), true); - - ImGui::TextUnformatted(this->logBuffer.begin()); - ImGui::SetScrollHereY(1.0f); - ImGui::EndChild(); - - ImGui::End(); -} - -void Menu::DrawInstanceViewer(bool* show_instance_viewer) -{ - ImGui::Begin("Instances", show_instance_viewer); - - ImGui::Columns(2, "instances"); - ImGui::BeginChild("InstanceListTree"); - - for (auto instance = *(Instance**)INSTANCELIST; instance != nullptr; instance = instance->next) - { - if (ImGui::TreeNodeEx((void*)instance, ImGuiTreeNodeFlags_Leaf, "%d %s", instance->introUniqueID, instance->object->name)) - { - if (ImGui::IsItemClicked()) - { - clickedInstance = (DWORD)instance; - } - - ImGui::TreePop(); - } - } - ImGui::EndChild(); - - ImGui::NextColumn(); - - if (clickedInstance && *(int*)clickedInstance == 0) - { - // points to nullptr remove - clickedInstance = 0; - } - - if (clickedInstance) - { - auto oInstance = (Instance*)clickedInstance; - -#if TRAE || TR7 - auto object = *(DWORD*)(clickedInstance + 0x94); - auto data = *(DWORD*)(clickedInstance + 448); - auto extraData = *(DWORD*)(clickedInstance + 572); - - auto numAnims = *(__int16*)(object + 0x1A); - auto numModels = *(__int16*)(object + 0x18); - - auto name = (char*)*(DWORD*)(object + 0x48); - auto intro = *(int*)(clickedInstance + 0x1D0); -#elif TR8 - auto object = *(DWORD*)(clickedInstance + 0x10); - auto data = *(DWORD*)(clickedInstance + 80); - - auto numAnims = *(__int16*)(object + 0x38); - auto numModels = *(__int16*)(object + 0x3A); - - auto name = (char*)*(DWORD*)(object + 0x60); - auto intro = *(int*)(clickedInstance + 0x58); -#endif - - ImGui::Text("%s", name); - - auto coords = oInstance->position; - auto rotation = oInstance->rotation; - - ImGui::Text("Position: %f %f %f", coords.x, coords.y, coords.z); - ImGui::Text("Rotation: %f %f %f", rotation.x, rotation.y, rotation.z); - - ImGui::Text("Intro: %d", intro); - - if (data) - { - ImGui::Text("Family %d", *(unsigned __int16*)(data + 2)); - } - - ImGui::Text("Address %p", clickedInstance); - -#if TRAE || TR7 - - if (extraData) - { -#if TRAE - auto health = *(float*)(extraData + 5280); -#elif TR7 - auto health = *(float*)(extraData + 5040); -#endif - ImGui::Text("Health: %8.2f", health); - } - - if (ImGui::Button("Switch down")) - { - Game::InstancePost((Instance*)clickedInstance, 8388753, 1); - } - if (ImGui::Button("Switch up")) - { - Game::InstancePost((Instance*)clickedInstance, 8388753, 2); - } - ImGui::Text("Switch status: %d", Game::InstanceQuery((Instance*)clickedInstance, 233)); -#endif - - auto player = *(Instance**)PLAYERINSTANCE; - if (ImGui::Button("Goto")) - { - player->position = oInstance->position; - } - - if (ImGui::Button("Bring")) - { -#if TRAE || TR7 - oInstance->oldPosition = player->position; -#endif - oInstance->position = player->position; - } - - if (ImGui::Button("Delete")) - { - INSTANCE_ReallyRemoveInstance((Instance*)clickedInstance, 0, 0); - } - - static int message; - static int postdata; - - ImGui::InputInt("msg", &message); - ImGui::InputInt("data", &postdata); - if (ImGui::Button("Post")) - { - Game::InstancePost(oInstance, message, postdata); - } - -#if TRAE || TR7 - static int group; - static int grouptoggle; - ImGui::InputInt("drawgroup", &group); - ImGui::InputInt("drawgrou toggle", &grouptoggle); - - if (ImGui::Button("Toggle drawgroup")) - { - INSTANCE_HideUnhideDrawGroup(oInstance, group, grouptoggle); - } -#endif - - ImGui::Text("numModels %d", numModels); - - static int model; - ImGui::InputInt("model index", &model); - if (ImGui::Button("Set model")) - { - INSTANCE_SetModel(oInstance, model); - } - - ImGui::Text("numAnims %d", numAnims); - - static int anim; - ImGui::InputInt("anim", &anim); - if (ImGui::Button("Play anim")) - { - G2EmulationInstanceSetAnimation(oInstance, 0, anim, 0, 0); - G2EmulationInstanceSetMode(oInstance, 0, 2); - } - -#if TRAE - if (ImGui::Button("Attach camera")) - { - void(__cdecl* CAMERA_SetInstanceFocus)(int a1, Instance* instance); - CAMERA_SetInstanceFocus = reinterpret_cast(0x00489380); - - CAMERA_SetInstanceFocus(0x850670, oInstance); - *(cdc::Vector*)0x850670 = oInstance->position; - - auto cameraMode = (int*)0x850984; - *cameraMode = 6; - } -#endif - -#if TR8 - if (ImGui::Button("Unhide")) - { - Game::InstancePost(oInstance, 7, 0); - } - - static int maxList; - - if (ImGui::Button("List anims")) - { - auto animList = *(int*)(object + 0x44); - for (int i = 0; i < maxList; i++) - { - auto anim = *(char**)(animList + (i * 0x0c) + 0x08); - - Hooking::GetInstance().GetMenu()->Log("%d %s\n", i, anim); - } - } - ImGui::SameLine(); - ImGui::InputInt("num anims", &maxList); -#endif - -#if TRAE || TR7 - auto modelList = *(int*)(object + 0x20); - for (int i = 0; i < numModels; i++) - { - auto model = **(Model**)(modelList + (i * 4)); - - char label[10]; - sprintf_s(label, "model %d", i); - - if (ImGui::CollapsingHeader(label)) - { - ImGui::Text("version %d", model.version); - ImGui::Text("numSegments %d", model.numSegments); - ImGui::Text("numVirtSegments %d", model.numVirtSegments); - ImGui::Text("numVertices %d", model.numVertices); - ImGui::Text("numNormals %d", model.numNormals); - ImGui::Text("numFaces %d", model.numFaces); - } - } -#endif - } - - ImGui::End(); -} - -void Menu::DrawEventDebugViewer(bool* show_event_debug_viewer) const noexcept -{ - ImGui::Begin("Event debug", show_event_debug_viewer); - - auto level = *(Level**)(GAMETRACKER + 8); - auto eventVarVals = *(int**)(GLOBALDATA + 0xE8 /* event vars */); - - if (level && level->reloc) - { - // get the level reloc module - auto reloc = level->reloc; - auto eventVars = reinterpret_cast(RELOC_GetProcAddress(reloc, "EventDebug")); - - if (eventVars) - { - int i = 0; - while (true) - { - auto var = eventVars[i++]; - - if (var.name == nullptr) - break; - - ImGui::Text("%d %s = %d", var.offset, var.name, eventVarVals[var.offset]); - } - - auto unsavedVars = reinterpret_cast(eventVars); - while (true) - { - auto var = unsavedVars[i++]; - - if (var.name == nullptr || var.var == nullptr) - break; - - ImGui::Text("%s %d", var.name, *var.var); - } - - ImGui::Separator(); - } - } - - static int var; - static int value; - ImGui::InputInt("eventVar", &var); - ImGui::InputInt("value", &value); - - if (ImGui::Button("Set")) - { - eventVarVals[var] = value; - } - - ImGui::End(); -} - -Instance* Menu::GetClickedInstance() noexcept -{ - return (Instance*)clickedInstance; -} - -void Menu::Log(const char* fmt, ...) IM_FMTARGS(2) -{ - va_list args; - va_start(args, fmt); - this->logBuffer.appendfv(fmt, args); - va_end(args); -} - -bool Menu::IsFocus() const noexcept -{ - return m_focus; -} - -FreeCameraMode Menu::GetFreeCamMode() const noexcept -{ - return m_freeCamMode; -} - -void Menu::SetFocus(bool value) noexcept -{ - m_focus = value; - - // enable/disable game input -#if TRAE - * (bool*)0x8551A9 = m_focus; -#elif TR8 - * (bool*)0xA02B79 = m_focus; -#elif TR7 - * (bool*)ADDR(0x110AF09, 0x1101689) = m_focus; -#endif -} - -bool Menu::IsVisible() const noexcept -{ - return m_visible; -} - -void Menu::SetVisibility(bool value) noexcept -{ - m_visible = value; -} - -void Menu::OnLayoutChange() noexcept -{ - // thanks to Xwilarg - auto hkl = GetKeyboardLayout(0); - auto id = (int)((unsigned int)hkl & 0x0000FFFF); - m_isAzertyLayout = id == 2060 || id == 1036; -} - -void Menu::LoadSettings() noexcept -{ - HKEY phkResult; - if (!RegOpenKeyA(HKEY_CURRENT_USER, "SOFTWARE\\TRAE-Menu-Hook", &phkResult)) - { - DWORD value; - DWORD len = sizeof(value); - - RegQueryValueExA(phkResult, "NoCinematicBars", 0, 0, (LPBYTE)&value, &len); - m_drawSettings.noMovieBars = value; - - RegQueryValueExA(phkResult, "NoMotionBlur", 0, 0, (LPBYTE)&value, &len); - m_drawSettings.noMotionBlur = value; - - RegCloseKey(phkResult); - } -} - -void Menu::SaveSettings() const noexcept -{ - HKEY phkResult; - if (!RegCreateKeyA(HKEY_CURRENT_USER, "SOFTWARE\\TRAE-Menu-Hook", &phkResult)) - { - DWORD value = m_drawSettings.noMovieBars; - RegSetValueExA(phkResult, "NoCinematicBars", 0, REG_DWORD, (LPBYTE)&value, 4); - - value = m_drawSettings.noMotionBlur; - RegSetValueExA(phkResult, "NoMotionBlur", 0, REG_DWORD, (LPBYTE)&value, 4); - - RegCloseKey(phkResult); - } -} diff --git a/Menu.hpp b/Menu.hpp deleted file mode 100644 index e4dd9aa..0000000 --- a/Menu.hpp +++ /dev/null @@ -1,94 +0,0 @@ -#pragma once -#include -#include - -#include "imgui.h" -#include "vendor/imgui/backends/imgui_impl_dx9.h" -#include "vendor/imgui/backends/imgui_impl_win32.h" - -#include "Game.hpp" - -enum class FreeCameraMode -{ - enabled, - nocontrol, - disabled, -}; - -struct DrawSettings -{ - bool draw = false; - bool filter = true; - bool drawIntro = false; - bool drawAddress = false; - bool drawFamily = false; - bool drawHealth = false; - bool drawAnim = false; - char filterName[100] = ""; - - bool drawDebug = false; - bool drawMarkup = false; - bool drawPath = false; - bool drawCollision = false; - bool drawEnemyCollision = false; - bool drawPortals = false; - bool drawSignals = false; - bool printFileRequests = false; - - // "DrawSettings" - bool noRespawn = false; - bool noMovieBars = false; - bool noMotionBlur = false; -}; - -class Menu -{ -public: - Menu(LPDIRECT3DDEVICE9 pd3dDevice, HWND hwnd); - - void OnDeviceReleased(); - void OnPresent(); - void Process(HWND hwnd, UINT msg, WPARAM wparam, LPARAM lparam); - void ToggleFlight(bool flight); - void SetDevice(LPDIRECT3DDEVICE9 pd3dDevice); - void Log(const char* fmt, ...); - - FreeCameraMode GetFreeCamMode() const noexcept; - bool IsFocus() const noexcept; - bool IsVisible() const noexcept; - void SetFocus(bool value) noexcept; - void SetVisibility(bool value) noexcept; - - void LoadSettings() noexcept; - void SaveSettings() const noexcept; - - Instance* GetClickedInstance() noexcept; - - DrawSettings m_drawSettings; -private: - void Draw(); - void ProcessFlight(UINT msg, WPARAM wparam); - void DrawInstanceViewer(bool* show_instance_viewer); - void DrawEventDebugViewer(bool* show_event_debug_viewer) const noexcept; - - void OnLayoutChange() noexcept; - -private: - bool m_focus = false; - bool m_flight = false; - float m_flightSpeed = 75.f; - - bool m_visible = true; - bool m_isAzertyLayout = false; - - FreeCameraMode m_freeCamMode = FreeCameraMode::disabled; - - ImGuiTextBuffer logBuffer; - - LPDIRECT3DDEVICE9 m_pd3dDevice; - HWND m_hwnd; - - DWORD clickedInstance = 0; -}; - -extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); \ No newline at end of file diff --git a/dllmain.cpp b/dllmain.cpp deleted file mode 100644 index a60db99..0000000 --- a/dllmain.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include "Hooking.hpp" - -static bool hit = false; - -BOOL (WINAPI* dGetVersionExA)(LPSTARTUPINFOA lpStartupInfo); - -BOOL WINAPI hGetVersionExA(LPSTARTUPINFOA lpStartupInfo) -{ - if (!hit) - { - // insert this hook early -#if TRAE - MH_CreateHook((void*)0x45F640, GetFS, nullptr); -#elif TR7 - MH_CreateHook((void*)ADDR(0x45F420, 0x45C700), GetFS, nullptr); -#endif - - MH_EnableHook(GetFS); - - Hooking::GetInstance(); // Will call the ctor - hit = true; - - // TODO MH_RemoveHook - } - - return dGetVersionExA(lpStartupInfo); -} - -bool CheckVersion() -{ - auto hModule = GetModuleHandleA(nullptr); - auto header = (PIMAGE_NT_HEADERS)((DWORD_PTR)hModule + ((PIMAGE_DOS_HEADER)hModule)->e_lfanew); - -#if RETAIL_VERSION - // if retail check if not debug exe timestamp - return header->FileHeader.TimeDateStamp != 0x446DCF16; -#else - // if debug check for debug exe timestamp - return header->FileHeader.TimeDateStamp == 0x446DCF16; -#endif -} - -DWORD WINAPI Hook(LPVOID lpParam) -{ - MH_Initialize(); - -#if TRAE || TR7 - // we cannot insert our hooks now since game is not done yet unpacking - // hook one of the first functions called from unpacked code and insert our hooks then - MH_CreateHookApi(L"Kernel32", "GetStartupInfoA", hGetVersionExA, reinterpret_cast(&dGetVersionExA)); - MH_EnableHook(MH_ALL_HOOKS); -#else - MH_CreateHook((void*)0x478930, InitPatchArchive, (void**)&orgInitPatchArchive); - MH_EnableHook(InitPatchArchive); - - Hooking::GetInstance(); // Will call the ctor -#endif - - return 0; -} - -BOOL APIENTRY DllMain(HMODULE hModule, DWORD ul_reason_for_call, LPVOID lpReserved) -{ - // since there could be retail and debug asi in same folder - // unload early if exe timestamp is wrong one -#if TR7 - if (!CheckVersion()) - { - return FALSE; - } -#endif - - switch (ul_reason_for_call) - { - case DLL_PROCESS_ATTACH: - //CreateThread(nullptr, 0, Hook, NULL, 0, NULL); - Hook(NULL); - break; - case DLL_THREAD_ATTACH: - case DLL_THREAD_DETACH: - case DLL_PROCESS_DETACH: - break; - } - - return TRUE; -} - diff --git a/game/README.md b/game/README.md deleted file mode 100644 index f689135..0000000 --- a/game/README.md +++ /dev/null @@ -1,9 +0,0 @@ -# Game - -* **script/** Underworld and above script stuff -* **filesystem.hpp** FileSystem header -* **font.hpp** Game font functions -* **hookfilesystem.cpp** Mod loader FileSystem -* **msfilesystem.hpp** Game Microsoft FileSystem header -* **multifilesystem.cpp** Simplified MultiFileSystem -* **reloc.cpp** Part of the game PE loader for the event system \ No newline at end of file diff --git a/game/d3d/d3dterrain.cpp b/game/d3d/d3dterrain.cpp deleted file mode 100644 index a341509..0000000 --- a/game/d3d/d3dterrain.cpp +++ /dev/null @@ -1,27 +0,0 @@ -#ifndef TR8 -#include "d3dterrain.hpp" -#include "../../Hooking.hpp" - -TerrainDrawable*(__thiscall* origTerrainDrawable__TerrainDrawable)(TerrainDrawable* _this, XboxPcMaterialStripList* material, TerrainTextureStripInfo* strip, Terrain* terrain, TerrainGroup* terraingroup); - -TerrainDrawable* __fastcall TerrainDrawable__TerrainDrawable(TerrainDrawable* _this, int _, XboxPcMaterialStripList* material, TerrainTextureStripInfo* strip, Terrain* terrain, TerrainGroup* terraingroup) -{ - auto ret = origTerrainDrawable__TerrainDrawable(_this, material, strip, terrain, terraingroup); - - if (gWireFrame) - { - _this->tpageid |= 0x800; - } - - return ret; -} -#endif - -void InsertTerrainDrawableHooks() -{ -#if TRAE - MH_CreateHook((void*)0x40B9B0, TerrainDrawable__TerrainDrawable, (void**)&origTerrainDrawable__TerrainDrawable); -#elif TR7 - MH_CreateHook((void*)ADDR(0x40B2C0, 0x40ACF0), TerrainDrawable__TerrainDrawable, (void**)&origTerrainDrawable__TerrainDrawable); -#endif -} diff --git a/game/d3d/d3dterrain.hpp b/game/d3d/d3dterrain.hpp deleted file mode 100644 index 6a104e7..0000000 --- a/game/d3d/d3dterrain.hpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "../../Game.hpp" -#include "../../util.hpp" - -struct TerrainTextureStripInfo -{ - int vertexCount; - int vmoObjectIndex; - float texScrollOffset; - unsigned int lightFlags; -}; - -struct XboxPcMaterialStripList -{ - unsigned int tpageid; - unsigned int flags; - unsigned int vbBaseOffset; - unsigned int numStrips; -}; - -struct TerrainDrawable -{ - // cdc::PCDrawable shit - int __vftable; - int scene; - float sortZ; - - // TerrainDrawable - XboxPcMaterialStripList* material; - TerrainTextureStripInfo* textureStrip; - Terrain* terrain; - TerrainGroup* terrainGroup; - unsigned int tpageid; - void* indexBuffer; - void* vertexShader; - void* renderList; - unsigned int indices; -}; - -void InsertTerrainDrawableHooks(); - -#if TRAE - -#define gTextureLess VAR_U_(0x7C7CD0, int) -#define gWireFrame VAR_U_(0x7C7CD4, int) - -#elif TR7 - -#define gTextureLess VAR_U_(ADDR(0x107E57C, 0x1075BD0), int) -#define gWireFrame VAR_U_(ADDR(0x107E580, 0x1075BD4), int) - -#endif diff --git a/game/event.hpp b/game/event.hpp deleted file mode 100644 index 7592b44..0000000 --- a/game/event.hpp +++ /dev/null @@ -1,17 +0,0 @@ -#pragma once - -// code related to the level event system which control the level flow - -struct EventVar -{ - char* name; - int offset; -}; - -struct UnsavedVar -{ - char* name; - int* var; -}; - -extern int(__cdecl* RELOC_GetProcAddress)(int reloc, const char* symbol); diff --git a/game/filesystem.hpp b/game/filesystem.hpp deleted file mode 100644 index eda6b20..0000000 --- a/game/filesystem.hpp +++ /dev/null @@ -1,31 +0,0 @@ -#pragma once - -namespace cdc -{ - class FileSystem - { - public: - virtual void* RequestRead(void* receiver, const char* name, unsigned int startOffset) = 0; - virtual void* OpenFile(char const* fileName) = 0; - virtual bool FileExists(char const* fileName) = 0; - virtual unsigned int GetFileSize(char const* fileName) = 0; - virtual void SetSpecialisationMask(unsigned int specMask) = 0; - virtual unsigned int GetSpecialisationMask() = 0; - virtual int GetStatus() = 0; - virtual void Update() = 0; - virtual void Synchronize() = 0; -#if TR8 - virtual void Suspend() = 0; - virtual bool Resume() = 0; - virtual bool IsSuspended() = 0; -#endif - }; -} - -cdc::FileSystem* CreateHookFileSystem(cdc::FileSystem* pDiskFS); -cdc::FileSystem* CreateMultiFileSystem(cdc::FileSystem* pFS, cdc::FileSystem* pDiskFS); - -#define g_pDiskFS VAR_U_(DISKFS, cdc::FileSystem*) -#define g_pFS VAR_U_(ARCHIVEFS, cdc::FileSystem*) - -cdc::FileSystem* GetFS(); \ No newline at end of file diff --git a/game/font.cpp b/game/font.cpp deleted file mode 100644 index fe382aa..0000000 --- a/game/font.cpp +++ /dev/null @@ -1,87 +0,0 @@ -#include -#include -#include "font.hpp" - -// required for ADDR macro -#include "../Game.hpp" - -// orginal function pointers - -void(__thiscall* Font__PrintFormatted)(void* font, const char* formatted, bool backdrop); -void(__cdecl* Font__SetAlpha)(char alpha); -void(__cdecl* Font__SetScale)(float scaleX, float scaleY); -void(__cdecl* Font__SetRGBA)(unsigned int rgba); -float(__thiscall* Font__GetTextWidth)(void* font, const char* text); -float(__thiscall* Font__GetHeight)(void* font); -void(__cdecl* Font__SetCursor)(float x, float y); - -// not related to the real Font::Init but for us to define our functions pointers -void FONT_Init() -{ -#if TRAE - Font__PrintFormatted = reinterpret_cast(0x434A70); - Font__SetAlpha = reinterpret_cast(0x433F30); - Font__SetScale = reinterpret_cast(0x433E60); - Font__SetRGBA = reinterpret_cast(0x433F20); - Font__GetTextWidth = reinterpret_cast(0x434510); - Font__GetHeight = reinterpret_cast(0x434440); - Font__SetCursor = reinterpret_cast(0x433C70); -#elif TR7 - Font__PrintFormatted = reinterpret_cast(ADDR(0x434E80, 0x4323D0)); - Font__SetAlpha = reinterpret_cast(ADDR(0x4343E0, 0x431930)); - Font__SetScale = reinterpret_cast(ADDR(0x4342E0, 0x431860)); - Font__SetRGBA = reinterpret_cast(ADDR(0x4343D0, 0x431920)); - Font__GetTextWidth = reinterpret_cast(ADDR(0x434950, 0x431EA0)); - Font__GetHeight = reinterpret_cast(ADDR(0x4348D0, 0x431E20)); - Font__SetCursor = reinterpret_cast(ADDR(0x4340F0, 0x431670)); -#endif -} - -// font function wrappers around orginal ones, for both readability and sake of not having to pass font instance - -void FONT_Print(const char* fmt, ...) -{ - va_list va; - char formatted[1024]; - - va_start(va, fmt); - vsprintf_s(formatted, fmt, va); - va_end(va); - - Font__PrintFormatted(gMainFont, formatted, false); -} - -void FONT_PrintFormatted(const char* formatted, bool backdrop) -{ - Font__PrintFormatted(gMainFont, formatted, backdrop); -} - -void FONT_SetAlpha(char alpha) -{ - Font__SetAlpha(alpha); -} - -void FONT_SetScale(float scaleX, float scaleY) -{ - Font__SetScale(scaleX, scaleY); -} - -void FONT_SetRGBA(unsigned int rgba) -{ - Font__SetRGBA(rgba); -} - -float FONT_GetTextWidth(const char* text) -{ - return Font__GetTextWidth(gMainFont, text); -} - -float FONT_GetHeight() -{ - return Font__GetHeight(gMainFont); -} - -void FONT_SetCursor(float x, float y) -{ - return Font__SetCursor(x, y); -} \ No newline at end of file diff --git a/game/font.hpp b/game/font.hpp deleted file mode 100644 index b638bd9..0000000 --- a/game/font.hpp +++ /dev/null @@ -1,19 +0,0 @@ -#pragma once - -void FONT_Init(); -void FONT_Print(const char* fmt, ...); -void FONT_PrintFormatted(const char* formatted, bool backdrop = false); -void FONT_SetAlpha(char alpha); -void FONT_SetScale(float scaleX, float scaleY); -void FONT_SetRGBA(unsigned int rgba); -float FONT_GetTextWidth(const char* text); -float FONT_GetHeight(); -void FONT_SetCursor(float x, float y); - -#if TRAE - #define gMainFont *(void**)0x7D1800 -#elif TR7 - #define gMainFont *(void**)ADDR(0x1088A2C, 0x107F680) -#elif TR8 - #define gMainFont *(void**)0x9DE6D4 -#endif diff --git a/game/hookfilesystem.cpp b/game/hookfilesystem.cpp deleted file mode 100644 index 2b0a7d3..0000000 --- a/game/hookfilesystem.cpp +++ /dev/null @@ -1,147 +0,0 @@ -#include "filesystem.hpp" - -#include -#include -#include - -// so we don't need to include -#define _MAX_PATH 260 - -// the HookFileSystem will look for a file in the mods folder and rewrite the path -// for the disk filesystem to open, allowing it to load files without a bigfile - -class HookFileSystem : public cdc::FileSystem -{ -private: - cdc::FileSystem* m_pDiskFS; - - unsigned int m_specMask; - -public: - HookFileSystem(cdc::FileSystem* pDiskFS) - { - m_pDiskFS = pDiskFS; - m_specMask = 1; - } - - // finds a file in the mods folder and rewrites path to it - bool FindFile(const char* fileName, char* path) - { - strcpy_s(path, _MAX_PATH, fileName); - - for (char* p = path; *p; ++p) *p = tolower(*p); - - // rewrite pc-w to mods folder - if (strncmp(path, "pc-w", 4) == 0) - { - strncpy(path, "mods", 4); - } - - // rewrite root to mods folder - if (strncmp(path, "\\", 1) == 0) - { - // move string 4 bytes - memmove(path + 4, path, _MAX_PATH - 4); - strncpy(path, "mods", 4); - } - - // check first for file on disk suffixed by our specialisation mask - char specPath[_MAX_PATH]; - sprintf_s(specPath, "%s_%03d", path, m_specMask); - - if (m_pDiskFS->FileExists(specPath)) - { - strcpy_s(path, _MAX_PATH, specPath); - - return true; - } - - // check if file exists on disk, if so return the diskFS - if (m_pDiskFS->FileExists(path)) - { - return true; - } - - return false; - } - - virtual void* RequestRead(void* receiver, const char* fileName, unsigned int startOffset) - { - char path[_MAX_PATH]; - FindFile(fileName, path); - - return m_pDiskFS->RequestRead(receiver, path, startOffset); - } - - virtual void* OpenFile(char const* fileName) - { - char path[_MAX_PATH]; - FindFile(fileName, path); - - return m_pDiskFS->OpenFile(path); - } - - virtual bool FileExists(char const* fileName) - { - char path[_MAX_PATH]; - return FindFile(fileName, path); - } - - virtual unsigned int GetFileSize(char const* fileName) - { - char path[_MAX_PATH]; - FindFile(fileName, path); - - return m_pDiskFS->GetFileSize(path); - } - - virtual void SetSpecialisationMask(unsigned int specMask) - { - m_pDiskFS->SetSpecialisationMask(specMask); - - // unset next generation bit and set our spec mask - m_specMask = specMask & ~0x80000000; - } - - virtual unsigned int GetSpecialisationMask() - { - return 0; - } - - virtual int GetStatus() - { - return m_pDiskFS->GetStatus(); - } - - virtual void Update() - { - m_pDiskFS->Update(); - } - - virtual void Synchronize() - { - m_pDiskFS->Synchronize(); - } - -#if TR8 - virtual void Suspend() - { - m_pDiskFS->Suspend(); - } - - virtual bool Resume() - { - return m_pDiskFS->Resume(); - } - - virtual bool IsSuspended() - { - return m_pDiskFS->IsSuspended(); - } -#endif -}; - -cdc::FileSystem* CreateHookFileSystem(cdc::FileSystem* pDiskFS) -{ - return new HookFileSystem(pDiskFS); -} diff --git a/game/markup.hpp b/game/markup.hpp deleted file mode 100644 index e3dc822..0000000 --- a/game/markup.hpp +++ /dev/null @@ -1,58 +0,0 @@ -#pragma once - -#include "../Game.hpp" - -struct PolyLine -{ - int numPoints; - char pad[12]; - cdc::Vector points[1]; -}; - -struct CameraAntic -{ - char pad[24]; -}; - -struct MarkUp -{ - int OverrideMovementCamera; - int DTPCameraDataID; - int DTPMarkupDataID; -#if TRAE - int AnimatedSegment; - CameraAntic cameraAntic; -#endif - unsigned int flags; - __int16 introID; - __int16 markupID; - float position[3]; - __int16 bbox[6]; - PolyLine* polyLine; -}; - -struct MarkUpBox -{ - MarkUpBox* prev; - MarkUpBox* next; - void* sapbox; -#if TRAE - MarkUp* markup; - cdc::Vector velocity; - Instance* instance; - int instanceID; -#else - Instance* instance; - cdc::Vector velocity; - MarkUp* markup; -#endif - unsigned int flags; - float attach_delay; - float attach_frames; -#if TRAE - unsigned int matrix_index; -#endif - unsigned int debugColor; - int nSegments; - void* associateRef; -}; diff --git a/game/msfilesystem.hpp b/game/msfilesystem.hpp deleted file mode 100644 index 19949bf..0000000 --- a/game/msfilesystem.hpp +++ /dev/null @@ -1,23 +0,0 @@ -namespace cdc -{ - struct MSFileSystem - { - struct Request - { - char pad1[20]; - char m_pFileName[128]; - char pad2[20]; - unsigned int m_bytesRead; - unsigned int m_bytesProcessed; - int m_readState; - unsigned int m_offset; - unsigned int m_size; - Request* m_next; - }; - - char pad[1099812]; - Request* m_queue; - Request* m_free; - unsigned int m_numUsedRequests; - }; -} \ No newline at end of file diff --git a/game/multifilesystem.cpp b/game/multifilesystem.cpp deleted file mode 100644 index 38a52c0..0000000 --- a/game/multifilesystem.cpp +++ /dev/null @@ -1,91 +0,0 @@ -#include "filesystem.hpp" - -// very simplified version of cdc::MultiFileSystem since it does not exist in Legend and Anniversary -// the real MultiFileSystem can dynamically add and re-order filesystems - -class MultiFileSystem : public cdc::FileSystem -{ -private: - cdc::FileSystem* m_pFS; - cdc::FileSystem* m_pHookFS; - -public: - MultiFileSystem(cdc::FileSystem* pFS, cdc::FileSystem* pHookFS) - { - m_pFS = pFS; - m_pHookFS = pHookFS; - } - - cdc::FileSystem* GetBestFileSystem(const char* fileName) - { - // first check hook filesystem, else fall back to default filesystem - if (m_pHookFS->FileExists(fileName)) - { - return m_pHookFS; - } - - return m_pFS; - } - - virtual void* RequestRead(void* receiver, const char* fileName, unsigned int startOffset) - { - auto pFS = GetBestFileSystem(fileName); - - return pFS->RequestRead(receiver, fileName, startOffset); - } - - virtual void* OpenFile(char const* fileName) - { - auto pFS = GetBestFileSystem(fileName); - - return pFS->OpenFile(fileName); - } - - virtual bool FileExists(char const* fileName) - { - return m_pFS->FileExists(fileName) || m_pHookFS->FileExists(fileName); - } - - virtual unsigned int GetFileSize(char const* fileName) - { - auto pFS = GetBestFileSystem(fileName); - - return pFS->GetFileSize(fileName); - } - - virtual void SetSpecialisationMask(unsigned int specMask) - { - m_pFS->SetSpecialisationMask(specMask); - m_pHookFS->SetSpecialisationMask(specMask); - } - - virtual unsigned int GetSpecialisationMask() - { - return m_pFS->GetSpecialisationMask(); - } - - // only need to call the default filesystem, both will end at the same place - virtual int GetStatus() - { - return m_pFS->GetStatus(); - } - - virtual void Update() - { - m_pFS->Update(); - } - - virtual void Synchronize() - { - m_pFS->Synchronize(); - } -}; - -#ifndef TR8 -cdc::FileSystem* CreateMultiFileSystem(cdc::FileSystem* pFS, cdc::FileSystem* pDiskFS) -{ - auto pHookFS = CreateHookFileSystem(pDiskFS); - - return new MultiFileSystem(pFS, pHookFS); -} -#endif diff --git a/game/obtable.hpp b/game/obtable.hpp deleted file mode 100644 index 4efc6cf..0000000 --- a/game/obtable.hpp +++ /dev/null @@ -1,26 +0,0 @@ -#pragma once - -#if TRAE - #define gObjectList *(ObjectList**)0x842C70 -#elif TR7 - #define gObjectList *(ObjectList**)ADDR(0x10F9110, 0x10EFC90) -#elif TR8 - #define gObjectList *(ObjectList**)0xDB94D0 -#endif - -struct ObjectEntry -{ -#if TRAE || TR7 - char* name; - int index; -#elif TR8 - int index; - char* name; -#endif -}; - -struct ObjectList -{ - int numObjects; - ObjectEntry objects[1]; -}; \ No newline at end of file diff --git a/game/reloc.cpp b/game/reloc.cpp deleted file mode 100644 index a485026..0000000 --- a/game/reloc.cpp +++ /dev/null @@ -1,70 +0,0 @@ -#include "reloc.hpp" - -int __cdecl MakePeHandle(_IMAGE_DOS_HEADER* peData, PEHANDLE* pe) -{ - pe->lpImage = peData; - - if (peData->e_magic != IMAGE_DOS_SIGNATURE) - { - return RELOC_ERROR_NON_EXECUTABLE; - } - - IMAGE_NT_HEADERS* header = (IMAGE_NT_HEADERS*)((int)peData + peData->e_lfanew); - pe->lpHeader = header; - pe->firstReloc = 1; - - if (header->Signature != IMAGE_NT_SIGNATURE) - { - if (header->Signature != 0x4551) - { - return RELOC_ERROR_NON_EXECUTABLE; - } - - pe->firstReloc = 0; - } - - if (header->FileHeader.Machine != IMAGE_FILE_MACHINE_I386) - { - return RELOC_ERROR_NON_EXECUTABLE; - } - - auto flags = header->FileHeader.Characteristics; - - if ((flags & IMAGE_FILE_EXECUTABLE_IMAGE) == 0 || (flags & IMAGE_FILE_DLL) == 0) - { - return RELOC_ERROR_NON_EXECUTABLE; - } - - pe->lpSectionTable = IMAGE_FIRST_SECTION(header); - - if (header->FileHeader.NumberOfSections <= 0) - { - // not sure why this would be success, but as in orginal game code - return RELOC_SUCCESS; - } - - for (int i = 0; i < header->FileHeader.NumberOfSections; i++) - { - auto section = pe->lpSectionTable[i]; - - if (section.Misc.VirtualSize > section.SizeOfRawData) - { - // actually valid in Windows PE loader, but rejected in the orginal game code - return RELOC_ERROR_INVALID_SECTION; - } - -#if GENERAL_FIXES - // check if section is executable - if (pe->firstReloc && (section.Characteristics & IMAGE_SCN_CNT_CODE) != 0) - { - // mark the section memory as PAGE_EXECUTE so DEP isn't triggered - DWORD lpflOldProtect; - VirtualProtect((void*)((int)peData + section.PointerToRawData), section.SizeOfRawData, PAGE_EXECUTE_READWRITE, &lpflOldProtect); - } -#endif - - pe->lpSectionTable[i].Misc.VirtualSize = section.SizeOfRawData; - } - - return RELOC_SUCCESS; -} diff --git a/game/reloc.hpp b/game/reloc.hpp deleted file mode 100644 index 1079dea..0000000 --- a/game/reloc.hpp +++ /dev/null @@ -1,20 +0,0 @@ -#pragma once -#include - -struct PEHANDLE -{ - IMAGE_NT_HEADERS* lpHeader; - IMAGE_SECTION_HEADER* lpSectionTable; - int firstReloc; - IMAGE_DOS_HEADER* lpImage; -}; - -// guessed enum, does not exist in pdb -enum RelocCode -{ - RELOC_ERROR_INVALID_SECTION = -1, - RELOC_SUCCESS = 0, - RELOC_ERROR_NON_EXECUTABLE = 3, -}; - -int __cdecl MakePeHandle(_IMAGE_DOS_HEADER* peData, PEHANDLE* pe); diff --git a/game/script/script.cpp b/game/script/script.cpp deleted file mode 100644 index 982b259..0000000 --- a/game/script/script.cpp +++ /dev/null @@ -1,77 +0,0 @@ -#include "../../Hooking.hpp" -#include "script.hpp" - -void __stdcall ScriptPrintInt(int value, int time, bool appendNewLine) -{ - auto menu = Hooking::GetInstance().GetMenu(); - - menu->Log("%d", value); - - if (appendNewLine) - { - menu->Log("\n"); - } -} - -void __stdcall ScriptPrintFloat(float value, int time, bool appendNewLine) -{ - // these should actually print on the screen but didn't look into Font:: code yet so now they log to console - auto menu = Hooking::GetInstance().GetMenu(); - - menu->Log("%f", value); - - if (appendNewLine) - { - menu->Log("\n"); - } -} - -void __cdecl NsCoreBase_PrintString(int instance, int numArgs, void** args) -{ - auto menu = Hooking::GetInstance().GetMenu(); - - menu->Log("%s", (char*)args[0]); - - // time is args[1] - - if ((__int8)args[1] != 0) - { - menu->Log("\n"); - } -} - -void __stdcall ScriptLogInt(int value, bool appendNewLine) -{ - auto menu = Hooking::GetInstance().GetMenu(); - - menu->Log("%d", value); - - if (appendNewLine) - { - menu->Log("\n"); - } -} - -void __stdcall ScriptLogFloat(float value, bool appendNewLine) -{ - auto menu = Hooking::GetInstance().GetMenu(); - - menu->Log("%f", value); - - if (appendNewLine) - { - menu->Log("\n"); - } -} - -void __cdecl NsCoreBase_LogString(int instance, int numArgs, void** args) -{ - auto menu = Hooking::GetInstance().GetMenu(); - - menu->Log("%s", (char*)args[0]); - - if ((__int8)args[1] != 0) - { - menu->Log("\n"); - } -} \ No newline at end of file diff --git a/game/script/script.hpp b/game/script/script.hpp deleted file mode 100644 index a293618..0000000 --- a/game/script/script.hpp +++ /dev/null @@ -1,13 +0,0 @@ -#pragma once - -void __stdcall ScriptPrintInt(int value, int time, bool appendNewLine); - -void __stdcall ScriptPrintFloat(float value, int time, bool appendNewLine); - -void __cdecl NsCoreBase_PrintString(int instance, int numArgs, void** args); - -void __stdcall ScriptLogInt(int value, bool appendNewLine); - -void __stdcall ScriptLogFloat(float value, bool appendNewLine); - -void __cdecl NsCoreBase_LogString(int instance, int numArgs, void** args); \ No newline at end of file diff --git a/game/stream/stream.hpp b/game/stream/stream.hpp deleted file mode 100644 index 1d8ae0d..0000000 --- a/game/stream/stream.hpp +++ /dev/null @@ -1,23 +0,0 @@ -#pragma once - -#include "../../Game.hpp" - -#if TRAE - #define gUnitList *(StreamUnitList**)0x8AF44C -#elif TR7 - #define gUnitList *(StreamUnitList**)ADDR(0x1162C54, 0x1159314) -#elif TR8 - #define gUnitList *(StreamUnitList**)0xDBA188 -#endif - -struct UnitInfo -{ - char name[MAX_UNIT_LEN]; - void* objectList; -}; - -struct StreamUnitList -{ - int numUnits; - UnitInfo units[1]; -}; \ No newline at end of file diff --git a/premake5.lua b/premake5.lua index ef06c36..e442d01 100644 --- a/premake5.lua +++ b/premake5.lua @@ -1,65 +1,43 @@ workspace "TRAE-menu-hook" architecture "x86" configurations { "Debug", "Release" } - platforms { "TR7", "TR7DEBUG", "TRAE", "TR8" } project "TRAE-menu-hook" kind "SharedLib" - targetextension ".asi" - - files { - "*.hpp", - "*.cpp", - "**/*.cpp", - "**/*.hpp" - } - - -- ignore all vendor files in above glob - removefiles { "vendor/**" } - - -- imgui files - files { - "vendor/imgui/*.cpp", - "vendor/imgui/*.h", - "vendor/imgui/backends/imgui_impl_win32.cpp", - "vendor/imgui/backends/imgui_impl_win32.h", - "vendor/imgui/backends/imgui_impl_dx9.cpp", - "vendor/imgui/backends/imgui_impl_dx9.h" - } - - includedirs { "vendor/imgui", "vendor/json/include" } - - files "vendor/MinHook.h" + targetextension ".asi" language "C++" - links { "d3d9.lib" } - - -- Xinput disabled since it causes issues with Windows 7 - defines { "IMGUI_IMPL_WIN32_DISABLE_GAMEPAD", "GENERAL_FIXES" } - + cppdialect "C++17" + + links { "d3d9.lib" } + + -- Source files' + files "src/**" + includedirs { "src" } + + -- Vendor files + files { + "vendor/minhook/src/**", + "vendor/patterns/*.cpp", + "vendor/imgui/*.cpp", + "vendor/imgui/backends/imgui_impl_win32.cpp", + "vendor/imgui/backends/imgui_impl_dx9.cpp" + } + + includedirs { + "vendor/minhook/include", + "vendor/patterns", + "vendor/imgui", + "vendor/imgui/backends", + "vendor/json/include" + } + + -- Build configurations + symbols "On" + filter "configurations:Debug" defines { "DEBUG", "_DEBUG" } - links { "libMinHook-mdd-x86.lib" } - symbols "On" filter "configurations:Release" defines { "NDEBUG" } - links { "libMinHook-md-x86.lib" } - optimize "On" - symbols "On" - - filter "platforms:TR7" - defines { "TR7", "RETAIL_VERSION" } - targetname "TR7-Menu-Hook" - - filter "platforms:TR7DEBUG" - defines { "TR7" } - targetname "TR7DEBUG-Menu-Hook" - - filter "platforms:TRAE" - defines { "TRAE" } - targetname "TRAE-Menu-Hook" - - filter "platforms:TR8" - defines { "TR8" } - targetname "TR8-Menu-Hook" \ No newline at end of file + optimize "On" \ No newline at end of file diff --git a/src/Hook.cpp b/src/Hook.cpp new file mode 100644 index 0000000..eae8843 --- /dev/null +++ b/src/Hook.cpp @@ -0,0 +1,44 @@ +#include + +#include "Hook.h" + +#include "cdc/render/PCDeviceManager.h" + +static bool(*s_D3D_Init)(); + +static bool D3D_Init() +{ + // Initialize the device + auto ret = s_D3D_Init(); + + Hook::GetInstance().OnDevice(); + + return ret; +} + +Hook::Hook() : m_menu(nullptr) +{ + Initialize(); +} + +void Hook::Initialize() +{ + MH_Initialize(); + MH_CreateHook((void*)0x4153E0, D3D_Init, (void**)&s_D3D_Init); + MH_EnableHook(MH_ALL_HOOKS); +} + +void Hook::OnDevice() +{ + // Assign the DeviceManager instance + cdc::PCDeviceManager::s_pInstance = *(cdc::PCDeviceManager**)0xA6669C; + + // Creat the menu + m_menu = std::make_unique(); +} + +Hook& Hook::GetInstance() +{ + static Hook instance; + return instance; +} \ No newline at end of file diff --git a/src/Hook.h b/src/Hook.h new file mode 100644 index 0000000..f738bc7 --- /dev/null +++ b/src/Hook.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +#include "menu/Menu.h" + +class Hook +{ +private: + std::unique_ptr m_menu; + + void Initialize(); + +public: + Hook(); + + void OnDevice(); + + static Hook& GetInstance(); +}; \ No newline at end of file diff --git a/src/Main.cpp b/src/Main.cpp new file mode 100644 index 0000000..395b26e --- /dev/null +++ b/src/Main.cpp @@ -0,0 +1,20 @@ +#include + +#include "Hook.h" + +BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) +{ + switch (fdwReason) + { + case DLL_PROCESS_ATTACH: + Hook::GetInstance(); + + break; + case DLL_THREAD_ATTACH: + case DLL_THREAD_DETACH: + case DLL_PROCESS_DETACH: + break; + } + + return TRUE; +} \ No newline at end of file diff --git a/src/cdc/render/PCDeviceManager.cpp b/src/cdc/render/PCDeviceManager.cpp new file mode 100644 index 0000000..ed595e5 --- /dev/null +++ b/src/cdc/render/PCDeviceManager.cpp @@ -0,0 +1,30 @@ +#include "PCDeviceManager.h" + +cdc::PCDeviceManager* cdc::PCDeviceManager::s_pInstance = nullptr; + +bool cdc::PCDeviceManager::IsStatusOk() +{ + return m_status == kStatusOk; +} + +bool cdc::PCDeviceManager::IsCreatingResources() +{ + return m_bIsRecreatingResources; +} + +IDirect3DDevice9* cdc::PCDeviceManager::GetD3DDevice() +{ + return m_pD3DDevice; +} + +HWND cdc::PCDeviceManager::GetWindow() +{ + return m_hFocusWindow; +} + +void cdc::PCDeviceManager::AddDeviceResource(PCInternalResource* resource) +{ + auto func = reinterpret_cast(0x00616F10); + + func(this, resource); +} \ No newline at end of file diff --git a/src/cdc/render/PCDeviceManager.h b/src/cdc/render/PCDeviceManager.h new file mode 100644 index 0000000..0e58291 --- /dev/null +++ b/src/cdc/render/PCDeviceManager.h @@ -0,0 +1,42 @@ +#pragma once + +#include +#include + +#include "PCInternalResource.h" + +namespace cdc +{ + class PCDeviceManager + { + public: + enum Status + { + kStatusOk + }; + + private: + char pad1[16]; + + Status m_status; + HWND m_hFocusWindow; + + char pad2[8]; + + IDirect3DDevice9* m_pD3DDevice; + D3DCAPS9 m_d3dCaps; + + bool m_bIsRecreatingResources; + + public: + bool IsStatusOk(); + bool IsCreatingResources(); + + IDirect3DDevice9* GetD3DDevice(); + HWND GetWindow(); + + void AddDeviceResource(PCInternalResource* resource); + + static PCDeviceManager* s_pInstance; + }; +} \ No newline at end of file diff --git a/src/cdc/render/PCInternalResource.cpp b/src/cdc/render/PCInternalResource.cpp new file mode 100644 index 0000000..285b7a4 --- /dev/null +++ b/src/cdc/render/PCInternalResource.cpp @@ -0,0 +1,21 @@ +#include "PCInternalResource.h" +#include "PCDeviceManager.h" + +cdc::PCInternalResource::PCInternalResource() : m_pPrev(nullptr), m_pNext(nullptr) +{ + PCDeviceManager::s_pInstance->AddDeviceResource(this); +} + +cdc::PCInternalResource::~PCInternalResource() +{ +} + +void cdc::PCInternalResource::OnConstruct() +{ + auto deviceManager = cdc::PCDeviceManager::s_pInstance; + + if (deviceManager->IsStatusOk() && !deviceManager->IsCreatingResources()) + { + OnCreateDevice(); + } +} \ No newline at end of file diff --git a/src/cdc/render/PCInternalResource.h b/src/cdc/render/PCInternalResource.h new file mode 100644 index 0000000..67403e2 --- /dev/null +++ b/src/cdc/render/PCInternalResource.h @@ -0,0 +1,21 @@ +#pragma once + +namespace cdc +{ + class PCInternalResource + { + private: + PCInternalResource* m_pPrev; + PCInternalResource* m_pNext; + + protected: + void OnConstruct(); + + public: + PCInternalResource(); + + virtual ~PCInternalResource(); + virtual bool OnCreateDevice() = 0; + virtual void OnDestroyDevice() = 0; + }; +} \ No newline at end of file diff --git a/src/menu/Menu.cpp b/src/menu/Menu.cpp new file mode 100644 index 0000000..6ff7c8f --- /dev/null +++ b/src/menu/Menu.cpp @@ -0,0 +1,67 @@ +#include +#include +#include + +#include "Menu.h" +#include "render/RenderContext.h" + +#include "cdc/render/PCDeviceManager.h" + +Menu::Menu() : PCInternalResource() +{ + ImGui::CreateContext(); + + // We can do this since we always create the menu after D3D_Init + ImGui_ImplWin32_Init(cdc::PCDeviceManager::s_pInstance->GetWindow()); + + // Add present callback to draw the UI + RenderContext::OnPresent([this] { OnPresent(); }); + + OnConstruct(); +} + +bool Menu::OnCreateDevice() +{ + // Initialize the DirectX9 backend for ImGui + ImGui_ImplDX9_Init(cdc::PCDeviceManager::s_pInstance->GetD3DDevice()); + + m_initialized = true; + + return true; +} + +void Menu::OnDestroyDevice() +{ + if (m_initialized) + { + // We need to destroy the full backend since the game destroys the full device + // not only call reset + ImGui_ImplDX9_Shutdown(); + + m_initialized = false; + } +} + +void Menu::OnPresent() +{ + ImGui_ImplDX9_NewFrame(); + ImGui_ImplWin32_NewFrame(); + + ImGui::NewFrame(); + + // Draw the menu + Draw(); + + // End the frame + ImGui::EndFrame(); + + ImGui::Render(); + ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); +} + +void Menu::Draw() +{ + ImGui::Begin("Menu"); + ImGui::Text("Hello, World!"); + ImGui::End(); +} \ No newline at end of file diff --git a/src/menu/Menu.h b/src/menu/Menu.h new file mode 100644 index 0000000..0d3189f --- /dev/null +++ b/src/menu/Menu.h @@ -0,0 +1,20 @@ +#pragma once + +#include "cdc/render/PCInternalResource.h" + +class Menu : public cdc::PCInternalResource +{ +private: + bool m_initialized = false; + +public: + Menu(); + + void OnPresent(); + + bool OnCreateDevice(); + void OnDestroyDevice(); + +private: + void Draw(); +}; \ No newline at end of file diff --git a/src/render/RenderContext.cpp b/src/render/RenderContext.cpp new file mode 100644 index 0000000..721c051 --- /dev/null +++ b/src/render/RenderContext.cpp @@ -0,0 +1,27 @@ +#include + +#include + +#include "RenderContext.h" + +static std::function s_callback; + +// cdc::PCRenderContext::Present +static void(__thiscall* s_Present)(void*, RECT*, RECT*, HWND); + +static void __fastcall Present(void* _this, void*, RECT* pSourceRect, RECT* pDestRect, HWND hOverrideWND) +{ + s_callback(); + s_Present(_this, pSourceRect, pDestRect, hOverrideWND); +} + +void RenderContext::OnPresent(std::function callback) +{ + if (!s_callback) + { + MH_CreateHook((void*)0x61BB80, Present, (void**)&s_Present); + MH_EnableHook(MH_ALL_HOOKS); + } + + s_callback = callback; +} \ No newline at end of file diff --git a/src/render/RenderContext.h b/src/render/RenderContext.h new file mode 100644 index 0000000..cc13da5 --- /dev/null +++ b/src/render/RenderContext.h @@ -0,0 +1,10 @@ +#pragma once + +#include + +class RenderContext +{ +public: + // Registers a callback called just before device present + static void OnPresent(std::function callback); +}; \ No newline at end of file diff --git a/util.cpp b/util.cpp deleted file mode 100644 index e63cb0a..0000000 --- a/util.cpp +++ /dev/null @@ -1,36 +0,0 @@ -#include -#include -#include "Hooking.hpp" - -// taken from sunbeam906 -uint8_t __declspec(noinline)* GetAddress(uint8_t* ptr, uint8_t offset, uint8_t instr_size) -{ - return (ptr + *(int32_t*)(ptr + offset) + instr_size); -} - -bool __declspec(noinline) bDataCompare(const BYTE* pData, const BYTE* bMask, const char* szMask) -{ - for (; *szMask; ++szMask, ++pData, ++bMask) - { - if (*szMask == 'x' && *pData != *bMask) - return FALSE; - } - - return (*szMask) == NULL; -} - -uint8_t __declspec(noinline)* FindPattern(BYTE* bMask, char* szMask) -{ - MODULEINFO moduleInfo = { 0 }; - GetModuleInformation(GetCurrentProcess(), GetModuleHandle(NULL), &moduleInfo, sizeof(MODULEINFO)); - - uint8_t* dwBaseAddress = (uint8_t*)moduleInfo.lpBaseOfDll + 0x1000; // start from first section - int dwModuleSize = (int)moduleInfo.SizeOfImage - 0x1000; // scan size - 0x1000, due to the above - - for (int i = 0; i < dwModuleSize; i++) - { - if (bDataCompare((BYTE*)(dwBaseAddress + i), bMask, szMask)) - return (uint8_t*)(dwBaseAddress + i); - } - return 0; -} diff --git a/util.hpp b/util.hpp deleted file mode 100644 index deede01..0000000 --- a/util.hpp +++ /dev/null @@ -1,4 +0,0 @@ -#pragma once - -// taken from tomb5 -#define VAR_U_(address, type) (*(type*)(address)) diff --git a/vendor/MinHook.h b/vendor/MinHook.h deleted file mode 100644 index 15c0a87..0000000 --- a/vendor/MinHook.h +++ /dev/null @@ -1,186 +0,0 @@ -/* - * MinHook - The Minimalistic API Hooking Library for x64/x86 - * Copyright (C) 2009-2017 Tsuda Kageyu. - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED - * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A - * PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER - * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ - -#pragma once - -#if !(defined _M_IX86) && !(defined _M_X64) && !(defined __i386__) && !(defined __x86_64__) - #error MinHook supports only x86 and x64 systems. -#endif - -#include - -// MinHook Error Codes. -typedef enum MH_STATUS -{ - // Unknown error. Should not be returned. - MH_UNKNOWN = -1, - - // Successful. - MH_OK = 0, - - // MinHook is already initialized. - MH_ERROR_ALREADY_INITIALIZED, - - // MinHook is not initialized yet, or already uninitialized. - MH_ERROR_NOT_INITIALIZED, - - // The hook for the specified target function is already created. - MH_ERROR_ALREADY_CREATED, - - // The hook for the specified target function is not created yet. - MH_ERROR_NOT_CREATED, - - // The hook for the specified target function is already enabled. - MH_ERROR_ENABLED, - - // The hook for the specified target function is not enabled yet, or already - // disabled. - MH_ERROR_DISABLED, - - // The specified pointer is invalid. It points the address of non-allocated - // and/or non-executable region. - MH_ERROR_NOT_EXECUTABLE, - - // The specified target function cannot be hooked. - MH_ERROR_UNSUPPORTED_FUNCTION, - - // Failed to allocate memory. - MH_ERROR_MEMORY_ALLOC, - - // Failed to change the memory protection. - MH_ERROR_MEMORY_PROTECT, - - // The specified module is not loaded. - MH_ERROR_MODULE_NOT_FOUND, - - // The specified function is not found. - MH_ERROR_FUNCTION_NOT_FOUND -} -MH_STATUS; - -// Can be passed as a parameter to MH_EnableHook, MH_DisableHook, -// MH_QueueEnableHook or MH_QueueDisableHook. -#define MH_ALL_HOOKS NULL - -#ifdef __cplusplus -extern "C" { -#endif - - // Initialize the MinHook library. You must call this function EXACTLY ONCE - // at the beginning of your program. - MH_STATUS WINAPI MH_Initialize(VOID); - - // Uninitialize the MinHook library. You must call this function EXACTLY - // ONCE at the end of your program. - MH_STATUS WINAPI MH_Uninitialize(VOID); - - // Creates a Hook for the specified target function, in disabled state. - // Parameters: - // pTarget [in] A pointer to the target function, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHook(LPVOID pTarget, LPVOID pDetour, LPVOID *ppOriginal); - - // Creates a Hook for the specified API function, in disabled state. - // Parameters: - // pszModule [in] A pointer to the loaded module name which contains the - // target function. - // pszTarget [in] A pointer to the target function name, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHookApi( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal); - - // Creates a Hook for the specified API function, in disabled state. - // Parameters: - // pszModule [in] A pointer to the loaded module name which contains the - // target function. - // pszTarget [in] A pointer to the target function name, which will be - // overridden by the detour function. - // pDetour [in] A pointer to the detour function, which will override - // the target function. - // ppOriginal [out] A pointer to the trampoline function, which will be - // used to call the original target function. - // This parameter can be NULL. - // ppTarget [out] A pointer to the target function, which will be used - // with other functions. - // This parameter can be NULL. - MH_STATUS WINAPI MH_CreateHookApiEx( - LPCWSTR pszModule, LPCSTR pszProcName, LPVOID pDetour, LPVOID *ppOriginal, LPVOID *ppTarget); - - // Removes an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - MH_STATUS WINAPI MH_RemoveHook(LPVOID pTarget); - - // Enables an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // enabled in one go. - MH_STATUS WINAPI MH_EnableHook(LPVOID pTarget); - - // Disables an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // disabled in one go. - MH_STATUS WINAPI MH_DisableHook(LPVOID pTarget); - - // Queues to enable an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // queued to be enabled. - MH_STATUS WINAPI MH_QueueEnableHook(LPVOID pTarget); - - // Queues to disable an already created hook. - // Parameters: - // pTarget [in] A pointer to the target function. - // If this parameter is MH_ALL_HOOKS, all created hooks are - // queued to be disabled. - MH_STATUS WINAPI MH_QueueDisableHook(LPVOID pTarget); - - // Applies all queued changes in one go. - MH_STATUS WINAPI MH_ApplyQueued(VOID); - - // Translates the MH_STATUS to its name as a string. - const char * WINAPI MH_StatusToString(MH_STATUS status); - -#ifdef __cplusplus -} -#endif - diff --git a/vendor/imgui b/vendor/imgui index 81160fe..d6cb3c9 160000 --- a/vendor/imgui +++ b/vendor/imgui @@ -1 +1 @@ -Subproject commit 81160fee56027226bc80b48e196d0332f5541a8c +Subproject commit d6cb3c923d28dcebb2d8d9605ccc7229ccef19eb diff --git a/vendor/json b/vendor/json index d4daaa8..9cca280 160000 --- a/vendor/json +++ b/vendor/json @@ -1 +1 @@ -Subproject commit d4daaa897f48bf7bb2f96b46b84e49f32dd11daf +Subproject commit 9cca280a4d0ccf0c08f47a99aa71d1b0e52f8d03 diff --git a/vendor/minhook b/vendor/minhook new file mode 160000 index 0000000..f5485b8 --- /dev/null +++ b/vendor/minhook @@ -0,0 +1 @@ +Subproject commit f5485b8454544c2f034c78f8f127c1d03dea3636 diff --git a/vendor/patterns b/vendor/patterns new file mode 160000 index 0000000..62d4ff8 --- /dev/null +++ b/vendor/patterns @@ -0,0 +1 @@ +Subproject commit 62d4ff80e4a9c587f75bc6eaf6325e2b140bde28 From 8e3adb961e71e850be7b4df35716322991318c77 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 14 Jan 2024 14:29:02 +0100 Subject: [PATCH 02/55] Make PCInternalResource TR8 compatible --- src/cdc/render/PCInternalResource.cpp | 15 +++++++++++++++ src/cdc/render/PCInternalResource.h | 6 ++++++ 2 files changed, 21 insertions(+) diff --git a/src/cdc/render/PCInternalResource.cpp b/src/cdc/render/PCInternalResource.cpp index 285b7a4..5906ff9 100644 --- a/src/cdc/render/PCInternalResource.cpp +++ b/src/cdc/render/PCInternalResource.cpp @@ -10,6 +10,21 @@ cdc::PCInternalResource::~PCInternalResource() { } +#if TR8 +bool cdc::PCInternalResource::OnPostResetDevice() +{ + return true; +} + +void cdc::PCInternalResource::OnPreResetDevice() +{ +} + +void cdc::PCInternalResource::OnSettingsChanged() +{ +} +#endif + void cdc::PCInternalResource::OnConstruct() { auto deviceManager = cdc::PCDeviceManager::s_pInstance; diff --git a/src/cdc/render/PCInternalResource.h b/src/cdc/render/PCInternalResource.h index 67403e2..21a472a 100644 --- a/src/cdc/render/PCInternalResource.h +++ b/src/cdc/render/PCInternalResource.h @@ -17,5 +17,11 @@ namespace cdc virtual ~PCInternalResource(); virtual bool OnCreateDevice() = 0; virtual void OnDestroyDevice() = 0; + +#if TR8 + virtual bool OnPostResetDevice(); + virtual void OnPreResetDevice(); + virtual void OnSettingsChanged(); +#endif }; } \ No newline at end of file From 0d085e48b4b3d96c59e6bd6207bf450a371ff268 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 14 Jan 2024 17:42:06 +0100 Subject: [PATCH 03/55] Add menu input and input hooks --- src/Hook.cpp | 21 ++++++++++++++-- src/Hook.h | 3 +++ src/input/Input.cpp | 6 +++++ src/input/Input.h | 7 ++++++ src/input/MessageHook.cpp | 24 ++++++++++++++++++ src/input/MessageHook.h | 10 ++++++++ src/input/MouseHook.cpp | 28 +++++++++++++++++++++ src/input/MouseHook.h | 8 ++++++ src/menu/Menu.cpp | 52 +++++++++++++++++++++++++++++++++++---- src/menu/Menu.h | 7 ++++++ 10 files changed, 159 insertions(+), 7 deletions(-) create mode 100644 src/input/Input.cpp create mode 100644 src/input/Input.h create mode 100644 src/input/MessageHook.cpp create mode 100644 src/input/MessageHook.h create mode 100644 src/input/MouseHook.cpp create mode 100644 src/input/MouseHook.h diff --git a/src/Hook.cpp b/src/Hook.cpp index eae8843..15d83e1 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -1,9 +1,12 @@ #include #include "Hook.h" +#include "input/MessageHook.h" #include "cdc/render/PCDeviceManager.h" +using namespace std::placeholders; + static bool(*s_D3D_Init)(); static bool D3D_Init() @@ -28,13 +31,27 @@ void Hook::Initialize() MH_EnableHook(MH_ALL_HOOKS); } +void Hook::PostInitialize() +{ + // Create the menu + m_menu = std::make_unique(); + + // Register the message hook + MessageHook::OnMessage(std::bind(&Hook::OnMessage, this, _1, _2, _3, _4)); +} + +void Hook::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + m_menu->OnMessage(hWnd, msg, wParam, lParam); +} + void Hook::OnDevice() { // Assign the DeviceManager instance cdc::PCDeviceManager::s_pInstance = *(cdc::PCDeviceManager**)0xA6669C; - // Creat the menu - m_menu = std::make_unique(); + // Initialize the hook + PostInitialize(); } Hook& Hook::GetInstance() diff --git a/src/Hook.h b/src/Hook.h index f738bc7..37a5ee3 100644 --- a/src/Hook.h +++ b/src/Hook.h @@ -10,6 +10,9 @@ class Hook std::unique_ptr m_menu; void Initialize(); + void PostInitialize(); + + void OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); public: Hook(); diff --git a/src/input/Input.cpp b/src/input/Input.cpp new file mode 100644 index 0000000..e68a1dd --- /dev/null +++ b/src/input/Input.cpp @@ -0,0 +1,6 @@ +#include "Input.h" + +void Input::DisableInput(bool disable) +{ + *(bool*)0x8551A9 = disable; +} \ No newline at end of file diff --git a/src/input/Input.h b/src/input/Input.h new file mode 100644 index 0000000..6a8514f --- /dev/null +++ b/src/input/Input.h @@ -0,0 +1,7 @@ +#pragma once + +class Input +{ +public: + static void DisableInput(bool disable); +}; \ No newline at end of file diff --git a/src/input/MessageHook.cpp b/src/input/MessageHook.cpp new file mode 100644 index 0000000..bc30938 --- /dev/null +++ b/src/input/MessageHook.cpp @@ -0,0 +1,24 @@ +#include "MessageHook.h" + +#include "cdc/render/PCDeviceManager.h" + +static std::function s_callback; +static WNDPROC s_original; + +static LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + s_callback(hWnd, msg, wParam, lParam); + + return CallWindowProc(s_original, hWnd, msg, wParam, lParam); +} + +void MessageHook::OnMessage(std::function callback) +{ + // Get the window handle + auto hWnd = cdc::PCDeviceManager::s_pInstance->GetWindow(); + + // Set the new message handler to our handler + s_original = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (LONG_PTR)WndProc); + + s_callback = callback; +} \ No newline at end of file diff --git a/src/input/MessageHook.h b/src/input/MessageHook.h new file mode 100644 index 0000000..6d2f9e0 --- /dev/null +++ b/src/input/MessageHook.h @@ -0,0 +1,10 @@ +#pragma once + +#include +#include + +class MessageHook +{ +public: + static void OnMessage(std::function callback); +}; \ No newline at end of file diff --git a/src/input/MouseHook.cpp b/src/input/MouseHook.cpp new file mode 100644 index 0000000..ce3c49d --- /dev/null +++ b/src/input/MouseHook.cpp @@ -0,0 +1,28 @@ +#include +#include + +#include "MouseHook.h" + +static bool s_disabled; +static BOOL(WINAPI* s_SetCursorPos)(int, int); + +BOOL WINAPI SetCursorPosHook(int X, int Y) +{ + if (s_disabled) + { + return TRUE; + } + + return s_SetCursorPos(X, Y); +} + +void MouseHook::Init() +{ + MH_CreateHookApi(L"user32", "SetCursorPos", SetCursorPosHook, (void**)&s_SetCursorPos); + MH_EnableHook(MH_ALL_HOOKS); +} + +void MouseHook::DisableCursorLock(bool disable) +{ + s_disabled = disable; +} \ No newline at end of file diff --git a/src/input/MouseHook.h b/src/input/MouseHook.h new file mode 100644 index 0000000..f7b9700 --- /dev/null +++ b/src/input/MouseHook.h @@ -0,0 +1,8 @@ +#pragma once + +class MouseHook +{ +public: + static void Init(); + static void DisableCursorLock(bool disable); +}; \ No newline at end of file diff --git a/src/menu/Menu.cpp b/src/menu/Menu.cpp index 6ff7c8f..6710dab 100644 --- a/src/menu/Menu.cpp +++ b/src/menu/Menu.cpp @@ -2,11 +2,17 @@ #include #include +#include + #include "Menu.h" #include "render/RenderContext.h" +#include "input/MouseHook.h" +#include "input/Input.h" #include "cdc/render/PCDeviceManager.h" +extern IMGUI_IMPL_API LRESULT ImGui_ImplWin32_WndProcHandler(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + Menu::Menu() : PCInternalResource() { ImGui::CreateContext(); @@ -15,7 +21,8 @@ Menu::Menu() : PCInternalResource() ImGui_ImplWin32_Init(cdc::PCDeviceManager::s_pInstance->GetWindow()); // Add present callback to draw the UI - RenderContext::OnPresent([this] { OnPresent(); }); + RenderContext::OnPresent(std::bind(&Menu::OnPresent, this)); + MouseHook::Init(); OnConstruct(); } @@ -44,13 +51,20 @@ void Menu::OnDestroyDevice() void Menu::OnPresent() { + // Set the mouse cursor visible when we have focus + ImGui::GetIO().MouseDrawCursor = m_focus; + + // Start the frame ImGui_ImplDX9_NewFrame(); ImGui_ImplWin32_NewFrame(); ImGui::NewFrame(); // Draw the menu - Draw(); + if (m_focus) + { + Draw(); + } // End the frame ImGui::EndFrame(); @@ -59,9 +73,37 @@ void Menu::OnPresent() ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); } +void Menu::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_KEYUP && wParam == VK_F8) + { + SetFocus(!m_focus); + } + + if (m_focus) + { + ImGui_ImplWin32_WndProcHandler(hWnd, msg, wParam, lParam); + } +} + void Menu::Draw() { - ImGui::Begin("Menu"); - ImGui::Text("Hello, World!"); - ImGui::End(); + if (ImGui::BeginMainMenuBar()) + { + ImGui::EndMainMenuBar(); + } +} + +void Menu::SetFocus(bool focus) +{ + m_focus = focus; + + // Disable the cursor lock and game input + MouseHook::DisableCursorLock(focus); + Input::DisableInput(focus); +} + +bool Menu::HasFocus() +{ + return m_focus; } \ No newline at end of file diff --git a/src/menu/Menu.h b/src/menu/Menu.h index 0d3189f..d3a1c59 100644 --- a/src/menu/Menu.h +++ b/src/menu/Menu.h @@ -1,20 +1,27 @@ #pragma once +#include + #include "cdc/render/PCInternalResource.h" class Menu : public cdc::PCInternalResource { private: bool m_initialized = false; + bool m_focus = false; public: Menu(); void OnPresent(); + void OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); bool OnCreateDevice(); void OnDestroyDevice(); + void SetFocus(bool focus); + bool HasFocus(); + private: void Draw(); }; \ No newline at end of file From 8a136c437bf4bfa146655649e08fe0e5b3e7697e Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 14 Jan 2024 20:04:26 +0100 Subject: [PATCH 04/55] Add modules and instance viewer --- src/Hook.cpp | 25 +++++++++++- src/Hook.h | 9 +++++ src/cdc/math/Matrix.h | 15 +++++++ src/cdc/math/Vector.h | 39 ++++++++++++++++++ src/instance/Instance.h | 64 ++++++++++++++++++++++++++++++ src/instance/Instances.cpp | 11 ++++++ src/instance/Instances.h | 11 ++++++ src/instance/Object.h | 42 ++++++++++++++++++++ src/menu/Menu.cpp | 15 +++++++ src/modules/InstanceViewer.cpp | 72 ++++++++++++++++++++++++++++++++++ src/modules/InstanceViewer.h | 20 ++++++++++ src/modules/Module.h | 11 ++++++ 12 files changed, 333 insertions(+), 1 deletion(-) create mode 100644 src/cdc/math/Matrix.h create mode 100644 src/cdc/math/Vector.h create mode 100644 src/instance/Instance.h create mode 100644 src/instance/Instances.cpp create mode 100644 src/instance/Instances.h create mode 100644 src/instance/Object.h create mode 100644 src/modules/InstanceViewer.cpp create mode 100644 src/modules/InstanceViewer.h create mode 100644 src/modules/Module.h diff --git a/src/Hook.cpp b/src/Hook.cpp index 15d83e1..9fd1a37 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -3,6 +3,9 @@ #include "Hook.h" #include "input/MessageHook.h" +// Modules +#include "modules/InstanceViewer.h" + #include "cdc/render/PCDeviceManager.h" using namespace std::placeholders; @@ -19,13 +22,15 @@ static bool D3D_Init() return ret; } -Hook::Hook() : m_menu(nullptr) +Hook::Hook() : m_menu(nullptr), m_modules() { Initialize(); } void Hook::Initialize() { + RegisterModules(); + MH_Initialize(); MH_CreateHook((void*)0x4153E0, D3D_Init, (void**)&s_D3D_Init); MH_EnableHook(MH_ALL_HOOKS); @@ -43,6 +48,11 @@ void Hook::PostInitialize() void Hook::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { m_menu->OnMessage(hWnd, msg, wParam, lParam); + + for (auto& mod : m_modules) + { + mod->OnInput(hWnd, msg, wParam, lParam); + } } void Hook::OnDevice() @@ -54,6 +64,19 @@ void Hook::OnDevice() PostInitialize(); } +template +void Hook::RegisterModule() +{ + auto module = std::make_shared(); + + m_modules.push_back(module); +} + +void Hook::RegisterModules() +{ + RegisterModule(); +} + Hook& Hook::GetInstance() { static Hook instance; diff --git a/src/Hook.h b/src/Hook.h index 37a5ee3..a053ef1 100644 --- a/src/Hook.h +++ b/src/Hook.h @@ -1,17 +1,24 @@ #pragma once #include +#include #include "menu/Menu.h" +#include "modules/Module.h" class Hook { private: std::unique_ptr m_menu; + std::list> m_modules; void Initialize(); void PostInitialize(); + template + void RegisterModule(); + void RegisterModules(); + void OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); public: @@ -19,5 +26,7 @@ class Hook void OnDevice(); + const auto& GetModules() { return m_modules; } + static Hook& GetInstance(); }; \ No newline at end of file diff --git a/src/cdc/math/Matrix.h b/src/cdc/math/Matrix.h new file mode 100644 index 0000000..91eef5c --- /dev/null +++ b/src/cdc/math/Matrix.h @@ -0,0 +1,15 @@ +#pragma once + +#include "Vector.h" + +namespace cdc +{ + class Matrix + { + public: + cdc::Vector col0; + cdc::Vector col1; + cdc::Vector col2; + cdc::Vector col3; + }; +} \ No newline at end of file diff --git a/src/cdc/math/Vector.h b/src/cdc/math/Vector.h new file mode 100644 index 0000000..1bdcd22 --- /dev/null +++ b/src/cdc/math/Vector.h @@ -0,0 +1,39 @@ +#pragma once + +#include + +namespace cdc +{ + class Vector + { + public: + union + { + __m128 vec128; + + struct + { + float x; + float y; + float z; + float w; + }; + }; + }; + + class Vector2 : public Vector + { + }; + + class Vector3 : public Vector + { + }; + + class Vector4 : public Vector + { + }; + + class Euler : public Vector + { + }; +} \ No newline at end of file diff --git a/src/instance/Instance.h b/src/instance/Instance.h new file mode 100644 index 0000000..dd6126b --- /dev/null +++ b/src/instance/Instance.h @@ -0,0 +1,64 @@ +#pragma once + +#include "Object.h" + +#include "cdc/math/Vector.h" +#include "cdc/math/Matrix.h" + +struct NodeType +{ + NodeType* prev; + NodeType* next; +}; + +struct Intro +{ +}; + +struct HModel; +struct SoundInstanceData; +struct Body; +struct AnimatedGeoms; +struct AnimComponent; +struct CharacterProxy; + +struct Instance; + +struct BaseInstance +{ + NodeType node; + + Instance* next; + Instance* prev; + + cdc::Vector3 position; + cdc::Vector3 oldPos; + + cdc::Euler rotation; + cdc::Euler oldRotation; + + cdc::Vector3 scale; + cdc::Vector3 shadowPosition; + cdc::Vector3 centerOfMass; + + cdc::Matrix* matrix; + cdc::Matrix* oldMatrix; + + char pad1[12]; + + Object* object; + Intro* intro; + + char pad2[100]; +}; + +struct Instance : BaseInstance +{ + char pad1[192]; + + void* data; + + char pad2[12]; + + int introUniqueID; +}; \ No newline at end of file diff --git a/src/instance/Instances.cpp b/src/instance/Instances.cpp new file mode 100644 index 0000000..15a6e3c --- /dev/null +++ b/src/instance/Instances.cpp @@ -0,0 +1,11 @@ +#include "Instances.h" + +void Instances::Iterate(std::function callback) +{ + auto first = *(Instance**)0x817D64; + + for (auto instance = first; instance != nullptr; instance = instance->next) + { + callback(instance); + } +} \ No newline at end of file diff --git a/src/instance/Instances.h b/src/instance/Instances.h new file mode 100644 index 0000000..ba9914d --- /dev/null +++ b/src/instance/Instances.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +#include "Instance.h" + +class Instances +{ +public: + static void Iterate(std::function callback); +}; \ No newline at end of file diff --git a/src/instance/Object.h b/src/instance/Object.h new file mode 100644 index 0000000..a015c55 --- /dev/null +++ b/src/instance/Object.h @@ -0,0 +1,42 @@ +#pragma once + +struct Model +{ +}; + +struct AnimListEntry; +struct AnimFxHeader; +struct AnimScriptObject; +struct ObjectBase; + +struct Object +{ + int oflags; + int oflags2; + + int uniqueID; + unsigned int guiID; + int functionTableID; + + void* obsoleteSoundBank; + + __int16 numModels; + __int16 numAnims; + __int16 numAnimPatterns; + + Model** modelList; + + AnimListEntry* animList; + AnimFxHeader** animFXList; + AnimScriptObject** animPatternList; + + int introDist; + int vvIntroDist; + int removeDist; + int vvRemoveDist; + + ObjectBase* baseData; + + void* data; + char* name; +}; \ No newline at end of file diff --git a/src/menu/Menu.cpp b/src/menu/Menu.cpp index 6710dab..be7a27d 100644 --- a/src/menu/Menu.cpp +++ b/src/menu/Menu.cpp @@ -8,6 +8,7 @@ #include "render/RenderContext.h" #include "input/MouseHook.h" #include "input/Input.h" +#include "Hook.h" #include "cdc/render/PCDeviceManager.h" @@ -88,10 +89,24 @@ void Menu::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) void Menu::Draw() { + auto& modules = Hook::GetInstance().GetModules(); + if (ImGui::BeginMainMenuBar()) { + // Draw all modules menus + for (auto& mod : modules) + { + mod->OnMenu(); + } + ImGui::EndMainMenuBar(); } + + // Draw all menus + for (auto& mod : modules) + { + mod->OnDraw(); + } } void Menu::SetFocus(bool focus) diff --git a/src/modules/InstanceViewer.cpp b/src/modules/InstanceViewer.cpp new file mode 100644 index 0000000..08e2dd4 --- /dev/null +++ b/src/modules/InstanceViewer.cpp @@ -0,0 +1,72 @@ +#include + +#include "InstanceViewer.h" +#include "instance/Instances.h" + +void InstanceViewer::OnMenu() +{ + if (ImGui::BeginMenu("Instance")) + { + ImGui::MenuItem("Instances", nullptr, &m_show); + + ImGui::EndMenu(); + } +} + +void InstanceViewer::OnDraw() +{ + if (m_show) + { + ImGui::Begin("Instances", &m_show); + ImGui::Columns(2, "instances"); + + // Instance list + ImGui::BeginChild("InstancesTree"); + + Instances::Iterate([this](Instance* instance) + { + if (ImGui::TreeNodeEx((void*)instance, ImGuiTreeNodeFlags_Leaf, "%d %s", instance->introUniqueID, instance->object->name)) + { + if (ImGui::IsItemClicked()) + { + m_selected = instance; + } + + ImGui::TreePop(); + } + }); + + ImGui::EndChild(); + + // Instance properties + ImGui::NextColumn(); + + // Check if the instance still exists + if (m_selected && m_selected->node.prev == nullptr) + { + m_selected = nullptr; + } + + if (m_selected) + { + DrawInstance(); + } + + ImGui::End(); + } +} + +void InstanceViewer::DrawInstance() +{ + auto instance = m_selected; + + ImGui::Text("%s", instance->object->name); + + auto position = instance->position; + auto rotation = instance->rotation; + + ImGui::Text("Position: %f %f %f", position.x, position.y, position.z); + ImGui::Text("Rotation: %f %f %f", rotation.x, rotation.y, rotation.z); + ImGui::Text("Intro: %d", instance->introUniqueID); + ImGui::Text("Address: %p", instance); +} \ No newline at end of file diff --git a/src/modules/InstanceViewer.h b/src/modules/InstanceViewer.h new file mode 100644 index 0000000..3b41b30 --- /dev/null +++ b/src/modules/InstanceViewer.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Module.h" + +#include "instance/Instance.h" + +class InstanceViewer : public Module +{ +private: + bool m_show = false; + Instance* m_selected = nullptr; + + void DrawInstance(); + +public: + void OnMenu(); + void OnDraw(); + + Instance* GetSelected() { return m_selected; } +}; \ No newline at end of file diff --git a/src/modules/Module.h b/src/modules/Module.h new file mode 100644 index 0000000..e2a8d95 --- /dev/null +++ b/src/modules/Module.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +class Module +{ +public: + virtual void OnMenu() { }; + virtual void OnDraw() { }; + virtual void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { }; +}; \ No newline at end of file From 22594d7a2ae3d8e8748b5511707bfa62cb35ca6a Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 14 Jan 2024 21:09:25 +0100 Subject: [PATCH 05/55] Add skew cheat --- src/Hook.cpp | 7 +++++ src/game/Game.cpp | 15 ++++++++++ src/game/Game.h | 61 ++++++++++++++++++++++++++++++++++++++ src/instance/Instances.cpp | 12 ++++++++ src/instance/Instances.h | 3 ++ src/modules/Skew.cpp | 48 ++++++++++++++++++++++++++++++ src/modules/Skew.h | 13 ++++++++ 7 files changed, 159 insertions(+) create mode 100644 src/game/Game.cpp create mode 100644 src/game/Game.h create mode 100644 src/modules/Skew.cpp create mode 100644 src/modules/Skew.h diff --git a/src/Hook.cpp b/src/Hook.cpp index 9fd1a37..dcd836f 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -2,9 +2,12 @@ #include "Hook.h" #include "input/MessageHook.h" +#include "instance/Instances.h" +#include "game/Game.h" // Modules #include "modules/InstanceViewer.h" +#include "modules/Skew.h" #include "cdc/render/PCDeviceManager.h" @@ -29,6 +32,9 @@ Hook::Hook() : m_menu(nullptr), m_modules() void Hook::Initialize() { + Instances::Init(); + Game::Init(); + RegisterModules(); MH_Initialize(); @@ -75,6 +81,7 @@ void Hook::RegisterModule() void Hook::RegisterModules() { RegisterModule(); + RegisterModule(); } Hook& Hook::GetInstance() diff --git a/src/game/Game.cpp b/src/game/Game.cpp new file mode 100644 index 0000000..78a087a --- /dev/null +++ b/src/game/Game.cpp @@ -0,0 +1,15 @@ +#include "Game.h" + +void Game::Init() +{ +} + +Instance* Game::GetPlayerInstance() +{ + return GetGameTracker()->playerInstance; +} + +GameTracker* Game::GetGameTracker() +{ + return (GameTracker*)0x838330; +} \ No newline at end of file diff --git a/src/game/Game.h b/src/game/Game.h new file mode 100644 index 0000000..9e0b187 --- /dev/null +++ b/src/game/Game.h @@ -0,0 +1,61 @@ +#pragma once + +#include "instance/Instance.h" + +struct menu_t; +struct MemCardInfo; +struct Level; +struct VertexPool; + +struct WipeInfo +{ + float wipeCur; + float wipeTarget; + float wipeStep; +}; + +struct GameTracker +{ + menu_t* menu; + MemCardInfo* memcard; + Level* level; + Instance* playerInstance; + VertexPool* vertexPool; + + int debugFlags; + int debugFlags2; + int debugFlags3; + int debugFlags4; + + WipeInfo wipes[12]; + + unsigned int displayFrameCount; + unsigned int frameCount; + + int fpsFast; + int gameFlags; + int streamFlags; + Level* mainDrawUnit; + + char baseAreaName[20]; + + char levelDone; + char levelChange; + char loadingDisplayType; + char gameMode; + char cheatMode; + char savingGame; + + char pad1[66]; + + float timeMult; +}; + +class Game +{ +public: + static void Init(); + + static Instance* GetPlayerInstance(); + static GameTracker* GetGameTracker(); +}; \ No newline at end of file diff --git a/src/instance/Instances.cpp b/src/instance/Instances.cpp index 15a6e3c..c77cbf1 100644 --- a/src/instance/Instances.cpp +++ b/src/instance/Instances.cpp @@ -1,5 +1,12 @@ #include "Instances.h" +static void(*INSTANCE_Post)(Instance*, int, int); + +void Instances::Init() +{ + INSTANCE_Post = reinterpret_cast(0x4580B0); +} + void Instances::Iterate(std::function callback) { auto first = *(Instance**)0x817D64; @@ -8,4 +15,9 @@ void Instances::Iterate(std::function callback) { callback(instance); } +} + +void Instances::Post(Instance* instance, int message, int data) +{ + INSTANCE_Post(instance, message, data); } \ No newline at end of file diff --git a/src/instance/Instances.h b/src/instance/Instances.h index ba9914d..6e09e94 100644 --- a/src/instance/Instances.h +++ b/src/instance/Instances.h @@ -7,5 +7,8 @@ class Instances { public: + static void Init(); + static void Iterate(std::function callback); + static void Post(Instance* instance, int message, int data); }; \ No newline at end of file diff --git a/src/modules/Skew.cpp b/src/modules/Skew.cpp new file mode 100644 index 0000000..24398c8 --- /dev/null +++ b/src/modules/Skew.cpp @@ -0,0 +1,48 @@ +#include "Skew.h" + +#include "instance/Instances.h" +#include "game/Game.h" + +void Skew::ToggleSkew() +{ + auto tracker = Game::GetGameTracker(); + + tracker->cheatMode = !tracker->cheatMode; + Instances::Post(tracker->playerInstance, 1048592, tracker->cheatMode); +} + +void Skew::Process(UINT msg, WPARAM wParam) +{ + // TODO different keyboard layouts + // TODO configurable horizontal speed + + auto player = Game::GetPlayerInstance(); + auto tracker = Game::GetGameTracker(); + + auto speed = 200.f * tracker->timeMult; + + if (msg == WM_KEYDOWN && wParam == 0x51) + { + player->position.z += speed; + } + + if (msg == WM_KEYDOWN && wParam == 0x5A) + { + player->position.z -= speed; + } +} + +void Skew::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_KEYUP && wParam == VK_F2) + { + ToggleSkew(); + } + + auto tracker = Game::GetGameTracker(); + + if (tracker->cheatMode) + { + Process(msg, wParam); + } +} \ No newline at end of file diff --git a/src/modules/Skew.h b/src/modules/Skew.h new file mode 100644 index 0000000..9d649b6 --- /dev/null +++ b/src/modules/Skew.h @@ -0,0 +1,13 @@ +#pragma once + +#include "Module.h" + +class Skew : public Module +{ +private: + void ToggleSkew(); + void Process(UINT msg, WPARAM wParam); + +public: + virtual void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); +}; \ No newline at end of file From f9f7a8da4b72e2dc5d4046816e14772abfbd141a Mon Sep 17 00:00:00 2001 From: Indra Date: Mon, 15 Jan 2024 23:12:30 +0100 Subject: [PATCH 06/55] Add function call helpers --- src/Hook.cpp | 1 - src/instance/Instance.cpp | 13 +++++++++++++ src/instance/Instance.h | 19 +++++++++++++++++-- src/instance/Instances.cpp | 12 ------------ src/instance/Instances.h | 3 --- src/modules/Skew.cpp | 2 +- src/util/Hooking.h | 20 ++++++++++++++++++++ 7 files changed, 51 insertions(+), 19 deletions(-) create mode 100644 src/instance/Instance.cpp create mode 100644 src/util/Hooking.h diff --git a/src/Hook.cpp b/src/Hook.cpp index dcd836f..3dec70c 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -32,7 +32,6 @@ Hook::Hook() : m_menu(nullptr), m_modules() void Hook::Initialize() { - Instances::Init(); Game::Init(); RegisterModules(); diff --git a/src/instance/Instance.cpp b/src/instance/Instance.cpp new file mode 100644 index 0000000..0e20636 --- /dev/null +++ b/src/instance/Instance.cpp @@ -0,0 +1,13 @@ +#include "Instance.h" + +#include "util/Hooking.h" + +void INSTANCE_Post(Instance* instance, int message, int data) +{ + Hooking::Call(0x4580B0, instance, message, data); +} + +void INSTANCE_HideUnhideDrawGroup(Instance* instance, int drawGroup, int on) +{ + Hooking::Call(0x4319B0, instance, drawGroup, on); +} \ No newline at end of file diff --git a/src/instance/Instance.h b/src/instance/Instance.h index dd6126b..ef0c84d 100644 --- a/src/instance/Instance.h +++ b/src/instance/Instance.h @@ -15,6 +15,14 @@ struct Intro { }; +struct ObjectData +{ + unsigned __int16 version; + unsigned __int16 family; + unsigned __int16 id; + unsigned __int16 type; +}; + struct HModel; struct SoundInstanceData; struct Body; @@ -49,7 +57,11 @@ struct BaseInstance Object* object; Intro* intro; - char pad2[100]; + char pad2[64]; + + unsigned int noDrawGroups; + + char pad3[32]; }; struct Instance : BaseInstance @@ -61,4 +73,7 @@ struct Instance : BaseInstance char pad2[12]; int introUniqueID; -}; \ No newline at end of file +}; + +void INSTANCE_Post(Instance* instance, int message, int data); +void INSTANCE_HideUnhideDrawGroup(Instance* instance, int drawGroup, int on); \ No newline at end of file diff --git a/src/instance/Instances.cpp b/src/instance/Instances.cpp index c77cbf1..15a6e3c 100644 --- a/src/instance/Instances.cpp +++ b/src/instance/Instances.cpp @@ -1,12 +1,5 @@ #include "Instances.h" -static void(*INSTANCE_Post)(Instance*, int, int); - -void Instances::Init() -{ - INSTANCE_Post = reinterpret_cast(0x4580B0); -} - void Instances::Iterate(std::function callback) { auto first = *(Instance**)0x817D64; @@ -15,9 +8,4 @@ void Instances::Iterate(std::function callback) { callback(instance); } -} - -void Instances::Post(Instance* instance, int message, int data) -{ - INSTANCE_Post(instance, message, data); } \ No newline at end of file diff --git a/src/instance/Instances.h b/src/instance/Instances.h index 6e09e94..ba9914d 100644 --- a/src/instance/Instances.h +++ b/src/instance/Instances.h @@ -7,8 +7,5 @@ class Instances { public: - static void Init(); - static void Iterate(std::function callback); - static void Post(Instance* instance, int message, int data); }; \ No newline at end of file diff --git a/src/modules/Skew.cpp b/src/modules/Skew.cpp index 24398c8..d2b6026 100644 --- a/src/modules/Skew.cpp +++ b/src/modules/Skew.cpp @@ -8,7 +8,7 @@ void Skew::ToggleSkew() auto tracker = Game::GetGameTracker(); tracker->cheatMode = !tracker->cheatMode; - Instances::Post(tracker->playerInstance, 1048592, tracker->cheatMode); + INSTANCE_Post(tracker->playerInstance, 1048592, tracker->cheatMode); } void Skew::Process(UINT msg, WPARAM wParam) diff --git a/src/util/Hooking.h b/src/util/Hooking.h new file mode 100644 index 0000000..824ca3c --- /dev/null +++ b/src/util/Hooking.h @@ -0,0 +1,20 @@ +#pragma once + +// Helpers for hooking and calling functions +class Hooking +{ +public: + // Calls a function + template + static inline void Call(unsigned int address, Args... args) + { + reinterpret_cast(address)(args...); + } + + // Calls a function with a return value + template + static inline T CallReturn(unsigned int address, Args... args) + { + reinterpret_cast(address)(args...); + } +}; \ No newline at end of file From db47a296d0f9021aeebe22db2f46b3865b49b2ec Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 20 Jan 2024 04:23:14 +0100 Subject: [PATCH 07/55] Add patterns for showing menu in all games --- src/Hook.cpp | 42 ++++++++++++++++++-- src/Hook.h | 1 + src/cdc/render/PCDeviceManager.cpp | 9 ++++- src/game/Game.cpp | 9 +++-- src/game/Game.h | 6 +-- src/modules/Module.h | 8 ++++ src/render/Font.cpp | 62 ++++++++++++++++++++++++++++++ src/render/Font.h | 20 ++++++++++ src/render/RenderContext.cpp | 10 ++++- src/util/Hooking.h | 16 +++++++- 10 files changed, 169 insertions(+), 14 deletions(-) create mode 100644 src/render/Font.cpp create mode 100644 src/render/Font.h diff --git a/src/Hook.cpp b/src/Hook.cpp index 3dec70c..9e573a3 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -1,13 +1,19 @@ #include +#include #include "Hook.h" #include "input/MessageHook.h" #include "instance/Instances.h" #include "game/Game.h" +#include "render/Font.h" // Modules +#include "modules/MainMenu.h" #include "modules/InstanceViewer.h" #include "modules/Skew.h" +#include "modules/Render.h" +#include "modules/Draw.h" +#include "modules/Log.h" #include "cdc/render/PCDeviceManager.h" @@ -15,6 +21,9 @@ using namespace std::placeholders; static bool(*s_D3D_Init)(); +// Pointer to the cdc::DeviceManager::s_pInstance pointer +static void* s_deviceManager; + static bool D3D_Init() { // Initialize the device @@ -32,12 +41,19 @@ Hook::Hook() : m_menu(nullptr), m_modules() void Hook::Initialize() { - Game::Init(); - RegisterModules(); +#ifndef TR8 + auto match = hook::pattern("A1 ? ? ? ? 8B 0D ? ? ? ? 68 ? ? ? ? 50 E8").count(1); + s_deviceManager = *match.get_first(7); +#else + auto match = hook::pattern("8B 0D ? ? ? ? 8B 01 8B 15 ? ? ? ? 8B 00 68").count(1); + s_deviceManager = *match.get_first(2); +#endif + + // Create the initial hook MH_Initialize(); - MH_CreateHook((void*)0x4153E0, D3D_Init, (void**)&s_D3D_Init); + MH_CreateHook(match.get_first(), D3D_Init, (void**)&s_D3D_Init); MH_EnableHook(MH_ALL_HOOKS); } @@ -48,6 +64,10 @@ void Hook::PostInitialize() // Register the message hook MessageHook::OnMessage(std::bind(&Hook::OnMessage, this, _1, _2, _3, _4)); + +#ifndef TR8 + Font::OnFlush(std::bind(&Hook::OnFrame, this)); +#endif } void Hook::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -60,10 +80,18 @@ void Hook::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) } } +void Hook::OnFrame() +{ + for (auto& mod : m_modules) + { + mod->OnFrame(); + } +} + void Hook::OnDevice() { // Assign the DeviceManager instance - cdc::PCDeviceManager::s_pInstance = *(cdc::PCDeviceManager**)0xA6669C; + cdc::PCDeviceManager::s_pInstance = *(cdc::PCDeviceManager**)s_deviceManager; // Initialize the hook PostInitialize(); @@ -79,8 +107,14 @@ void Hook::RegisterModule() void Hook::RegisterModules() { + RegisterModule(); RegisterModule(); RegisterModule(); +#ifndef TR8 + RegisterModule(); + RegisterModule(); + RegisterModule(); +#endif } Hook& Hook::GetInstance() diff --git a/src/Hook.h b/src/Hook.h index a053ef1..fee81fa 100644 --- a/src/Hook.h +++ b/src/Hook.h @@ -20,6 +20,7 @@ class Hook void RegisterModules(); void OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void OnFrame(); public: Hook(); diff --git a/src/cdc/render/PCDeviceManager.cpp b/src/cdc/render/PCDeviceManager.cpp index ed595e5..27efc8a 100644 --- a/src/cdc/render/PCDeviceManager.cpp +++ b/src/cdc/render/PCDeviceManager.cpp @@ -1,3 +1,5 @@ +#include + #include "PCDeviceManager.h" cdc::PCDeviceManager* cdc::PCDeviceManager::s_pInstance = nullptr; @@ -24,7 +26,12 @@ HWND cdc::PCDeviceManager::GetWindow() void cdc::PCDeviceManager::AddDeviceResource(PCInternalResource* resource) { - auto func = reinterpret_cast(0x00616F10); +#ifndef TR8 + auto match = hook::pattern("8B 41 08 33 D2 3B C2 8B 44 24 04").count(1); +#else + auto match = hook::pattern("8B 44 24 04 33 D2 39 51 08 89 50").count(1); +#endif + auto func = (void(__thiscall*)(PCDeviceManager*, PCInternalResource*))match.get_first(); func(this, resource); } \ No newline at end of file diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 78a087a..bb5f7f2 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -1,8 +1,6 @@ #include "Game.h" -void Game::Init() -{ -} +#include "util/Hooking.h" Instance* Game::GetPlayerInstance() { @@ -12,4 +10,9 @@ Instance* Game::GetPlayerInstance() GameTracker* Game::GetGameTracker() { return (GameTracker*)0x838330; +} + +void GAMELOOP_ExitGame(char* name, GameTracker* gameTracker, int doneType) +{ + Hooking::Call(0xC61CFA, name, gameTracker, doneType); } \ No newline at end of file diff --git a/src/game/Game.h b/src/game/Game.h index 9e0b187..6721d52 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -54,8 +54,8 @@ struct GameTracker class Game { public: - static void Init(); - static Instance* GetPlayerInstance(); static GameTracker* GetGameTracker(); -}; \ No newline at end of file +}; + +void GAMELOOP_ExitGame(char* name, GameTracker* gameTracker, int doneType); \ No newline at end of file diff --git a/src/modules/Module.h b/src/modules/Module.h index e2a8d95..b34c737 100644 --- a/src/modules/Module.h +++ b/src/modules/Module.h @@ -5,7 +5,15 @@ class Module { public: + // Called during drawing of the main menu bar, can be used to add menu items virtual void OnMenu() { }; + + // Called during menu drawing, used for ImGui draw code virtual void OnDraw() { }; + + // Called just before a frame ends, Font::Flush to be specific + virtual void OnFrame() { }; + + // Called when a message is processed by the window procedure virtual void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { }; }; \ No newline at end of file diff --git a/src/render/Font.cpp b/src/render/Font.cpp new file mode 100644 index 0000000..d059d26 --- /dev/null +++ b/src/render/Font.cpp @@ -0,0 +1,62 @@ +#include +#include + +#include + +#include "Font.h" +#include "util/Hooking.h" + +static std::function s_callback; + +// Font::Flush +static void(* s_Flush)(); + +static void Flush() +{ + s_callback(); + s_Flush(); +} + +char Font::s_formatted[1024]; + +Font* Font::GetMainFont() +{ + return *(Font**)0x7D1800; +} + +void Font::SetCursor(float x, float y) +{ + Hooking::Call(0x433C70, x, y); +} + +void Font::SetScale(float scaleX, float scaleY) +{ + Hooking::Call(0x433E60, scaleX, scaleY); +} + +void Font::Print(const char* fmt, ...) +{ + va_list va; + + va_start(va, fmt); + vsprintf_s(s_formatted, fmt, va); + va_end(va); + + PrintFormatted(s_formatted); +} + +void Font::PrintFormatted(const char* formatted, int backdrop) +{ + Hooking::ThisCall(0x434A70, this, formatted, backdrop); +} + +void Font::OnFlush(std::function callback) +{ + if (!s_callback) + { + MH_CreateHook((void*)0x434C40, Flush, (void**)&s_Flush); + MH_EnableHook(MH_ALL_HOOKS); + } + + s_callback = callback; +} \ No newline at end of file diff --git a/src/render/Font.h b/src/render/Font.h new file mode 100644 index 0000000..b9a0ab5 --- /dev/null +++ b/src/render/Font.h @@ -0,0 +1,20 @@ +#pragma once + +#include + +class Font +{ +private: + static char s_formatted[1024]; + +public: + static Font* GetMainFont(); + + static void SetCursor(float x, float y); + static void SetScale(float scaleX, float scaleY); + + void Print(const char* fmt, ...); + void PrintFormatted(const char* formatted, int backdrop = 0); + + static void OnFlush(std::function callback); +}; \ No newline at end of file diff --git a/src/render/RenderContext.cpp b/src/render/RenderContext.cpp index 721c051..d3a0700 100644 --- a/src/render/RenderContext.cpp +++ b/src/render/RenderContext.cpp @@ -1,5 +1,5 @@ #include - +#include #include #include "RenderContext.h" @@ -19,7 +19,13 @@ void RenderContext::OnPresent(std::function callback) { if (!s_callback) { - MH_CreateHook((void*)0x61BB80, Present, (void**)&s_Present); +#ifndef TR8 + auto match = hook::pattern("8B 41 14 85 C0 74 19 8B 54 24 0C").count(1); +#else + auto match = hook::pattern("A1 ? ? ? ? 83 78 10 00 75 39 80 B8").count(1); +#endif + + MH_CreateHook(match.get_first(), Present, (void**)&s_Present); MH_EnableHook(MH_ALL_HOOKS); } diff --git a/src/util/Hooking.h b/src/util/Hooking.h index 824ca3c..67aa738 100644 --- a/src/util/Hooking.h +++ b/src/util/Hooking.h @@ -17,4 +17,18 @@ class Hooking { reinterpret_cast(address)(args...); } -}; \ No newline at end of file + + // Calls a class function + template + static inline void ThisCall(unsigned int address, Args... args) + { + reinterpret_cast(address)(args...); + } + + // Calls a class function with a return value + template + static inline T ThisCallReturn(unsigned int address, Args... args) + { + reinterpret_cast(address)(args...); + } +}; From eb6876180402a46038330337f5d91ac2f008c2d4 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 21 Jan 2024 02:21:05 +0100 Subject: [PATCH 08/55] Implement all Underworld menu functions --- src/game/Game.cpp | 8 ++- src/game/Game.h | 54 +++++++++++++++- src/input/Input.cpp | 4 +- src/instance/Animation.cpp | 16 +++++ src/instance/Animation.h | 6 ++ src/instance/Instance.cpp | 22 ++++++- src/instance/Instance.h | 34 +++++++++- src/instance/Instances.cpp | 3 +- src/instance/Object.h | 16 ++++- src/level/Stream.cpp | 16 +++++ src/level/Stream.h | 23 +++++++ src/modules/InstanceViewer.cpp | 113 ++++++++++++++++++++++++++++++++- src/modules/InstanceViewer.h | 2 + src/modules/MainMenu.cpp | 48 ++++++++++++++ src/modules/MainMenu.h | 12 ++++ src/modules/Skew.cpp | 11 ++++ src/util/Hooking.h | 12 +++- 17 files changed, 387 insertions(+), 13 deletions(-) create mode 100644 src/instance/Animation.cpp create mode 100644 src/instance/Animation.h create mode 100644 src/level/Stream.cpp create mode 100644 src/level/Stream.h create mode 100644 src/modules/MainMenu.cpp create mode 100644 src/modules/MainMenu.h diff --git a/src/game/Game.cpp b/src/game/Game.cpp index bb5f7f2..30d1eb2 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -9,10 +9,12 @@ Instance* Game::GetPlayerInstance() GameTracker* Game::GetGameTracker() { - return (GameTracker*)0x838330; + return (GameTracker*)GET_ADDRESS(0x10E5370, 0x838330, 0x00E7F088); } -void GAMELOOP_ExitGame(char* name, GameTracker* gameTracker, int doneType) +void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType) { - Hooking::Call(0xC61CFA, name, gameTracker, doneType); + auto addr = GET_ADDRESS(0x451970, 0xC61CFA, 0x5DF8C0); + + Hooking::Call(addr, name, gameTracker, doneType); } \ No newline at end of file diff --git a/src/game/Game.h b/src/game/Game.h index 6721d52..53125f7 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -14,6 +14,7 @@ struct WipeInfo float wipeStep; }; +#ifndef TR8 struct GameTracker { menu_t* menu; @@ -50,6 +51,57 @@ struct GameTracker float timeMult; }; +#else +struct GameTracker +{ + int field_0; + int field_4; + Level* level; + Instance* playerInstance; + + int debugFlags; + int debugFlags2; + int debugFlags3; + int debugFlags4; + + int displayFrameCount; + int field_24; + int field_28; + int field_2C; + int field_30; + int field_34; + + char baseAreaName[128]; + char field_B8; + char field_B9; + char field_BA; + + char gameMode; + char cheatMode; + + char field_BD; + char field_BE; + char field_BF; + int StreamUnitID; + int field_C4; + int field_C8; + int field_CC; + int field_D0; + int field_D4; + int field_D8; + int field_DC; + int field_E0; + int field_E4; + int field_E8; + int field_EC; + int field_F0; + int field_F4; + int field_F8; + int field_FC; + + float timeMult; +}; +#endif class Game { @@ -58,4 +110,4 @@ class Game static GameTracker* GetGameTracker(); }; -void GAMELOOP_ExitGame(char* name, GameTracker* gameTracker, int doneType); \ No newline at end of file +void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType); \ No newline at end of file diff --git a/src/input/Input.cpp b/src/input/Input.cpp index e68a1dd..1ad0c71 100644 --- a/src/input/Input.cpp +++ b/src/input/Input.cpp @@ -1,6 +1,8 @@ #include "Input.h" +#include "util/Hooking.h" + void Input::DisableInput(bool disable) { - *(bool*)0x8551A9 = disable; + *(bool*)GET_ADDRESS(0x1101689, 0x8551A9, 0xA02B79) = disable; } \ No newline at end of file diff --git a/src/instance/Animation.cpp b/src/instance/Animation.cpp new file mode 100644 index 0000000..f3e18d3 --- /dev/null +++ b/src/instance/Animation.cpp @@ -0,0 +1,16 @@ +#include "Animation.h" +#include "util/Hooking.h" + +void G2EmulationInstanceSetAnimation(Instance* instance, int CurrentSection, int NewAnim, int NewFrame, int Frames) +{ + auto addr = GET_ADDRESS(0x4DEC30, 0x4DE690, 0x5B1EA0); + + Hooking::Call(addr, instance, CurrentSection, NewAnim, NewFrame, Frames); +} + +void G2EmulationInstanceSetMode(Instance* instance, int CurrentSection, int Mode) +{ + auto addr = GET_ADDRESS(0x4DED90, 0x4DE7F0, 0x5B1F50); + + Hooking::Call(addr, instance, CurrentSection, Mode); +} \ No newline at end of file diff --git a/src/instance/Animation.h b/src/instance/Animation.h new file mode 100644 index 0000000..1cbe68c --- /dev/null +++ b/src/instance/Animation.h @@ -0,0 +1,6 @@ +#pragma once + +#include "Instance.h" + +void G2EmulationInstanceSetAnimation(Instance* instance, int CurrentSection, int NewAnim, int NewFrame, int Frames); +void G2EmulationInstanceSetMode(Instance* instance, int CurrentSection, int Mode); \ No newline at end of file diff --git a/src/instance/Instance.cpp b/src/instance/Instance.cpp index 0e20636..1cfb596 100644 --- a/src/instance/Instance.cpp +++ b/src/instance/Instance.cpp @@ -4,10 +4,28 @@ void INSTANCE_Post(Instance* instance, int message, int data) { - Hooking::Call(0x4580B0, instance, message, data); + auto addr = GET_ADDRESS(0x455510, 0x4580B0, 0x5B3750); + + Hooking::Call(addr, instance, message, data); } void INSTANCE_HideUnhideDrawGroup(Instance* instance, int drawGroup, int on) { - Hooking::Call(0x4319B0, instance, drawGroup, on); + auto addr = GET_ADDRESS(0x456230, 0x4319B0, 0x5B3DD0); + + Hooking::Call(addr, instance, drawGroup, on); +} + +void INSTANCE_ReallyRemoveInstance(Instance* instance, int reset, bool keepSave) +{ + auto addr = GET_ADDRESS(0x4575B0, 0x45A3A0, 0x5BC4E0); + + Hooking::Call(addr, instance, reset, keepSave); +} + +Instance* INSTANCE_BirthObjectNoParent(unsigned int unitID, cdc::Vector3* position, cdc::Euler* rotation, IntroData* introData, Object* object, int modelNum, int initEffects) +{ + auto addr = GET_ADDRESS(0x458990, 0x45BA90, 0x5BD0F0); + + return Hooking::CallReturn(addr, unitID, position, rotation, introData, object, modelNum, initEffects); } \ No newline at end of file diff --git a/src/instance/Instance.h b/src/instance/Instance.h index ef0c84d..260a482 100644 --- a/src/instance/Instance.h +++ b/src/instance/Instance.h @@ -15,6 +15,10 @@ struct Intro { }; +struct IntroData +{ +}; + struct ObjectData { unsigned __int16 version; @@ -32,6 +36,7 @@ struct CharacterProxy; struct Instance; +#ifndef TR8 struct BaseInstance { NodeType node; @@ -73,7 +78,34 @@ struct Instance : BaseInstance char pad2[12]; int introUniqueID; + + char pad3[104]; + + void* extraData; +}; +#else +struct Instance +{ + NodeType node; + + Instance* next; + Instance* prev; + + Object* object; + + char pad1[12]; + + cdc::Vector3 position; + cdc::Euler rotation; + + char pad2[24]; + + int introUniqueID; }; +#endif void INSTANCE_Post(Instance* instance, int message, int data); -void INSTANCE_HideUnhideDrawGroup(Instance* instance, int drawGroup, int on); \ No newline at end of file +void INSTANCE_HideUnhideDrawGroup(Instance* instance, int drawGroup, int on); +void INSTANCE_ReallyRemoveInstance(Instance* instance, int reset, bool keepSave); + +Instance* INSTANCE_BirthObjectNoParent(unsigned int unitID, cdc::Vector3* position, cdc::Euler* rotation, IntroData* introData, Object* object, int modelNum, int initEffects); \ No newline at end of file diff --git a/src/instance/Instances.cpp b/src/instance/Instances.cpp index 15a6e3c..e0b261b 100644 --- a/src/instance/Instances.cpp +++ b/src/instance/Instances.cpp @@ -1,8 +1,9 @@ #include "Instances.h" +#include "util/Hooking.h" void Instances::Iterate(std::function callback) { - auto first = *(Instance**)0x817D64; + auto first = *(Instance**)GET_ADDRESS(0x10C5AA4, 0x817D64, 0xD98D54); for (auto instance = first; instance != nullptr; instance = instance->next) { diff --git a/src/instance/Object.h b/src/instance/Object.h index a015c55..5b82e29 100644 --- a/src/instance/Object.h +++ b/src/instance/Object.h @@ -9,6 +9,7 @@ struct AnimFxHeader; struct AnimScriptObject; struct ObjectBase; +#ifndef TR8 struct Object { int oflags; @@ -39,4 +40,17 @@ struct Object void* data; char* name; -}; \ No newline at end of file +}; +#else +struct Object +{ + char pad1[58]; + + __int16 numModels; + __int16 numAnims; + + char pad2[34]; + + char* name; +}; +#endif \ No newline at end of file diff --git a/src/level/Stream.cpp b/src/level/Stream.cpp new file mode 100644 index 0000000..c9e2055 --- /dev/null +++ b/src/level/Stream.cpp @@ -0,0 +1,16 @@ +#include "Stream.h" +#include "util/Hooking.h" + +ObjectTracker* STREAM_GetObjectTrackerByName(char* name) +{ + auto addr = GET_ADDRESS(0x5D4270, 0x5DA260, 0x5C17D0); + + return Hooking::CallReturn(addr, name); +} + +bool STREAM_PollLoadQueue() +{ + auto addr = GET_ADDRESS(0x5D51C0, 0x5DB190, 0x5C1DA0); + + return Hooking::CallReturn(addr); +} \ No newline at end of file diff --git a/src/level/Stream.h b/src/level/Stream.h new file mode 100644 index 0000000..965974d --- /dev/null +++ b/src/level/Stream.h @@ -0,0 +1,23 @@ +#pragma once + +#include "instance/Object.h" + +#if TRAE || TR7 +#define MAX_UNIT_NAME_LENGTH 20 +#else +#define MAX_UNIT_NAME_LENGTH 128 +#endif + +struct ResolveObject; + +struct ObjectTracker +{ + ResolveObject* resolveObj; + char* objectName; + Object* object; + __int16 objectID; + __int16 objectStatus; +}; + +ObjectTracker* STREAM_GetObjectTrackerByName(char* name); +bool STREAM_PollLoadQueue(); \ No newline at end of file diff --git a/src/modules/InstanceViewer.cpp b/src/modules/InstanceViewer.cpp index 08e2dd4..b5ab24f 100644 --- a/src/modules/InstanceViewer.cpp +++ b/src/modules/InstanceViewer.cpp @@ -1,7 +1,10 @@ +#include #include #include "InstanceViewer.h" #include "instance/Instances.h" +#include "instance/Animation.h" +#include "game/Game.h" void InstanceViewer::OnMenu() { @@ -20,12 +23,23 @@ void InstanceViewer::OnDraw() ImGui::Begin("Instances", &m_show); ImGui::Columns(2, "instances"); + // Filter + ImGui::InputText("Name", m_filter, sizeof(m_filter)); + // Instance list ImGui::BeginChild("InstancesTree"); Instances::Iterate([this](Instance* instance) { - if (ImGui::TreeNodeEx((void*)instance, ImGuiTreeNodeFlags_Leaf, "%d %s", instance->introUniqueID, instance->object->name)) + auto name = instance->object->name; + + // Check filter + if (strlen(m_filter) > 0 && strstr(name, m_filter) == 0) + { + return; + } + + if (ImGui::TreeNodeEx((void*)instance, ImGuiTreeNodeFlags_Leaf, "%d %s", instance->introUniqueID, name)) { if (ImGui::IsItemClicked()) { @@ -65,8 +79,105 @@ void InstanceViewer::DrawInstance() auto position = instance->position; auto rotation = instance->rotation; + ImGui::Text("Intro: %d", instance->introUniqueID); ImGui::Text("Position: %f %f %f", position.x, position.y, position.z); ImGui::Text("Rotation: %f %f %f", rotation.x, rotation.y, rotation.z); ImGui::Text("Intro: %d", instance->introUniqueID); ImGui::Text("Address: %p", instance); + + // Buttons + if (ImGui::Button("Goto")) + { + SkewTo(instance); + } + + ImGui::SameLine(); + + if (ImGui::Button("Bring")) + { + auto player = Game::GetPlayerInstance(); + +#ifndef TR8 + instance->oldPos = player->position; +#endif + instance->position = player->position; + } + + ImGui::SameLine(); + + if (ImGui::Button("Delete")) + { + INSTANCE_ReallyRemoveInstance(instance, 0, false); + } + +#ifdef TR8 + ImGui::SameLine(); + + if (ImGui::Button("Unhide")) + { + INSTANCE_Post(instance, 7, 0); + } +#endif + + // Object info + if (ImGui::CollapsingHeader("Object")) + { + auto object = instance->object; + + ImGui::Text("Models: %d", object->numModels); + ImGui::Text("Animations: %d", object->numAnims); + } + + // Draw groups + if (ImGui::CollapsingHeader("Draw groups")) + { + static int drawGroup = 0; + static bool enabled = false; + + ImGui::InputInt("Draw group", &drawGroup); + ImGui::Checkbox("Enabled", &enabled); + + if (ImGui::Button("Toggle")) + { + INSTANCE_HideUnhideDrawGroup(instance, drawGroup, enabled); + } + } + + // Animations + if (ImGui::CollapsingHeader("Animations")) + { + static int animation = 0; + static bool loop = true; + + ImGui::InputInt("Animation", &animation); + ImGui::Checkbox("Loop", &loop); + + if (ImGui::Button("Play")) + { + G2EmulationInstanceSetAnimation(instance, 0, animation, 0, 0); + G2EmulationInstanceSetMode(instance, 0, loop ? 2 : 1); + } + } + + // Messaging + if (ImGui::CollapsingHeader("Messaging")) + { + static int message = 0; + static int data = 0; + + ImGui::InputInt("Message", &message); + ImGui::InputInt("Data", &data); + + if (ImGui::Button("Post")) + { + INSTANCE_Post(instance, message, data); + } + } +} + +void InstanceViewer::SkewTo(Instance* instance) +{ + auto player = Game::GetPlayerInstance(); + + player->position = instance->position; } \ No newline at end of file diff --git a/src/modules/InstanceViewer.h b/src/modules/InstanceViewer.h index 3b41b30..b4fdd5a 100644 --- a/src/modules/InstanceViewer.h +++ b/src/modules/InstanceViewer.h @@ -9,8 +9,10 @@ class InstanceViewer : public Module private: bool m_show = false; Instance* m_selected = nullptr; + char m_filter[64] = ""; void DrawInstance(); + void SkewTo(Instance* instance); public: void OnMenu(); diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp new file mode 100644 index 0000000..bb812fe --- /dev/null +++ b/src/modules/MainMenu.cpp @@ -0,0 +1,48 @@ +#include + +#include "MainMenu.h" +#include "level/Stream.h" +#include "game/Game.h" + +void MainMenu::OnDraw() +{ + ImGui::Begin("Menu"); + + // Unit select + static char unit[MAX_UNIT_NAME_LENGTH] = ""; + ImGui::InputText("Unit", unit, sizeof(unit)); + + if (ImGui::Button("Load unit")) + { + GAMELOOP_RequestLevelChangeByName(unit, Game::GetGameTracker(), 4); + } + + // Birth instance + static char object[64] = ""; + ImGui::InputText("Object", object, sizeof(object)); + + if (ImGui::Button("Birth object")) + { + BirthObject(object); + } + + ImGui::End(); +} + +void MainMenu::BirthObject(char* name) +{ + auto game = Game::GetGameTracker(); + auto player = Game::GetPlayerInstance(); + + if (player == nullptr) + { + return; + } + + // Load the object + auto tracker = STREAM_GetObjectTrackerByName(name); + while (tracker->objectStatus != 2 && STREAM_PollLoadQueue()); + + // Birth the instance at the player position + INSTANCE_BirthObjectNoParent(game->StreamUnitID, &player->position, &player->rotation, nullptr, tracker->object, 0, 1); +} \ No newline at end of file diff --git a/src/modules/MainMenu.h b/src/modules/MainMenu.h new file mode 100644 index 0000000..dbfed32 --- /dev/null +++ b/src/modules/MainMenu.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Module.h" + +class MainMenu : public Module +{ +private: + void BirthObject(char* name); + +public: + void OnDraw(); +}; \ No newline at end of file diff --git a/src/modules/Skew.cpp b/src/modules/Skew.cpp index d2b6026..3717380 100644 --- a/src/modules/Skew.cpp +++ b/src/modules/Skew.cpp @@ -7,8 +7,19 @@ void Skew::ToggleSkew() { auto tracker = Game::GetGameTracker(); + // Ignore if there's no player instance + if (tracker->playerInstance == nullptr) + { + return; + } + tracker->cheatMode = !tracker->cheatMode; + +#ifndef TR8 INSTANCE_Post(tracker->playerInstance, 1048592, tracker->cheatMode); +#else + INSTANCE_Post(tracker->playerInstance, 12, tracker->cheatMode); +#endif } void Skew::Process(UINT msg, WPARAM wParam) diff --git a/src/util/Hooking.h b/src/util/Hooking.h index 67aa738..55218c5 100644 --- a/src/util/Hooking.h +++ b/src/util/Hooking.h @@ -15,7 +15,7 @@ class Hooking template static inline T CallReturn(unsigned int address, Args... args) { - reinterpret_cast(address)(args...); + return reinterpret_cast(address)(args...); } // Calls a class function @@ -29,6 +29,14 @@ class Hooking template static inline T ThisCallReturn(unsigned int address, Args... args) { - reinterpret_cast(address)(args...); + return reinterpret_cast(address)(args...); } }; + +#if TR7 +#define GET_ADDRESS(tr7, trae, tr8) trl +#elif TRAE +#define GET_ADDRESS(tr7, trae, tr8) trae +#elif TR8 +#define GET_ADDRESS(tr7, trae, tr8) tr8 +#endif \ No newline at end of file From 0b8c8fdeb8bfbb96e1e3feca8400a96dd74b5337 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 21 Jan 2024 03:46:09 +0100 Subject: [PATCH 09/55] Implement log, and add service locator for modules --- src/Hook.cpp | 10 ++++----- src/Hook.h | 20 +++++++++++++++--- src/menu/Menu.cpp | 4 ++-- src/modules/Log.cpp | 49 +++++++++++++++++++++++++++++++++++++++++++++ src/modules/Log.h | 22 ++++++++++++++++++++ 5 files changed, 95 insertions(+), 10 deletions(-) create mode 100644 src/modules/Log.cpp create mode 100644 src/modules/Log.h diff --git a/src/Hook.cpp b/src/Hook.cpp index 9e573a3..224115c 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -74,7 +74,7 @@ void Hook::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { m_menu->OnMessage(hWnd, msg, wParam, lParam); - for (auto& mod : m_modules) + for (auto& [hash, mod] : m_modules) { mod->OnInput(hWnd, msg, wParam, lParam); } @@ -82,7 +82,7 @@ void Hook::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) void Hook::OnFrame() { - for (auto& mod : m_modules) + for (auto& [hash, mod] : m_modules) { mod->OnFrame(); } @@ -100,9 +100,9 @@ void Hook::OnDevice() template void Hook::RegisterModule() { - auto module = std::make_shared(); + auto mod = std::make_shared(); - m_modules.push_back(module); + m_modules.insert({ typeid(T).hash_code(), mod }); } void Hook::RegisterModules() @@ -113,8 +113,8 @@ void Hook::RegisterModules() #ifndef TR8 RegisterModule(); RegisterModule(); - RegisterModule(); #endif + RegisterModule(); } Hook& Hook::GetInstance() diff --git a/src/Hook.h b/src/Hook.h index fee81fa..a88d6bf 100644 --- a/src/Hook.h +++ b/src/Hook.h @@ -1,7 +1,7 @@ #pragma once #include -#include +#include #include "menu/Menu.h" #include "modules/Module.h" @@ -10,7 +10,7 @@ class Hook { private: std::unique_ptr m_menu; - std::list> m_modules; + std::map> m_modules; void Initialize(); void PostInitialize(); @@ -27,7 +27,21 @@ class Hook void OnDevice(); - const auto& GetModules() { return m_modules; } + // These need to be defined here, else the linker becomes angry + + // Gets all modules + const auto& GetModules() + { + return m_modules; + } + + // Get a module by T + template + std::shared_ptr GetModule() + { + auto it = m_modules.find(typeid(T).hash_code()); + return it != m_modules.end() ? std::dynamic_pointer_cast(it->second) : nullptr; + } static Hook& GetInstance(); }; \ No newline at end of file diff --git a/src/menu/Menu.cpp b/src/menu/Menu.cpp index be7a27d..de95ec9 100644 --- a/src/menu/Menu.cpp +++ b/src/menu/Menu.cpp @@ -94,7 +94,7 @@ void Menu::Draw() if (ImGui::BeginMainMenuBar()) { // Draw all modules menus - for (auto& mod : modules) + for (auto& [hash, mod] : modules) { mod->OnMenu(); } @@ -103,7 +103,7 @@ void Menu::Draw() } // Draw all menus - for (auto& mod : modules) + for (auto& [hash, mod] : modules) { mod->OnDraw(); } diff --git a/src/modules/Log.cpp b/src/modules/Log.cpp new file mode 100644 index 0000000..89c887e --- /dev/null +++ b/src/modules/Log.cpp @@ -0,0 +1,49 @@ +#include + +#include "Log.h" + +void Log::OnMenu() +{ + if (ImGui::BeginMenu("Log")) + { + ImGui::MenuItem("Show log", nullptr, &m_show); + + if (ImGui::MenuItem("Clear")) + { + Clear(); + } + + ImGui::EndMenu(); + } +} + +void Log::OnDraw() +{ + if (m_show) + { + ImGui::Begin("Log", &m_show); + ImGui::BeginChild("Log"); + + ImGui::TextUnformatted(m_buffer.begin()); + + ImGui::EndChild(); + ImGui::End(); + } +} + +void Log::LogMessage(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + // Append-format the log to the buffer + m_buffer.appendfv(fmt, args); + m_buffer.append("\n"); + + va_end(args); +} + +void Log::Clear() +{ + m_buffer.clear(); +} \ No newline at end of file diff --git a/src/modules/Log.h b/src/modules/Log.h new file mode 100644 index 0000000..1e11126 --- /dev/null +++ b/src/modules/Log.h @@ -0,0 +1,22 @@ +#pragma once + +#include + +#include "Module.h" + +class Log : public Module +{ +private: + bool m_show = true; + ImGuiTextBuffer m_buffer; + +public: + void OnMenu(); + void OnDraw(); + + // Logs a message + void LogMessage(const char* fmt, ...); + + // Clears the log + void Clear(); +}; \ No newline at end of file From f4cfbea6cc65576b2c04107c4cdd71e532c08ed8 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 21 Jan 2024 04:33:35 +0100 Subject: [PATCH 10/55] Add back Underworld script log --- src/Hook.cpp | 11 ++++++- src/modules/Log.cpp | 14 +++++++-- src/modules/Log.h | 6 ++-- src/modules/ScriptLog.cpp | 63 +++++++++++++++++++++++++++++++++++++++ src/modules/ScriptLog.h | 9 ++++++ 5 files changed, 98 insertions(+), 5 deletions(-) create mode 100644 src/modules/ScriptLog.cpp create mode 100644 src/modules/ScriptLog.h diff --git a/src/Hook.cpp b/src/Hook.cpp index 224115c..c09fa83 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -14,6 +14,7 @@ #include "modules/Render.h" #include "modules/Draw.h" #include "modules/Log.h" +#include "modules/ScriptLog.h" #include "cdc/render/PCDeviceManager.h" @@ -41,6 +42,10 @@ Hook::Hook() : m_menu(nullptr), m_modules() void Hook::Initialize() { + // Initialize MinHook + MH_Initialize(); + + // Register all modules RegisterModules(); #ifndef TR8 @@ -52,7 +57,6 @@ void Hook::Initialize() #endif // Create the initial hook - MH_Initialize(); MH_CreateHook(match.get_first(), D3D_Init, (void**)&s_D3D_Init); MH_EnableHook(MH_ALL_HOOKS); } @@ -110,11 +114,16 @@ void Hook::RegisterModules() RegisterModule(); RegisterModule(); RegisterModule(); + #ifndef TR8 RegisterModule(); RegisterModule(); +#else + RegisterModule(); #endif + RegisterModule(); + } Hook& Hook::GetInstance() diff --git a/src/modules/Log.cpp b/src/modules/Log.cpp index 89c887e..276cd73 100644 --- a/src/modules/Log.cpp +++ b/src/modules/Log.cpp @@ -25,18 +25,28 @@ void Log::OnDraw() ImGui::BeginChild("Log"); ImGui::TextUnformatted(m_buffer.begin()); + ImGui::SetScrollHereY(1.f); ImGui::EndChild(); ImGui::End(); } } -void Log::LogMessage(const char* fmt, ...) +void Log::Write(const char* fmt, ...) +{ + va_list args; + va_start(args, fmt); + + m_buffer.appendfv(fmt, args); + + va_end(args); +} + +void Log::WriteLine(const char* fmt, ...) { va_list args; va_start(args, fmt); - // Append-format the log to the buffer m_buffer.appendfv(fmt, args); m_buffer.append("\n"); diff --git a/src/modules/Log.h b/src/modules/Log.h index 1e11126..6c96225 100644 --- a/src/modules/Log.h +++ b/src/modules/Log.h @@ -14,8 +14,10 @@ class Log : public Module void OnMenu(); void OnDraw(); - // Logs a message - void LogMessage(const char* fmt, ...); + // Writes to the log + void Write(const char* fmt, ...); + // Writes a line to the log + void WriteLine(const char* fmt, ...); // Clears the log void Clear(); diff --git a/src/modules/ScriptLog.cpp b/src/modules/ScriptLog.cpp new file mode 100644 index 0000000..96fb718 --- /dev/null +++ b/src/modules/ScriptLog.cpp @@ -0,0 +1,63 @@ +#include +#include + +#include "ScriptLog.h" + +#include "Log.h" +#include "Hook.h" + +static void LogValue(const char* fmt, std::any value, bool newLine) +{ + auto log = Hook::GetInstance().GetModule(); + log->Write(fmt, value); + + if (newLine) + { + log->Write("\n"); + } +} + +// Script function hooks + +void __stdcall ScriptPrintInt(int value, int time, bool appendNewLine) +{ + LogValue("%d", value, appendNewLine); +} + +void __stdcall ScriptPrintFloat(float value, int time, bool appendNewLine) +{ + LogValue("%f", value, appendNewLine); +} + +void __stdcall ScriptLogInt(int value, bool appendNewLine) +{ + LogValue("%d", value, appendNewLine); +} + +void __stdcall ScriptLogFloat(float value, bool appendNewLine) +{ + LogValue("%f", value, appendNewLine); +} + +void __cdecl NsCoreBase_PrintString(int instance, int numArgs, void** args) +{ + LogValue("%s", args[0], (__int8)args[1] != 0); +} + +void __cdecl NsCoreBase_LogString(int instance, int numArgs, void** args) +{ + LogValue("%s", args[0], (__int8)args[1] != 0); +} + +ScriptLog::ScriptLog() +{ + // Insert all hooks + MH_CreateHook((void*)0x4A3280, ScriptPrintInt, nullptr); + MH_CreateHook((void*)0x4A32C0, ScriptPrintFloat, nullptr); + MH_CreateHook((void*)0x4A3200, ScriptLogInt, nullptr); + MH_CreateHook((void*)0x4A3240, ScriptLogFloat, nullptr); + MH_CreateHook((void*)0x795E30, NsCoreBase_PrintString, nullptr); + MH_CreateHook((void*)0x795DB0, NsCoreBase_PrintString, nullptr); + + MH_EnableHook(MH_ALL_HOOKS); +} \ No newline at end of file diff --git a/src/modules/ScriptLog.h b/src/modules/ScriptLog.h new file mode 100644 index 0000000..4e8f591 --- /dev/null +++ b/src/modules/ScriptLog.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Module.h" + +class ScriptLog : public Module +{ +public: + ScriptLog(); +}; \ No newline at end of file From 13cbff58f1d9da378c4521a5c69c9d5c8e6f26a0 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 21 Jan 2024 04:54:40 +0100 Subject: [PATCH 11/55] Fix build --- .github/workflows/build.yml | 12 +----------- premake5.lua | 18 +++++++++++++++++- src/Hook.cpp | 8 ++++---- src/game/Game.h | 5 ++++- src/util/Hooking.h | 2 +- 5 files changed, 27 insertions(+), 18 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 00ca965..2b9768a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -26,7 +26,7 @@ jobs: tar -xf lib.zip - name: Generate project files - run: .\premake5 vs2019 + run: .\premake5 vs2022 # compile - name: Build Anniversary @@ -38,9 +38,6 @@ jobs: - name: Build Legend run: MSBuild /p:Configuration=Release /p:Platform=TR7 - - name: Build Legend Nextgen Demo - run: MSBuild /p:Configuration=Release /p:Platform=TR7DEBUG - # upload - uses: actions/upload-artifact@v3 with: @@ -62,10 +59,3 @@ jobs: path: | bin/TR7/Release/TR7-Menu-Hook.asi bin/TR7/Release/TR7-Menu-Hook.pdb - - - uses: actions/upload-artifact@v3 - with: - name: Legend Nextgen Demo - path: | - bin/TR7DEBUG/Release/TR7DEBUG-Menu-Hook.asi - bin/TR7DEBUG/Release/TR7DEBUG-Menu-Hook.pdb diff --git a/premake5.lua b/premake5.lua index e442d01..15560b1 100644 --- a/premake5.lua +++ b/premake5.lua @@ -1,6 +1,7 @@ workspace "TRAE-menu-hook" architecture "x86" configurations { "Debug", "Release" } + platforms { "TR7", "TRAE", "TR8" } project "TRAE-menu-hook" kind "SharedLib" @@ -32,6 +33,8 @@ project "TRAE-menu-hook" "vendor/json/include" } + defines { "IMGUI_IMPL_WIN32_DISABLE_GAMEPAD" } + -- Build configurations symbols "On" @@ -40,4 +43,17 @@ project "TRAE-menu-hook" filter "configurations:Release" defines { "NDEBUG" } - optimize "On" \ No newline at end of file + optimize "On" + + -- Game configurations + filter "platforms:TR7" + defines { "TR7" } + targetname "TR7-Menu-Hook" + + filter "platforms:TRAE" + defines { "TRAE" } + targetname "TRAE-Menu-Hook" + + filter "platforms:TR8" + defines { "TR8" } + targetname "TR8-Menu-Hook" diff --git a/src/Hook.cpp b/src/Hook.cpp index c09fa83..da8966f 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -11,8 +11,8 @@ #include "modules/MainMenu.h" #include "modules/InstanceViewer.h" #include "modules/Skew.h" -#include "modules/Render.h" -#include "modules/Draw.h" +//#include "modules/Render.h" +//#include "modules/Draw.h" #include "modules/Log.h" #include "modules/ScriptLog.h" @@ -116,8 +116,8 @@ void Hook::RegisterModules() RegisterModule(); #ifndef TR8 - RegisterModule(); - RegisterModule(); + //RegisterModule(); + //RegisterModule(); #else RegisterModule(); #endif diff --git a/src/game/Game.h b/src/game/Game.h index 53125f7..48e1058 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -46,8 +46,11 @@ struct GameTracker char gameMode; char cheatMode; char savingGame; + __int16 postSaveScreenID; - char pad1[66]; + int StreamUnitID; + + char pad1[60]; float timeMult; }; diff --git a/src/util/Hooking.h b/src/util/Hooking.h index 55218c5..3bd348f 100644 --- a/src/util/Hooking.h +++ b/src/util/Hooking.h @@ -34,7 +34,7 @@ class Hooking }; #if TR7 -#define GET_ADDRESS(tr7, trae, tr8) trl +#define GET_ADDRESS(tr7, trae, tr8) tr7 #elif TRAE #define GET_ADDRESS(tr7, trae, tr8) trae #elif TR8 From 7947602c9e1c5f1e24d35440bf4c7218a96c47be Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 21 Jan 2024 15:01:01 +0100 Subject: [PATCH 12/55] Add draw group visualization --- src/modules/InstanceViewer.cpp | 20 ++++++++++++++++++++ src/modules/InstanceViewer.h | 4 ++++ 2 files changed, 24 insertions(+) diff --git a/src/modules/InstanceViewer.cpp b/src/modules/InstanceViewer.cpp index b5ab24f..5674d3b 100644 --- a/src/modules/InstanceViewer.cpp +++ b/src/modules/InstanceViewer.cpp @@ -131,6 +131,13 @@ void InstanceViewer::DrawInstance() // Draw groups if (ImGui::CollapsingHeader("Draw groups")) { +#ifndef TR8 + // Draw the bit representation + auto binary = GetBinary(~instance->noDrawGroups); + + ImGui::Text("%s", binary.c_str()); +#endif + static int drawGroup = 0; static bool enabled = false; @@ -180,4 +187,17 @@ void InstanceViewer::SkewTo(Instance* instance) auto player = Game::GetPlayerInstance(); player->position = instance->position; +} + +std::string InstanceViewer::GetBinary(int value) +{ + std::string binary; + + // Not actually binary, reversed binary + for (int i = 0; i <= 31; i++) + { + binary += (value & (1 << i)) ? "1" : "0"; + } + + return binary; } \ No newline at end of file diff --git a/src/modules/InstanceViewer.h b/src/modules/InstanceViewer.h index b4fdd5a..fd4d77c 100644 --- a/src/modules/InstanceViewer.h +++ b/src/modules/InstanceViewer.h @@ -1,5 +1,7 @@ #pragma once +#include + #include "Module.h" #include "instance/Instance.h" @@ -14,6 +16,8 @@ class InstanceViewer : public Module void DrawInstance(); void SkewTo(Instance* instance); + std::string GetBinary(int value); + public: void OnMenu(); void OnDraw(); From 60f6db4fb6f48333133d1a553cfcd63b274406d3 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 21 Jan 2024 17:05:55 +0100 Subject: [PATCH 13/55] Add event debug --- src/Hook.cpp | 2 + src/game/Game.cpp | 5 ++ src/game/Game.h | 2 + src/level/Event.cpp | 9 ++ src/level/Event.h | 22 +++++ src/level/Level.h | 111 +++++++++++++++++++++++++ src/level/Stream.h | 24 ++++++ src/modules/InstanceViewer.cpp | 2 +- src/modules/Level.cpp | 147 +++++++++++++++++++++++++++++++++ src/modules/Level.h | 20 +++++ 10 files changed, 343 insertions(+), 1 deletion(-) create mode 100644 src/level/Event.cpp create mode 100644 src/level/Event.h create mode 100644 src/level/Level.h create mode 100644 src/modules/Level.cpp create mode 100644 src/modules/Level.h diff --git a/src/Hook.cpp b/src/Hook.cpp index da8966f..7667dc3 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -15,6 +15,7 @@ //#include "modules/Draw.h" #include "modules/Log.h" #include "modules/ScriptLog.h" +#include "modules/Level.h" #include "cdc/render/PCDeviceManager.h" @@ -116,6 +117,7 @@ void Hook::RegisterModules() RegisterModule(); #ifndef TR8 + RegisterModule(); //RegisterModule(); //RegisterModule(); #else diff --git a/src/game/Game.cpp b/src/game/Game.cpp index 30d1eb2..fe09907 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -12,6 +12,11 @@ GameTracker* Game::GetGameTracker() return (GameTracker*)GET_ADDRESS(0x10E5370, 0x838330, 0x00E7F088); } +STracker* Game::GetStreamTracker() +{ + return (STracker*)GET_ADDRESS(0x11582F8, 0x8AE378, 0xDBAB40); +} + void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType) { auto addr = GET_ADDRESS(0x451970, 0xC61CFA, 0x5DF8C0); diff --git a/src/game/Game.h b/src/game/Game.h index 48e1058..9a4283f 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -1,6 +1,7 @@ #pragma once #include "instance/Instance.h" +#include "level/Stream.h" struct menu_t; struct MemCardInfo; @@ -111,6 +112,7 @@ class Game public: static Instance* GetPlayerInstance(); static GameTracker* GetGameTracker(); + static STracker* GetStreamTracker(); }; void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType); \ No newline at end of file diff --git a/src/level/Event.cpp b/src/level/Event.cpp new file mode 100644 index 0000000..04a2a7d --- /dev/null +++ b/src/level/Event.cpp @@ -0,0 +1,9 @@ +#include "Event.h" +#include "util/Hooking.h" + +void* RELOC_GetProcAddress(void* peData, char* lpszName) +{ + auto addr = GET_ADDRESS(0x464550, 0x4680C0, 0x000000); + + return Hooking::CallReturn(addr, peData, lpszName); +} \ No newline at end of file diff --git a/src/level/Event.h b/src/level/Event.h new file mode 100644 index 0000000..f362953 --- /dev/null +++ b/src/level/Event.h @@ -0,0 +1,22 @@ +#pragma once + +struct EventVar +{ + char* name; + int variable; +}; + +struct UnsavedVar +{ + char* name; + int* variable; +}; + +struct GlobalData +{ + char pad1[232]; + + int* eventVars; +}; + +void* RELOC_GetProcAddress(void* peData, char* lpszName); \ No newline at end of file diff --git a/src/level/Level.h b/src/level/Level.h new file mode 100644 index 0000000..464ffb8 --- /dev/null +++ b/src/level/Level.h @@ -0,0 +1,111 @@ +#pragma once + +#include "cdc/math/Vector.h" + +struct Intro; +struct StreamUnitPortal; +struct Signal; +struct Instance; +struct KDNode; + +struct IndexedFace +{ + unsigned __int16 i0; + unsigned __int16 i1; + unsigned __int16 i2; + + unsigned __int8 adjacencyFlags; + unsigned __int8 collisionFlags; + unsigned __int8 clientFlags; + unsigned __int8 materialType; +}; + +struct MeshVertex +{ + __int16 x; + __int16 y; + __int16 z; +}; + +struct MeshVertex32 +{ + float x; + float y; + float z; + float w; +}; + +struct BBox +{ + cdc::Vector3 bMin; + cdc::Vector3 bMax; +}; + +struct Mesh +{ + BBox m_box; + cdc::Vector3 m_position; + + void* m_vertices; + IndexedFace* m_faces; + KDNode* m_root; + + void* m_clientData; + + unsigned __int16 m_vertexType; + unsigned __int16 m_numNodes; + unsigned __int16 m_numFaces; + unsigned __int16 m_numVertices; + unsigned __int16 m_height; + unsigned __int16 m_numDegenerateFaces; + unsigned __int16 m_numNonManifoldEdges; +}; + +struct Level; + +struct TerrainGroup +{ + cdc::Vector3 globalOffset; + cdc::Vector3 localOffset; + + int flags; + int ID; + int uniqueID; + int splineID; + Instance* instanceSpline; + Level* level; + Mesh* mesh; + + char pad1[116]; +}; + +struct Terrain +{ + __int16 UnitChangeFlags; + __int16 spad; + + int numIntros; + Intro* introList; + + int numStreamUnitPortals; + StreamUnitPortal* streamUnitPortals; + + int numTerrainGroups; + TerrainGroup* terrainGroups; + + TerrainGroup* signalTerrainGroup; + Signal* signals; +}; + +struct Level +{ + Terrain* terrain; + + char pad1[140]; + + Signal* SignalListStart; + __int16* SignalIDList; + void* splineCameraData; + + void* relocModule; // Pointer to script executable +}; \ No newline at end of file diff --git a/src/level/Stream.h b/src/level/Stream.h index 965974d..5bd4085 100644 --- a/src/level/Stream.h +++ b/src/level/Stream.h @@ -1,6 +1,7 @@ #pragma once #include "instance/Object.h" +#include "level/Level.h" #if TRAE || TR7 #define MAX_UNIT_NAME_LENGTH 20 @@ -8,8 +9,31 @@ #define MAX_UNIT_NAME_LENGTH 128 #endif +#define MAX_STREAM_UNITS 8 + struct ResolveObject; +struct StreamUnit +{ + int StreamUnitID; + + char used; + char unitHidden; + __int16 unitFlags; + + Level* level; + char baseAreaName[20]; + + ResolveObject* resolveObj; + + char pad1[56]; +}; + +struct STracker +{ + StreamUnit StreamList[MAX_STREAM_UNITS]; +}; + struct ObjectTracker { ResolveObject* resolveObj; diff --git a/src/modules/InstanceViewer.cpp b/src/modules/InstanceViewer.cpp index 5674d3b..bc1e455 100644 --- a/src/modules/InstanceViewer.cpp +++ b/src/modules/InstanceViewer.cpp @@ -21,7 +21,7 @@ void InstanceViewer::OnDraw() if (m_show) { ImGui::Begin("Instances", &m_show); - ImGui::Columns(2, "instances"); + ImGui::Columns(2); // Filter ImGui::InputText("Name", m_filter, sizeof(m_filter)); diff --git a/src/modules/Level.cpp b/src/modules/Level.cpp new file mode 100644 index 0000000..1fd1d7f --- /dev/null +++ b/src/modules/Level.cpp @@ -0,0 +1,147 @@ +#include +#include + +#include "Level.h" +#include "level/Stream.h" +#include "level/Event.h" +#include "game/Game.h" +#include "util/Hooking.h" + +static bool s_disableScript = false; +static void (*s_STREAM_FinishLoad)(StreamUnit*); + +static void STREAM_FinishLoad(StreamUnit* streamUnit) +{ + if (s_disableScript) + { + streamUnit->level->relocModule = nullptr; + } + + s_STREAM_FinishLoad(streamUnit); +} + +LevelModule::LevelModule() +{ + // Insert hook + auto addr = GET_ADDRESS(0x5D5640, 0x5DB680, 0x000000); + + MH_CreateHook((void*)addr, STREAM_FinishLoad, (void**)&s_STREAM_FinishLoad); + MH_EnableHook(MH_ALL_HOOKS); +} + +void LevelModule::OnMenu() +{ + if (ImGui::BeginMenu("Level")) + { + ImGui::MenuItem("Disable script", nullptr, &s_disableScript); + ImGui::MenuItem("Event debug", nullptr, &m_eventDebug); + + ImGui::EndMenu(); + } +} + +void LevelModule::OnDraw() +{ + if (m_eventDebug) + { + DrawEventDebug(); + } +} + +void LevelModule::DrawEventDebug() +{ + ImGui::Begin("Event debug", &m_eventDebug); + ImGui::Columns(2); + + auto stream = Game::GetStreamTracker(); + + // Level list + ImGui::BeginChild("LevelsTree"); + + for (int i = 0; i < MAX_STREAM_UNITS; i++) + { + auto unit = &stream->StreamList[i]; + + if (unit->used != 2) + { + continue; + } + + if (ImGui::TreeNodeEx((void*)unit, ImGuiTreeNodeFlags_Leaf, "%s", unit->baseAreaName)) + { + if (ImGui::IsItemClicked()) + { + m_selected = unit; + } + + ImGui::TreePop(); + } + } + + ImGui::EndChild(); + + // Event debug + ImGui::NextColumn(); + + if (m_selected && m_selected->used != 2) + { + m_selected = nullptr; + } + + if (m_selected) + { + ImGui::BeginChild("EventDebug"); + + DrawEventDebug(m_selected); + + ImGui::EndChild(); + } + + ImGui::End(); +} + +void LevelModule::DrawEventDebug(StreamUnit* unit) +{ + auto level = unit->level; + + if (!level->relocModule) + { + return; + } + + // Get the event debug export + auto eventVars = (EventVar*)RELOC_GetProcAddress(level->relocModule, "EventDebug"); + + if (eventVars) + { + auto globalData = (GlobalData*)GET_ADDRESS(0x1076980, 0x7C8A50, 0x000000); + auto i = 0; + + // Display all event variables + while (true) + { + auto var = &eventVars[i++]; + + if (var->name == nullptr) + break; + + ImGui::Text("%d %s = %d", var->variable, var->name, globalData->eventVars[var->variable]); + } + + // Display all unsaved variables + auto unsavedVars = (UnsavedVar*)eventVars; + while (true) + { + auto var = &unsavedVars[i++]; + + if (var->name == nullptr || var->variable == nullptr) + break; + + ImGui::Text("%s %d", var->name, *var->variable); + } + } + else + { + ImGui::Text("No event debug for this level"); + } +} \ No newline at end of file diff --git a/src/modules/Level.h b/src/modules/Level.h new file mode 100644 index 0000000..0b0f367 --- /dev/null +++ b/src/modules/Level.h @@ -0,0 +1,20 @@ +#pragma once + +#include "Module.h" +#include "level/Stream.h" + +class LevelModule : public Module +{ +private: + bool m_eventDebug = false; + StreamUnit* m_selected = nullptr; + + void DrawEventDebug(); + void DrawEventDebug(StreamUnit* unit); + +public: + LevelModule(); + + void OnMenu(); + void OnDraw(); +}; \ No newline at end of file From 20d446822a290e9d616ba0361ba82388c86cba79 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 21 Jan 2024 18:10:14 +0100 Subject: [PATCH 14/55] Rename modules --- src/Hook.cpp | 4 ++-- src/modules/{InstanceViewer.cpp => Instance.cpp} | 12 ++++++------ src/modules/{InstanceViewer.h => Instance.h} | 2 +- 3 files changed, 9 insertions(+), 9 deletions(-) rename src/modules/{InstanceViewer.cpp => Instance.cpp} (94%) rename src/modules/{InstanceViewer.h => Instance.h} (90%) diff --git a/src/Hook.cpp b/src/Hook.cpp index 7667dc3..f98d278 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -9,7 +9,7 @@ // Modules #include "modules/MainMenu.h" -#include "modules/InstanceViewer.h" +#include "modules/Instance.h" #include "modules/Skew.h" //#include "modules/Render.h" //#include "modules/Draw.h" @@ -113,7 +113,7 @@ void Hook::RegisterModule() void Hook::RegisterModules() { RegisterModule(); - RegisterModule(); + RegisterModule(); RegisterModule(); #ifndef TR8 diff --git a/src/modules/InstanceViewer.cpp b/src/modules/Instance.cpp similarity index 94% rename from src/modules/InstanceViewer.cpp rename to src/modules/Instance.cpp index bc1e455..5cd98a3 100644 --- a/src/modules/InstanceViewer.cpp +++ b/src/modules/Instance.cpp @@ -1,12 +1,12 @@ #include #include -#include "InstanceViewer.h" +#include "Instance.h" #include "instance/Instances.h" #include "instance/Animation.h" #include "game/Game.h" -void InstanceViewer::OnMenu() +void InstanceModule::OnMenu() { if (ImGui::BeginMenu("Instance")) { @@ -16,7 +16,7 @@ void InstanceViewer::OnMenu() } } -void InstanceViewer::OnDraw() +void InstanceModule::OnDraw() { if (m_show) { @@ -70,7 +70,7 @@ void InstanceViewer::OnDraw() } } -void InstanceViewer::DrawInstance() +void InstanceModule::DrawInstance() { auto instance = m_selected; @@ -182,14 +182,14 @@ void InstanceViewer::DrawInstance() } } -void InstanceViewer::SkewTo(Instance* instance) +void InstanceModule::SkewTo(Instance* instance) { auto player = Game::GetPlayerInstance(); player->position = instance->position; } -std::string InstanceViewer::GetBinary(int value) +std::string InstanceModule::GetBinary(int value) { std::string binary; diff --git a/src/modules/InstanceViewer.h b/src/modules/Instance.h similarity index 90% rename from src/modules/InstanceViewer.h rename to src/modules/Instance.h index fd4d77c..5a54d12 100644 --- a/src/modules/InstanceViewer.h +++ b/src/modules/Instance.h @@ -6,7 +6,7 @@ #include "instance/Instance.h" -class InstanceViewer : public Module +class InstanceModule : public Module { private: bool m_show = false; From 4e125a46fae83cd420f0b0ace46afcfb4f49bc87 Mon Sep 17 00:00:00 2001 From: Indra Date: Tue, 23 Jan 2024 14:21:53 +0100 Subject: [PATCH 15/55] Add more font and vector methods --- src/cdc/math/Vector.cpp | 31 +++++++++++++++++++++++++++++++ src/cdc/math/Vector.h | 8 ++++++++ src/render/Font.cpp | 27 +++++++++++++++++++++++++++ src/render/Font.h | 4 ++++ 4 files changed, 70 insertions(+) create mode 100644 src/cdc/math/Vector.cpp diff --git a/src/cdc/math/Vector.cpp b/src/cdc/math/Vector.cpp new file mode 100644 index 0000000..120919a --- /dev/null +++ b/src/cdc/math/Vector.cpp @@ -0,0 +1,31 @@ +#include "Vector.h" + +void cdc::Vector::operator +=(Vector* b) +{ + vec128 = _mm_add_ps(vec128, b->vec128); +} + +void cdc::Vector::operator -=(Vector* b) +{ + vec128 = _mm_sub_ps(vec128, b->vec128); +} + +void cdc::Vector::operator *=(Vector* b) +{ + vec128 = _mm_mul_ps(vec128, b->vec128); +} + +void cdc::Vector::operator /=(Vector* b) +{ + vec128 = _mm_div_ps(vec128, b->vec128); +} + +void cdc::Vector::operator *=(float b) +{ + vec128 = _mm_mul_ps(vec128, _mm_shuffle_ps(_mm_set_ss(b), _mm_set_ss(b), _MM_SHUFFLE(0, 0, 0, 0))); +} + +void cdc::Vector::operator /=(float b) +{ + vec128 = _mm_div_ps(vec128, _mm_shuffle_ps(_mm_set_ss(b), _mm_set_ss(b), _MM_SHUFFLE(0, 0, 0, 0))); +} \ No newline at end of file diff --git a/src/cdc/math/Vector.h b/src/cdc/math/Vector.h index 1bdcd22..2f9231e 100644 --- a/src/cdc/math/Vector.h +++ b/src/cdc/math/Vector.h @@ -19,6 +19,14 @@ namespace cdc float w; }; }; + + void operator +=(Vector* b); + void operator -=(Vector* b); + void operator *=(Vector* b); + void operator /=(Vector* b); + + void operator *=(float b); + void operator /=(float b); }; class Vector2 : public Vector diff --git a/src/render/Font.cpp b/src/render/Font.cpp index d059d26..016e9f0 100644 --- a/src/render/Font.cpp +++ b/src/render/Font.cpp @@ -29,6 +29,11 @@ void Font::SetCursor(float x, float y) Hooking::Call(0x433C70, x, y); } +void Font::GetCursor(float* x, float* y) +{ + Hooking::Call(0x433C90, x, y); +} + void Font::SetScale(float scaleX, float scaleY) { Hooking::Call(0x433E60, scaleX, scaleY); @@ -45,11 +50,33 @@ void Font::Print(const char* fmt, ...) PrintFormatted(s_formatted); } +void Font::PrintCentered(const char* fmt, ...) +{ + va_list va; + + va_start(va, fmt); + vsprintf_s(s_formatted, fmt, va); + va_end(va); + + float x, y; + GetCursor(&x, &y); + + auto width = GetTextWidth(s_formatted); + SetCursor(x - width / 2, y); + + PrintFormatted(s_formatted); +} + void Font::PrintFormatted(const char* formatted, int backdrop) { Hooking::ThisCall(0x434A70, this, formatted, backdrop); } +float Font::GetTextWidth(const char* text) +{ + return Hooking::ThisCallReturn(0x434510, this, text); +} + void Font::OnFlush(std::function callback) { if (!s_callback) diff --git a/src/render/Font.h b/src/render/Font.h index b9a0ab5..f86dfa2 100644 --- a/src/render/Font.h +++ b/src/render/Font.h @@ -11,10 +11,14 @@ class Font static Font* GetMainFont(); static void SetCursor(float x, float y); + static void GetCursor(float* x, float* y); static void SetScale(float scaleX, float scaleY); void Print(const char* fmt, ...); + void PrintCentered(const char* fmt, ...); void PrintFormatted(const char* formatted, int backdrop = 0); + float GetTextWidth(const char* text); + static void OnFlush(std::function callback); }; \ No newline at end of file From f84f8bf5f4258661df648a3ce0e7e640db1d4763 Mon Sep 17 00:00:00 2001 From: Indra Date: Tue, 23 Jan 2024 18:35:18 +0100 Subject: [PATCH 16/55] Add mod loader code --- src/Hook.cpp | 2 + src/cdc/file/FileSystem.h | 49 ++++++++++++ src/cdc/file/MultiFileSystem.cpp | 12 +++ src/cdc/file/MultiFileSystem.h | 12 +++ src/file/FileSystem.cpp | 9 +++ src/file/FileSystem.h | 6 ++ src/file/HookFileSystem.cpp | 131 +++++++++++++++++++++++++++++++ src/file/HookFileSystem.h | 32 ++++++++ src/file/MultiFileSystem.cpp | 73 +++++++++++++++++ src/file/MultiFileSystem.h | 35 +++++++++ src/modules/ModLoader.cpp | 62 +++++++++++++++ src/modules/ModLoader.h | 9 +++ 12 files changed, 432 insertions(+) create mode 100644 src/cdc/file/FileSystem.h create mode 100644 src/cdc/file/MultiFileSystem.cpp create mode 100644 src/cdc/file/MultiFileSystem.h create mode 100644 src/file/FileSystem.cpp create mode 100644 src/file/FileSystem.h create mode 100644 src/file/HookFileSystem.cpp create mode 100644 src/file/HookFileSystem.h create mode 100644 src/file/MultiFileSystem.cpp create mode 100644 src/file/MultiFileSystem.h create mode 100644 src/modules/ModLoader.cpp create mode 100644 src/modules/ModLoader.h diff --git a/src/Hook.cpp b/src/Hook.cpp index f98d278..69f087b 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -16,6 +16,7 @@ #include "modules/Log.h" #include "modules/ScriptLog.h" #include "modules/Level.h" +#include "modules/ModLoader.h" #include "cdc/render/PCDeviceManager.h" @@ -115,6 +116,7 @@ void Hook::RegisterModules() RegisterModule(); RegisterModule(); RegisterModule(); + RegisterModule(); #ifndef TR8 RegisterModule(); diff --git a/src/cdc/file/FileSystem.h b/src/cdc/file/FileSystem.h new file mode 100644 index 0000000..c0229c4 --- /dev/null +++ b/src/cdc/file/FileSystem.h @@ -0,0 +1,49 @@ +#pragma once + +namespace cdc +{ + class FileRequest + { + }; + + class FileReceiver + { + public: + virtual int ReceiveData(const char* data, unsigned int dataSize, unsigned int requestOffset) = 0; + virtual void ReceiveStarted(FileRequest* request, unsigned int requestSize) = 0; + virtual void ReceiveCancelled(FileRequest* request) = 0; + virtual void ReceiveDone(FileRequest* request) = 0; + }; + + class File + { + }; + + class FileSystem + { + public: + enum Status + { + IDLE, + BUSY + }; + + virtual FileRequest* RequestRead(FileReceiver* receiver, const char* fileName, unsigned int startOffset) = 0; + virtual File* OpenFile(const char* fileName) = 0; + virtual bool FileExists(const char* fileName) = 0; + virtual unsigned int GetFileSize(const char* fileName) = 0; + virtual void SetSpecialisationMask(unsigned int specMask) = 0; + virtual unsigned int GetSpecialisationMask() = 0; + virtual Status GetStatus() = 0; + virtual void Update() = 0; + virtual void Synchronize() = 0; + +#ifdef TR8 + virtual void Suspend() = 0; + virtual bool Resume() = 0; + virtual bool IsSuspended() = 0; +#endif + + virtual ~FileSystem() { }; + }; +} \ No newline at end of file diff --git a/src/cdc/file/MultiFileSystem.cpp b/src/cdc/file/MultiFileSystem.cpp new file mode 100644 index 0000000..c255b33 --- /dev/null +++ b/src/cdc/file/MultiFileSystem.cpp @@ -0,0 +1,12 @@ +#include + +#include "MultiFileSystem.h" +#include "util/Hooking.h" + +// Adds a file system to the multi file system +void cdc::MultiFileSystem::Add(FileSystem* fileSystem, bool bReprioritize, bool bAddToFront) +{ + auto match = hook::pattern("80 7C 24 0C 00 8B C1 74 5A 53").count(1); + + Hooking::ThisCall((unsigned int)match.get_first(), this, fileSystem, bReprioritize, bAddToFront); +} \ No newline at end of file diff --git a/src/cdc/file/MultiFileSystem.h b/src/cdc/file/MultiFileSystem.h new file mode 100644 index 0000000..df0a260 --- /dev/null +++ b/src/cdc/file/MultiFileSystem.h @@ -0,0 +1,12 @@ +#pragma once + +#include "FileSystem.h" + +namespace cdc +{ + class MultiFileSystem : public cdc::FileSystem + { + public: + void Add(FileSystem* fileSystem, bool bReprioritize, bool bAddToFront); + }; +} \ No newline at end of file diff --git a/src/file/FileSystem.cpp b/src/file/FileSystem.cpp new file mode 100644 index 0000000..06ceee6 --- /dev/null +++ b/src/file/FileSystem.cpp @@ -0,0 +1,9 @@ +#include "FileSystem.h" +#include "util/Hooking.h" + +cdc::FileSystem* GetFS() +{ + auto addr = GET_ADDRESS(0x45C700, 0x45F640, 0x472B50); + + return Hooking::CallReturn(addr); +} \ No newline at end of file diff --git a/src/file/FileSystem.h b/src/file/FileSystem.h new file mode 100644 index 0000000..63bd988 --- /dev/null +++ b/src/file/FileSystem.h @@ -0,0 +1,6 @@ +#pragma once + +#include "cdc/file/FileSystem.h" + +// Gets the current file system +cdc::FileSystem* GetFS(); \ No newline at end of file diff --git a/src/file/HookFileSystem.cpp b/src/file/HookFileSystem.cpp new file mode 100644 index 0000000..eb4488a --- /dev/null +++ b/src/file/HookFileSystem.cpp @@ -0,0 +1,131 @@ +#include +#include + +#include "HookFileSystem.h" +#include "Hook.h" +#include "modules/Log.h" + +HookFileSystem::HookFileSystem(cdc::FileSystem* pDiskFS) +{ + m_pDiskFS = pDiskFS; + m_specMask = 1; +} + +// Finds a file in the mods folder and rewrites the path +bool HookFileSystem::FindFile(const char* fileName, char* path) +{ + strcpy_s(path, _MAX_PATH, fileName); + + for (char* p = path; *p; ++p) + *p = tolower(*p); + + // Rewrite pc-w to mods folder + if (strncmp(path, "pc-w", 4) == 0) + { + strncpy(path, "mods", 4); + } + + // Rewrite root to mods folder + if (strncmp(path, "\\", 1) == 0) + { + // Move string 4 bytes + memmove(path + 4, path, _MAX_PATH - 4); + strncpy(path, "mods", 4); + } + + // Check first for file on disk suffixed by our specialisation mask + char specPath[_MAX_PATH]; + sprintf_s(specPath, "%s_%03d", path, m_specMask); + + if (m_pDiskFS->FileExists(specPath)) + { + strcpy_s(path, _MAX_PATH, specPath); + + return true; + } + + // Check if file exists on disk, if so return the diskFS + if (m_pDiskFS->FileExists(path)) + { + return true; + } + + return false; +} + +cdc::FileRequest* HookFileSystem::RequestRead(cdc::FileReceiver* receiver, const char* fileName, unsigned int startOffset) +{ + char path[_MAX_PATH]; + FindFile(fileName, path); + + Hook::GetInstance().GetModule()->WriteLine("Loading %s from mods folder", fileName); + + return m_pDiskFS->RequestRead(receiver, path, startOffset); +} + +cdc::File* HookFileSystem::OpenFile(char const* fileName) +{ + char path[_MAX_PATH]; + FindFile(fileName, path); + + return m_pDiskFS->OpenFile(path); +} + +bool HookFileSystem::FileExists(char const* fileName) +{ + char path[_MAX_PATH]; + return FindFile(fileName, path); +} + +unsigned int HookFileSystem::GetFileSize(char const* fileName) +{ + char path[_MAX_PATH]; + FindFile(fileName, path); + + return m_pDiskFS->GetFileSize(path); +} + +void HookFileSystem::SetSpecialisationMask(unsigned int specMask) +{ + m_pDiskFS->SetSpecialisationMask(specMask); + + // Unset next generation bit and set our spec mask + m_specMask = specMask & ~0x80000000; +} + +unsigned int HookFileSystem::GetSpecialisationMask() +{ + return 0; +} + +cdc::FileSystem::Status HookFileSystem::GetStatus() +{ + return m_pDiskFS->GetStatus(); +} + +void HookFileSystem::Update() +{ + m_pDiskFS->Update(); +} + +void HookFileSystem::Synchronize() +{ + m_pDiskFS->Synchronize(); +} + +#ifdef TR8 +void HookFileSystem::Suspend() +{ + m_pDiskFS->Suspend(); +} + +bool HookFileSystem::Resume() +{ + return m_pDiskFS->Resume(); +} + +bool HookFileSystem::IsSuspended() +{ + return m_pDiskFS->IsSuspended(); +} +#endif \ No newline at end of file diff --git a/src/file/HookFileSystem.h b/src/file/HookFileSystem.h new file mode 100644 index 0000000..1cf0814 --- /dev/null +++ b/src/file/HookFileSystem.h @@ -0,0 +1,32 @@ +#pragma once + +#include "cdc/file/FileSystem.h" + +// This file system will read files from the mods folder in the game directory +// the path will be rewritten and passed to the native file system +class HookFileSystem : public cdc::FileSystem +{ +private: + cdc::FileSystem* m_pDiskFS; + unsigned int m_specMask; + + bool FindFile(const char* fileName, char* path); +public: + HookFileSystem(cdc::FileSystem* pDiskFS); + + cdc::FileRequest* RequestRead(cdc::FileReceiver* receiver, const char* fileName, unsigned int startOffset); + cdc::File* OpenFile(const char* fileName); + bool FileExists(const char* fileName); + unsigned int GetFileSize(const char* fileName); + void SetSpecialisationMask(unsigned int specMask); + unsigned int GetSpecialisationMask(); + Status GetStatus(); + void Update(); + void Synchronize(); + +#ifdef TR8 + void Suspend(); + bool Resume(); + bool IsSuspended(); +#endif +}; \ No newline at end of file diff --git a/src/file/MultiFileSystem.cpp b/src/file/MultiFileSystem.cpp new file mode 100644 index 0000000..b162dda --- /dev/null +++ b/src/file/MultiFileSystem.cpp @@ -0,0 +1,73 @@ +#include "MultiFileSystem.h" + +MultiFileSystem::MultiFileSystem(cdc::FileSystem* pFS, cdc::FileSystem* pHookFS) +{ + m_pFS = pFS; + m_pHookFS = pHookFS; +} + +// Gets the best file system for a file simply by checking the hook file system first +cdc::FileSystem* MultiFileSystem::GetBestFileSystem(const char* fileName) +{ + // First check the hook file system, else fall back to default filesystem + if (m_pHookFS->FileExists(fileName)) + { + return m_pHookFS; + } + + return m_pFS; +} + +cdc::FileRequest* MultiFileSystem::RequestRead(cdc::FileReceiver* receiver, const char* fileName, unsigned int startOffset) +{ + auto pFS = GetBestFileSystem(fileName); + + return pFS->RequestRead(receiver, fileName, startOffset); +} + +cdc::File* MultiFileSystem::OpenFile(char const* fileName) +{ + auto pFS = GetBestFileSystem(fileName); + + return pFS->OpenFile(fileName); +} + +bool MultiFileSystem::FileExists(char const* fileName) +{ + return m_pFS->FileExists(fileName) || m_pHookFS->FileExists(fileName); +} + +unsigned int MultiFileSystem::GetFileSize(char const* fileName) +{ + auto pFS = GetBestFileSystem(fileName); + + return pFS->GetFileSize(fileName); +} + +void MultiFileSystem::SetSpecialisationMask(unsigned int specMask) +{ + m_pFS->SetSpecialisationMask(specMask); + m_pHookFS->SetSpecialisationMask(specMask); +} + +unsigned int MultiFileSystem::GetSpecialisationMask() +{ + return m_pFS->GetSpecialisationMask(); +} + +// These only need to call the default file system, both will end at the same place + +cdc::FileSystem::Status MultiFileSystem::GetStatus() +{ + return m_pFS->GetStatus(); +} + +void MultiFileSystem::Update() +{ + m_pFS->Update(); +} + +void MultiFileSystem::Synchronize() +{ + m_pFS->Synchronize(); +} \ No newline at end of file diff --git a/src/file/MultiFileSystem.h b/src/file/MultiFileSystem.h new file mode 100644 index 0000000..c24ca26 --- /dev/null +++ b/src/file/MultiFileSystem.h @@ -0,0 +1,35 @@ +#pragma once + +#include "cdc/file/FileSystem.h" + +// Simplified version for cdc::MultiFileSystem since it does not exist in Legend and Anniversary +// The real MultiFileSystem can dynamically add and re-order systems +// +// This just checks the hook file system first and else falls back to the +// original file system +class MultiFileSystem : public cdc::FileSystem +{ +private: + cdc::FileSystem* m_pFS; + cdc::FileSystem* m_pHookFS; + + cdc::FileSystem* GetBestFileSystem(const char* fileName); +public: + MultiFileSystem(cdc::FileSystem* pFS, cdc::FileSystem* pHookFS); + + cdc::FileRequest* RequestRead(cdc::FileReceiver* receiver, const char* fileName, unsigned int startOffset); + cdc::File* OpenFile(const char* fileName); + bool FileExists(const char* fileName); + unsigned int GetFileSize(const char* fileName); + void SetSpecialisationMask(unsigned int specMask); + unsigned int GetSpecialisationMask(); + Status GetStatus(); + void Update(); + void Synchronize(); + +#ifdef TR8 + void Suspend() { } + bool Resume() { return false; } + bool IsSuspended() { return false; } +#endif +}; \ No newline at end of file diff --git a/src/modules/ModLoader.cpp b/src/modules/ModLoader.cpp new file mode 100644 index 0000000..812357f --- /dev/null +++ b/src/modules/ModLoader.cpp @@ -0,0 +1,62 @@ +#include +#include + +#include "ModLoader.h" +#include "util/Hooking.h" + +#include "cdc/file/MultiFileSystem.h" + +#include "file/FileSystem.h" +#include "file/HookFileSystem.h" +#include "file/MultiFileSystem.h" + +static cdc::FileSystem* CreateHookFileSystem() +{ + auto fileSystem = new HookFileSystem(*(cdc::FileSystem**)GET_ADDRESS(0x10E58C0, 0x838890, 0x9CE27C)); + + return fileSystem; +} + +// Hooks +static bool(*s_InitPatchArchive)(const char*); +static void(*s_InitArchive)(); + +static bool InitPatchArchive(const char* name) +{ + // Let the game add the patch archive to the file system + auto ret = s_InitPatchArchive(name); + + // Create our hook file system + auto fileSystem = CreateHookFileSystem(); + + // Add the hook file system in front of all other file systems + auto multiFileSystem = (cdc::MultiFileSystem*)GetFS(); + multiFileSystem->Add(fileSystem, false, true); + + return ret; +} + +static void InitArchive() +{ + // Let the game initialize the archive file system + s_InitArchive(); + + // Create our hook file system and multi file system + auto fileSystem = CreateHookFileSystem(); + auto multiFileSystem = new MultiFileSystem(GetFS(), fileSystem); + + // Overwrite the original file system with ours + *(cdc::FileSystem**)GET_ADDRESS(0x10E58BC, 0x83888C, 0x9CE278) = multiFileSystem; +} + +// Initialize the mod loader and insert all hooks +ModLoader::ModLoader() +{ +#ifndef TR8 + MH_CreateHook((void*)GET_ADDRESS(0x45C670, 0x45F5B0, 0x473840), InitArchive, (void**)&s_InitArchive); +#else + MH_CreateHook((void*)0x478930, InitPatchArchive, (void**)&s_InitPatchArchive); +#endif + + MH_EnableHook(MH_ALL_HOOKS); +} \ No newline at end of file diff --git a/src/modules/ModLoader.h b/src/modules/ModLoader.h new file mode 100644 index 0000000..c42da41 --- /dev/null +++ b/src/modules/ModLoader.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Module.h" + +class ModLoader : public Module +{ +public: + ModLoader(); +}; \ No newline at end of file From b5cd5bd17a3005244089dfcede44bf622a52d1b9 Mon Sep 17 00:00:00 2001 From: Indra Date: Wed, 24 Jan 2024 03:10:38 +0100 Subject: [PATCH 17/55] Add back intro removal --- src/Hook.cpp | 2 ++ src/modules/Patches.cpp | 24 ++++++++++++++++++++++++ src/modules/Patches.h | 11 +++++++++++ src/util/Hooking.cpp | 33 +++++++++++++++++++++++++++++++++ src/util/Hooking.h | 11 +++++++++++ 5 files changed, 81 insertions(+) create mode 100644 src/modules/Patches.cpp create mode 100644 src/modules/Patches.h create mode 100644 src/util/Hooking.cpp diff --git a/src/Hook.cpp b/src/Hook.cpp index 69f087b..7de0866 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -17,6 +17,7 @@ #include "modules/ScriptLog.h" #include "modules/Level.h" #include "modules/ModLoader.h" +#include "modules/Patches.h" #include "cdc/render/PCDeviceManager.h" @@ -117,6 +118,7 @@ void Hook::RegisterModules() RegisterModule(); RegisterModule(); RegisterModule(); + RegisterModule(); #ifndef TR8 RegisterModule(); diff --git a/src/modules/Patches.cpp b/src/modules/Patches.cpp new file mode 100644 index 0000000..9d7d898 --- /dev/null +++ b/src/modules/Patches.cpp @@ -0,0 +1,24 @@ +#include + +#include "Patches.h" +#include "util/Hooking.h" + +Patches::Patches() +{ +#ifndef TR8 + RemoveIntro(); +#endif +} + +void Patches::RemoveIntro() +{ + auto match = hook::pattern("8D 0C 8D 03 00 00 00 89 0D").count(1); + auto mainState = *match.get_first(9); + + // Nop out the code setting the main state to intros + Hooking::Nop(match.get_first(), 13); + + // mov [mainState], 6 + Hooking::Patch(match.get_first(), { 0xC7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00 }); + Hooking::Patch(match.get_first(2), mainState); +} \ No newline at end of file diff --git a/src/modules/Patches.h b/src/modules/Patches.h new file mode 100644 index 0000000..588365d --- /dev/null +++ b/src/modules/Patches.h @@ -0,0 +1,11 @@ +#pragma once + +#include "Module.h" + +class Patches : public Module +{ +public: + Patches(); + + void RemoveIntro(); +}; \ No newline at end of file diff --git a/src/util/Hooking.cpp b/src/util/Hooking.cpp new file mode 100644 index 0000000..30df5be --- /dev/null +++ b/src/util/Hooking.cpp @@ -0,0 +1,33 @@ +#include + +#include "Hooking.h" + +void Hooking::Nop(void* address, unsigned int length) +{ + DWORD oldProtect; + VirtualProtect(address, length, PAGE_EXECUTE_READWRITE, &oldProtect); + + memset(address, 0x90, length); + + VirtualProtect(address, length, oldProtect, &oldProtect); +} + +void Hooking::Patch(void* address, std::initializer_list data) +{ + DWORD oldProtect; + VirtualProtect(address, data.size(), PAGE_EXECUTE_READWRITE, &oldProtect); + + memcpy(address, data.begin(), data.size()); + + VirtualProtect(address, data.size(), oldProtect, &oldProtect); +} + +void Hooking::Patch(void* address, unsigned int data) +{ + DWORD oldProtect; + VirtualProtect(address, sizeof(data), PAGE_EXECUTE_READWRITE, &oldProtect); + + memcpy(address, &data, sizeof(data)); + + VirtualProtect(address, sizeof(data), oldProtect, &oldProtect); +} \ No newline at end of file diff --git a/src/util/Hooking.h b/src/util/Hooking.h index 3bd348f..1033ab0 100644 --- a/src/util/Hooking.h +++ b/src/util/Hooking.h @@ -1,5 +1,7 @@ #pragma once +#include + // Helpers for hooking and calling functions class Hooking { @@ -31,6 +33,15 @@ class Hooking { return reinterpret_cast(address)(args...); } + + // Sets a memory region to no-op + static void Nop(void* address, unsigned int length); + + // Patches data at a memory region + static void Patch(void* address, std::initializer_list data); + + // Patches data at a memory region + static void Patch(void* address, unsigned int data); }; #if TR7 From 096e963847b464236b4dd5936811c53ac9f11e84 Mon Sep 17 00:00:00 2001 From: Indra Date: Wed, 24 Jan 2024 03:52:31 +0100 Subject: [PATCH 18/55] Fix font functions and add watermark --- src/game/Game.h | 15 +++++++++++++++ src/modules/MainMenu.cpp | 15 +++++++++++++++ src/modules/MainMenu.h | 1 + src/render/Font.cpp | 27 ++++++++++++++++++++------- 4 files changed, 51 insertions(+), 7 deletions(-) diff --git a/src/game/Game.h b/src/game/Game.h index 9a4283f..634ca74 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -8,6 +8,21 @@ struct MemCardInfo; struct Level; struct VertexPool; +enum MainState +{ + MS_NONE, + MS_PLAYGAME, + MS_LOADLEVEL, + MS_PLAY_CINEMATIC, + MS_SAMPLER_DEMO_DONE, + MS_ATTRACT_MODE, + MS_DISPLAY_MAIN_MENU, + MS_SHOW_STATIC_SCREEN, + MS_QUITGAME, + MS_PLAY_DARKCHRONICLE_CINEMATIC, + MS_PLAY_DARKCHRONICLE_CINEMATICMOVIE, +}; + struct WipeInfo { float wipeCur; diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index bb812fe..f615f27 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -3,6 +3,8 @@ #include "MainMenu.h" #include "level/Stream.h" #include "game/Game.h" +#include "util/Hooking.h" +#include "render/Font.h" void MainMenu::OnDraw() { @@ -45,4 +47,17 @@ void MainMenu::BirthObject(char* name) // Birth the instance at the player position INSTANCE_BirthObjectNoParent(game->StreamUnitID, &player->position, &player->rotation, nullptr, tracker->object, 0, 1); +} + +void MainMenu::OnFrame() +{ + auto mainState = *(int*)GET_ADDRESS(0x10E5868, 0x838838, 0x000000); + + if (mainState == MS_DISPLAY_MAIN_MENU) + { + auto font = Font::GetMainFont(); + + font->SetCursor(5.f, 430.f); + font->Print("TRLAU-Menu-Hook"); + } } \ No newline at end of file diff --git a/src/modules/MainMenu.h b/src/modules/MainMenu.h index dbfed32..7bfb723 100644 --- a/src/modules/MainMenu.h +++ b/src/modules/MainMenu.h @@ -9,4 +9,5 @@ class MainMenu : public Module public: void OnDraw(); + void OnFrame(); }; \ No newline at end of file diff --git a/src/render/Font.cpp b/src/render/Font.cpp index 016e9f0..340c366 100644 --- a/src/render/Font.cpp +++ b/src/render/Font.cpp @@ -21,22 +21,28 @@ char Font::s_formatted[1024]; Font* Font::GetMainFont() { - return *(Font**)0x7D1800; + return *(Font**)GET_ADDRESS(0x107F680, 0x7D1800, 0x9DE6D4); } void Font::SetCursor(float x, float y) { - Hooking::Call(0x433C70, x, y); + auto addr = GET_ADDRESS(0x431670, 0x433C70, 0x474C90); + + Hooking::Call(addr, x, y); } void Font::GetCursor(float* x, float* y) { - Hooking::Call(0x433C90, x, y); + auto addr = GET_ADDRESS(0x431690, 0x433C90, 0x474CB0); + + Hooking::Call(addr, x, y); } void Font::SetScale(float scaleX, float scaleY) { - Hooking::Call(0x433E60, scaleX, scaleY); + auto addr = GET_ADDRESS(0x431860, 0x433E60, 0x000000); + + Hooking::Call(addr, scaleX, scaleY); } void Font::Print(const char* fmt, ...) @@ -69,19 +75,26 @@ void Font::PrintCentered(const char* fmt, ...) void Font::PrintFormatted(const char* formatted, int backdrop) { - Hooking::ThisCall(0x434A70, this, formatted, backdrop); + auto addr = GET_ADDRESS(0x4323D0, 0x434A70, 0x476BC0); + + Hooking::ThisCall(addr, this, formatted, backdrop); } float Font::GetTextWidth(const char* text) { - return Hooking::ThisCallReturn(0x434510, this, text); + auto addr = GET_ADDRESS(0x431EA0, 0x434510, 0x000000); + + return Hooking::ThisCallReturn(addr, this, text); } void Font::OnFlush(std::function callback) { if (!s_callback) { - MH_CreateHook((void*)0x434C40, Flush, (void**)&s_Flush); + // TODO pattern + auto addr = GET_ADDRESS(0x432570, 0x434C40, 0x476D80); + + MH_CreateHook((void*)addr, Flush, (void**)&s_Flush); MH_EnableHook(MH_ALL_HOOKS); } From 24bd4b8c15e086c8fffab58efced707653f0d884 Mon Sep 17 00:00:00 2001 From: Indra Date: Thu, 25 Jan 2024 00:32:10 +0100 Subject: [PATCH 19/55] Hide HUD, Quit message removal and time multiplier --- src/Hook.cpp | 2 ++ src/game/Game.h | 8 ++++++++ src/modules/MainMenu.cpp | 8 ++++++++ src/modules/Patches.cpp | 11 +++++++++++ src/modules/Patches.h | 5 ++++- 5 files changed, 33 insertions(+), 1 deletion(-) diff --git a/src/Hook.cpp b/src/Hook.cpp index 7de0866..656f003 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -18,6 +18,7 @@ #include "modules/Level.h" #include "modules/ModLoader.h" #include "modules/Patches.h" +#include "modules/Frontend.h" #include "cdc/render/PCDeviceManager.h" @@ -122,6 +123,7 @@ void Hook::RegisterModules() #ifndef TR8 RegisterModule(); + RegisterModule(); //RegisterModule(); //RegisterModule(); #else diff --git a/src/game/Game.h b/src/game/Game.h index 634ca74..6b65acb 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -69,6 +69,14 @@ struct GameTracker char pad1[60]; float timeMult; + float unmodifiedActualTimeMult; + float actualTimeMult; + float globalTimeMult; + + float scrollProgress; + float timeDilation; + + int debugTimeMult; }; #else struct GameTracker diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index f615f27..40327d8 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -28,6 +28,14 @@ void MainMenu::OnDraw() BirthObject(object); } + // Time + if (ImGui::CollapsingHeader("Time")) + { + auto gameTracker = Game::GetGameTracker(); + + ImGui::SliderFloat("Time multiplier", &gameTracker->timeDilation, 0.f, 10.f, "%.2f"); + } + ImGui::End(); } diff --git a/src/modules/Patches.cpp b/src/modules/Patches.cpp index 9d7d898..6dc1879 100644 --- a/src/modules/Patches.cpp +++ b/src/modules/Patches.cpp @@ -21,4 +21,15 @@ void Patches::RemoveIntro() // mov [mainState], 6 Hooking::Patch(match.get_first(), { 0xC7, 0x05, 0x00, 0x00, 0x00, 0x00, 0x06, 0x00, 0x00, 0x00 }); Hooking::Patch(match.get_first(2), mainState); +} + +void Patches::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + // Remove the quit message +#ifdef TRAE + if (msg == WM_CLOSE) + { + PostQuitMessage(0); + } +#endif } \ No newline at end of file diff --git a/src/modules/Patches.h b/src/modules/Patches.h index 588365d..5c9e29b 100644 --- a/src/modules/Patches.h +++ b/src/modules/Patches.h @@ -4,8 +4,11 @@ class Patches : public Module { +private: + void RemoveIntro(); + public: Patches(); - void RemoveIntro(); + void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); }; \ No newline at end of file From 2204e8a68ec2b65e39624bced4f3d3cc6b6e0dc7 Mon Sep 17 00:00:00 2001 From: Indra Date: Thu, 25 Jan 2024 00:52:09 +0100 Subject: [PATCH 20/55] Fix TR8 build --- src/game/Game.h | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/src/game/Game.h b/src/game/Game.h index 6b65acb..21f877b 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -127,6 +127,26 @@ struct GameTracker int field_FC; float timeMult; + + int field_104; + int field_108; + int field_10C; + int field_110; + int field_114; + int field_118; + int field_11C; + int field_120; + int field_124; + int field_128; + int field_12C; + int field_130; + int field_134; + int field_138; + int field_13C; + int field_140; + int field_144; + + float timeDilation; }; #endif From 8fb3f32d12ecd3343998c57ee10e260652507427 Mon Sep 17 00:00:00 2001 From: Indra Date: Thu, 25 Jan 2024 01:09:28 +0100 Subject: [PATCH 21/55] Fix build --- src/modules/Frontend.cpp | 26 ++++++++++++++++++++++++++ src/modules/Frontend.h | 12 ++++++++++++ 2 files changed, 38 insertions(+) create mode 100644 src/modules/Frontend.cpp create mode 100644 src/modules/Frontend.h diff --git a/src/modules/Frontend.cpp b/src/modules/Frontend.cpp new file mode 100644 index 0000000..6f71e77 --- /dev/null +++ b/src/modules/Frontend.cpp @@ -0,0 +1,26 @@ +#include + +#include "Frontend.h" +#include "game/Game.h" + +void Frontend::OnMenu() +{ + if (ImGui::BeginMenu("Frontend")) + { + if (ImGui::MenuItem("Hide HUD", nullptr, &m_hideHud)) + { + auto gameTracker = Game::GetGameTracker(); + + if (m_hideHud) + { + gameTracker->debugFlags3 |= 0x8000; + } + else + { + gameTracker->debugFlags3 &= ~0x8000; + } + } + + ImGui::EndMenu(); + } +} \ No newline at end of file diff --git a/src/modules/Frontend.h b/src/modules/Frontend.h new file mode 100644 index 0000000..63a5edc --- /dev/null +++ b/src/modules/Frontend.h @@ -0,0 +1,12 @@ +#pragma once + +#include "Module.h" + +class Frontend : public Module +{ +private: + bool m_hideHud = false; + +public: + void OnMenu(); +}; \ No newline at end of file From a4e4b1fa859c56ea9e8bb4054cde701b0c5ee1e2 Mon Sep 17 00:00:00 2001 From: Indra Date: Thu, 25 Jan 2024 19:23:41 +0100 Subject: [PATCH 22/55] Add wireframe --- src/Hook.cpp | 4 ++-- src/modules/Render.cpp | 15 +++++++++++++++ src/modules/Render.h | 9 +++++++++ 3 files changed, 26 insertions(+), 2 deletions(-) create mode 100644 src/modules/Render.cpp create mode 100644 src/modules/Render.h diff --git a/src/Hook.cpp b/src/Hook.cpp index 656f003..b1ec4e5 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -11,7 +11,7 @@ #include "modules/MainMenu.h" #include "modules/Instance.h" #include "modules/Skew.h" -//#include "modules/Render.h" +#include "modules/Render.h" //#include "modules/Draw.h" #include "modules/Log.h" #include "modules/ScriptLog.h" @@ -124,7 +124,7 @@ void Hook::RegisterModules() #ifndef TR8 RegisterModule(); RegisterModule(); - //RegisterModule(); + RegisterModule(); //RegisterModule(); #else RegisterModule(); diff --git a/src/modules/Render.cpp b/src/modules/Render.cpp new file mode 100644 index 0000000..8dcc98b --- /dev/null +++ b/src/modules/Render.cpp @@ -0,0 +1,15 @@ +#include + +#include "Render.h" +#include "util/Hooking.h" + +void Render::OnMenu() +{ + if (ImGui::BeginMenu("Render")) + { + // TODO Wireframe for Underworld + ImGui::MenuItem("Wireframe", nullptr, (bool*)GET_ADDRESS(0x1075BD4, 0x7C7CD4, 0x000000)); + + ImGui::EndMenu(); + } +} \ No newline at end of file diff --git a/src/modules/Render.h b/src/modules/Render.h new file mode 100644 index 0000000..84bfdf8 --- /dev/null +++ b/src/modules/Render.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Module.h" + +class Render : public Module +{ +public: + void OnMenu(); +}; \ No newline at end of file From 5c413bd8254069a2eab1cc9254a317a40ec6ee1c Mon Sep 17 00:00:00 2001 From: Indra Date: Thu, 25 Jan 2024 20:47:49 +0100 Subject: [PATCH 23/55] Add freeze game and ragdoll --- src/game/Game.h | 2 +- src/modules/MainMenu.cpp | 21 +++++++++++++++++++++ src/modules/MainMenu.h | 1 + 3 files changed, 23 insertions(+), 1 deletion(-) diff --git a/src/game/Game.h b/src/game/Game.h index 21f877b..07e2d79 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -95,7 +95,7 @@ struct GameTracker int field_24; int field_28; int field_2C; - int field_30; + int streamFlags; int field_34; char baseAreaName[128]; diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index 40327d8..fe71d64 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -68,4 +68,25 @@ void MainMenu::OnFrame() font->SetCursor(5.f, 430.f); font->Print("TRLAU-Menu-Hook"); } +} + +void MainMenu::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + auto gameTracker = Game::GetGameTracker(); + + // Freeze the game + if (msg == WM_KEYUP && wParam == VK_F3) + { + gameTracker->streamFlags ^= 0x1000; + } + + // Ragdoll death + if (msg == WM_KEYUP && wParam == VK_F11) + { +#ifndef TR8 + INSTANCE_Post(gameTracker->playerInstance, 262167, 3); +#else + INSTANCE_Post(gameTracker->playerInstance, 4, 3); +#endif + } } \ No newline at end of file diff --git a/src/modules/MainMenu.h b/src/modules/MainMenu.h index 7bfb723..8bf0811 100644 --- a/src/modules/MainMenu.h +++ b/src/modules/MainMenu.h @@ -10,4 +10,5 @@ class MainMenu : public Module public: void OnDraw(); void OnFrame(); + void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); }; \ No newline at end of file From 309d94c20fc153ea893a5311e45483d320229e1a Mon Sep 17 00:00:00 2001 From: Indra Date: Thu, 25 Jan 2024 22:20:33 +0100 Subject: [PATCH 24/55] Add game loop hook and outfit switch --- src/Hook.cpp | 11 +++++++++++ src/Hook.h | 1 + src/game/Game.cpp | 14 ++++++++++++++ src/game/Game.h | 14 +++++++++++--- src/game/GameLoop.cpp | 25 ++++++++++++++++++++++++ src/game/GameLoop.h | 9 +++++++++ src/modules/MainMenu.cpp | 42 ++++++++++++++++++++++++++++++++++++++++ src/modules/MainMenu.h | 4 ++++ src/modules/Module.h | 3 +++ 9 files changed, 120 insertions(+), 3 deletions(-) create mode 100644 src/game/GameLoop.cpp create mode 100644 src/game/GameLoop.h diff --git a/src/Hook.cpp b/src/Hook.cpp index b1ec4e5..d1bf630 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -6,6 +6,7 @@ #include "instance/Instances.h" #include "game/Game.h" #include "render/Font.h" +#include "game/GameLoop.h" // Modules #include "modules/MainMenu.h" @@ -76,6 +77,8 @@ void Hook::PostInitialize() #ifndef TR8 Font::OnFlush(std::bind(&Hook::OnFrame, this)); #endif + + GameLoop::OnLoop(std::bind(&Hook::OnLoop, this)); } void Hook::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -96,6 +99,14 @@ void Hook::OnFrame() } } +void Hook::OnLoop() +{ + for (auto& [hash, mod] : m_modules) + { + mod->OnLoop(); + } +} + void Hook::OnDevice() { // Assign the DeviceManager instance diff --git a/src/Hook.h b/src/Hook.h index a88d6bf..85360df 100644 --- a/src/Hook.h +++ b/src/Hook.h @@ -21,6 +21,7 @@ class Hook void OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); void OnFrame(); + void OnLoop(); public: Hook(); diff --git a/src/game/Game.cpp b/src/game/Game.cpp index fe09907..edb47cc 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -22,4 +22,18 @@ void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int auto addr = GET_ADDRESS(0x451970, 0xC61CFA, 0x5DF8C0); Hooking::Call(addr, name, gameTracker, doneType); +} + +void PLAYER_DebugSwitchPlayerCharacter() +{ + auto addr = GET_ADDRESS(0x5A40B0, 0x5A39A0, 0x79DB50); + + Hooking::Call(addr); +} + +int OBTABLE_GetObjectID(char* name) +{ + auto addr = GET_ADDRESS(0x462590, 0x465DE0, 0x5BF770); + + return Hooking::CallReturn(addr, name); } \ No newline at end of file diff --git a/src/game/Game.h b/src/game/Game.h index 07e2d79..4a82b47 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -77,6 +77,11 @@ struct GameTracker float timeDilation; int debugTimeMult; + + char pad2[752]; + + int currentPlayerObjectID; + int altPlayerObjectID; }; #else struct GameTracker @@ -143,8 +148,8 @@ struct GameTracker int field_134; int field_138; int field_13C; - int field_140; - int field_144; + int currentPlayerObjectID; + int altPlayerObjectID; float timeDilation; }; @@ -158,4 +163,7 @@ class Game static STracker* GetStreamTracker(); }; -void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType); \ No newline at end of file +void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType); + +void PLAYER_DebugSwitchPlayerCharacter(); +int OBTABLE_GetObjectID(char* name); \ No newline at end of file diff --git a/src/game/GameLoop.cpp b/src/game/GameLoop.cpp new file mode 100644 index 0000000..8c870a9 --- /dev/null +++ b/src/game/GameLoop.cpp @@ -0,0 +1,25 @@ +#include + +#include "GameLoop.h" +#include "Game.h" +#include "util/Hooking.h" + +static std::function s_callback; +static void(*s_GAMELOOP_Process)(GameTracker*); + +static void GAMELOOP_Process(GameTracker* gameTracker) +{ + s_callback(); + s_GAMELOOP_Process(gameTracker); +} + +void GameLoop::OnLoop(std::function callback) +{ + if (!s_callback) + { + MH_CreateHook((void*)GET_ADDRESS(0x452140, 0x454AC0, 0x5DFBE0), GAMELOOP_Process, (void**)&s_GAMELOOP_Process); + MH_EnableHook(MH_ALL_HOOKS); + } + + s_callback = callback; +} \ No newline at end of file diff --git a/src/game/GameLoop.h b/src/game/GameLoop.h new file mode 100644 index 0000000..8302866 --- /dev/null +++ b/src/game/GameLoop.h @@ -0,0 +1,9 @@ +#pragma once + +#include + +class GameLoop +{ +public: + static void OnLoop(std::function callback); +}; \ No newline at end of file diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index fe71d64..ea57dd3 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -28,6 +28,25 @@ void MainMenu::OnDraw() BirthObject(object); } + // Player + if (ImGui::CollapsingHeader("Player")) + { + static char outfit[64] = ""; + ImGui::InputText("Outfit", outfit, sizeof(outfit)); + + if (ImGui::Button("Change")) + { + SwitchPlayerCharacter(outfit); + } + + ImGui::SameLine(); + + if (ImGui::Button("Next")) + { + SwitchPlayerCharacter(); + } + } + // Time if (ImGui::CollapsingHeader("Time")) { @@ -57,8 +76,22 @@ void MainMenu::BirthObject(char* name) INSTANCE_BirthObjectNoParent(game->StreamUnitID, &player->position, &player->rotation, nullptr, tracker->object, 0, 1); } +void MainMenu::SwitchPlayerCharacter(char* name) +{ + auto game = Game::GetGameTracker(); + + if (name) + { + auto object = OBTABLE_GetObjectID(name); + game->altPlayerObjectID = object; + } + + m_switchPlayerNextFrame = true; +} + void MainMenu::OnFrame() { + // Shows the watermark in th main menu auto mainState = *(int*)GET_ADDRESS(0x10E5868, 0x838838, 0x000000); if (mainState == MS_DISPLAY_MAIN_MENU) @@ -70,6 +103,15 @@ void MainMenu::OnFrame() } } +void MainMenu::OnLoop() +{ + if (m_switchPlayerNextFrame) + { + m_switchPlayerNextFrame = false; + PLAYER_DebugSwitchPlayerCharacter(); + } +} + void MainMenu::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { auto gameTracker = Game::GetGameTracker(); diff --git a/src/modules/MainMenu.h b/src/modules/MainMenu.h index 8bf0811..db8cf24 100644 --- a/src/modules/MainMenu.h +++ b/src/modules/MainMenu.h @@ -5,10 +5,14 @@ class MainMenu : public Module { private: + bool m_switchPlayerNextFrame = false; + void BirthObject(char* name); + void SwitchPlayerCharacter(char* name = nullptr); public: void OnDraw(); void OnFrame(); + void OnLoop(); void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); }; \ No newline at end of file diff --git a/src/modules/Module.h b/src/modules/Module.h index b34c737..39e7295 100644 --- a/src/modules/Module.h +++ b/src/modules/Module.h @@ -14,6 +14,9 @@ class Module // Called just before a frame ends, Font::Flush to be specific virtual void OnFrame() { }; + // Called every frame before the game loop + virtual void OnLoop() { }; + // Called when a message is processed by the window procedure virtual void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { }; }; \ No newline at end of file From 66af27af60c8e2a9b002f42e2c5a43fee090986c Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 27 Jan 2024 02:01:19 +0100 Subject: [PATCH 25/55] Add transforms and animations to instance viewer --- src/Hook.cpp | 1 - src/instance/Animation.cpp | 8 ++++++++ src/instance/Animation.h | 35 +++++++++++++++++++++++++++++++++-- src/instance/Instance.h | 8 ++++++-- src/modules/Instance.cpp | 27 +++++++++++++++++++++++++++ src/modules/MainMenu.cpp | 2 ++ 6 files changed, 76 insertions(+), 5 deletions(-) diff --git a/src/Hook.cpp b/src/Hook.cpp index d1bf630..d647a7d 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -142,7 +142,6 @@ void Hook::RegisterModules() #endif RegisterModule(); - } Hook& Hook::GetInstance() diff --git a/src/instance/Animation.cpp b/src/instance/Animation.cpp index f3e18d3..68e6790 100644 --- a/src/instance/Animation.cpp +++ b/src/instance/Animation.cpp @@ -13,4 +13,12 @@ void G2EmulationInstanceSetMode(Instance* instance, int CurrentSection, int Mode auto addr = GET_ADDRESS(0x4DED90, 0x4DE7F0, 0x5B1F50); Hooking::Call(addr, instance, CurrentSection, Mode); +} + +int G2EmulationInstanceQueryAnimation(Instance* instance, int CurrentSection) +{ + // TODO Underworld + auto addr = GET_ADDRESS(0x4DEE50, 0x4DE8B0, 0x000000); + + return Hooking::CallReturn(addr, instance, CurrentSection); } \ No newline at end of file diff --git a/src/instance/Animation.h b/src/instance/Animation.h index 1cbe68c..d4dee4e 100644 --- a/src/instance/Animation.h +++ b/src/instance/Animation.h @@ -1,6 +1,37 @@ #pragma once -#include "Instance.h" +struct Instance; + +class AnimProcessor +{ +public: + char pad1[336]; + + unsigned __int8 mSectionCount; + unsigned __int8 mSectionsAllocated; +}; + +class AnimComponent; + +class BlendProcessor +{ +public: + AnimComponent* mAnimComponent; +}; + +class AnimComponent +{ +public: + BlendProcessor mBlendProcessor; + AnimProcessor* mAnimProcessor; +}; + +struct AnimListEntry +{ + __int16 animationID; + __int16 animationNumber; +}; void G2EmulationInstanceSetAnimation(Instance* instance, int CurrentSection, int NewAnim, int NewFrame, int Frames); -void G2EmulationInstanceSetMode(Instance* instance, int CurrentSection, int Mode); \ No newline at end of file +void G2EmulationInstanceSetMode(Instance* instance, int CurrentSection, int Mode); +int G2EmulationInstanceQueryAnimation(Instance* instance, int CurrentSection); \ No newline at end of file diff --git a/src/instance/Instance.h b/src/instance/Instance.h index 260a482..c5326e0 100644 --- a/src/instance/Instance.h +++ b/src/instance/Instance.h @@ -1,6 +1,7 @@ #pragma once #include "Object.h" +#include "Animation.h" #include "cdc/math/Vector.h" #include "cdc/math/Matrix.h" @@ -31,7 +32,6 @@ struct HModel; struct SoundInstanceData; struct Body; struct AnimatedGeoms; -struct AnimComponent; struct CharacterProxy; struct Instance; @@ -66,7 +66,11 @@ struct BaseInstance unsigned int noDrawGroups; - char pad3[32]; + char pad3[20]; + + AnimComponent* animComponent; + + char pad4[8]; }; struct Instance : BaseInstance diff --git a/src/modules/Instance.cpp b/src/modules/Instance.cpp index 5cd98a3..c2cfe5c 100644 --- a/src/modules/Instance.cpp +++ b/src/modules/Instance.cpp @@ -119,6 +119,13 @@ void InstanceModule::DrawInstance() } #endif + // Transforms + if (ImGui::CollapsingHeader("Transforms")) + { + ImGui::InputFloat3("Position", (float*)&instance->position); + ImGui::InputFloat3("Rotation", (float*)&instance->rotation); + } + // Object info if (ImGui::CollapsingHeader("Object")) { @@ -153,6 +160,26 @@ void InstanceModule::DrawInstance() // Animations if (ImGui::CollapsingHeader("Animations")) { +#ifndef TR8 + // Show all allocated animation sections with the current playing animation + for (int section = 0; section < instance->animComponent->mAnimProcessor->mSectionsAllocated; section++) + { + // Query the current animation + auto currentAnim = G2EmulationInstanceQueryAnimation(instance, section); + + // Can be null for some reason + if (instance->object->animList) + { + // (Section): (Anim index) (Anim ID) + ImGui::Text("Section %d: %d (%X)", section, currentAnim, instance->object->animList[currentAnim].animationID); + } + else + { + ImGui::Text("Section %d: %d", section, currentAnim); + } + } +#endif + static int animation = 0; static bool loop = true; diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index ea57dd3..21d38ff 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -86,6 +86,8 @@ void MainMenu::SwitchPlayerCharacter(char* name) game->altPlayerObjectID = object; } + // We can't do this here since switching the player character waits a couple of frames + // and we're in the middle of a frame m_switchPlayerNextFrame = true; } From 4a533f498c906f51e9eeef65165e41dced57fde9 Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 27 Jan 2024 02:17:43 +0100 Subject: [PATCH 26/55] Allow updating event variables --- src/modules/MainMenu.cpp | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index 21d38ff..8ae96a8 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -5,6 +5,7 @@ #include "game/Game.h" #include "util/Hooking.h" #include "render/Font.h" +#include "level/Event.h" void MainMenu::OnDraw() { @@ -55,6 +56,22 @@ void MainMenu::OnDraw() ImGui::SliderFloat("Time multiplier", &gameTracker->timeDilation, 0.f, 10.f, "%.2f"); } + if (ImGui::CollapsingHeader("Save")) + { + static int var = 0; + static int value = 0; + + ImGui::InputInt("Event variable", &var); + ImGui::InputInt("Value", &value); + + if (ImGui::Button("Update")) + { + auto globalData = (GlobalData*)GET_ADDRESS(0x1076980, 0x7C8A50, 0x000000); + + globalData->eventVars[var] = value; + } + } + ImGui::End(); } From a8716a39033eb9978b648e4a3029d75ab5d95d7f Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 27 Jan 2024 18:59:09 +0100 Subject: [PATCH 27/55] Cleanup --- .github/workflows/build.yml | 2 +- docs/notes.txt | 3 --- src/Hook.cpp | 2 -- src/render/Draw.cpp | 22 ++++++++++++++++++++++ src/render/Draw.h | 28 ++++++++++++++++++++++++++++ 5 files changed, 51 insertions(+), 6 deletions(-) delete mode 100644 docs/notes.txt create mode 100644 src/render/Draw.cpp create mode 100644 src/render/Draw.h diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 2b9768a..41a7589 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -8,7 +8,7 @@ jobs: steps: # setup - - uses: actions/checkout@v3 + - uses: actions/checkout@v4 with: submodules: true diff --git a/docs/notes.txt b/docs/notes.txt deleted file mode 100644 index 924812d..0000000 --- a/docs/notes.txt +++ /dev/null @@ -1,3 +0,0 @@ -future todo - -- being able to replace some section types by id e.g. textures without entire drm \ No newline at end of file diff --git a/src/Hook.cpp b/src/Hook.cpp index d647a7d..4c3eb24 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -13,7 +13,6 @@ #include "modules/Instance.h" #include "modules/Skew.h" #include "modules/Render.h" -//#include "modules/Draw.h" #include "modules/Log.h" #include "modules/ScriptLog.h" #include "modules/Level.h" @@ -136,7 +135,6 @@ void Hook::RegisterModules() RegisterModule(); RegisterModule(); RegisterModule(); - //RegisterModule(); #else RegisterModule(); #endif diff --git a/src/render/Draw.cpp b/src/render/Draw.cpp new file mode 100644 index 0000000..c697a55 --- /dev/null +++ b/src/render/Draw.cpp @@ -0,0 +1,22 @@ +#include "Draw.h" +#include "util/Hooking.h" + +void TRANS_TransToDrawVertexV4f(DRAWVERTEX* v, cdc::Vector3* vec) +{ + Hooking::Call(0x402F20, v, vec); +} + +void TRANS_RotTransPersVectorf(cdc::Vector3* srcvector, cdc::Vector3* dstvector) +{ + Hooking::Call(0x402B50, srcvector, dstvector); +} + +void DRAW_DrawQuads(int flags, int tpage, DRAWVERTEX* verts, int numquads) +{ + Hooking::Call(0x406D70, flags, tpage, verts, numquads); +} + +void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris) +{ + Hooking::Call(0x407570, flags, tpage, verts, numtris); +} \ No newline at end of file diff --git a/src/render/Draw.h b/src/render/Draw.h new file mode 100644 index 0000000..3a363b1 --- /dev/null +++ b/src/render/Draw.h @@ -0,0 +1,28 @@ +#pragma once + +#include "cdc/math/Vector.h" + +struct DRAWVERTEX +{ + float x; + float y; + float z; + + unsigned int color; + + float u; + float v; + + unsigned int anPad[2]; +}; + +#undef RGB + +#define RGBA(r, g, b, a) r | (g << 8) | (b << 16) | (a << 24) +#define RGB(r, g, b) r | (g << 8) | (b << 16) | (255 << 24) + +void TRANS_TransToDrawVertexV4f(DRAWVERTEX* v, cdc::Vector3* vec); +void TRANS_RotTransPersVectorf(cdc::Vector3* srcvector, cdc::Vector3* dstvector); + +void DRAW_DrawQuads(int flags, int tpage, DRAWVERTEX* verts, int numquads); +void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris); \ No newline at end of file From 7a32f4946e3eea034d20757d226d4781137923c1 Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 27 Jan 2024 20:39:07 +0100 Subject: [PATCH 28/55] Disable event variable input in TR8 --- src/modules/MainMenu.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index 8ae96a8..729ee22 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -56,6 +56,8 @@ void MainMenu::OnDraw() ImGui::SliderFloat("Time multiplier", &gameTracker->timeDilation, 0.f, 10.f, "%.2f"); } + // Save +#ifndef TR8 if (ImGui::CollapsingHeader("Save")) { static int var = 0; @@ -71,6 +73,7 @@ void MainMenu::OnDraw() globalData->eventVars[var] = value; } } +#endif ImGui::End(); } From 0ad8d2ec3033395308f4aec94227a3ce0272846a Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 27 Jan 2024 21:12:31 +0100 Subject: [PATCH 29/55] Add early hook for packed executables --- src/Hook.cpp | 3 --- src/Main.cpp | 29 ++++++++++++++++++++++++++++- 2 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/Hook.cpp b/src/Hook.cpp index 4c3eb24..6397176 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -46,9 +46,6 @@ Hook::Hook() : m_menu(nullptr), m_modules() void Hook::Initialize() { - // Initialize MinHook - MH_Initialize(); - // Register all modules RegisterModules(); diff --git a/src/Main.cpp b/src/Main.cpp index 395b26e..97c48f8 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -1,13 +1,40 @@ #include +#include #include "Hook.h" +static void* s_target = nullptr; +static void (WINAPI* s_GetStartupInfoA)(LPSTARTUPINFOA); + +static void WINAPI StartupInfoW(LPSTARTUPINFOA lpStartupInfo) +{ + // Initialize + Hook::GetInstance(); + + // Call the original GetStartupInfoA + s_GetStartupInfoA(lpStartupInfo); + + // Remove the hook + MH_RemoveHook(s_target); +} + +static void Initialize() +{ + // Initialize MinHook + MH_Initialize(); + + // We insert an early hook on GetStartupInfoA to know when the executable is done unpacking + // some executables are packed/protected so we can't insert our hooks yet. + MH_CreateHookApiEx(L"kernel32", "GetStartupInfoA", StartupInfoW, (void**)&s_GetStartupInfoA, &s_target); + MH_EnableHook(MH_ALL_HOOKS); +} + BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpvReserved) { switch (fdwReason) { case DLL_PROCESS_ATTACH: - Hook::GetInstance(); + Initialize(); break; case DLL_THREAD_ATTACH: From 32522e59b93564f5a605c34197b2756fdc2cbdd3 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 28 Jan 2024 03:40:21 +0100 Subject: [PATCH 30/55] Add initial free camera for Legend --- src/Hook.cpp | 2 + src/game/Camera.cpp | 14 +++++ src/game/Camera.h | 23 ++++++++ src/input/Input.cpp | 24 ++++++++ src/input/Input.h | 31 ++++++++++ src/modules/camera/FreeCamera.cpp | 96 +++++++++++++++++++++++++++++++ src/modules/camera/FreeCamera.h | 52 +++++++++++++++++ src/modules/camera/Legend.cpp | 63 ++++++++++++++++++++ 8 files changed, 305 insertions(+) create mode 100644 src/game/Camera.cpp create mode 100644 src/game/Camera.h create mode 100644 src/modules/camera/FreeCamera.cpp create mode 100644 src/modules/camera/FreeCamera.h create mode 100644 src/modules/camera/Legend.cpp diff --git a/src/Hook.cpp b/src/Hook.cpp index 6397176..f258ac0 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -19,6 +19,7 @@ #include "modules/ModLoader.h" #include "modules/Patches.h" #include "modules/Frontend.h" +#include "modules/camera/FreeCamera.h" #include "cdc/render/PCDeviceManager.h" @@ -127,6 +128,7 @@ void Hook::RegisterModules() RegisterModule(); RegisterModule(); RegisterModule(); + RegisterModule(); #ifndef TR8 RegisterModule(); diff --git a/src/game/Camera.cpp b/src/game/Camera.cpp new file mode 100644 index 0000000..9141a89 --- /dev/null +++ b/src/game/Camera.cpp @@ -0,0 +1,14 @@ +#include "Camera.h" +#include "util/Hooking.h" + +Camera* CAMERA_GetCamera() +{ + return (Camera*)GET_ADDRESS(0x000000, 0x850670, 0x000000); +} + +void CAMERA_CalcPosition(cdc::Vector3* position, cdc::Vector3* base, cdc::Euler* rotation, float distance) +{ + auto addr = GET_ADDRESS(0x48D8B0, 0x491320, 0x000000); + + Hooking::Call(addr, position, base, rotation, distance); +} \ No newline at end of file diff --git a/src/game/Camera.h b/src/game/Camera.h new file mode 100644 index 0000000..1fcc862 --- /dev/null +++ b/src/game/Camera.h @@ -0,0 +1,23 @@ +#pragma once + +#include "cdc/math/Vector.h" + +struct CameraCore +{ + cdc::Vector3 position; + char pad1[608]; + cdc::Euler rotation; + char pad2[128]; +}; + +struct Camera : CameraCore +{ + char pad3[12]; + + int flags; + int lock; + __int16 mode; +}; + +Camera* CAMERA_GetCamera(); +void CAMERA_CalcPosition(cdc::Vector3* position, cdc::Vector3* base, cdc::Euler* rotation, float distance); \ No newline at end of file diff --git a/src/input/Input.cpp b/src/input/Input.cpp index 1ad0c71..b67ab4a 100644 --- a/src/input/Input.cpp +++ b/src/input/Input.cpp @@ -5,4 +5,28 @@ void Input::DisableInput(bool disable) { *(bool*)GET_ADDRESS(0x1101689, 0x8551A9, 0xA02B79) = disable; +} + +void Input::DisablePlayerControl(bool disable) +{ + *(bool*)GET_ADDRESS(0xF15AB4, 0x666C34, 0x8AB4E6) = !disable; +} + +bool Input::IsInputActionPressed(int action) +{ + auto inputSystem = GetInputSystem(); + + return (inputSystem->m_pActionMapper->m_pActionResults[action].state & 4) == 4; +} + +InputSystem* Input::GetInputSystem() +{ + return *(InputSystem**)GET_ADDRESS(0x1101680, 0x8551A0, 0xA02B68); +} + +float InputSystem::GetAxisValue(int axisID) +{ + auto addr = GET_ADDRESS(0x4E3FC0, 0x4E38C0, 0x480E50); + + return Hooking::ThisCallReturn(addr, this, axisID); } \ No newline at end of file diff --git a/src/input/Input.h b/src/input/Input.h index 6a8514f..487f91c 100644 --- a/src/input/Input.h +++ b/src/input/Input.h @@ -1,7 +1,38 @@ #pragma once +class InputActionMapper +{ +public: + struct ActionResult + { + unsigned int state; + + char pad1[20]; + }; + + ActionResult* m_pActionResults; +}; + +class InputSystem +{ +public: + unsigned int m_numButtons; + unsigned int m_numAxis; + + char pad1[16]; + + InputActionMapper* m_pActionMapper; + +public: + float GetAxisValue(int axisID); +}; + class Input { public: static void DisableInput(bool disable); + static void DisablePlayerControl(bool disable); + static bool IsInputActionPressed(int action); + + static InputSystem* GetInputSystem(); }; \ No newline at end of file diff --git a/src/modules/camera/FreeCamera.cpp b/src/modules/camera/FreeCamera.cpp new file mode 100644 index 0000000..9f2146e --- /dev/null +++ b/src/modules/camera/FreeCamera.cpp @@ -0,0 +1,96 @@ +#include "FreeCamera.h" + +#include "input/Input.h" +#include "game/Game.h" +#include "render/Font.h" + +FreeCameraBase::FreeCameraBase() : m_vKeys() +{ +} + +void FreeCameraBase::ToggleMode() +{ + // Switch between disabled -> enabled -> no control + m_mode = m_mode == Disabled ? Enabled : m_mode == NoControl ? Disabled : NoControl; + + // Disable player control when the free camera is enabled + Input::DisablePlayerControl(m_mode == Enabled); +} + +void FreeCameraBase::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) +{ + if (msg == WM_KEYUP && wParam == VK_F4) + { + ToggleMode(); + } + + // 1-3 virtual keys, kinda hack.. TODO global thing like this? + if (msg == WM_KEYDOWN && wParam >= 0x31 && wParam <= 0x33) + { + m_vKeys[wParam - 0x31] = true; + } + + if (msg == WM_KEYUP && wParam >= 0x31 && wParam <= 0x33) + { + m_vKeys[wParam - 0x31] = false; + } +} + +void FreeCameraBase::OnControl() +{ + auto input = Input::GetInputSystem(); + auto gameTracker = Game::GetGameTracker(); + + // Camera rotation based on mouse/controller axis + auto rotX = input->GetAxisValue(17) * gameTracker->timeMult; + auto rotZ = input->GetAxisValue(16) * gameTracker->timeMult; + + Rotate(rotX, rotZ); + + // Camera roll + if (m_vKeys[0] || m_vKeys[2]) + { + // TODO + auto roll = m_vKeys[0] ? 0.02454f : -0.02454f; + + Rotate(roll * gameTracker->timeMult); + } + + // Change the speed depending on if shift/alt is pressed + auto shift = Input::IsInputActionPressed(18); + auto control = Input::IsInputActionPressed(23); + + auto speed = shift ? 200.f : control ? 20.f : 80.f; + + // Camera forward/backward + if (Input::IsInputActionPressed(1) || Input::IsInputActionPressed(2)) + { + auto distance = Input::IsInputActionPressed(1) ? -speed : speed; + + MoveForward(distance * gameTracker->timeMult); + } + + // Camera left/right + if (Input::IsInputActionPressed(3) || Input::IsInputActionPressed(4)) + { + auto distance = Input::IsInputActionPressed(3) ? -speed : speed; + + MoveLeft(distance * gameTracker->timeMult); + } + + // Camera up/down + if (Input::IsInputActionPressed(16) || Input::IsInputActionPressed(17)) + { + auto distance = Input::IsInputActionPressed(16) ? -speed : speed; + + MoveUp(distance * gameTracker->timeMult); + } +} + +void FreeCameraBase::OnLoop() +{ + if (m_mode == Enabled) + { + OnControl(); + } +} \ No newline at end of file diff --git a/src/modules/camera/FreeCamera.h b/src/modules/camera/FreeCamera.h new file mode 100644 index 0000000..341f0aa --- /dev/null +++ b/src/modules/camera/FreeCamera.h @@ -0,0 +1,52 @@ +#pragma once + +#include "modules/Module.h" + +// Base class for both free camera implementations +class FreeCameraBase : public Module +{ +public: + enum ControlMode + { + Disabled, + Enabled, + NoControl + }; + +private: + bool m_vKeys[3]; + +protected: + ControlMode m_mode = Disabled; + + virtual void ToggleMode(); + virtual void OnControl(); + + // Game specific + virtual void Rotate(float x, float z) = 0; + virtual void Rotate(float y) = 0; + virtual void MoveForward(float distance) = 0; + virtual void MoveLeft(float distance) = 0; + virtual void MoveUp(float distance) = 0; + +public: + FreeCameraBase(); + + void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); + void OnLoop(); +}; + +// Free camera implementation for Legend and Anniversary +class LegendCamera : public FreeCameraBase +{ +protected: + void ToggleMode(); + + void Rotate(float x, float z); + void Rotate(float y); + void MoveForward(float distance); + void MoveLeft(float distance); + void MoveUp(float distance); +}; + +using FreeCamera = LegendCamera; \ No newline at end of file diff --git a/src/modules/camera/Legend.cpp b/src/modules/camera/Legend.cpp new file mode 100644 index 0000000..8285ded --- /dev/null +++ b/src/modules/camera/Legend.cpp @@ -0,0 +1,63 @@ +#define _USE_MATH_DEFINES +#include + +#include "FreeCamera.h" +#include "game/Camera.h" + +void LegendCamera::ToggleMode() +{ + FreeCameraBase::ToggleMode(); + + auto camera = CAMERA_GetCamera(); + + if (m_mode == Enabled) + { + camera->mode = 7; + } + + if (m_mode == Disabled) + { + // Switch back to gameplay camera + camera->mode = 2; + } +} + +void LegendCamera::Rotate(float x, float z) +{ + auto camera = CAMERA_GetCamera(); + + camera->rotation.x -= x; + camera->rotation.z -= z; +} + +void LegendCamera::Rotate(float y) +{ + auto camera = CAMERA_GetCamera(); + + camera->rotation.y += y; +} + +void LegendCamera::MoveForward(float distance) +{ + auto camera = CAMERA_GetCamera(); + + CAMERA_CalcPosition(&camera->position, &camera->position, &camera->rotation, distance); +} + +void LegendCamera::MoveLeft(float distance) +{ + auto camera = CAMERA_GetCamera(); + auto rotation = camera->rotation; + + rotation.x = 0.f; + rotation.z += static_cast(M_PI) / 2.f; + + CAMERA_CalcPosition(&camera->position, &camera->position, &rotation, distance); +} + +void LegendCamera::MoveUp(float distance) +{ + auto camera = CAMERA_GetCamera(); + + camera->position.z += distance; +} \ No newline at end of file From 9e70054e2421da6c5bdae9f84e5967c33bc0b954 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 28 Jan 2024 14:33:50 +0100 Subject: [PATCH 31/55] Fix delete instance crash and add time reset button --- src/modules/Instance.cpp | 4 ++++ src/modules/MainMenu.cpp | 5 +++++ 2 files changed, 9 insertions(+) diff --git a/src/modules/Instance.cpp b/src/modules/Instance.cpp index c2cfe5c..540e77b 100644 --- a/src/modules/Instance.cpp +++ b/src/modules/Instance.cpp @@ -108,6 +108,10 @@ void InstanceModule::DrawInstance() if (ImGui::Button("Delete")) { INSTANCE_ReallyRemoveInstance(instance, 0, false); + + // Code below might crash if the instance got removed + // so return early + return; } #ifdef TR8 diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index 729ee22..1d6e8b1 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -54,6 +54,11 @@ void MainMenu::OnDraw() auto gameTracker = Game::GetGameTracker(); ImGui::SliderFloat("Time multiplier", &gameTracker->timeDilation, 0.f, 10.f, "%.2f"); + + if (ImGui::Button("Reset")) + { + gameTracker->timeDilation = 1.f; + } } // Save From 0591fa744d294ee1cb6e9676b3c72a8e94244b2f Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 28 Jan 2024 15:42:55 +0100 Subject: [PATCH 32/55] Free camera improvements --- src/input/Input.cpp | 5 +++++ src/input/Input.h | 1 + src/modules/MainMenu.cpp | 7 +++++++ src/modules/camera/Legend.cpp | 1 + 4 files changed, 14 insertions(+) diff --git a/src/input/Input.cpp b/src/input/Input.cpp index b67ab4a..ba6185a 100644 --- a/src/input/Input.cpp +++ b/src/input/Input.cpp @@ -12,6 +12,11 @@ void Input::DisablePlayerControl(bool disable) *(bool*)GET_ADDRESS(0xF15AB4, 0x666C34, 0x8AB4E6) = !disable; } +bool Input::IsPlayerControlEnabled() +{ + return *(bool*)GET_ADDRESS(0xF15AB4, 0x666C34, 0x8AB4E6); +} + bool Input::IsInputActionPressed(int action) { auto inputSystem = GetInputSystem(); diff --git a/src/input/Input.h b/src/input/Input.h index 487f91c..4c637d0 100644 --- a/src/input/Input.h +++ b/src/input/Input.h @@ -32,6 +32,7 @@ class Input public: static void DisableInput(bool disable); static void DisablePlayerControl(bool disable); + static bool IsPlayerControlEnabled(); static bool IsInputActionPressed(int action); static InputSystem* GetInputSystem(); diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index 1d6e8b1..c637773 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -6,6 +6,7 @@ #include "util/Hooking.h" #include "render/Font.h" #include "level/Event.h" +#include "input/Input.h" void MainMenu::OnDraw() { @@ -149,6 +150,12 @@ void MainMenu::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) gameTracker->streamFlags ^= 0x1000; } + // Toggle player control + if (msg == WM_KEYUP && wParam == VK_F5) + { + Input::DisablePlayerControl(Input::IsPlayerControlEnabled()); + } + // Ragdoll death if (msg == WM_KEYUP && wParam == VK_F11) { diff --git a/src/modules/camera/Legend.cpp b/src/modules/camera/Legend.cpp index 8285ded..9521c9a 100644 --- a/src/modules/camera/Legend.cpp +++ b/src/modules/camera/Legend.cpp @@ -50,6 +50,7 @@ void LegendCamera::MoveLeft(float distance) auto rotation = camera->rotation; rotation.x = 0.f; + rotation.y = 0.f; rotation.z += static_cast(M_PI) / 2.f; CAMERA_CalcPosition(&camera->position, &camera->position, &rotation, distance); From e43346a051178d226c169218fb95f5bca4ef50d0 Mon Sep 17 00:00:00 2001 From: Indra Date: Mon, 29 Jan 2024 00:28:14 +0100 Subject: [PATCH 33/55] Add free camera impl for TR8 --- src/cdc/math/Math.cpp | 14 +++++ src/cdc/math/Math.h | 9 ++++ src/cdc/math/Matrix.cpp | 10 ++++ src/cdc/math/Matrix.h | 10 ++-- src/game/Camera.cpp | 13 ++++- src/game/Camera.h | 16 +++++- src/input/Input.h | 63 ++++++++++++++++++++++ src/modules/camera/FreeCamera.cpp | 31 +++++++---- src/modules/camera/FreeCamera.h | 31 ++++++++++- src/modules/camera/Legend.cpp | 6 ++- src/modules/camera/Underworld.cpp | 90 +++++++++++++++++++++++++++++++ 11 files changed, 275 insertions(+), 18 deletions(-) create mode 100644 src/cdc/math/Math.cpp create mode 100644 src/cdc/math/Math.h create mode 100644 src/cdc/math/Matrix.cpp create mode 100644 src/modules/camera/Underworld.cpp diff --git a/src/cdc/math/Math.cpp b/src/cdc/math/Math.cpp new file mode 100644 index 0000000..8e1b2d0 --- /dev/null +++ b/src/cdc/math/Math.cpp @@ -0,0 +1,14 @@ +#include "Math.h" + +cdc::Vector3 cdc::Mul3x3(Matrix* matA, Vector3* vecB) +{ + cdc::Vector3 result; + + result.vec128 = _mm_add_ps( + _mm_mul_ps(matA->col0.vec128, _mm_shuffle_ps(vecB->vec128, vecB->vec128, _MM_SHUFFLE(0, 0, 0, 0))), + _mm_add_ps( + _mm_mul_ps(matA->col1.vec128, _mm_shuffle_ps(vecB->vec128, vecB->vec128, _MM_SHUFFLE(1, 1, 1, 1))), + _mm_mul_ps(matA->col2.vec128, _mm_shuffle_ps(vecB->vec128, vecB->vec128, _MM_SHUFFLE(2, 2, 2, 2))))); + + return result; +} \ No newline at end of file diff --git a/src/cdc/math/Math.h b/src/cdc/math/Math.h new file mode 100644 index 0000000..ae34dc4 --- /dev/null +++ b/src/cdc/math/Math.h @@ -0,0 +1,9 @@ +#pragma once + +#include "Vector.h" +#include "Matrix.h" + +namespace cdc +{ + Vector3 Mul3x3(Matrix* matA, Vector3* vecB); +} \ No newline at end of file diff --git a/src/cdc/math/Matrix.cpp b/src/cdc/math/Matrix.cpp new file mode 100644 index 0000000..3dfa8fe --- /dev/null +++ b/src/cdc/math/Matrix.cpp @@ -0,0 +1,10 @@ +#include "Matrix.h" +#include "util/Hooking.h" + +void cdc::Matrix::Build(Euler* euler) +{ + // Too lazy to implement myself + auto addr = GET_ADDRESS(0x000000, 0x000000, 0x49DD70); + + Hooking::ThisCall(addr, this, euler); +} \ No newline at end of file diff --git a/src/cdc/math/Matrix.h b/src/cdc/math/Matrix.h index 91eef5c..9bc7fe3 100644 --- a/src/cdc/math/Matrix.h +++ b/src/cdc/math/Matrix.h @@ -7,9 +7,11 @@ namespace cdc class Matrix { public: - cdc::Vector col0; - cdc::Vector col1; - cdc::Vector col2; - cdc::Vector col3; + Vector col0; + Vector col1; + Vector col2; + Vector col3; + + void Build(Euler* euler); }; } \ No newline at end of file diff --git a/src/game/Camera.cpp b/src/game/Camera.cpp index 9141a89..48893e1 100644 --- a/src/game/Camera.cpp +++ b/src/game/Camera.cpp @@ -3,7 +3,11 @@ Camera* CAMERA_GetCamera() { +#ifndef TR8 return (Camera*)GET_ADDRESS(0x000000, 0x850670, 0x000000); +#else + return *(Camera**)0xE80534; +#endif } void CAMERA_CalcPosition(cdc::Vector3* position, cdc::Vector3* base, cdc::Euler* rotation, float distance) @@ -11,4 +15,11 @@ void CAMERA_CalcPosition(cdc::Vector3* position, cdc::Vector3* base, cdc::Euler* auto addr = GET_ADDRESS(0x48D8B0, 0x491320, 0x000000); Hooking::Call(addr, position, base, rotation, distance); -} \ No newline at end of file +} + +#ifdef TR8 +void CAMERA_SetMode(int mode) +{ + Hooking::Call(0x5F39F0, mode); +} +#endif \ No newline at end of file diff --git a/src/game/Camera.h b/src/game/Camera.h index 1fcc862..5cb34c0 100644 --- a/src/game/Camera.h +++ b/src/game/Camera.h @@ -1,7 +1,9 @@ #pragma once #include "cdc/math/Vector.h" +#include "cdc/math/Matrix.h" +#ifndef TR8 struct CameraCore { cdc::Vector3 position; @@ -18,6 +20,18 @@ struct Camera : CameraCore int lock; __int16 mode; }; +#else +struct Camera +{ + char pad1[16]; + + cdc::Matrix transform; +}; +#endif Camera* CAMERA_GetCamera(); -void CAMERA_CalcPosition(cdc::Vector3* position, cdc::Vector3* base, cdc::Euler* rotation, float distance); \ No newline at end of file +void CAMERA_CalcPosition(cdc::Vector3* position, cdc::Vector3* base, cdc::Euler* rotation, float distance); + +#ifdef TR8 +void CAMERA_SetMode(int mode); +#endif \ No newline at end of file diff --git a/src/input/Input.h b/src/input/Input.h index 4c637d0..398468d 100644 --- a/src/input/Input.h +++ b/src/input/Input.h @@ -1,5 +1,64 @@ #pragma once +enum INPUTACTION +{ + INPUTACTION_NONE, + + INPUTACTION_UP, + INPUTACTION_DOWN, + INPUTACTION_LEFT, + INPUTACTION_RIGHT, + INPUTACTION_DPAD_UP, + INPUTACTION_DPAD_DOWN, + INPUTACTION_DPAD_LEFT, + INPUTACTION_DPAD_RIGHT, + INPUTACTION_START, + INPUTACTION_SELECT, + +#ifndef TR8 + INPUTACTION_TRIANGLE = 16, + INPUTACTION_SQUARE = 17, + INPUTACTION_CIRCLE = 18, + INPUTACTION_CROSS = 19, + INPUTACTION_L1 = 20, + INPUTACTION_R1 = 21, + INPUTACTION_L2 = 22, + INPUTACTION_R2 = 23, + + // Kinda hack, instead we should probably see how to register an input action + // which would also allow rebinding of them, but this will work for now + INPUTACTION_CAMERA_UP = INPUTACTION_TRIANGLE, + INPUTACTION_CAMERA_DOWN = INPUTACTION_SQUARE, + INPUTACTION_CAMERA_FAST = INPUTACTION_CIRCLE, + INPUTACTION_CAMERA_SLOW = INPUTACTION_R2, +#else + INPUTACTION_TRIANGLE = 11, + INPUTACTION_SQUARE = 12, + INPUTACTION_CIRCLE = 13, + INPUTACTION_CROSS = 14, + INPUTACTION_L1 = 15, + INPUTACTION_R1 = 16, + INPUTACTION_L2 = 17, + INPUTACTION_R2 = 18, + + INPUTACTION_CAMERA_UP = INPUTACTION_TRIANGLE, + INPUTACTION_CAMERA_DOWN = INPUTACTION_SQUARE, + INPUTACTION_CAMERA_FAST = INPUTACTION_L2, + INPUTACTION_CAMERA_SLOW = INPUTACTION_CIRCLE, +#endif +}; + +enum INPUTAXIS +{ + INPUTAXIS_LANALOGUEX, + INPUTAXIS_LANALOGUEY, + INPUTAXIS_RANALOGUEX, + INPUTAXIS_RANALOGUEY, + + INPUTAXIS_MOUSEX = 16, + INPUTAXIS_MOUSEY = 17, +}; + class InputActionMapper { public: @@ -7,7 +66,11 @@ class InputActionMapper { unsigned int state; +#ifndef TR8 char pad1[20]; +#else + char pad1[44]; +#endif }; ActionResult* m_pActionResults; diff --git a/src/modules/camera/FreeCamera.cpp b/src/modules/camera/FreeCamera.cpp index 9f2146e..2788151 100644 --- a/src/modules/camera/FreeCamera.cpp +++ b/src/modules/camera/FreeCamera.cpp @@ -42,8 +42,8 @@ void FreeCameraBase::OnControl() auto gameTracker = Game::GetGameTracker(); // Camera rotation based on mouse/controller axis - auto rotX = input->GetAxisValue(17) * gameTracker->timeMult; - auto rotZ = input->GetAxisValue(16) * gameTracker->timeMult; + auto rotX = input->GetAxisValue(INPUTAXIS_MOUSEY) * gameTracker->timeMult; + auto rotZ = input->GetAxisValue(INPUTAXIS_MOUSEX) * gameTracker->timeMult; Rotate(rotX, rotZ); @@ -57,40 +57,51 @@ void FreeCameraBase::OnControl() } // Change the speed depending on if shift/alt is pressed - auto shift = Input::IsInputActionPressed(18); - auto control = Input::IsInputActionPressed(23); + auto shift = Input::IsInputActionPressed(INPUTACTION_CAMERA_FAST); + auto control = Input::IsInputActionPressed(INPUTACTION_CAMERA_SLOW); auto speed = shift ? 200.f : control ? 20.f : 80.f; // Camera forward/backward - if (Input::IsInputActionPressed(1) || Input::IsInputActionPressed(2)) + if (Input::IsInputActionPressed(INPUTACTION_UP) || Input::IsInputActionPressed(INPUTACTION_DOWN)) { - auto distance = Input::IsInputActionPressed(1) ? -speed : speed; + auto distance = Input::IsInputActionPressed(INPUTACTION_UP) ? -speed : speed; MoveForward(distance * gameTracker->timeMult); } // Camera left/right - if (Input::IsInputActionPressed(3) || Input::IsInputActionPressed(4)) + if (Input::IsInputActionPressed(INPUTACTION_LEFT) || Input::IsInputActionPressed(INPUTACTION_RIGHT)) { - auto distance = Input::IsInputActionPressed(3) ? -speed : speed; + auto distance = Input::IsInputActionPressed(INPUTACTION_LEFT) ? -speed : speed; MoveLeft(distance * gameTracker->timeMult); } // Camera up/down - if (Input::IsInputActionPressed(16) || Input::IsInputActionPressed(17)) + if (Input::IsInputActionPressed(INPUTACTION_CAMERA_UP) || Input::IsInputActionPressed(INPUTACTION_CAMERA_DOWN)) { - auto distance = Input::IsInputActionPressed(16) ? -speed : speed; + auto distance = Input::IsInputActionPressed(INPUTACTION_CAMERA_UP) ? -speed : speed; MoveUp(distance * gameTracker->timeMult); } } +#include "Hook.h" +#include "modules/Log.h" + void FreeCameraBase::OnLoop() { if (m_mode == Enabled) { OnControl(); } + + for (int i = 0; i < 100; i++) + { + if (Input::IsInputActionPressed(i)) + { + Hook::GetInstance().GetModule()->WriteLine("%d", i); + } + } } \ No newline at end of file diff --git a/src/modules/camera/FreeCamera.h b/src/modules/camera/FreeCamera.h index 341f0aa..300426c 100644 --- a/src/modules/camera/FreeCamera.h +++ b/src/modules/camera/FreeCamera.h @@ -1,6 +1,7 @@ #pragma once #include "modules/Module.h" +#include "cdc/math/Vector.h" // Base class for both free camera implementations class FreeCameraBase : public Module @@ -36,6 +37,8 @@ class FreeCameraBase : public Module void OnLoop(); }; +#ifndef TR8 + // Free camera implementation for Legend and Anniversary class LegendCamera : public FreeCameraBase { @@ -49,4 +52,30 @@ class LegendCamera : public FreeCameraBase void MoveUp(float distance); }; -using FreeCamera = LegendCamera; \ No newline at end of file +#else + +// Free camera implementation for Underworld +class UnderworldCamera : public FreeCameraBase +{ +private: + cdc::Vector m_position = { }; + cdc::Euler m_rotation = { }; + +protected: + void ToggleMode(); + void OnControl(); + + void Rotate(float x, float z); + void Rotate(float y); + void MoveForward(float distance); + void MoveLeft(float distance); + void MoveUp(float distance); +}; + +#endif + +#ifndef TR8 +using FreeCamera = LegendCamera; +#else +using FreeCamera = UnderworldCamera; +#endif \ No newline at end of file diff --git a/src/modules/camera/Legend.cpp b/src/modules/camera/Legend.cpp index 9521c9a..57fd927 100644 --- a/src/modules/camera/Legend.cpp +++ b/src/modules/camera/Legend.cpp @@ -1,3 +1,5 @@ +#ifndef TR8 + #define _USE_MATH_DEFINES #include @@ -61,4 +63,6 @@ void LegendCamera::MoveUp(float distance) auto camera = CAMERA_GetCamera(); camera->position.z += distance; -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/modules/camera/Underworld.cpp b/src/modules/camera/Underworld.cpp new file mode 100644 index 0000000..bd8b7df --- /dev/null +++ b/src/modules/camera/Underworld.cpp @@ -0,0 +1,90 @@ +#if TR8 + +#define _USE_MATH_DEFINES +#include + +#include "FreeCamera.h" +#include "game/Camera.h" +#include "game/Game.h" +#include "util/Hooking.h" +#include "cdc/math/Math.h" + +static cdc::Vector3 RotationToDirection(cdc::Euler* rotation, float distance) +{ + cdc::Matrix m; + m.Build(rotation); + + cdc::Vector3 v = { 0.f, -distance, 0.f }; + + // TODO fixme + return cdc::Mul3x3(&m, &v); +} + +void UnderworldCamera::ToggleMode() +{ + FreeCameraBase::ToggleMode(); + + if (m_mode == Enabled) + { + CAMERA_SetMode(11); + + // Set the camera position to the player position + auto camera = CAMERA_GetCamera(); + camera->transform.col3 = Game::GetPlayerInstance()->position; + } + + if (m_mode == Disabled) + { + // Restore the gameplay camera + Hooking::ThisCall(0x5EDF50, 0xE804F0 /* CameraManager */); + } +} + +void UnderworldCamera::OnControl() +{ + auto camera = CAMERA_GetCamera(); + m_position = camera->transform.col3; + + FreeCameraBase::OnControl(); + + // Apply the rotation and position + camera->transform.Build(&m_rotation); + camera->transform.col3 = m_position; +} + +void UnderworldCamera::Rotate(float x, float z) +{ + m_rotation.x -= x; + m_rotation.z -= z; +} + +void UnderworldCamera::Rotate(float y) +{ + m_rotation.y += y; +} + +void UnderworldCamera::MoveForward(float distance) +{ + auto forward = RotationToDirection(&m_rotation, distance); + + m_position += &forward; +} + +void UnderworldCamera::MoveLeft(float distance) +{ + auto rotation = m_rotation; + rotation.x = 0.f; + rotation.y = 0.f; + rotation.z += static_cast(M_PI) / 2.f; + + auto forward = RotationToDirection(&rotation, distance); + + m_position += &forward; +} + +void UnderworldCamera::MoveUp(float distance) +{ + m_position.z += distance; +} + +#endif \ No newline at end of file From 2c09512047436d6bb97e68afc9038b3f8dc6dc3b Mon Sep 17 00:00:00 2001 From: Indra Date: Mon, 29 Jan 2024 16:59:55 +0100 Subject: [PATCH 34/55] Fix outfit change in TRA --- src/game/Game.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/game/Game.cpp b/src/game/Game.cpp index edb47cc..ed449f3 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -33,7 +33,7 @@ void PLAYER_DebugSwitchPlayerCharacter() int OBTABLE_GetObjectID(char* name) { - auto addr = GET_ADDRESS(0x462590, 0x465DE0, 0x5BF770); + auto addr = GET_ADDRESS(0x462590, 0x4660C0, 0x5BF770); return Hooking::CallReturn(addr, name); } \ No newline at end of file From 72c791681e34bcaa79c6352f7449242a8a2760c0 Mon Sep 17 00:00:00 2001 From: Indra Date: Mon, 29 Jan 2024 17:02:06 +0100 Subject: [PATCH 35/55] Fix free camera movement --- src/game/Camera.cpp | 2 +- src/modules/camera/FreeCamera.cpp | 8 -------- src/modules/camera/Underworld.cpp | 17 +++++++++++------ 3 files changed, 12 insertions(+), 15 deletions(-) diff --git a/src/game/Camera.cpp b/src/game/Camera.cpp index 48893e1..1c3ef5e 100644 --- a/src/game/Camera.cpp +++ b/src/game/Camera.cpp @@ -4,7 +4,7 @@ Camera* CAMERA_GetCamera() { #ifndef TR8 - return (Camera*)GET_ADDRESS(0x000000, 0x850670, 0x000000); + return (Camera*)GET_ADDRESS(0x10FC660, 0x850670, 0x000000); #else return *(Camera**)0xE80534; #endif diff --git a/src/modules/camera/FreeCamera.cpp b/src/modules/camera/FreeCamera.cpp index 2788151..f93e944 100644 --- a/src/modules/camera/FreeCamera.cpp +++ b/src/modules/camera/FreeCamera.cpp @@ -96,12 +96,4 @@ void FreeCameraBase::OnLoop() { OnControl(); } - - for (int i = 0; i < 100; i++) - { - if (Input::IsInputActionPressed(i)) - { - Hook::GetInstance().GetModule()->WriteLine("%d", i); - } - } } \ No newline at end of file diff --git a/src/modules/camera/Underworld.cpp b/src/modules/camera/Underworld.cpp index bd8b7df..020f44f 100644 --- a/src/modules/camera/Underworld.cpp +++ b/src/modules/camera/Underworld.cpp @@ -11,13 +11,17 @@ static cdc::Vector3 RotationToDirection(cdc::Euler* rotation, float distance) { - cdc::Matrix m; - m.Build(rotation); + //cdc::Matrix m; + //m.Build(rotation); - cdc::Vector3 v = { 0.f, -distance, 0.f }; + //cdc::Vector3 v = { 0.f, -distance, 0.f }; - // TODO fixme - return cdc::Mul3x3(&m, &v); + //return cdc::Mul3x3(&m, &v); + auto x = -sin(rotation->z); + auto y = cos(rotation->z); + auto z = cos(rotation->x); + + return cdc::Vector3{ static_cast(x * -distance), static_cast(y * -distance), static_cast(z * -distance) }; } void UnderworldCamera::ToggleMode() @@ -31,6 +35,8 @@ void UnderworldCamera::ToggleMode() // Set the camera position to the player position auto camera = CAMERA_GetCamera(); camera->transform.col3 = Game::GetPlayerInstance()->position; + + m_rotation = { static_cast(M_PI) / -2.f, 0.f, 0.f }; } if (m_mode == Disabled) @@ -73,7 +79,6 @@ void UnderworldCamera::MoveForward(float distance) void UnderworldCamera::MoveLeft(float distance) { auto rotation = m_rotation; - rotation.x = 0.f; rotation.y = 0.f; rotation.z += static_cast(M_PI) / 2.f; From c65a1d663ea197a7ff7995dfc4392023a29f1e67 Mon Sep 17 00:00:00 2001 From: Indra Date: Tue, 30 Jan 2024 13:06:58 +0100 Subject: [PATCH 36/55] Defer initialization until after construct --- src/Hook.cpp | 1 - src/Hook.h | 2 +- src/Main.cpp | 3 ++- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/Hook.cpp b/src/Hook.cpp index f258ac0..5adf247 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -42,7 +42,6 @@ static bool D3D_Init() Hook::Hook() : m_menu(nullptr), m_modules() { - Initialize(); } void Hook::Initialize() diff --git a/src/Hook.h b/src/Hook.h index 85360df..c7c015d 100644 --- a/src/Hook.h +++ b/src/Hook.h @@ -12,7 +12,6 @@ class Hook std::unique_ptr m_menu; std::map> m_modules; - void Initialize(); void PostInitialize(); template @@ -26,6 +25,7 @@ class Hook public: Hook(); + void Initialize(); void OnDevice(); // These need to be defined here, else the linker becomes angry diff --git a/src/Main.cpp b/src/Main.cpp index 97c48f8..dfdfc5b 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -9,7 +9,8 @@ static void (WINAPI* s_GetStartupInfoA)(LPSTARTUPINFOA); static void WINAPI StartupInfoW(LPSTARTUPINFOA lpStartupInfo) { // Initialize - Hook::GetInstance(); + auto& hook = Hook::GetInstance(); + hook.Initialize(); // Call the original GetStartupInfoA s_GetStartupInfoA(lpStartupInfo); From c317bfbb3088221d3c3cdf9332b2d8e595d5dd9a Mon Sep 17 00:00:00 2001 From: Indra Date: Thu, 1 Feb 2024 00:26:26 +0100 Subject: [PATCH 37/55] Add initial options system --- src/Hook.cpp | 7 +++- src/Options.cpp | 93 ++++++++++++++++++++++++++++++++++++++++++ src/Options.h | 101 ++++++++++++++++++++++++++++++++++++++++++++++ src/menu/Menu.cpp | 22 ++++++++++ src/menu/Menu.h | 1 + 5 files changed, 222 insertions(+), 2 deletions(-) create mode 100644 src/Options.cpp create mode 100644 src/Options.h diff --git a/src/Hook.cpp b/src/Hook.cpp index 5adf247..da14765 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -2,6 +2,7 @@ #include #include "Hook.h" +#include "Options.h" #include "input/MessageHook.h" #include "instance/Instances.h" #include "game/Game.h" @@ -122,6 +123,10 @@ void Hook::RegisterModule() void Hook::RegisterModules() { + // Register these first + RegisterModule(); + RegisterModule(); + RegisterModule(); RegisterModule(); RegisterModule(); @@ -136,8 +141,6 @@ void Hook::RegisterModules() #else RegisterModule(); #endif - - RegisterModule(); } Hook& Hook::GetInstance() diff --git a/src/Options.cpp b/src/Options.cpp new file mode 100644 index 0000000..498a1d6 --- /dev/null +++ b/src/Options.cpp @@ -0,0 +1,93 @@ +#include + +#include "Options.h" +#include "Hook.h" + +BaseOption::BaseOption(const char* name) : m_name(name) +{ +} + +Options::Options() : m_options() +{ +} + +void Options::OnDraw() +{ + if (m_show) + { + DrawOptions(); + } +} + +void Options::DrawOptions() +{ + ImGui::Begin("Options", &m_show); + + if (ImGui::CollapsingHeader("General")) + { + DrawOption("DisableIntro", "Disable legal screen and intros"); + DrawOption("NoCinematicBars", "Disable cinematic bars"); + DrawOption("NoMotionBlur", "Disable motion blur"); + } + + if (ImGui::CollapsingHeader("Camera")) + { + DrawOption("CameraSlowSpeed", "Slow speed", 0.f, 1000.f); + DrawOption("CameraNormalSpeed", "Normal speed", 0.f, 1000.f); + DrawOption("CameraFastSpeed", "Fast speed", 0.f, 1000.f); + DrawOption("CameraRollSpeed", "Roll speed", 0.f, 1.f); + } + + if (ImGui::CollapsingHeader("Controls")) + { + DrawOption("SkewSpeed", "Vertical skew speed", 0.f, 1000.f); + } + + ImGui::End(); +} + +void Options::DrawOption(const char* name, const char* description, float min, float max) +{ + // TODO use a map for better performance + for (auto& option : m_options) + { + if (option->GetName() == name) + { + DrawOption(option, description, min, max); + } + } +} + +void Options::DrawOption(BaseOption* option, const char* description, float min, float max) +{ + auto type = option->GetType(); + + if (type == typeid(bool).hash_code()) + { + if (ImGui::Checkbox(description, (bool*)option->GetValuePtr())) + { + option->SaveValue(); + } + } + + if (type == typeid(float).hash_code()) + { + if (ImGui::SliderFloat(description, (float*)option->GetValuePtr(), min, max)) + { + option->SaveValue(); + } + } +} + +void Options::Show() +{ + m_show = true; +} + +void Options::AddOption(BaseOption* option) +{ + m_options.push_back(option); + + // Load the value from the registry + option->LoadValue(); +} \ No newline at end of file diff --git a/src/Options.h b/src/Options.h new file mode 100644 index 0000000..dc4ecd4 --- /dev/null +++ b/src/Options.h @@ -0,0 +1,101 @@ +#pragma once + +#include +#include + +#include "modules/Module.h" +#include "Hook.h" + +template +class Option : public BaseOption +{ +private: + T m_value; + +public: + Option(const char* name, T defaultValue) : BaseOption(name), m_value(defaultValue) + { + Hook::GetInstance().GetModule()->AddOption(this); + } + + // Loads the value from the registry + void LoadValue() + { + HKEY hKey; + if (RegOpenKeyA(HKEY_CURRENT_USER, "SOFTWARE\\TRAE-Menu-Hook", &hKey) == ERROR_SUCCESS) + { + auto size = sizeof(T); + RegQueryValueExA(hKey, m_name, NULL, NULL, (LPBYTE)&m_value, (LPDWORD)&size); + + RegCloseKey(hKey); + } + } + + // Saves the current value to the registry + void SaveValue() + { + HKEY hKey; + if (RegCreateKeyA(HKEY_CURRENT_USER, "SOFTWARE\\TRAE-Menu-Hook", &hKey) == ERROR_SUCCESS) + { + RegSetValueExA(hKey, m_name, NULL, REG_BINARY, (LPBYTE)&m_value, sizeof(T)); + RegCloseKey(hKey); + } + } + + // Gets the type hash + size_t GetType() + { + return typeid(T).hash_code(); + } + + // Gets a pointer to the value + void* GetValuePtr() + { + return &m_value; + } + + // Gets the value + T GetValue() + { + return m_value; + } +}; + +class BaseOption +{ +protected: + const char* m_name; + +public: + BaseOption(const char* name); + + // Gets the name + const char* GetName() + { + return m_name; + } + + // Methods to be implemented by Option + virtual void LoadValue() = 0; + virtual void SaveValue() = 0; + virtual size_t GetType() = 0; + virtual void* GetValuePtr() = 0; +}; + +class Options : public Module +{ +private: + std::vector m_options; + bool m_show = false; + + void DrawOptions(); + void DrawOption(const char* name, const char* description, float min = 0.f, float max = 0.f); + void DrawOption(BaseOption* option, const char* description, float min, float max); + +public: + Options(); + + void OnDraw(); + void Show(); + void AddOption(BaseOption* option); +}; \ No newline at end of file diff --git a/src/menu/Menu.cpp b/src/menu/Menu.cpp index de95ec9..763f881 100644 --- a/src/menu/Menu.cpp +++ b/src/menu/Menu.cpp @@ -9,6 +9,7 @@ #include "input/MouseHook.h" #include "input/Input.h" #include "Hook.h" +#include "Options.h" #include "cdc/render/PCDeviceManager.h" @@ -99,6 +100,9 @@ void Menu::Draw() mod->OnMenu(); } + // Draw last menu + DrawMenu(); + ImGui::EndMainMenuBar(); } @@ -109,6 +113,24 @@ void Menu::Draw() } } +void Menu::DrawMenu() +{ + if (ImGui::BeginMenu("Help")) + { + if (ImGui::MenuItem("Options")) + { + Hook::GetInstance().GetModule()->Show(); + } + + if (ImGui::MenuItem("GitHub")) + { + ShellExecuteA(NULL, NULL, "https://github.com/TheIndra55/TRAE-menu-hook", NULL, NULL, SW_SHOW); + } + + ImGui::EndMenu(); + } +} + void Menu::SetFocus(bool focus) { m_focus = focus; diff --git a/src/menu/Menu.h b/src/menu/Menu.h index d3a1c59..5bb2071 100644 --- a/src/menu/Menu.h +++ b/src/menu/Menu.h @@ -24,4 +24,5 @@ class Menu : public cdc::PCInternalResource private: void Draw(); + void DrawMenu(); }; \ No newline at end of file From e5aba77cd37b9a936055da63bd16061413c2b7ea Mon Sep 17 00:00:00 2001 From: Indra Date: Thu, 1 Feb 2024 00:27:37 +0100 Subject: [PATCH 38/55] Add options for existing modules --- src/modules/Patches.cpp | 5 ++++- src/modules/Patches.h | 3 +++ src/modules/Skew.cpp | 3 +-- src/modules/Skew.h | 3 +++ src/modules/camera/FreeCamera.cpp | 6 +++--- src/modules/camera/FreeCamera.h | 7 +++++++ 6 files changed, 21 insertions(+), 6 deletions(-) diff --git a/src/modules/Patches.cpp b/src/modules/Patches.cpp index 6dc1879..b6689ba 100644 --- a/src/modules/Patches.cpp +++ b/src/modules/Patches.cpp @@ -6,7 +6,10 @@ Patches::Patches() { #ifndef TR8 - RemoveIntro(); + if (m_disableIntro.GetValue()) + { + RemoveIntro(); + } #endif } diff --git a/src/modules/Patches.h b/src/modules/Patches.h index 5c9e29b..572a70a 100644 --- a/src/modules/Patches.h +++ b/src/modules/Patches.h @@ -1,10 +1,13 @@ #pragma once #include "Module.h" +#include "Options.h" class Patches : public Module { private: + Option m_disableIntro{ "DisableIntro", true }; + void RemoveIntro(); public: diff --git a/src/modules/Skew.cpp b/src/modules/Skew.cpp index 3717380..7d55707 100644 --- a/src/modules/Skew.cpp +++ b/src/modules/Skew.cpp @@ -25,12 +25,11 @@ void Skew::ToggleSkew() void Skew::Process(UINT msg, WPARAM wParam) { // TODO different keyboard layouts - // TODO configurable horizontal speed auto player = Game::GetPlayerInstance(); auto tracker = Game::GetGameTracker(); - auto speed = 200.f * tracker->timeMult; + auto speed = m_speed.GetValue() * tracker->timeMult; if (msg == WM_KEYDOWN && wParam == 0x51) { diff --git a/src/modules/Skew.h b/src/modules/Skew.h index 9d649b6..2aa60a2 100644 --- a/src/modules/Skew.h +++ b/src/modules/Skew.h @@ -1,6 +1,7 @@ #pragma once #include "Module.h" +#include "Options.h" class Skew : public Module { @@ -8,6 +9,8 @@ class Skew : public Module void ToggleSkew(); void Process(UINT msg, WPARAM wParam); + Option m_speed{ "SkewSpeed", 300.f }; + public: virtual void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); }; \ No newline at end of file diff --git a/src/modules/camera/FreeCamera.cpp b/src/modules/camera/FreeCamera.cpp index f93e944..6d5aa3f 100644 --- a/src/modules/camera/FreeCamera.cpp +++ b/src/modules/camera/FreeCamera.cpp @@ -50,8 +50,8 @@ void FreeCameraBase::OnControl() // Camera roll if (m_vKeys[0] || m_vKeys[2]) { - // TODO - auto roll = m_vKeys[0] ? 0.02454f : -0.02454f; + auto value = m_rollSpeed.GetValue(); + auto roll = m_vKeys[0] ? value : -value; Rotate(roll * gameTracker->timeMult); } @@ -60,7 +60,7 @@ void FreeCameraBase::OnControl() auto shift = Input::IsInputActionPressed(INPUTACTION_CAMERA_FAST); auto control = Input::IsInputActionPressed(INPUTACTION_CAMERA_SLOW); - auto speed = shift ? 200.f : control ? 20.f : 80.f; + auto speed = shift ? m_fastSpeed.GetValue() : control ? m_slowSpeed.GetValue() : m_normalSpeed.GetValue(); // Camera forward/backward if (Input::IsInputActionPressed(INPUTACTION_UP) || Input::IsInputActionPressed(INPUTACTION_DOWN)) diff --git a/src/modules/camera/FreeCamera.h b/src/modules/camera/FreeCamera.h index 300426c..768cb0d 100644 --- a/src/modules/camera/FreeCamera.h +++ b/src/modules/camera/FreeCamera.h @@ -2,6 +2,7 @@ #include "modules/Module.h" #include "cdc/math/Vector.h" +#include "Options.h" // Base class for both free camera implementations class FreeCameraBase : public Module @@ -17,6 +18,12 @@ class FreeCameraBase : public Module private: bool m_vKeys[3]; + // Camera options + Option m_slowSpeed { "CameraSlowSpeed", 20.f }; + Option m_normalSpeed{ "CameraNormalSpeed", 80.f }; + Option m_fastSpeed { "CameraFastSpeed", 200.f }; + Option m_rollSpeed { "CameraRollSpeed", 0.02454f }; + protected: ControlMode m_mode = Disabled; From 2d1683cb9e9745de8a1db028409591056078b400 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 4 Feb 2024 00:00:06 +0100 Subject: [PATCH 39/55] Add player flags --- src/modules/MainMenu.cpp | 21 ++++++++++++++++++++- src/modules/MainMenu.h | 3 +++ src/modules/Patches.h | 2 ++ 3 files changed, 25 insertions(+), 1 deletion(-) diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index c637773..52c0a33 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -33,6 +33,7 @@ void MainMenu::OnDraw() // Player if (ImGui::CollapsingHeader("Player")) { + // Switch outfit static char outfit[64] = ""; ImGui::InputText("Outfit", outfit, sizeof(outfit)); @@ -47,6 +48,24 @@ void MainMenu::OnDraw() { SwitchPlayerCharacter(); } + +#ifndef TR8 + // Player flags + auto flags = (unsigned int*)GET_ADDRESS(0x1075B88, 0x7C7C78, 0x000000); + + static bool noInterp = *flags & 0x80; + if (ImGui::Checkbox("No interpolation", &noInterp)) + { + if (noInterp) + { + *flags |= 0x80; + } + else + { + *flags &= ~0x80; + } + } +#endif } // Time @@ -122,7 +141,7 @@ void MainMenu::OnFrame() // Shows the watermark in th main menu auto mainState = *(int*)GET_ADDRESS(0x10E5868, 0x838838, 0x000000); - if (mainState == MS_DISPLAY_MAIN_MENU) + if (mainState == MS_DISPLAY_MAIN_MENU && !m_noWatermark.GetValue()) { auto font = Font::GetMainFont(); diff --git a/src/modules/MainMenu.h b/src/modules/MainMenu.h index db8cf24..0efb8d2 100644 --- a/src/modules/MainMenu.h +++ b/src/modules/MainMenu.h @@ -1,12 +1,15 @@ #pragma once #include "Module.h" +#include "Options.h" class MainMenu : public Module { private: bool m_switchPlayerNextFrame = false; + Option m_noWatermark{ "NoWatermark", false }; + void BirthObject(char* name); void SwitchPlayerCharacter(char* name = nullptr); diff --git a/src/modules/Patches.h b/src/modules/Patches.h index 572a70a..993348a 100644 --- a/src/modules/Patches.h +++ b/src/modules/Patches.h @@ -7,6 +7,8 @@ class Patches : public Module { private: Option m_disableIntro{ "DisableIntro", true }; + Option m_noCinematicBars{ "NoCinematicBars", true }; + Option m_noMotionBlur{ "NoMotionBlur", false }; void RemoveIntro(); From 347d611143f710fb963eb025cd7a447fab559631 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 4 Feb 2024 02:54:12 +0100 Subject: [PATCH 40/55] Add instance and portal drawing --- src/Hook.cpp | 2 + src/instance/Enemy.h | 20 ++++ src/level/Level.h | 38 ++++++- src/modules/Draw.cpp | 232 +++++++++++++++++++++++++++++++++++++++++++ src/modules/Draw.h | 39 ++++++++ src/render/Draw.cpp | 24 +++++ src/render/Draw.h | 4 +- src/render/Font.cpp | 7 ++ src/render/Font.h | 1 + 9 files changed, 365 insertions(+), 2 deletions(-) create mode 100644 src/instance/Enemy.h create mode 100644 src/modules/Draw.cpp create mode 100644 src/modules/Draw.h diff --git a/src/Hook.cpp b/src/Hook.cpp index da14765..349c95f 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -20,6 +20,7 @@ #include "modules/ModLoader.h" #include "modules/Patches.h" #include "modules/Frontend.h" +#include "modules/Draw.h" #include "modules/camera/FreeCamera.h" #include "cdc/render/PCDeviceManager.h" @@ -138,6 +139,7 @@ void Hook::RegisterModules() RegisterModule(); RegisterModule(); RegisterModule(); + RegisterModule(); #else RegisterModule(); #endif diff --git a/src/instance/Enemy.h b/src/instance/Enemy.h new file mode 100644 index 0000000..dc340f2 --- /dev/null +++ b/src/instance/Enemy.h @@ -0,0 +1,20 @@ +#pragma once + +class EnemyHealth +{ +public: + float m_hitPoints; + float m_damageSinceMove; +}; + +class EnemyData +{ +public: +#ifdef TR7 + char pad1[5040]; +#else + char pad1[5280]; +#endif + + EnemyHealth m_health; +}; \ No newline at end of file diff --git a/src/level/Level.h b/src/level/Level.h index 464ffb8..8c452aa 100644 --- a/src/level/Level.h +++ b/src/level/Level.h @@ -3,10 +3,10 @@ #include "cdc/math/Vector.h" struct Intro; -struct StreamUnitPortal; struct Signal; struct Instance; struct KDNode; +struct StreamUnit; struct IndexedFace { @@ -20,6 +20,18 @@ struct IndexedFace unsigned __int8 materialType; }; +struct SignalFace +{ + unsigned __int16 i0; + unsigned __int16 i1; + unsigned __int16 i2; + + char adjacencyFlags; + char collisionFlags; + + unsigned __int16 id; +}; + struct MeshVertex { __int16 x; @@ -79,6 +91,24 @@ struct TerrainGroup char pad1[116]; }; +struct StreamUnitPortal +{ + char tolevelname[30]; + + __int16 toSignalID; + __int16 MSignalID; + __int16 streamID; + + unsigned __int16* closeVertList; + float activeDistance; + StreamUnit* toStreamUnit; + + cdc::Vector3 min; + cdc::Vector3 max; + cdc::Vector3 quad[4]; + cdc::Vector3 normal; +}; + struct Terrain { __int16 UnitChangeFlags; @@ -108,4 +138,10 @@ struct Level void* splineCameraData; void* relocModule; // Pointer to script executable + + char pad2[84]; + + void* pCdcPlannerData; + void* pAreaDBase; + void* pUnitData; }; \ No newline at end of file diff --git a/src/modules/Draw.cpp b/src/modules/Draw.cpp new file mode 100644 index 0000000..ac47898 --- /dev/null +++ b/src/modules/Draw.cpp @@ -0,0 +1,232 @@ +#include +#include + +#include "Draw.h" +#include "render/Font.h" +#include "render/Draw.h" +#include "game/Game.h" +#include "instance/Instances.h" +#include "instance/Enemy.h" + +void Draw::OnMenu() +{ + if (ImGui::BeginMenu("Draw")) + { + ImGui::MenuItem("Draw instances", nullptr, &m_drawInstances); + ImGui::MenuItem("Draw markup", nullptr, &m_drawMarkUp); + ImGui::MenuItem("Draw enemy route", nullptr, &m_drawEnemyRouting); + ImGui::MenuItem("Draw collision", nullptr, &m_drawCollision); + ImGui::MenuItem("Draw portals", nullptr, &m_drawPortals); + ImGui::MenuItem("Draw signals", nullptr, &m_drawSignals); + + ImGui::EndMenu(); + } +} + +void Draw::OnFrame() +{ + auto gameTracker = Game::GetGameTracker(); + auto level = gameTracker->level; + + if (m_drawInstances || m_drawEnemyRouting) + { + DrawInstances(); + } + + if (m_drawMarkUp) + { + DrawMarkUp(); + } + + if (level) + { + if (m_drawCollision) + { + DrawCollision(level); + } + + if (m_drawPortals) + { + DrawPortals(level); + } + + if (m_drawSignals) + { + DrawSignals(level); + } + } +} + +void Draw::OnDraw() +{ + if (m_drawInstances) + { + ImGui::Begin("Draw options"); + + // Filter + ImGui::InputText("Filter", m_filter, sizeof(m_filter)); + + // Options + ImGui::Checkbox("Draw intro", &m_drawIntro); + ImGui::Checkbox("Draw family", &m_drawFamily); + ImGui::Checkbox("Draw animations", &m_drawAnimation); + ImGui::Checkbox("Draw health", &m_drawHealth); + + ImGui::End(); + } +} + +void Draw::DrawInstances() +{ + // Iterate through all instances + Instances::Iterate([this](Instance* instance) + { + // Draw the instance info + if (m_drawInstances) + { + DrawInstance(instance); + } + + // Draw the route + if (m_drawEnemyRouting) + { + DrawEnemyRoute(instance); + } + }); +} + +void Draw::DrawInstance(Instance* instance) +{ + auto name = instance->object->name; + auto data = (ObjectData*)instance->data; + + // Check filter + if (strlen(m_filter) > 0 && strstr(name, m_filter) == 0) + { + return; + } + + cdc::Vector3 position; + TRANS_RotTransPersVectorf(&instance->position, &position); + + // Check if the instance position is on screen + if (position.z > 16.f) + { + auto font = Font::GetMainFont(); + auto heigth = font->GetHeight(); + + // Draw the instance name + font->SetCursor(position.x, position.y); + font->Print("%s", name); + + // Draw intro + if (m_drawIntro) + { + position.y += heigth; + + font->SetCursor(position.x, position.y); + font->Print("Intro: %d", instance->introUniqueID); + } + + // Draw family + if (m_drawFamily && data) + { + position.y += heigth; + + font->SetCursor(position.x, position.y); + font->Print("Family: %d", data->family); + } + + // Draw enemy health + if (m_drawHealth && data && data->family == 0xDAF0) + { + position.y += heigth; + + auto extraData = (EnemyData*)instance->extraData; + + font->SetCursor(position.x, position.y); + font->Print("Health: %g", extraData->m_health.m_hitPoints); + } + + // Draw animations + if (m_drawAnimation) + { + // Draw all allocated sections + for (int section = 0; section < instance->animComponent->mAnimProcessor->mSectionsAllocated; section++) + { + // Go to the next line + position.y += heigth; + font->SetCursor(position.x, position.y); + + // Query the current animation + auto currentAnim = G2EmulationInstanceQueryAnimation(instance, section); + + // Can be null for some reason + if (instance->object->animList) + { + // (Section): (Anim index) (Anim ID) + font->Print("Anim %d: %d (%X)", section, currentAnim, instance->object->animList[currentAnim].animationID); + } + else + { + font->Print("Anim %d: %d", section, currentAnim); + } + } + } + } +} + +void Draw::DrawEnemyRoute(Instance* instance) +{ + auto data = (ObjectData*)instance->data; + + // Check if the instance is an enemy + if (!data || data->family != 0xDAF0) + { + return; + } +} + +void Draw::DrawMarkUp() +{ +} + +void Draw::DrawCollision(Level* level) +{ +} + +void Draw::DrawCollision(TerrainGroup* terrainGroup) +{ +} + +void Draw::DrawPortals(Level* level) +{ + auto terrain = level->terrain; + auto font = Font::GetMainFont(); + + for (int i = 0; i < terrain->numStreamUnitPortals; i++) + { + auto portal = &terrain->streamUnitPortals[i]; + auto position = portal->min; + + position += &portal->max; + position /= 2; + + TRANS_RotTransPersVectorf(&position, &position); + + // Check if the portal is on screen + if (position.z > 16.f) + { + // Draw the portal destination + font->SetCursor(position.x, position.y); + font->PrintCentered("Portal to %s", portal->tolevelname); + + // Draw the portal bounds + DrawPlane(&portal->min, &portal->max, RGBA(0, 0, 255, 10)); + } + } +} + +void Draw::DrawSignals(Level* level) +{ +} \ No newline at end of file diff --git a/src/modules/Draw.h b/src/modules/Draw.h new file mode 100644 index 0000000..07780d5 --- /dev/null +++ b/src/modules/Draw.h @@ -0,0 +1,39 @@ +#pragma once + +#include "Module.h" + +struct Level; +struct Instance; +struct TerrainGroup; + +class Draw : public Module +{ +private: + bool m_drawInstances = false; + bool m_drawMarkUp = false; + bool m_drawEnemyRouting = false; + bool m_drawCollision = false; + bool m_drawPortals = false; + bool m_drawSignals = false; + + bool m_drawIntro = false; + bool m_drawFamily = false; + bool m_drawHealth = false; + bool m_drawAnimation = false; + + char m_filter[100] = ""; + + void DrawInstances(); + void DrawInstance(Instance* instance); + void DrawEnemyRoute(Instance* instance); + void DrawMarkUp(); + void DrawCollision(Level* level); + void DrawCollision(TerrainGroup* group); + void DrawPortals(Level* level); + void DrawSignals(Level* level); + +public: + void OnMenu(); + void OnDraw(); + void OnFrame(); +}; \ No newline at end of file diff --git a/src/render/Draw.cpp b/src/render/Draw.cpp index c697a55..9d9c803 100644 --- a/src/render/Draw.cpp +++ b/src/render/Draw.cpp @@ -19,4 +19,28 @@ void DRAW_DrawQuads(int flags, int tpage, DRAWVERTEX* verts, int numquads) void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris) { Hooking::Call(0x407570, flags, tpage, verts, numtris); +} + +void DrawPlane(cdc::Vector3* v0, cdc::Vector3* v1, int color) +{ + DRAWVERTEX verts[6]; + + auto v2 = cdc::Vector3{ v0->x, v0->y, v1->z }; + auto v3 = cdc::Vector3{ v1->x, v1->y, v0->z }; + + TRANS_TransToDrawVertexV4f(verts, v0); + TRANS_TransToDrawVertexV4f(&verts[1], &v2); + TRANS_TransToDrawVertexV4f(&verts[2], v1); + TRANS_TransToDrawVertexV4f(&verts[3], &v3); + TRANS_TransToDrawVertexV4f(&verts[4], v1); + TRANS_TransToDrawVertexV4f(&verts[5], v0); + + verts[0].color = color; + verts[1].color = color; + verts[2].color = color; + verts[3].color = color; + verts[4].color = color; + verts[5].color = color; + + DRAW_DrawTriangles(2, 0, verts, 2); } \ No newline at end of file diff --git a/src/render/Draw.h b/src/render/Draw.h index 3a363b1..4832a10 100644 --- a/src/render/Draw.h +++ b/src/render/Draw.h @@ -25,4 +25,6 @@ void TRANS_TransToDrawVertexV4f(DRAWVERTEX* v, cdc::Vector3* vec); void TRANS_RotTransPersVectorf(cdc::Vector3* srcvector, cdc::Vector3* dstvector); void DRAW_DrawQuads(int flags, int tpage, DRAWVERTEX* verts, int numquads); -void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris); \ No newline at end of file +void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris); + +void DrawPlane(cdc::Vector3* v0, cdc::Vector3* v1, int color); \ No newline at end of file diff --git a/src/render/Font.cpp b/src/render/Font.cpp index 340c366..e80fda5 100644 --- a/src/render/Font.cpp +++ b/src/render/Font.cpp @@ -87,6 +87,13 @@ float Font::GetTextWidth(const char* text) return Hooking::ThisCallReturn(addr, this, text); } +float Font::GetHeight() +{ + auto addr = GET_ADDRESS(0x431E20, 0x434440, 0x000000); + + return Hooking::ThisCallReturn(addr, this); +} + void Font::OnFlush(std::function callback) { if (!s_callback) diff --git a/src/render/Font.h b/src/render/Font.h index f86dfa2..88e3e9b 100644 --- a/src/render/Font.h +++ b/src/render/Font.h @@ -19,6 +19,7 @@ class Font void PrintFormatted(const char* formatted, int backdrop = 0); float GetTextWidth(const char* text); + float GetHeight(); static void OnFlush(std::function callback); }; \ No newline at end of file From d29e62ee81e035c33d2554d3ec03f72c64006789 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 4 Feb 2024 03:02:48 +0100 Subject: [PATCH 41/55] Fix TR8 build --- src/modules/Draw.cpp | 6 +++++- src/modules/Draw.h | 5 ++++- 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/modules/Draw.cpp b/src/modules/Draw.cpp index ac47898..828c74b 100644 --- a/src/modules/Draw.cpp +++ b/src/modules/Draw.cpp @@ -1,3 +1,5 @@ +#ifndef TR8 + #include #include @@ -229,4 +231,6 @@ void Draw::DrawPortals(Level* level) void Draw::DrawSignals(Level* level) { -} \ No newline at end of file +} + +#endif \ No newline at end of file diff --git a/src/modules/Draw.h b/src/modules/Draw.h index 07780d5..c6b5b8b 100644 --- a/src/modules/Draw.h +++ b/src/modules/Draw.h @@ -1,4 +1,5 @@ #pragma once +#ifndef TR8 #include "Module.h" @@ -36,4 +37,6 @@ class Draw : public Module void OnMenu(); void OnDraw(); void OnFrame(); -}; \ No newline at end of file +}; + +#endif \ No newline at end of file From ac86dd9e23ace1f18e79091717f4cb839eca42aa Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 4 Feb 2024 03:38:08 +0100 Subject: [PATCH 42/55] Fix draw functions for Legend --- src/render/Draw.cpp | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/render/Draw.cpp b/src/render/Draw.cpp index 9d9c803..f06f50d 100644 --- a/src/render/Draw.cpp +++ b/src/render/Draw.cpp @@ -3,22 +3,30 @@ void TRANS_TransToDrawVertexV4f(DRAWVERTEX* v, cdc::Vector3* vec) { - Hooking::Call(0x402F20, v, vec); + auto addr = GET_ADDRESS(0x402EF0, 0x402F20, 0x000000); + + Hooking::Call(addr, v, vec); } void TRANS_RotTransPersVectorf(cdc::Vector3* srcvector, cdc::Vector3* dstvector) { - Hooking::Call(0x402B50, srcvector, dstvector); + auto addr = GET_ADDRESS(0x402B20, 0x402B50, 0x000000); + + Hooking::Call(addr, srcvector, dstvector); } void DRAW_DrawQuads(int flags, int tpage, DRAWVERTEX* verts, int numquads) { - Hooking::Call(0x406D70, flags, tpage, verts, numquads); + auto addr = GET_ADDRESS(0x406720, 0x406D70, 0x000000); + + Hooking::Call(addr, flags, tpage, verts, numquads); } void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris) { - Hooking::Call(0x407570, flags, tpage, verts, numtris); + auto addr = GET_ADDRESS(0x406A40, 0x407570, 0x000000); + + Hooking::Call(addr, flags, tpage, verts, numtris); } void DrawPlane(cdc::Vector3* v0, cdc::Vector3* v1, int color) From 6eda1d0ca8471cf97167eefa73f3606ff82d1b85 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 4 Feb 2024 04:45:18 +0100 Subject: [PATCH 43/55] Add signal and collision drawing --- src/modules/Draw.cpp | 65 ++++++++++++++++++++++++++++++++++++++++++++ src/render/Draw.cpp | 15 ++++++++++ src/render/Draw.h | 1 + 3 files changed, 81 insertions(+) diff --git a/src/modules/Draw.cpp b/src/modules/Draw.cpp index 828c74b..745aa71 100644 --- a/src/modules/Draw.cpp +++ b/src/modules/Draw.cpp @@ -10,6 +10,17 @@ #include "instance/Instances.h" #include "instance/Enemy.h" +template +static inline cdc::Vector3 GetVertice(unsigned int vertice, Mesh* mesh, cdc::Vector* offset) +{ + auto vertex = &((T*)mesh->m_vertices)[vertice]; + auto position = cdc::Vector3{ static_cast(vertex->x), static_cast(vertex->y), static_cast(vertex->z) }; + + position += offset; + + return position; +} + void Draw::OnMenu() { if (ImGui::BeginMenu("Draw")) @@ -195,10 +206,40 @@ void Draw::DrawMarkUp() void Draw::DrawCollision(Level* level) { + auto terrain = level->terrain; + + // Draw the collision mesh for all terrain groups + for (int i = 0; i < terrain->numTerrainGroups; i++) + { + auto terrainGroup = &terrain->terrainGroups[i]; + + if (terrainGroup->mesh) + { + DrawCollision(terrainGroup); + } + } } void Draw::DrawCollision(TerrainGroup* terrainGroup) { + auto mesh = terrainGroup->mesh; + + // Draw all mesh faces + for (int i = 0; i < mesh->m_numFaces; i++) + { + auto face = &mesh->m_faces[i]; + + // Get the position of every vertice in world coordinates + auto x = GetVertice(face->i0, mesh, &mesh->m_position); + auto y = GetVertice(face->i1, mesh, &mesh->m_position); + auto z = GetVertice(face->i2, mesh, &mesh->m_position); + + // TODO collision face lines + // TODO collision type/mask colors + + // Draw the face + DrawTriangle(&x, &y, &z, RGBA(0, 255, 0, 10)); + } } void Draw::DrawPortals(Level* level) @@ -231,6 +272,30 @@ void Draw::DrawPortals(Level* level) void Draw::DrawSignals(Level* level) { + auto terrain = level->terrain; + auto terrainGroup = terrain->signalTerrainGroup; + + // Make sure there is a signal mesh + if (!terrainGroup || !terrainGroup->mesh) + { + return; + } + + auto mesh = terrainGroup->mesh; + + // Draw all mesh faces + for (int i = 0; i < mesh->m_numFaces; i++) + { + auto face = (SignalFace*)&mesh->m_faces[i]; + + // Get the position of every vertice in world coordinates + auto x = GetVertice(face->i0, mesh, &mesh->m_position); + auto y = GetVertice(face->i1, mesh, &mesh->m_position); + auto z = GetVertice(face->i2, mesh, &mesh->m_position); + + // Draw the face + DrawTriangle(&x, &y, &z, RGBA(255, 0, 0, 10)); + } } #endif \ No newline at end of file diff --git a/src/render/Draw.cpp b/src/render/Draw.cpp index f06f50d..aa0e619 100644 --- a/src/render/Draw.cpp +++ b/src/render/Draw.cpp @@ -29,6 +29,21 @@ void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris) Hooking::Call(addr, flags, tpage, verts, numtris); } +void DrawTriangle(cdc::Vector3* v0, cdc::Vector3* v1, cdc::Vector3* v2, int color) +{ + DRAWVERTEX verts[3]; + + TRANS_TransToDrawVertexV4f(verts, v0); + TRANS_TransToDrawVertexV4f(&verts[1], v1); + TRANS_TransToDrawVertexV4f(&verts[2], v2); + + verts[0].color = color; + verts[1].color = color; + verts[2].color = color; + + DRAW_DrawTriangles(2, 0, verts, 1); +} + void DrawPlane(cdc::Vector3* v0, cdc::Vector3* v1, int color) { DRAWVERTEX verts[6]; diff --git a/src/render/Draw.h b/src/render/Draw.h index 4832a10..0b2a192 100644 --- a/src/render/Draw.h +++ b/src/render/Draw.h @@ -27,4 +27,5 @@ void TRANS_RotTransPersVectorf(cdc::Vector3* srcvector, cdc::Vector3* dstvector) void DRAW_DrawQuads(int flags, int tpage, DRAWVERTEX* verts, int numquads); void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris); +void DrawTriangle(cdc::Vector3* v0, cdc::Vector3* v1, cdc::Vector3* v2, int color); void DrawPlane(cdc::Vector3* v0, cdc::Vector3* v1, int color); \ No newline at end of file From cee489f35b0b5aa86581b2062b49cdbc7c3bddf2 Mon Sep 17 00:00:00 2001 From: Indra Date: Tue, 6 Feb 2024 02:13:47 +0100 Subject: [PATCH 44/55] Improve collision drawing and filtering --- src/modules/Draw.cpp | 50 ++++++++++++++++++++++++++++++++------------ src/modules/Draw.h | 7 +++++++ src/render/Draw.cpp | 28 +++++++++++++++++++++++++ src/render/Draw.h | 3 ++- 4 files changed, 74 insertions(+), 14 deletions(-) diff --git a/src/modules/Draw.cpp b/src/modules/Draw.cpp index 745aa71..0ee8056 100644 --- a/src/modules/Draw.cpp +++ b/src/modules/Draw.cpp @@ -72,18 +72,29 @@ void Draw::OnFrame() void Draw::OnDraw() { - if (m_drawInstances) + if (m_drawInstances || m_drawCollision) { ImGui::Begin("Draw options"); - // Filter - ImGui::InputText("Filter", m_filter, sizeof(m_filter)); + if (ImGui::CollapsingHeader("Instance")) + { + // Filter + ImGui::InputText("Filter", m_filter, sizeof(m_filter)); + + // Options + ImGui::Checkbox("Draw intro", &m_drawIntro); + ImGui::Checkbox("Draw family", &m_drawFamily); + ImGui::Checkbox("Draw animations", &m_drawAnimation); + ImGui::Checkbox("Draw health", &m_drawHealth); + } + + if (ImGui::CollapsingHeader("Collision")) + { + ImGui::Checkbox("Player collision", &m_drawPlayerCollision); + ImGui::Checkbox("Enemy collision", &m_drawEnemyCollision); - // Options - ImGui::Checkbox("Draw intro", &m_drawIntro); - ImGui::Checkbox("Draw family", &m_drawFamily); - ImGui::Checkbox("Draw animations", &m_drawAnimation); - ImGui::Checkbox("Draw health", &m_drawHealth); + ImGui::InputInt("Terrain group", &m_terrainGroup); + } ImGui::End(); } @@ -213,7 +224,17 @@ void Draw::DrawCollision(Level* level) { auto terrainGroup = &terrain->terrainGroups[i]; - if (terrainGroup->mesh) + // Filter on terrain group + if (m_terrainGroup >= 0 && m_terrainGroup != i) + { + continue; + } + + // Filter on player/enemy collision + auto flag = terrainGroup->flags & 0x4000; + auto filter = (m_drawPlayerCollision && flag == 0) || (m_drawEnemyCollision && flag != 0); + + if (terrainGroup->mesh && filter) { DrawCollision(terrainGroup); } @@ -234,11 +255,14 @@ void Draw::DrawCollision(TerrainGroup* terrainGroup) auto y = GetVertice(face->i1, mesh, &mesh->m_position); auto z = GetVertice(face->i2, mesh, &mesh->m_position); - // TODO collision face lines - // TODO collision type/mask colors - // Draw the face - DrawTriangle(&x, &y, &z, RGBA(0, 255, 0, 10)); + auto color = terrainGroup->flags & 0x4000 ? RGBA(255, 0, 255, 10) : RGBA(0, 255, 0, 10); + DrawTriangle(&x, &y, &z, color); + + // Draw the face outlines + DrawLine(&x, &y, RGB(255, 0, 0)); + DrawLine(&y, &z, RGB(255, 0, 0)); + DrawLine(&z, &x, RGB(255, 0, 0)); } } diff --git a/src/modules/Draw.h b/src/modules/Draw.h index c6b5b8b..ba40ba8 100644 --- a/src/modules/Draw.h +++ b/src/modules/Draw.h @@ -17,6 +17,7 @@ class Draw : public Module bool m_drawPortals = false; bool m_drawSignals = false; + // Instance options bool m_drawIntro = false; bool m_drawFamily = false; bool m_drawHealth = false; @@ -24,6 +25,12 @@ class Draw : public Module char m_filter[100] = ""; + // Collision options + bool m_drawPlayerCollision = true; + bool m_drawEnemyCollision = true; + + int m_terrainGroup = -1; + void DrawInstances(); void DrawInstance(Instance* instance); void DrawEnemyRoute(Instance* instance); diff --git a/src/render/Draw.cpp b/src/render/Draw.cpp index aa0e619..3a0446e 100644 --- a/src/render/Draw.cpp +++ b/src/render/Draw.cpp @@ -65,5 +65,33 @@ void DrawPlane(cdc::Vector3* v0, cdc::Vector3* v1, int color) verts[4].color = color; verts[5].color = color; + DRAW_DrawTriangles(2, 0, verts, 2); +} + +// Scuffed ass line, TODO fix +void DrawLine(cdc::Vector3* v0, cdc::Vector3* v1, int color) +{ + DRAWVERTEX verts[6]; + + auto v2 = *v1; + auto v3 = *v0; + + v2.z += 50.f; + v3.x += 50.f; + + TRANS_TransToDrawVertexV4f(verts, v0); + TRANS_TransToDrawVertexV4f(&verts[1], &v2); + TRANS_TransToDrawVertexV4f(&verts[2], v1); + TRANS_TransToDrawVertexV4f(&verts[3], &v3); + TRANS_TransToDrawVertexV4f(&verts[4], v1); + TRANS_TransToDrawVertexV4f(&verts[5], v0); + + verts[0].color = color; + verts[1].color = color; + verts[2].color = color; + verts[3].color = color; + verts[4].color = color; + verts[5].color = color; + DRAW_DrawTriangles(2, 0, verts, 2); } \ No newline at end of file diff --git a/src/render/Draw.h b/src/render/Draw.h index 0b2a192..ab289b9 100644 --- a/src/render/Draw.h +++ b/src/render/Draw.h @@ -28,4 +28,5 @@ void DRAW_DrawQuads(int flags, int tpage, DRAWVERTEX* verts, int numquads); void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris); void DrawTriangle(cdc::Vector3* v0, cdc::Vector3* v1, cdc::Vector3* v2, int color); -void DrawPlane(cdc::Vector3* v0, cdc::Vector3* v1, int color); \ No newline at end of file +void DrawPlane(cdc::Vector3* v0, cdc::Vector3* v1, int color); +void DrawLine(cdc::Vector3* v0, cdc::Vector3* v1, int color); \ No newline at end of file From 60963fad9c56fccc90bb7ca69dc385d5417c82b2 Mon Sep 17 00:00:00 2001 From: Indra Date: Wed, 7 Feb 2024 01:57:14 +0100 Subject: [PATCH 45/55] Add markup drawing --- src/level/Markup.cpp | 14 +++++ src/level/Markup.h | 120 +++++++++++++++++++++++++++++++++++++++++++ src/modules/Draw.cpp | 106 ++++++++++++++++++++++++++++++++++++++ src/modules/Draw.h | 2 + src/render/Draw.cpp | 29 +++++++++++ src/render/Draw.h | 3 +- 6 files changed, 273 insertions(+), 1 deletion(-) create mode 100644 src/level/Markup.cpp create mode 100644 src/level/Markup.h diff --git a/src/level/Markup.cpp b/src/level/Markup.cpp new file mode 100644 index 0000000..251794c --- /dev/null +++ b/src/level/Markup.cpp @@ -0,0 +1,14 @@ +#include "Markup.h" +#include "util/Hooking.h" + +MarkupManager* MarkupManager::GetInstance() +{ + return *(MarkupManager**)GET_ADDRESS(0x1117544, 0x86CD14, 0x000000); +} + +cdc::Vector3* MarkUpBox::GetSegmentPos(cdc::Vector3* result, int n) +{ + auto addr = GET_ADDRESS(0x45F3A0, 0x462D90, 0x000000); + + return Hooking::ThisCallReturn(addr, this, result, n); +} \ No newline at end of file diff --git a/src/level/Markup.h b/src/level/Markup.h new file mode 100644 index 0000000..617c9e5 --- /dev/null +++ b/src/level/Markup.h @@ -0,0 +1,120 @@ +#pragma once + +#include "instance/Instance.h" + +struct SweepAndPrune; +struct SAPBox; + +class MarkupManager +{ +public: + SweepAndPrune* m_sap; + + NodeType m_freeBoxList; + NodeType m_dynamicBoxList; + NodeType m_staticBoxList; + + static MarkupManager* GetInstance(); +}; + +struct PolyLine +{ + int numPoints; + cdc::Vector3 point[1]; +}; + +struct CameraAntic +{ + int UseAnticCamera; + int newDTPCameraAnticDataID[5]; +}; + +// Markup flags, compatible with both Legend and Anniversary +enum MUD_FLAGS : unsigned int +{ + MUD_FLAG_NONE, + MUD_FLAG_DISABLED = 0x80000000, + + MUD_FLAG_LEDGEGRAB = 1 << 30, + MUD_FLAG_JUMPLANDING = 1 << 29, + MUD_FLAG_ENEMYDROPDOWN = 1 << 28, + MUD_FLAG_WALLVERTPOLE = 1 << 27, + MUD_FLAG_VERTPOLE = 1 << 26, + MUD_FLAG_HORIZPOLE = 1 << 25, + MUD_FLAG_ZIPLINE = 1 << 24, + MUD_FLAG_MANTLEDOWN = 1 << 23, + MUD_FLAG_LADDER = 1 << 22, + MUD_FLAG_CLOSEJUMPTARGET = 1 << 21, + MUD_FLAG_WATER = 1 << 20, + MUD_FLAG_ROPE = 1 << 19, + MUD_FLAG_PERCH = 1 << 18, + MUD_FLAG_ATTACHPOINTS = 1 << 17, + MUD_FLAG_ENEMY_JUMP_UP = 1 << 16, + MUD_FLAG_ENEMY_JUMP_DOWN = 1 << 15, + MUD_FLAG_ENEMY_JUMP_ACROSS = 1 << 14, + MUD_FLAG_PATTERN_INTERACT = 1 << 13, + + MUD_FLAG_WRAPS = 1 << 2, + MUD_FLAG_INSTANCE = 1 << 1, + MUD_FLAG_POLYLINE = 1 << 0, + + MUD_FLAG_ALL_TYPES = MUD_FLAG_ENEMY_JUMP_ACROSS | MUD_FLAG_ENEMY_JUMP_DOWN | MUD_FLAG_ENEMY_JUMP_UP | MUD_FLAG_PERCH | MUD_FLAG_ROPE | MUD_FLAG_WATER | MUD_FLAG_CLOSEJUMPTARGET | MUD_FLAG_LADDER | MUD_FLAG_MANTLEDOWN | MUD_FLAG_ZIPLINE | MUD_FLAG_HORIZPOLE | MUD_FLAG_VERTPOLE | MUD_FLAG_WALLVERTPOLE | MUD_FLAG_ENEMYDROPDOWN | MUD_FLAG_JUMPLANDING | MUD_FLAG_LEDGEGRAB | MUD_FLAG_DISABLED, + MUD_FLAG_ALL = 0xFFFFFFFF, +}; + +struct MarkUp +{ + int OverrideMovementCamera; + int DTPCameraDataID; + int DTPMarkupDataID; + +#ifdef TRAE + int AnimatedSegment; + CameraAntic cameraAntic; +#endif + + unsigned int flags; + + __int16 introID; + __int16 markupID; + + float pos[3]; + __int16 bbox[6]; + + PolyLine* polyLine; +}; + +#pragma pack(push, 16) +class MarkUpBox : NodeType +{ +public: + SAPBox* sapbox; + +#ifdef TRAE + MarkUp* markup; + cdc::Vector3 velocity; + Instance* instance; + int instanceID; +#else + Instance* instance; + cdc::Vector velocity; + MarkUp* markup; +#endif + + unsigned int flags; + + float attach_delay; + float attach_frames; + +#ifdef TRAE + unsigned int matrix_index; +#endif + + unsigned int debugColor; + + int nSegments; + void** associateRef; + + cdc::Vector3* GetSegmentPos(cdc::Vector3* result, int n); +}; +#pragma pack(pop) \ No newline at end of file diff --git a/src/modules/Draw.cpp b/src/modules/Draw.cpp index 0ee8056..7512a0b 100644 --- a/src/modules/Draw.cpp +++ b/src/modules/Draw.cpp @@ -1,5 +1,6 @@ #ifndef TR8 +#include #include #include @@ -9,6 +10,7 @@ #include "game/Game.h" #include "instance/Instances.h" #include "instance/Enemy.h" +#include "level/Markup.h" template static inline cdc::Vector3 GetVertice(unsigned int vertice, Mesh* mesh, cdc::Vector* offset) @@ -21,6 +23,31 @@ static inline cdc::Vector3 GetVertice(unsigned int vertice, Mesh* mesh, cdc::Vec return position; } +static std::pair s_mudFlags[] +{ + { MUD_FLAG_INSTANCE, "INSTANCE | " }, + { MUD_FLAG_WRAPS, "WRAPS | " }, + { MUD_FLAG_PATTERN_INTERACT, "PATTERN_INTERACT | " }, + { MUD_FLAG_ENEMY_JUMP_ACROSS, "ENEMY_JUMP_ACROSS | " }, + { MUD_FLAG_ENEMY_JUMP_DOWN, "ENEMY_JUMP_DOWN | " }, + { MUD_FLAG_ENEMY_JUMP_UP, "ENEMY_JUMP_UP | " }, + { MUD_FLAG_ATTACHPOINTS, "ATTACHPOINTS | " }, + { MUD_FLAG_PERCH, "PERCH | " }, + { MUD_FLAG_ROPE, "ROPE | " }, + { MUD_FLAG_WATER, "WATER | " }, + { MUD_FLAG_CLOSEJUMPTARGET, "CLOSEJUMPTARGET | " }, + { MUD_FLAG_LADDER, "LADDER | " }, + { MUD_FLAG_MANTLEDOWN, "MANTLEDOWN | " }, + { MUD_FLAG_ZIPLINE, "ZIPLINE | " }, + { MUD_FLAG_HORIZPOLE, "HORIZPOLE | " }, + { MUD_FLAG_VERTPOLE, "VERTPOLE | " }, + { MUD_FLAG_WALLVERTPOLE, "WALLVERTPOLE | " }, + { MUD_FLAG_ENEMYDROPDOWN, "ENEMYDROPDOWN | " }, + { MUD_FLAG_JUMPLANDING, "JUMPLANDING | " }, + { MUD_FLAG_LEDGEGRAB, "LEDGEGRAB | " }, + { MUD_FLAG_DISABLED, "DISABLED | " } +}; + void Draw::OnMenu() { if (ImGui::BeginMenu("Draw")) @@ -213,6 +240,64 @@ void Draw::DrawEnemyRoute(Instance* instance) void Draw::DrawMarkUp() { + auto manager = MarkupManager::GetInstance(); + auto font = Font::GetMainFont(); + + // Get all markup + for (auto node = manager->m_staticBoxList.next; node->next != nullptr; node = node->next) + { + auto box = (MarkUpBox*)node; + auto markup = box->markup; + + if (!box->markup) + { + continue; + } + + cdc::Vector3 position = { markup->pos[0], markup->pos[1], markup->pos[2] }; + + // Add the instance position + if (box->instance && (box->flags & MUD_FLAG_WATER) == 0) + { + position += &box->instance->position; + } + + TRANS_RotTransPersVectorf(&position, &position); + + // Check if the text is on screen + if (position.z > 16.f) + { + font->SetCursor(position.x, position.y); + font->PrintFormatted(FlagsToString(box->flags).c_str()); + } + + // Draw the poly line + if (markup->polyLine) + { + cdc::Vector3 x, y; + box->GetSegmentPos(&x, 0); + + for (int i = 0; i < box->markup->polyLine->numPoints - 1; i++) + { + box->GetSegmentPos(&y, i + 1); + + DrawLine(&x, &y, RGB(255, 0, 0)); + + x = y; + } + } + + // Draw the bounding box for water and perch markup + if (box->flags & (MUD_FLAG_WATER | MUD_FLAG_PERCH)) + { + auto bbox = markup->bbox; + + auto min = cdc::Vector3{ markup->pos[0] + static_cast(bbox[0]), markup->pos[1] + static_cast(bbox[1]), markup->pos[2] + static_cast(bbox[2]) }; + auto max = cdc::Vector3{ markup->pos[0] + static_cast(bbox[3]), markup->pos[1] + static_cast(bbox[4]), markup->pos[2] + static_cast(bbox[5]) }; + + DrawBoundingBox(&min, &max, RGB(255, 0, 0)); + } + } } void Draw::DrawCollision(Level* level) @@ -322,4 +407,25 @@ void Draw::DrawSignals(Level* level) } } +std::string Draw::FlagsToString(unsigned int flags) +{ + std::string result; + + for (int i = 0; i < sizeof(s_mudFlags) / sizeof(s_mudFlags[0]); i++) + { + if (flags & s_mudFlags[i].first) + { + result += s_mudFlags[i].second; + } + } + + // Somewhat stolen from https://stackoverflow.com/a/37795988/9398242 + if (result.empty()) + result = "NONE"; + else + result.erase(result.end() - 3, result.end()); + + return result; +} + #endif \ No newline at end of file diff --git a/src/modules/Draw.h b/src/modules/Draw.h index ba40ba8..35c1024 100644 --- a/src/modules/Draw.h +++ b/src/modules/Draw.h @@ -40,6 +40,8 @@ class Draw : public Module void DrawPortals(Level* level); void DrawSignals(Level* level); + static std::string FlagsToString(unsigned int flags); + public: void OnMenu(); void OnDraw(); diff --git a/src/render/Draw.cpp b/src/render/Draw.cpp index 3a0446e..770dde3 100644 --- a/src/render/Draw.cpp +++ b/src/render/Draw.cpp @@ -77,6 +77,7 @@ void DrawLine(cdc::Vector3* v0, cdc::Vector3* v1, int color) auto v3 = *v0; v2.z += 50.f; + v3.z += 50.f; v3.x += 50.f; TRANS_TransToDrawVertexV4f(verts, v0); @@ -94,4 +95,32 @@ void DrawLine(cdc::Vector3* v0, cdc::Vector3* v1, int color) verts[5].color = color; DRAW_DrawTriangles(2, 0, verts, 2); +} + +void DrawBoundingBox(cdc::Vector3* v0, cdc::Vector3* v1, int color) +{ + auto a1 = cdc::Vector3{ v0->x, v0->y, v0->z }; + auto a2 = cdc::Vector3{ v1->x, v0->y, v0->z }; + auto a3 = cdc::Vector3{ v1->x, v1->y, v0->z }; + auto a4 = cdc::Vector3{ v0->x, v1->y, v0->z }; + + auto b1 = cdc::Vector3{ v0->x, v0->y, v1->z }; + auto b2 = cdc::Vector3{ v1->x, v0->y, v1->z }; + auto b3 = cdc::Vector3{ v1->x, v1->y, v1->z }; + auto b4 = cdc::Vector3{ v0->x, v1->y, v1->z }; + + DrawLine(&a1, &a2, color); + DrawLine(&a2, &a3, color); + DrawLine(&a3, &a4, color); + DrawLine(&a4, &a1, color); + + DrawLine(&b1, &b2, color); + DrawLine(&b2, &b3, color); + DrawLine(&b3, &b4, color); + DrawLine(&b4, &b1, color); + + DrawLine(&b1, &a1, color); + DrawLine(&b2, &a2, color); + DrawLine(&b3, &a3, color); + DrawLine(&b4, &a4, color); } \ No newline at end of file diff --git a/src/render/Draw.h b/src/render/Draw.h index ab289b9..23f7188 100644 --- a/src/render/Draw.h +++ b/src/render/Draw.h @@ -29,4 +29,5 @@ void DRAW_DrawTriangles(int flags, int tpage, DRAWVERTEX* verts, int numtris); void DrawTriangle(cdc::Vector3* v0, cdc::Vector3* v1, cdc::Vector3* v2, int color); void DrawPlane(cdc::Vector3* v0, cdc::Vector3* v1, int color); -void DrawLine(cdc::Vector3* v0, cdc::Vector3* v1, int color); \ No newline at end of file +void DrawLine(cdc::Vector3* v0, cdc::Vector3* v1, int color); +void DrawBoundingBox(cdc::Vector3* v0, cdc::Vector3* v1, int color); \ No newline at end of file From 2376b0fd8005a9dad442bf6c1845fe24317a9472 Mon Sep 17 00:00:00 2001 From: Indra Date: Thu, 8 Feb 2024 01:34:26 +0100 Subject: [PATCH 46/55] Add enemy route drawing --- src/instance/Enemy.h | 41 ++++++++++++++++++++++++++++++++++++++--- src/modules/Draw.cpp | 15 +++++++++++++++ 2 files changed, 53 insertions(+), 3 deletions(-) diff --git a/src/instance/Enemy.h b/src/instance/Enemy.h index dc340f2..1e87839 100644 --- a/src/instance/Enemy.h +++ b/src/instance/Enemy.h @@ -1,5 +1,37 @@ #pragma once +class EnemyProcess +{ +public: + char pad1[20]; +}; + +#pragma pack(push, 1) +class EnemyRouting : public EnemyProcess +{ +public: + class PolyLinePath + { + public: + cdc::Vector3 m_path[16]; + cdc::Vector3 m_dest; + + char pad1[148]; + + int m_index; + int m_pathlength; + + char pad2[4]; + }; + + char pad1[124]; + + PolyLinePath m_plPath; + + char pad2[272]; +}; +#pragma pack(pop) + class EnemyHealth { public: @@ -7,14 +39,17 @@ class EnemyHealth float m_damageSinceMove; }; +#pragma pack(push, 1) class EnemyData { public: #ifdef TR7 - char pad1[5040]; + char pad1[4192]; #else - char pad1[5280]; + char pad1[4432]; #endif + EnemyRouting m_routing; EnemyHealth m_health; -}; \ No newline at end of file +}; +#pragma pack(pop) \ No newline at end of file diff --git a/src/modules/Draw.cpp b/src/modules/Draw.cpp index 7512a0b..64014e9 100644 --- a/src/modules/Draw.cpp +++ b/src/modules/Draw.cpp @@ -236,6 +236,21 @@ void Draw::DrawEnemyRoute(Instance* instance) { return; } + + // Get the enemy routing process + auto extraData = (EnemyData*)instance->extraData; + auto path = extraData->m_routing.m_plPath; + + // Draw the path + auto x = &path.m_path[0]; + for (int i = 0; i < path.m_pathlength - 1; i++) + { + auto y = &path.m_path[i + 1]; + + DrawLine(x, y, RGB(255, 0, 0)); + + x = y; + } } void Draw::DrawMarkUp() From f01661db4af6538a5fae48feae87c7e3de857da5 Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 10 Feb 2024 19:33:05 +0100 Subject: [PATCH 47/55] Add more patches --- src/modules/Patches.cpp | 42 +++++++++++++++++++++++++++++++++++++++++ src/modules/Patches.h | 3 +++ src/modules/Render.cpp | 33 ++++++++++++++++++++++++++++++++ src/modules/Render.h | 2 ++ 4 files changed, 80 insertions(+) diff --git a/src/modules/Patches.cpp b/src/modules/Patches.cpp index b6689ba..bb0df54 100644 --- a/src/modules/Patches.cpp +++ b/src/modules/Patches.cpp @@ -1,15 +1,57 @@ #include +#include #include "Patches.h" #include "util/Hooking.h" +#include "game/Game.h" + +// Instance of patches so we can get it in our hooks without calling GetModule +static Patches* s_patches; + +// Original functions +static void(*s_RenderG2_MotionBlur)(int, int, int); +static void(*s_GAMELOOP_HandleScreenWipes)(); + +static void RenderG2_MotionBlur(int blur, int time, int timeout) +{ + if (s_patches->IsNoMotionBlur()) + { + s_RenderG2_MotionBlur(0, 0, 0); + } + + s_RenderG2_MotionBlur(blur, time, timeout); +} + +static void GAMELOOP_HandleScreenWipes() +{ + if (s_patches->IsNoCinematicBars()) + { + auto gameTracker = Game::GetGameTracker(); + + // Set the bars wipe to 0, this is actually better than our previous approach + // where we patched the code for wipe 11, since it was shared with wipe 6 + auto wipe = &gameTracker->wipes[11]; + wipe->wipeCur = 0.f; + wipe->wipeTarget = 0.f; + } + + s_GAMELOOP_HandleScreenWipes(); +} Patches::Patches() { + s_patches = this; + #ifndef TR8 if (m_disableIntro.GetValue()) { RemoveIntro(); } + + // Insert hooks + MH_CreateHook((void*)GET_ADDRESS(0x40CA80, 0x43AB40, 0x000000), RenderG2_MotionBlur, (void**)&s_RenderG2_MotionBlur); + MH_CreateHook((void*)GET_ADDRESS(0x450430, 0x452A90, 0x000000), GAMELOOP_HandleScreenWipes, (void**)&s_GAMELOOP_HandleScreenWipes); + MH_EnableHook(MH_ALL_HOOKS); #endif } diff --git a/src/modules/Patches.h b/src/modules/Patches.h index 993348a..0bdc687 100644 --- a/src/modules/Patches.h +++ b/src/modules/Patches.h @@ -15,5 +15,8 @@ class Patches : public Module public: Patches(); + bool IsNoMotionBlur() { return m_noMotionBlur.GetValue(); } + bool IsNoCinematicBars() { return m_noCinematicBars.GetValue(); } + void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); }; \ No newline at end of file diff --git a/src/modules/Render.cpp b/src/modules/Render.cpp index 8dcc98b..8c77386 100644 --- a/src/modules/Render.cpp +++ b/src/modules/Render.cpp @@ -1,14 +1,47 @@ #include +#include #include "Render.h" #include "util/Hooking.h" +static bool s_terrainWireframe = false; + +// Code for terrain wireframe +class TerrainDrawable +{ +public: + char pad1[28]; + unsigned int m_tpageid; +}; + +static TerrainDrawable* (__thiscall* s_TerrainDrawable_TerrainDrawable)(TerrainDrawable*, void*, void*, void*, void*); + +static TerrainDrawable* __fastcall TerrainDrawable_TerrainDrawable(TerrainDrawable* drawable, void*, void* material, void* textureStrip, void* terrain, void* terrainGroup) +{ + auto ret = s_TerrainDrawable_TerrainDrawable(drawable, material, textureStrip, terrain, terrainGroup); + + if (s_terrainWireframe) + { + drawable->m_tpageid |= 0x800; + } + + return ret; +} + +Render::Render() +{ + // Insert terrain wireframe hook + MH_CreateHook((void*)GET_ADDRESS(0x40ACF0, 0x40B9B0, 0x000000), TerrainDrawable_TerrainDrawable, (void**)&s_TerrainDrawable_TerrainDrawable); + MH_EnableHook(MH_ALL_HOOKS); +} + void Render::OnMenu() { if (ImGui::BeginMenu("Render")) { // TODO Wireframe for Underworld ImGui::MenuItem("Wireframe", nullptr, (bool*)GET_ADDRESS(0x1075BD4, 0x7C7CD4, 0x000000)); + ImGui::MenuItem("Terrain wireframe", nullptr, &s_terrainWireframe); ImGui::EndMenu(); } diff --git a/src/modules/Render.h b/src/modules/Render.h index 84bfdf8..2860bc5 100644 --- a/src/modules/Render.h +++ b/src/modules/Render.h @@ -5,5 +5,7 @@ class Render : public Module { public: + Render(); + void OnMenu(); }; \ No newline at end of file From ce08bc0f095928efda6022ef1cfa027f2b287946 Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 10 Feb 2024 21:43:14 +0100 Subject: [PATCH 48/55] Fix TR8 build --- src/modules/Patches.cpp | 2 ++ src/modules/ScriptLog.cpp | 17 +++++++++++++---- 2 files changed, 15 insertions(+), 4 deletions(-) diff --git a/src/modules/Patches.cpp b/src/modules/Patches.cpp index bb0df54..d61c799 100644 --- a/src/modules/Patches.cpp +++ b/src/modules/Patches.cpp @@ -8,6 +8,7 @@ // Instance of patches so we can get it in our hooks without calling GetModule static Patches* s_patches; +#ifndef TR8 // Original functions static void(*s_RenderG2_MotionBlur)(int, int, int); static void(*s_GAMELOOP_HandleScreenWipes)(); @@ -37,6 +38,7 @@ static void GAMELOOP_HandleScreenWipes() s_GAMELOOP_HandleScreenWipes(); } +#endif Patches::Patches() { diff --git a/src/modules/ScriptLog.cpp b/src/modules/ScriptLog.cpp index 96fb718..863d267 100644 --- a/src/modules/ScriptLog.cpp +++ b/src/modules/ScriptLog.cpp @@ -6,6 +6,15 @@ #include "Log.h" #include "Hook.h" +namespace cdc +{ + union DataValue + { + const char* stringValue; + char boolValue; + }; +} + static void LogValue(const char* fmt, std::any value, bool newLine) { auto log = Hook::GetInstance().GetModule(); @@ -39,14 +48,14 @@ void __stdcall ScriptLogFloat(float value, bool appendNewLine) LogValue("%f", value, appendNewLine); } -void __cdecl NsCoreBase_PrintString(int instance, int numArgs, void** args) +void __cdecl NsCoreBase_PrintString(int instance, int numArgs, cdc::DataValue* args) { - LogValue("%s", args[0], (__int8)args[1] != 0); + LogValue("%s", args[0].stringValue, args[1].boolValue); } -void __cdecl NsCoreBase_LogString(int instance, int numArgs, void** args) +void __cdecl NsCoreBase_LogString(int instance, int numArgs, cdc::DataValue* args) { - LogValue("%s", args[0], (__int8)args[1] != 0); + LogValue("%s", args[0].stringValue, args[1].boolValue); } ScriptLog::ScriptLog() From 0916b4d921dc95b6b7358043958173a4f781b08e Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 11 Feb 2024 16:23:11 +0100 Subject: [PATCH 49/55] Fix auto scroll and duplicate text --- src/modules/Instance.cpp | 1 - src/modules/Log.cpp | 7 ++++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/src/modules/Instance.cpp b/src/modules/Instance.cpp index 540e77b..2c93c17 100644 --- a/src/modules/Instance.cpp +++ b/src/modules/Instance.cpp @@ -79,7 +79,6 @@ void InstanceModule::DrawInstance() auto position = instance->position; auto rotation = instance->rotation; - ImGui::Text("Intro: %d", instance->introUniqueID); ImGui::Text("Position: %f %f %f", position.x, position.y, position.z); ImGui::Text("Rotation: %f %f %f", rotation.x, rotation.y, rotation.z); ImGui::Text("Intro: %d", instance->introUniqueID); diff --git a/src/modules/Log.cpp b/src/modules/Log.cpp index 276cd73..3021574 100644 --- a/src/modules/Log.cpp +++ b/src/modules/Log.cpp @@ -25,7 +25,12 @@ void Log::OnDraw() ImGui::BeginChild("Log"); ImGui::TextUnformatted(m_buffer.begin()); - ImGui::SetScrollHereY(1.f); + + // Auto scroll to the bottom when already at the bottom + if (ImGui::GetScrollY() >= ImGui::GetScrollMaxY()) + { + ImGui::SetScrollHereY(1.f); + } ImGui::EndChild(); ImGui::End(); From 3a1a0577d82398d9f8d104ec0d9f833740f4ca17 Mon Sep 17 00:00:00 2001 From: Indra Date: Sun, 11 Feb 2024 23:58:55 +0100 Subject: [PATCH 50/55] Add more menu features --- src/game/Game.cpp | 12 ++++++++++++ src/game/Game.h | 5 ++++- src/game/Player.cpp | 9 +++++++++ src/game/Player.h | 36 ++++++++++++++++++++++++++++++++++++ src/modules/Instance.cpp | 15 +++++++++++++++ src/modules/Instance.h | 1 + src/modules/MainMenu.cpp | 17 ++++++++++++++--- 7 files changed, 91 insertions(+), 4 deletions(-) create mode 100644 src/game/Player.cpp create mode 100644 src/game/Player.h diff --git a/src/game/Game.cpp b/src/game/Game.cpp index ed449f3..f41bc62 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -36,4 +36,16 @@ int OBTABLE_GetObjectID(char* name) auto addr = GET_ADDRESS(0x462590, 0x4660C0, 0x5BF770); return Hooking::CallReturn(addr, name); +} + +void LOAD_ObjectFileName(char* name, char* object, char* extension) +{ + return LOAD_UnitFileName(name, object, extension); +} + +void LOAD_UnitFileName(char* name, char* unit, char* extension) +{ + auto addr = GET_ADDRESS(0x45C730, 0x45F650, 0x477970); + + return Hooking::Call(addr, name, unit, extension); } \ No newline at end of file diff --git a/src/game/Game.h b/src/game/Game.h index 4a82b47..73e2447 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -166,4 +166,7 @@ class Game void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType); void PLAYER_DebugSwitchPlayerCharacter(); -int OBTABLE_GetObjectID(char* name); \ No newline at end of file +int OBTABLE_GetObjectID(char* name); + +void LOAD_ObjectFileName(char* name, char* object, char* extension); +void LOAD_UnitFileName(char* name, char* unit, char* extension); \ No newline at end of file diff --git a/src/game/Player.cpp b/src/game/Player.cpp new file mode 100644 index 0000000..08eca89 --- /dev/null +++ b/src/game/Player.cpp @@ -0,0 +1,9 @@ +#include "Game.h" +#include "util/Hooking.h" + +void IncrHealth(float amount) +{ + auto addr = GET_ADDRESS(0x576CA0, 0x5715E0, 0x000000); + + Hooking::Call(addr, amount); +} \ No newline at end of file diff --git a/src/game/Player.h b/src/game/Player.h new file mode 100644 index 0000000..e683dfd --- /dev/null +++ b/src/game/Player.h @@ -0,0 +1,36 @@ +#pragma once + +struct TuneData; + +struct GeneralAttributes +{ + float height; + float crouchHeight; + float ankleUpperOffset; + float ankleLowerOffset; + float ankleLength; + float mass; + float leftFootLockHipLimitMin; + float leftFootLockHipLimitMax; + float rightFootLockHipLimitMin; + float rightFootLockHipLimitMax; +}; + +struct OldRazielData +{ + char pad1[116]; + + float HealthInitial; + float LowHealth; +}; + +struct PlayerProp +{ + unsigned int playerVersion; + + TuneData* tuneData; + GeneralAttributes general; + OldRazielData oldData; +}; + +void IncrHealth(float amount); \ No newline at end of file diff --git a/src/modules/Instance.cpp b/src/modules/Instance.cpp index 2c93c17..dc9388c 100644 --- a/src/modules/Instance.cpp +++ b/src/modules/Instance.cpp @@ -12,6 +12,13 @@ void InstanceModule::OnMenu() { ImGui::MenuItem("Instances", nullptr, &m_show); +#ifdef TR8 + if (ImGui::MenuItem("Unhide all")) + { + UnhideAll(); + } +#endif + ImGui::EndMenu(); } } @@ -219,6 +226,14 @@ void InstanceModule::SkewTo(Instance* instance) player->position = instance->position; } +void InstanceModule::UnhideAll() +{ + Instances::Iterate([](Instance* instance) + { + INSTANCE_Post(instance, 7, 0); + }); +} + std::string InstanceModule::GetBinary(int value) { std::string binary; diff --git a/src/modules/Instance.h b/src/modules/Instance.h index 5a54d12..da73702 100644 --- a/src/modules/Instance.h +++ b/src/modules/Instance.h @@ -15,6 +15,7 @@ class InstanceModule : public Module void DrawInstance(); void SkewTo(Instance* instance); + void UnhideAll(); std::string GetBinary(int value); diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index 52c0a33..44e8611 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -7,6 +7,7 @@ #include "render/Font.h" #include "level/Event.h" #include "input/Input.h" +#include "game/Player.h" void MainMenu::OnDraw() { @@ -30,9 +31,20 @@ void MainMenu::OnDraw() BirthObject(object); } +#ifndef TR8 // Player if (ImGui::CollapsingHeader("Player")) { + // Fill 'er up + if (ImGui::Button("Fill 'er up")) + { + auto player = Game::GetPlayerInstance(); + auto data = (PlayerProp*)player->data; + + // TODO fill up ammo + IncrHealth(data->oldData.HealthInitial); + } + // Switch outfit static char outfit[64] = ""; ImGui::InputText("Outfit", outfit, sizeof(outfit)); @@ -49,7 +61,6 @@ void MainMenu::OnDraw() SwitchPlayerCharacter(); } -#ifndef TR8 // Player flags auto flags = (unsigned int*)GET_ADDRESS(0x1075B88, 0x7C7C78, 0x000000); @@ -65,8 +76,8 @@ void MainMenu::OnDraw() *flags &= ~0x80; } } -#endif } +#endif // Time if (ImGui::CollapsingHeader("Time")) @@ -81,8 +92,8 @@ void MainMenu::OnDraw() } } - // Save #ifndef TR8 + // Save if (ImGui::CollapsingHeader("Save")) { static int var = 0; From e0c86f323b0d6ce9f71f9e9ceacf52d8d9872327 Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 17 Feb 2024 00:50:16 +0100 Subject: [PATCH 51/55] Add back event debug draw --- src/Hook.cpp | 8 ++++ src/modules/Debug.cpp | 101 ++++++++++++++++++++++++++++++++++++++++++ src/modules/Debug.h | 19 ++++++++ src/modules/Module.h | 3 ++ src/render/Font.cpp | 17 ++++++- src/render/Font.h | 5 ++- 6 files changed, 151 insertions(+), 2 deletions(-) create mode 100644 src/modules/Debug.cpp create mode 100644 src/modules/Debug.h diff --git a/src/Hook.cpp b/src/Hook.cpp index 349c95f..e14c2c5 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -21,6 +21,7 @@ #include "modules/Patches.h" #include "modules/Frontend.h" #include "modules/Draw.h" +#include "modules/Debug.h" #include "modules/camera/FreeCamera.h" #include "cdc/render/PCDeviceManager.h" @@ -77,6 +78,12 @@ void Hook::PostInitialize() #endif GameLoop::OnLoop(std::bind(&Hook::OnLoop, this)); + + // Post initialize all modules + for (auto& [hash, mod] : m_modules) + { + mod->OnPostInitialize(); + } } void Hook::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) @@ -140,6 +147,7 @@ void Hook::RegisterModules() RegisterModule(); RegisterModule(); RegisterModule(); + RegisterModule(); #else RegisterModule(); #endif diff --git a/src/modules/Debug.cpp b/src/modules/Debug.cpp new file mode 100644 index 0000000..2457b99 --- /dev/null +++ b/src/modules/Debug.cpp @@ -0,0 +1,101 @@ +#include + +#include "Debug.h" +#include "Hook.h" +#include "modules/Log.h" +#include "render/Font.h" +#include "level/Event.h" +#include "util/Hooking.h" +#include "game/Game.h" + +static Debug* s_debug; + +// Event debug print hooks +void EVENT_DisplayString(char* str, int time) +{ + Hook::GetInstance().GetModule()->WriteLine("%s", str); +} + +void EVENT_DisplayStringXY(char* str, int time, int x, int y) +{ + if (s_debug->IsDrawDebug()) + { + auto font = Font::GetMainFont(); + + font->SetCursor(static_cast(x), static_cast(y)); + font->PrintFormatted(str); + } +} + +void EVENT_PrintScalarExpression(int expval, int time) +{ + if (s_debug->IsDrawDebug()) + { + auto font = Font::GetMainFont(); + + font->Print("%d", expval); + } +} + +void EVENT_Trace(char* str, int level) +{ + Hook::GetInstance().GetModule()->WriteLine("%s", str); +} + +Debug::Debug() +{ + s_debug = this; +} + +#define OVERRIDE_FUNC(offset, func) *(void**)((char*)globalData + offset) = (void*)func + +void Debug::Initialize() +{ + // Override all nullsubs in the event functions structure + auto globalData = (GlobalData*)GET_ADDRESS(0x1076980, 0x7C8A50, 0x000000); + +#ifdef TR7 + OVERRIDE_FUNC(304, EVENT_DisplayString); + OVERRIDE_FUNC(444, FONT_Print); + OVERRIDE_FUNC(504, EVENT_DisplayString); + OVERRIDE_FUNC(604, EVENT_Trace); + OVERRIDE_FUNC(1232, EVENT_PrintScalarExpression); + OVERRIDE_FUNC(1336, EVENT_DisplayStringXY); +#else + OVERRIDE_FUNC(304, EVENT_DisplayString); + OVERRIDE_FUNC(464, FONT_Print); + OVERRIDE_FUNC(528, EVENT_DisplayString); + OVERRIDE_FUNC(628, EVENT_Trace); + OVERRIDE_FUNC(1292, EVENT_PrintScalarExpression); + OVERRIDE_FUNC(1400, EVENT_DisplayStringXY); +#endif +} + +void Debug::OnPostInitialize() +{ + Initialize(); +} + +void Debug::OnMenu() +{ + if (ImGui::BeginMenu("Debug")) + { + if (ImGui::MenuItem("Draw debug", nullptr, &m_drawDebug)) + { + auto gameTracker = Game::GetGameTracker(); + + if (m_drawDebug) + { + gameTracker->debugFlags2 |= 0x100; + } + else + { + gameTracker->debugFlags2 &= ~0x100; + } + } + + ImGui::MenuItem("Debug keypad", nullptr, (bool*)GET_ADDRESS(0x107696C, 0x7C8A3C, 0x000000)); + + ImGui::EndMenu(); + } +} \ No newline at end of file diff --git a/src/modules/Debug.h b/src/modules/Debug.h new file mode 100644 index 0000000..5474db6 --- /dev/null +++ b/src/modules/Debug.h @@ -0,0 +1,19 @@ +#pragma once + +#include "Module.h" + +class Debug : public Module +{ +private: + bool m_drawDebug = false; + + void Initialize(); + +public: + Debug(); + + bool IsDrawDebug() { return m_drawDebug; } + + void OnPostInitialize(); + void OnMenu(); +}; \ No newline at end of file diff --git a/src/modules/Module.h b/src/modules/Module.h index 39e7295..e7d0e6b 100644 --- a/src/modules/Module.h +++ b/src/modules/Module.h @@ -5,6 +5,9 @@ class Module { public: + // Called on post initialization after the device has been obtained + virtual void OnPostInitialize() { }; + // Called during drawing of the main menu bar, can be used to add menu items virtual void OnMenu() { }; diff --git a/src/render/Font.cpp b/src/render/Font.cpp index e80fda5..cf0201f 100644 --- a/src/render/Font.cpp +++ b/src/render/Font.cpp @@ -50,8 +50,13 @@ void Font::Print(const char* fmt, ...) va_list va; va_start(va, fmt); - vsprintf_s(s_formatted, fmt, va); + PrintV(fmt, va); va_end(va); +} + +void Font::PrintV(const char* fmt, va_list va) +{ + vsprintf_s(s_formatted, fmt, va); PrintFormatted(s_formatted); } @@ -106,4 +111,14 @@ void Font::OnFlush(std::function callback) } s_callback = callback; +} + +void FONT_Print(char* fmt, ...) +{ + auto font = Font::GetMainFont(); + + va_list va; + va_start(va, fmt); + font->PrintV(fmt, va); + va_end(va); } \ No newline at end of file diff --git a/src/render/Font.h b/src/render/Font.h index 88e3e9b..aa18e43 100644 --- a/src/render/Font.h +++ b/src/render/Font.h @@ -15,6 +15,7 @@ class Font static void SetScale(float scaleX, float scaleY); void Print(const char* fmt, ...); + void PrintV(const char* fmt, va_list va); void PrintCentered(const char* fmt, ...); void PrintFormatted(const char* formatted, int backdrop = 0); @@ -22,4 +23,6 @@ class Font float GetHeight(); static void OnFlush(std::function callback); -}; \ No newline at end of file +}; + +void FONT_Print(char* fmt, ...); \ No newline at end of file From 9ae57edac8ee9f955f953969a1207532727ee741 Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 17 Feb 2024 01:53:32 +0100 Subject: [PATCH 52/55] Add no death fade --- src/game/Game.cpp | 14 +++++++++++++ src/game/Game.h | 2 ++ src/modules/MainMenu.cpp | 6 ++++-- src/modules/MainMenu.h | 3 +++ src/modules/Patches.cpp | 43 +++++++++++++++++++++++++++++++++++++++- 5 files changed, 65 insertions(+), 3 deletions(-) diff --git a/src/game/Game.cpp b/src/game/Game.cpp index f41bc62..d8eab6e 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -24,6 +24,20 @@ void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int Hooking::Call(addr, name, gameTracker, doneType); } +bool GAMELOOP_IsWipeDone(int type) +{ + auto addr = GET_ADDRESS(0x450310, 0x452970, 0x52E880); + + return Hooking::CallReturn(addr, type); +} + +void GAMELOOP_SetScreenWipe(int type, int target, int time) +{ + auto addr = GET_ADDRESS(0x4503D0, 0x452A30, 0x52E8B0); + + Hooking::Call(addr, type, target, time); +} + void PLAYER_DebugSwitchPlayerCharacter() { auto addr = GET_ADDRESS(0x5A40B0, 0x5A39A0, 0x79DB50); diff --git a/src/game/Game.h b/src/game/Game.h index 73e2447..e9f777a 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -164,6 +164,8 @@ class Game }; void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType); +bool GAMELOOP_IsWipeDone(int type); +void GAMELOOP_SetScreenWipe(int type, int target, int time); void PLAYER_DebugSwitchPlayerCharacter(); int OBTABLE_GetObjectID(char* name); diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index 44e8611..c97ef41 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -31,10 +31,10 @@ void MainMenu::OnDraw() BirthObject(object); } -#ifndef TR8 // Player if (ImGui::CollapsingHeader("Player")) { +#ifndef TR8 // Fill 'er up if (ImGui::Button("Fill 'er up")) { @@ -76,9 +76,11 @@ void MainMenu::OnDraw() *flags &= ~0x80; } } - } #endif + ImGui::Checkbox("No death fade", &m_noDeathFade); + } + // Time if (ImGui::CollapsingHeader("Time")) { diff --git a/src/modules/MainMenu.h b/src/modules/MainMenu.h index 0efb8d2..cef36a5 100644 --- a/src/modules/MainMenu.h +++ b/src/modules/MainMenu.h @@ -7,6 +7,7 @@ class MainMenu : public Module { private: bool m_switchPlayerNextFrame = false; + bool m_noDeathFade = false; Option m_noWatermark{ "NoWatermark", false }; @@ -14,6 +15,8 @@ class MainMenu : public Module void SwitchPlayerCharacter(char* name = nullptr); public: + bool IsNoDeathFade() { return m_noDeathFade; } + void OnDraw(); void OnFrame(); void OnLoop(); diff --git a/src/modules/Patches.cpp b/src/modules/Patches.cpp index d61c799..9ef6ee8 100644 --- a/src/modules/Patches.cpp +++ b/src/modules/Patches.cpp @@ -4,9 +4,11 @@ #include "Patches.h" #include "util/Hooking.h" #include "game/Game.h" +#include "MainMenu.h" // Instance of patches so we can get it in our hooks without calling GetModule static Patches* s_patches; +static MainMenu* s_menu; #ifndef TR8 // Original functions @@ -40,9 +42,40 @@ static void GAMELOOP_HandleScreenWipes() } #endif +// No death fade code +int(__stdcall* s_DeathState_Entry)(int player, int data); +void(__stdcall* s_DeathState_Process)(int player, int data); + +static int __stdcall DeathState_Entry(int player, int data) +{ + auto ret = s_DeathState_Entry(player, data); + + if (!s_menu->IsNoDeathFade()) + { + // We NOPed the original code, so we call the wipe manually + // like the game code does + if (GAMELOOP_IsWipeDone(10)) + { + // TODO refactor, 90 is not a constant value in the game code + GAMELOOP_SetScreenWipe(10, 100, 90); + } + } + + return ret; +} + +static void __stdcall DeathState_Process(int player, int data) +{ + if (!s_menu->IsNoDeathFade()) + { + s_DeathState_Process(player, data); + } +} + Patches::Patches() { s_patches = this; + s_menu = Hook::GetInstance().GetModule().get(); #ifndef TR8 if (m_disableIntro.GetValue()) @@ -53,8 +86,16 @@ Patches::Patches() // Insert hooks MH_CreateHook((void*)GET_ADDRESS(0x40CA80, 0x43AB40, 0x000000), RenderG2_MotionBlur, (void**)&s_RenderG2_MotionBlur); MH_CreateHook((void*)GET_ADDRESS(0x450430, 0x452A90, 0x000000), GAMELOOP_HandleScreenWipes, (void**)&s_GAMELOOP_HandleScreenWipes); - MH_EnableHook(MH_ALL_HOOKS); #endif + + // Insert DeathState hooks + MH_CreateHook((void*)GET_ADDRESS(0x55DEC0, 0x5581D0, 0x75AA50), DeathState_Entry, (void**)&s_DeathState_Entry); + MH_CreateHook((void*)GET_ADDRESS(0x56EC70, 0x5699C0, 0x75AF90), DeathState_Process, (void**)&s_DeathState_Process); + + // NOP the original death wipe code in DeathState::Entry + Hooking::Nop((void*)GET_ADDRESS(0x55E188, 0x5584DC, 0x75AEDE), 5); + + MH_EnableHook(MH_ALL_HOOKS); } void Patches::RemoveIntro() From 77a85c5dd2aa8b5b925ed201b62d8bb08cbb195c Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 17 Feb 2024 02:47:33 +0100 Subject: [PATCH 53/55] Make class functions const/noexpect --- data/config.json | 10 ---------- src/Hook.cpp | 2 +- src/Hook.h | 6 +++--- src/Options.cpp | 8 ++++---- src/Options.h | 26 +++++++++++++------------- src/game/Game.cpp | 6 +++--- src/game/Game.h | 6 +++--- src/game/GameLoop.cpp | 2 +- src/game/GameLoop.h | 2 +- src/input/Input.cpp | 12 ++++++------ src/input/Input.h | 12 ++++++------ src/input/MessageHook.cpp | 2 +- src/input/MessageHook.h | 2 +- src/input/MouseHook.cpp | 4 ++-- src/input/MouseHook.h | 4 ++-- src/instance/Instances.cpp | 2 +- src/instance/Instances.h | 2 +- src/level/Markup.cpp | 4 ++-- src/level/Markup.h | 4 ++-- src/menu/Menu.cpp | 8 ++++---- src/menu/Menu.h | 8 ++++---- src/render/Font.cpp | 22 +++++++++++----------- src/render/Font.h | 22 +++++++++++----------- src/render/RenderContext.cpp | 2 +- src/render/RenderContext.h | 2 +- src/util/Hooking.cpp | 6 +++--- src/util/Hooking.h | 14 +++++++------- 27 files changed, 95 insertions(+), 105 deletions(-) delete mode 100644 data/config.json diff --git a/data/config.json b/data/config.json deleted file mode 100644 index 27c6a6b..0000000 --- a/data/config.json +++ /dev/null @@ -1,10 +0,0 @@ -{ - // Removes the ~5 seconds legal screen while starting the game (Legend, Anniversary). - "remove_legal_screen": true, - - // Removes the "Are you sure you want to quit?" message while pressing Alt+F4 - "remove_quit_message": true, - - // Doesn't show the menu automatically while starting the game - "hide_menu_on_start": false -} diff --git a/src/Hook.cpp b/src/Hook.cpp index e14c2c5..08c847b 100644 --- a/src/Hook.cpp +++ b/src/Hook.cpp @@ -153,7 +153,7 @@ void Hook::RegisterModules() #endif } -Hook& Hook::GetInstance() +Hook& Hook::GetInstance() noexcept { static Hook instance; return instance; diff --git a/src/Hook.h b/src/Hook.h index c7c015d..c3c94be 100644 --- a/src/Hook.h +++ b/src/Hook.h @@ -31,18 +31,18 @@ class Hook // These need to be defined here, else the linker becomes angry // Gets all modules - const auto& GetModules() + const auto& GetModules() const noexcept { return m_modules; } // Get a module by T template - std::shared_ptr GetModule() + std::shared_ptr GetModule() const { auto it = m_modules.find(typeid(T).hash_code()); return it != m_modules.end() ? std::dynamic_pointer_cast(it->second) : nullptr; } - static Hook& GetInstance(); + static Hook& GetInstance() noexcept; }; \ No newline at end of file diff --git a/src/Options.cpp b/src/Options.cpp index 498a1d6..96519e2 100644 --- a/src/Options.cpp +++ b/src/Options.cpp @@ -19,7 +19,7 @@ void Options::OnDraw() } } -void Options::DrawOptions() +void Options::DrawOptions() noexcept { ImGui::Begin("Options", &m_show); @@ -46,7 +46,7 @@ void Options::DrawOptions() ImGui::End(); } -void Options::DrawOption(const char* name, const char* description, float min, float max) +void Options::DrawOption(const char* name, const char* description, float min, float max) const noexcept { // TODO use a map for better performance for (auto& option : m_options) @@ -58,7 +58,7 @@ void Options::DrawOption(const char* name, const char* description, float min, f } } -void Options::DrawOption(BaseOption* option, const char* description, float min, float max) +void Options::DrawOption(BaseOption* option, const char* description, float min, float max) const noexcept { auto type = option->GetType(); @@ -79,7 +79,7 @@ void Options::DrawOption(BaseOption* option, const char* description, float min, } } -void Options::Show() +void Options::Show() noexcept { m_show = true; } diff --git a/src/Options.h b/src/Options.h index dc4ecd4..282749e 100644 --- a/src/Options.h +++ b/src/Options.h @@ -19,7 +19,7 @@ class Option : public BaseOption } // Loads the value from the registry - void LoadValue() + void LoadValue() noexcept { HKEY hKey; if (RegOpenKeyA(HKEY_CURRENT_USER, "SOFTWARE\\TRAE-Menu-Hook", &hKey) == ERROR_SUCCESS) @@ -32,7 +32,7 @@ class Option : public BaseOption } // Saves the current value to the registry - void SaveValue() + void SaveValue() const noexcept { HKEY hKey; if (RegCreateKeyA(HKEY_CURRENT_USER, "SOFTWARE\\TRAE-Menu-Hook", &hKey) == ERROR_SUCCESS) @@ -43,19 +43,19 @@ class Option : public BaseOption } // Gets the type hash - size_t GetType() + size_t GetType() const noexcept { return typeid(T).hash_code(); } // Gets a pointer to the value - void* GetValuePtr() + void* GetValuePtr() noexcept { return &m_value; } // Gets the value - T GetValue() + T GetValue() const noexcept { return m_value; } @@ -76,10 +76,10 @@ class BaseOption } // Methods to be implemented by Option - virtual void LoadValue() = 0; - virtual void SaveValue() = 0; - virtual size_t GetType() = 0; - virtual void* GetValuePtr() = 0; + virtual void LoadValue() noexcept = 0; + virtual void SaveValue() const noexcept = 0; + virtual size_t GetType() const noexcept = 0; + virtual void* GetValuePtr() noexcept = 0; }; class Options : public Module @@ -88,14 +88,14 @@ class Options : public Module std::vector m_options; bool m_show = false; - void DrawOptions(); - void DrawOption(const char* name, const char* description, float min = 0.f, float max = 0.f); - void DrawOption(BaseOption* option, const char* description, float min, float max); + void DrawOptions() noexcept; + void DrawOption(const char* name, const char* description, float min = 0.f, float max = 0.f) const noexcept; + void DrawOption(BaseOption* option, const char* description, float min, float max) const noexcept; public: Options(); void OnDraw(); - void Show(); + void Show() noexcept; void AddOption(BaseOption* option); }; \ No newline at end of file diff --git a/src/game/Game.cpp b/src/game/Game.cpp index d8eab6e..d541960 100644 --- a/src/game/Game.cpp +++ b/src/game/Game.cpp @@ -2,17 +2,17 @@ #include "util/Hooking.h" -Instance* Game::GetPlayerInstance() +Instance* Game::GetPlayerInstance() noexcept { return GetGameTracker()->playerInstance; } -GameTracker* Game::GetGameTracker() +GameTracker* Game::GetGameTracker() noexcept { return (GameTracker*)GET_ADDRESS(0x10E5370, 0x838330, 0x00E7F088); } -STracker* Game::GetStreamTracker() +STracker* Game::GetStreamTracker() noexcept { return (STracker*)GET_ADDRESS(0x11582F8, 0x8AE378, 0xDBAB40); } diff --git a/src/game/Game.h b/src/game/Game.h index e9f777a..ca239c0 100644 --- a/src/game/Game.h +++ b/src/game/Game.h @@ -158,9 +158,9 @@ struct GameTracker class Game { public: - static Instance* GetPlayerInstance(); - static GameTracker* GetGameTracker(); - static STracker* GetStreamTracker(); + static Instance* GetPlayerInstance() noexcept; + static GameTracker* GetGameTracker() noexcept; + static STracker* GetStreamTracker() noexcept; }; void GAMELOOP_RequestLevelChangeByName(char* name, GameTracker* gameTracker, int doneType); diff --git a/src/game/GameLoop.cpp b/src/game/GameLoop.cpp index 8c870a9..d778787 100644 --- a/src/game/GameLoop.cpp +++ b/src/game/GameLoop.cpp @@ -13,7 +13,7 @@ static void GAMELOOP_Process(GameTracker* gameTracker) s_GAMELOOP_Process(gameTracker); } -void GameLoop::OnLoop(std::function callback) +void GameLoop::OnLoop(std::function callback) noexcept { if (!s_callback) { diff --git a/src/game/GameLoop.h b/src/game/GameLoop.h index 8302866..ca96720 100644 --- a/src/game/GameLoop.h +++ b/src/game/GameLoop.h @@ -5,5 +5,5 @@ class GameLoop { public: - static void OnLoop(std::function callback); + static void OnLoop(std::function callback) noexcept; }; \ No newline at end of file diff --git a/src/input/Input.cpp b/src/input/Input.cpp index ba6185a..a8ca7f3 100644 --- a/src/input/Input.cpp +++ b/src/input/Input.cpp @@ -2,34 +2,34 @@ #include "util/Hooking.h" -void Input::DisableInput(bool disable) +void Input::DisableInput(bool disable) noexcept { *(bool*)GET_ADDRESS(0x1101689, 0x8551A9, 0xA02B79) = disable; } -void Input::DisablePlayerControl(bool disable) +void Input::DisablePlayerControl(bool disable) noexcept { *(bool*)GET_ADDRESS(0xF15AB4, 0x666C34, 0x8AB4E6) = !disable; } -bool Input::IsPlayerControlEnabled() +bool Input::IsPlayerControlEnabled() noexcept { return *(bool*)GET_ADDRESS(0xF15AB4, 0x666C34, 0x8AB4E6); } -bool Input::IsInputActionPressed(int action) +bool Input::IsInputActionPressed(int action) noexcept { auto inputSystem = GetInputSystem(); return (inputSystem->m_pActionMapper->m_pActionResults[action].state & 4) == 4; } -InputSystem* Input::GetInputSystem() +InputSystem* Input::GetInputSystem() noexcept { return *(InputSystem**)GET_ADDRESS(0x1101680, 0x8551A0, 0xA02B68); } -float InputSystem::GetAxisValue(int axisID) +float InputSystem::GetAxisValue(int axisID) const noexcept { auto addr = GET_ADDRESS(0x4E3FC0, 0x4E38C0, 0x480E50); diff --git a/src/input/Input.h b/src/input/Input.h index 398468d..4766f43 100644 --- a/src/input/Input.h +++ b/src/input/Input.h @@ -87,16 +87,16 @@ class InputSystem InputActionMapper* m_pActionMapper; public: - float GetAxisValue(int axisID); + float GetAxisValue(int axisID) const noexcept; }; class Input { public: - static void DisableInput(bool disable); - static void DisablePlayerControl(bool disable); - static bool IsPlayerControlEnabled(); - static bool IsInputActionPressed(int action); + static void DisableInput(bool disable) noexcept; + static void DisablePlayerControl(bool disable) noexcept; + static bool IsPlayerControlEnabled() noexcept; + static bool IsInputActionPressed(int action) noexcept; - static InputSystem* GetInputSystem(); + static InputSystem* GetInputSystem() noexcept; }; \ No newline at end of file diff --git a/src/input/MessageHook.cpp b/src/input/MessageHook.cpp index bc30938..c37bbc9 100644 --- a/src/input/MessageHook.cpp +++ b/src/input/MessageHook.cpp @@ -12,7 +12,7 @@ static LRESULT WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) return CallWindowProc(s_original, hWnd, msg, wParam, lParam); } -void MessageHook::OnMessage(std::function callback) +void MessageHook::OnMessage(std::function callback) noexcept { // Get the window handle auto hWnd = cdc::PCDeviceManager::s_pInstance->GetWindow(); diff --git a/src/input/MessageHook.h b/src/input/MessageHook.h index 6d2f9e0..660fb09 100644 --- a/src/input/MessageHook.h +++ b/src/input/MessageHook.h @@ -6,5 +6,5 @@ class MessageHook { public: - static void OnMessage(std::function callback); + static void OnMessage(std::function callback) noexcept; }; \ No newline at end of file diff --git a/src/input/MouseHook.cpp b/src/input/MouseHook.cpp index ce3c49d..a897ea7 100644 --- a/src/input/MouseHook.cpp +++ b/src/input/MouseHook.cpp @@ -16,13 +16,13 @@ BOOL WINAPI SetCursorPosHook(int X, int Y) return s_SetCursorPos(X, Y); } -void MouseHook::Init() +void MouseHook::Init() noexcept { MH_CreateHookApi(L"user32", "SetCursorPos", SetCursorPosHook, (void**)&s_SetCursorPos); MH_EnableHook(MH_ALL_HOOKS); } -void MouseHook::DisableCursorLock(bool disable) +void MouseHook::DisableCursorLock(bool disable) noexcept { s_disabled = disable; } \ No newline at end of file diff --git a/src/input/MouseHook.h b/src/input/MouseHook.h index f7b9700..473eba2 100644 --- a/src/input/MouseHook.h +++ b/src/input/MouseHook.h @@ -3,6 +3,6 @@ class MouseHook { public: - static void Init(); - static void DisableCursorLock(bool disable); + static void Init() noexcept; + static void DisableCursorLock(bool disable) noexcept; }; \ No newline at end of file diff --git a/src/instance/Instances.cpp b/src/instance/Instances.cpp index e0b261b..cbc3e16 100644 --- a/src/instance/Instances.cpp +++ b/src/instance/Instances.cpp @@ -1,7 +1,7 @@ #include "Instances.h" #include "util/Hooking.h" -void Instances::Iterate(std::function callback) +void Instances::Iterate(std::function callback) noexcept { auto first = *(Instance**)GET_ADDRESS(0x10C5AA4, 0x817D64, 0xD98D54); diff --git a/src/instance/Instances.h b/src/instance/Instances.h index ba9914d..565c705 100644 --- a/src/instance/Instances.h +++ b/src/instance/Instances.h @@ -7,5 +7,5 @@ class Instances { public: - static void Iterate(std::function callback); + static void Iterate(std::function callback) noexcept; }; \ No newline at end of file diff --git a/src/level/Markup.cpp b/src/level/Markup.cpp index 251794c..54fb83a 100644 --- a/src/level/Markup.cpp +++ b/src/level/Markup.cpp @@ -1,12 +1,12 @@ #include "Markup.h" #include "util/Hooking.h" -MarkupManager* MarkupManager::GetInstance() +MarkupManager* MarkupManager::GetInstance() noexcept { return *(MarkupManager**)GET_ADDRESS(0x1117544, 0x86CD14, 0x000000); } -cdc::Vector3* MarkUpBox::GetSegmentPos(cdc::Vector3* result, int n) +cdc::Vector3* MarkUpBox::GetSegmentPos(cdc::Vector3* result, int n) const noexcept { auto addr = GET_ADDRESS(0x45F3A0, 0x462D90, 0x000000); diff --git a/src/level/Markup.h b/src/level/Markup.h index 617c9e5..3f32e64 100644 --- a/src/level/Markup.h +++ b/src/level/Markup.h @@ -14,7 +14,7 @@ class MarkupManager NodeType m_dynamicBoxList; NodeType m_staticBoxList; - static MarkupManager* GetInstance(); + static MarkupManager* GetInstance() noexcept; }; struct PolyLine @@ -115,6 +115,6 @@ class MarkUpBox : NodeType int nSegments; void** associateRef; - cdc::Vector3* GetSegmentPos(cdc::Vector3* result, int n); + cdc::Vector3* GetSegmentPos(cdc::Vector3* result, int n) const noexcept; }; #pragma pack(pop) \ No newline at end of file diff --git a/src/menu/Menu.cpp b/src/menu/Menu.cpp index 763f881..47586bf 100644 --- a/src/menu/Menu.cpp +++ b/src/menu/Menu.cpp @@ -88,7 +88,7 @@ void Menu::OnMessage(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) } } -void Menu::Draw() +void Menu::Draw() noexcept { auto& modules = Hook::GetInstance().GetModules(); @@ -113,7 +113,7 @@ void Menu::Draw() } } -void Menu::DrawMenu() +void Menu::DrawMenu() noexcept { if (ImGui::BeginMenu("Help")) { @@ -131,7 +131,7 @@ void Menu::DrawMenu() } } -void Menu::SetFocus(bool focus) +void Menu::SetFocus(bool focus) noexcept { m_focus = focus; @@ -140,7 +140,7 @@ void Menu::SetFocus(bool focus) Input::DisableInput(focus); } -bool Menu::HasFocus() +bool Menu::HasFocus() const noexcept { return m_focus; } \ No newline at end of file diff --git a/src/menu/Menu.h b/src/menu/Menu.h index 5bb2071..9bb38e4 100644 --- a/src/menu/Menu.h +++ b/src/menu/Menu.h @@ -19,10 +19,10 @@ class Menu : public cdc::PCInternalResource bool OnCreateDevice(); void OnDestroyDevice(); - void SetFocus(bool focus); - bool HasFocus(); + void SetFocus(bool focus) noexcept; + bool HasFocus() const noexcept; private: - void Draw(); - void DrawMenu(); + void Draw() noexcept; + void DrawMenu() noexcept; }; \ No newline at end of file diff --git a/src/render/Font.cpp b/src/render/Font.cpp index cf0201f..31cf779 100644 --- a/src/render/Font.cpp +++ b/src/render/Font.cpp @@ -19,33 +19,33 @@ static void Flush() char Font::s_formatted[1024]; -Font* Font::GetMainFont() +Font* Font::GetMainFont() noexcept { return *(Font**)GET_ADDRESS(0x107F680, 0x7D1800, 0x9DE6D4); } -void Font::SetCursor(float x, float y) +void Font::SetCursor(float x, float y) noexcept { auto addr = GET_ADDRESS(0x431670, 0x433C70, 0x474C90); Hooking::Call(addr, x, y); } -void Font::GetCursor(float* x, float* y) +void Font::GetCursor(float* x, float* y) noexcept { auto addr = GET_ADDRESS(0x431690, 0x433C90, 0x474CB0); Hooking::Call(addr, x, y); } -void Font::SetScale(float scaleX, float scaleY) +void Font::SetScale(float scaleX, float scaleY) noexcept { auto addr = GET_ADDRESS(0x431860, 0x433E60, 0x000000); Hooking::Call(addr, scaleX, scaleY); } -void Font::Print(const char* fmt, ...) +void Font::Print(const char* fmt, ...) const noexcept { va_list va; @@ -54,14 +54,14 @@ void Font::Print(const char* fmt, ...) va_end(va); } -void Font::PrintV(const char* fmt, va_list va) +void Font::PrintV(const char* fmt, va_list va) const noexcept { vsprintf_s(s_formatted, fmt, va); PrintFormatted(s_formatted); } -void Font::PrintCentered(const char* fmt, ...) +void Font::PrintCentered(const char* fmt, ...) const noexcept { va_list va; @@ -78,28 +78,28 @@ void Font::PrintCentered(const char* fmt, ...) PrintFormatted(s_formatted); } -void Font::PrintFormatted(const char* formatted, int backdrop) +void Font::PrintFormatted(const char* formatted, int backdrop) const noexcept { auto addr = GET_ADDRESS(0x4323D0, 0x434A70, 0x476BC0); Hooking::ThisCall(addr, this, formatted, backdrop); } -float Font::GetTextWidth(const char* text) +float Font::GetTextWidth(const char* text) const noexcept { auto addr = GET_ADDRESS(0x431EA0, 0x434510, 0x000000); return Hooking::ThisCallReturn(addr, this, text); } -float Font::GetHeight() +float Font::GetHeight() const noexcept { auto addr = GET_ADDRESS(0x431E20, 0x434440, 0x000000); return Hooking::ThisCallReturn(addr, this); } -void Font::OnFlush(std::function callback) +void Font::OnFlush(std::function callback) noexcept { if (!s_callback) { diff --git a/src/render/Font.h b/src/render/Font.h index aa18e43..d74d955 100644 --- a/src/render/Font.h +++ b/src/render/Font.h @@ -8,21 +8,21 @@ class Font static char s_formatted[1024]; public: - static Font* GetMainFont(); + static Font* GetMainFont() noexcept; - static void SetCursor(float x, float y); - static void GetCursor(float* x, float* y); - static void SetScale(float scaleX, float scaleY); + static void SetCursor(float x, float y) noexcept; + static void GetCursor(float* x, float* y) noexcept; + static void SetScale(float scaleX, float scaleY) noexcept; - void Print(const char* fmt, ...); - void PrintV(const char* fmt, va_list va); - void PrintCentered(const char* fmt, ...); - void PrintFormatted(const char* formatted, int backdrop = 0); + void Print(const char* fmt, ...) const noexcept; + void PrintV(const char* fmt, va_list va) const noexcept; + void PrintCentered(const char* fmt, ...) const noexcept; + void PrintFormatted(const char* formatted, int backdrop = 0) const noexcept; - float GetTextWidth(const char* text); - float GetHeight(); + float GetTextWidth(const char* text) const noexcept; + float GetHeight() const noexcept; - static void OnFlush(std::function callback); + static void OnFlush(std::function callback) noexcept; }; void FONT_Print(char* fmt, ...); \ No newline at end of file diff --git a/src/render/RenderContext.cpp b/src/render/RenderContext.cpp index d3a0700..5982340 100644 --- a/src/render/RenderContext.cpp +++ b/src/render/RenderContext.cpp @@ -15,7 +15,7 @@ static void __fastcall Present(void* _this, void*, RECT* pSourceRect, RECT* pDes s_Present(_this, pSourceRect, pDestRect, hOverrideWND); } -void RenderContext::OnPresent(std::function callback) +void RenderContext::OnPresent(std::function callback) noexcept { if (!s_callback) { diff --git a/src/render/RenderContext.h b/src/render/RenderContext.h index cc13da5..ca685bd 100644 --- a/src/render/RenderContext.h +++ b/src/render/RenderContext.h @@ -6,5 +6,5 @@ class RenderContext { public: // Registers a callback called just before device present - static void OnPresent(std::function callback); + static void OnPresent(std::function callback) noexcept; }; \ No newline at end of file diff --git a/src/util/Hooking.cpp b/src/util/Hooking.cpp index 30df5be..d9319da 100644 --- a/src/util/Hooking.cpp +++ b/src/util/Hooking.cpp @@ -2,7 +2,7 @@ #include "Hooking.h" -void Hooking::Nop(void* address, unsigned int length) +void Hooking::Nop(void* address, unsigned int length) noexcept { DWORD oldProtect; VirtualProtect(address, length, PAGE_EXECUTE_READWRITE, &oldProtect); @@ -12,7 +12,7 @@ void Hooking::Nop(void* address, unsigned int length) VirtualProtect(address, length, oldProtect, &oldProtect); } -void Hooking::Patch(void* address, std::initializer_list data) +void Hooking::Patch(void* address, std::initializer_list data) noexcept { DWORD oldProtect; VirtualProtect(address, data.size(), PAGE_EXECUTE_READWRITE, &oldProtect); @@ -22,7 +22,7 @@ void Hooking::Patch(void* address, std::initializer_list data) VirtualProtect(address, data.size(), oldProtect, &oldProtect); } -void Hooking::Patch(void* address, unsigned int data) +void Hooking::Patch(void* address, unsigned int data) noexcept { DWORD oldProtect; VirtualProtect(address, sizeof(data), PAGE_EXECUTE_READWRITE, &oldProtect); diff --git a/src/util/Hooking.h b/src/util/Hooking.h index 1033ab0..0ba2d8d 100644 --- a/src/util/Hooking.h +++ b/src/util/Hooking.h @@ -8,40 +8,40 @@ class Hooking public: // Calls a function template - static inline void Call(unsigned int address, Args... args) + static inline void Call(unsigned int address, Args... args) noexcept { reinterpret_cast(address)(args...); } // Calls a function with a return value template - static inline T CallReturn(unsigned int address, Args... args) + static inline T CallReturn(unsigned int address, Args... args) noexcept { return reinterpret_cast(address)(args...); } // Calls a class function template - static inline void ThisCall(unsigned int address, Args... args) + static inline void ThisCall(unsigned int address, Args... args) noexcept { reinterpret_cast(address)(args...); } // Calls a class function with a return value template - static inline T ThisCallReturn(unsigned int address, Args... args) + static inline T ThisCallReturn(unsigned int address, Args... args) noexcept { return reinterpret_cast(address)(args...); } // Sets a memory region to no-op - static void Nop(void* address, unsigned int length); + static void Nop(void* address, unsigned int length) noexcept; // Patches data at a memory region - static void Patch(void* address, std::initializer_list data); + static void Patch(void* address, std::initializer_list data) noexcept; // Patches data at a memory region - static void Patch(void* address, unsigned int data); + static void Patch(void* address, unsigned int data) noexcept; }; #if TR7 From e72ecd32b3aabc642f87fa664ff430ef548a89fa Mon Sep 17 00:00:00 2001 From: Indra Date: Sat, 17 Feb 2024 03:45:43 +0100 Subject: [PATCH 54/55] Add version info --- src/resources/hook.rc | Bin 0 -> 4544 bytes src/resources/resource.h | 14 ++++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 src/resources/hook.rc create mode 100644 src/resources/resource.h diff --git a/src/resources/hook.rc b/src/resources/hook.rc new file mode 100644 index 0000000000000000000000000000000000000000..cb8ab66bab2272d3014a57ba8b9c433ead1a95d4 GIT binary patch literal 4544 zcmd6rS#J_i6vxlAiQi$QFB)r$XreySmeNKFO-+DISZrc!81K7Yuc5YX^?b~Cn#NOMbb-DY%hpa7N zeOA$pc)H*@;Z58+b`8#s-M4#4*$wL!&@Q`IV7CHpW`TakK3FDTi*hUAwF8}a=k3(0 zRhcQ!|yRx?_;O@Q2?Ifo{z}yWAG*TH`1m3zj+>Wjbe6;@Rc&Djos_zhI`)mZr1AB zB}BJN{aq2M0XdM^Lt@_L&FiqrQ>@B?JzA0lgPEG@1b;n9)m96Rc{?PlVog=4pE}jG zkK`uVRpJug?_9}^lr?yqk;6@>%FsUwQcFO~K#a|4eu&=dq5)1 zJVuk;E&5D?71#B3t_-3g>uQdU)gX?V9r8g237Rq2bdj!oBk#_6y9BP9%#bUZJkq~~ zRp(>5>4?|JtvuXjed4lwP0Mt@9Kd__+z@MID*FsQ^Jw~n+|zs<@pOc~J^P4u@@Stm z&yITHXK++$=lp&fYwGgn*R(zrC_2PTQPbJS_0wD08`08b`s{$KDvy!bLKk&yO(^;2 z#hrPlgeLI>*ZOZhd9gG?t3C4a&OD+Cs?$T?zJv_A;%#|7dDg6-EieDjGwFWja?@*C z3(wK?eDT`JaR$#$?%PiH2tG>>QYCJAKl=`lIK(XSvQ$|LVl+=dsOOW&P|O zXKCz6%fDxI>T3|;*UZ?M3!7(^Yb49`?Calt**+s9R)5*LFI|JpTczR6`QD`;>A#o7 K@7Tq-i_ Date: Sat, 17 Feb 2024 04:27:21 +0100 Subject: [PATCH 55/55] Minor tweaks --- src/modules/MainMenu.cpp | 37 ++++++++++++++++++++++++++++++++++--- src/modules/MainMenu.h | 9 ++++++--- src/modules/Patches.cpp | 2 +- src/modules/Patches.h | 6 +++--- src/modules/Skew.cpp | 29 +++++++++++++++++++++++------ src/modules/Skew.h | 9 +++++++-- src/render/Draw.cpp | 7 ++++--- 7 files changed, 78 insertions(+), 21 deletions(-) diff --git a/src/modules/MainMenu.cpp b/src/modules/MainMenu.cpp index c97ef41..b2b41c2 100644 --- a/src/modules/MainMenu.cpp +++ b/src/modules/MainMenu.cpp @@ -8,6 +8,8 @@ #include "level/Event.h" #include "input/Input.h" #include "game/Player.h" +#include "file/FileSystem.h" +#include "modules/Log.h" void MainMenu::OnDraw() { @@ -19,7 +21,7 @@ void MainMenu::OnDraw() if (ImGui::Button("Load unit")) { - GAMELOOP_RequestLevelChangeByName(unit, Game::GetGameTracker(), 4); + SwitchUnit(unit); } // Birth instance @@ -116,8 +118,15 @@ void MainMenu::OnDraw() ImGui::End(); } -void MainMenu::BirthObject(char* name) +void MainMenu::BirthObject(char* name) const noexcept { + // Make sure the object exists + if (!CheckDataFile(name) || OBTABLE_GetObjectID(name) == 0) + { + Hook::GetInstance().GetModule()->WriteLine("Not birthing %s, object does not exists", name); + return; + } + auto game = Game::GetGameTracker(); auto player = Game::GetPlayerInstance(); @@ -134,7 +143,19 @@ void MainMenu::BirthObject(char* name) INSTANCE_BirthObjectNoParent(game->StreamUnitID, &player->position, &player->rotation, nullptr, tracker->object, 0, 1); } -void MainMenu::SwitchPlayerCharacter(char* name) +void MainMenu::SwitchUnit(char* name) const noexcept +{ + // Make sure the unit exists, this only checks if a file with the same name exists + if (!CheckDataFile(name)) + { + Hook::GetInstance().GetModule()->WriteLine("Not switching to unit %s, level does not exist", name); + return; + } + + GAMELOOP_RequestLevelChangeByName(name, Game::GetGameTracker(), 4); +} + +void MainMenu::SwitchPlayerCharacter(char* name) noexcept { auto game = Game::GetGameTracker(); @@ -197,4 +218,14 @@ void MainMenu::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) INSTANCE_Post(gameTracker->playerInstance, 4, 3); #endif } +} + +bool MainMenu::CheckDataFile(char* name) noexcept +{ + auto fileSystem = GetFS(); + + char fileName[256]; + LOAD_UnitFileName(fileName, name, "drm"); + + return fileSystem->FileExists(fileName); } \ No newline at end of file diff --git a/src/modules/MainMenu.h b/src/modules/MainMenu.h index cef36a5..074bb73 100644 --- a/src/modules/MainMenu.h +++ b/src/modules/MainMenu.h @@ -11,11 +11,14 @@ class MainMenu : public Module Option m_noWatermark{ "NoWatermark", false }; - void BirthObject(char* name); - void SwitchPlayerCharacter(char* name = nullptr); + void BirthObject(char* name) const noexcept; + void SwitchUnit(char* name) const noexcept; + void SwitchPlayerCharacter(char* name = nullptr) noexcept; + + static bool CheckDataFile(char* name) noexcept; public: - bool IsNoDeathFade() { return m_noDeathFade; } + bool IsNoDeathFade() const noexcept { return m_noDeathFade; } void OnDraw(); void OnFrame(); diff --git a/src/modules/Patches.cpp b/src/modules/Patches.cpp index 9ef6ee8..8e74d84 100644 --- a/src/modules/Patches.cpp +++ b/src/modules/Patches.cpp @@ -98,7 +98,7 @@ Patches::Patches() MH_EnableHook(MH_ALL_HOOKS); } -void Patches::RemoveIntro() +void Patches::RemoveIntro() const noexcept { auto match = hook::pattern("8D 0C 8D 03 00 00 00 89 0D").count(1); auto mainState = *match.get_first(9); diff --git a/src/modules/Patches.h b/src/modules/Patches.h index 0bdc687..7b547ce 100644 --- a/src/modules/Patches.h +++ b/src/modules/Patches.h @@ -10,13 +10,13 @@ class Patches : public Module Option m_noCinematicBars{ "NoCinematicBars", true }; Option m_noMotionBlur{ "NoMotionBlur", false }; - void RemoveIntro(); + void RemoveIntro() const noexcept; public: Patches(); - bool IsNoMotionBlur() { return m_noMotionBlur.GetValue(); } - bool IsNoCinematicBars() { return m_noCinematicBars.GetValue(); } + bool IsNoMotionBlur() const noexcept { return m_noMotionBlur.GetValue(); } + bool IsNoCinematicBars() const noexcept { return m_noCinematicBars.GetValue(); } void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); }; \ No newline at end of file diff --git a/src/modules/Skew.cpp b/src/modules/Skew.cpp index 7d55707..98001e3 100644 --- a/src/modules/Skew.cpp +++ b/src/modules/Skew.cpp @@ -3,7 +3,12 @@ #include "instance/Instances.h" #include "game/Game.h" -void Skew::ToggleSkew() +Skew::Skew() +{ + UpdateLayout(); +} + +void Skew::ToggleSkew() const noexcept { auto tracker = Game::GetGameTracker(); @@ -22,21 +27,20 @@ void Skew::ToggleSkew() #endif } -void Skew::Process(UINT msg, WPARAM wParam) +void Skew::Process(UINT msg, WPARAM wParam) const noexcept { - // TODO different keyboard layouts - auto player = Game::GetPlayerInstance(); auto tracker = Game::GetGameTracker(); auto speed = m_speed.GetValue() * tracker->timeMult; - if (msg == WM_KEYDOWN && wParam == 0x51) + // TODO less hacky way? use game input system perhaps + if (msg == WM_KEYDOWN && wParam == (m_isAzerty ? 0x41 /* A */ : 0x51 /* Q */)) { player->position.z += speed; } - if (msg == WM_KEYDOWN && wParam == 0x5A) + if (msg == WM_KEYDOWN && wParam == (m_isAzerty ? 0x57 /* W */ : 0x5A /* Z */)) { player->position.z -= speed; } @@ -49,10 +53,23 @@ void Skew::OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) ToggleSkew(); } + if (msg == WM_INPUTLANGCHANGE) + { + UpdateLayout(); + } + auto tracker = Game::GetGameTracker(); if (tracker->cheatMode) { Process(msg, wParam); } +} + +void Skew::UpdateLayout() noexcept +{ + auto layout = GetKeyboardLayout(0); + auto id = LOWORD(layout); + + m_isAzerty = id == 2060 || id == 1036; } \ No newline at end of file diff --git a/src/modules/Skew.h b/src/modules/Skew.h index 2aa60a2..5ff4679 100644 --- a/src/modules/Skew.h +++ b/src/modules/Skew.h @@ -6,11 +6,16 @@ class Skew : public Module { private: - void ToggleSkew(); - void Process(UINT msg, WPARAM wParam); + bool m_isAzerty = false; + + void ToggleSkew() const noexcept; + void Process(UINT msg, WPARAM wParam) const noexcept; + void UpdateLayout() noexcept; Option m_speed{ "SkewSpeed", 300.f }; public: + Skew(); + virtual void OnInput(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam); }; \ No newline at end of file diff --git a/src/render/Draw.cpp b/src/render/Draw.cpp index 770dde3..f1d024d 100644 --- a/src/render/Draw.cpp +++ b/src/render/Draw.cpp @@ -76,9 +76,10 @@ void DrawLine(cdc::Vector3* v0, cdc::Vector3* v1, int color) auto v2 = *v1; auto v3 = *v0; - v2.z += 50.f; - v3.z += 50.f; - v3.x += 50.f; + v2.z += 20.f; + v3.z += 20.f; + v2.y += 20.f; + v3.y += 20.f; TRANS_TransToDrawVertexV4f(verts, v0); TRANS_TransToDrawVertexV4f(&verts[1], &v2);