From d0ddf8c8ba7806ce7db102e83eaeb955e33e67bf Mon Sep 17 00:00:00 2001 From: WSSDude <41929176+WSSDude@users.noreply.github.com> Date: Sun, 16 Feb 2025 01:33:30 +0100 Subject: [PATCH] API v1 * update `RED4ext.SDK` * completely rework `StateSystem` * completely rework `CGameApplication` hooking * remove no longer needed classes and addresses * clean includes in touched files * other small changes in touched files --- .gitignore | 2 + deps/red4ext.sdk | 2 +- src/dll/Addresses.cpp | 1 - src/dll/Addresses.hpp | 4 +- src/dll/App.cpp | 9 +- src/dll/App.hpp | 2 +- src/dll/Config.cpp | 2 +- src/dll/Detail/AddressHashes.hpp | 3 + src/dll/DetourTransaction.cpp | 1 + src/dll/DetourTransaction.hpp | 4 +- src/dll/DevConsole.cpp | 1 + src/dll/GameStateHook.hpp | 180 ---------- src/dll/Hooks/AssertionFailed.cpp | 4 +- src/dll/Hooks/CGameApplication.cpp | 193 +++++++--- src/dll/Hooks/CollectSaveableSystems.cpp | 3 +- src/dll/Hooks/ExecuteProcess.cpp | 5 +- src/dll/Hooks/ExecuteProcess.hpp | 1 + src/dll/Hooks/InitScripts.cpp | 1 + src/dll/Hooks/LoadScripts.cpp | 1 + src/dll/Hooks/Main_Hooks.cpp | 3 +- src/dll/Hooks/ValidateScripts.cpp | 1 + src/dll/Hooks/ValidateScripts.hpp | 1 + src/dll/Hooks/gsmState_SessionActive.cpp | 3 +- src/dll/Hooks/gsmState_SessionActive.hpp | 1 + src/dll/Image.cpp | 5 +- src/dll/MemoryProtection.cpp | 1 + src/dll/Paths.cpp | 1 + src/dll/PluginBase.cpp | 30 +- .../ScriptCompiler/ScriptCompilerFailure.hpp | 1 + .../ScriptCompiler/ScriptCompilerOutput.hpp | 2 + .../ScriptCompiler/ScriptCompilerSettings.hpp | 2 + .../ScriptCompilerSourceRef.hpp | 1 + src/dll/ScriptValidationError.cpp | 3 +- src/dll/States/BaseInitializationState.cpp | 64 ---- src/dll/States/BaseInitializationState.hpp | 11 - src/dll/States/InitializationState.cpp | 63 ---- src/dll/States/InitializationState.hpp | 11 - src/dll/States/RunningState.cpp | 51 --- src/dll/States/RunningState.hpp | 11 - src/dll/States/ShutdownState.cpp | 60 ---- src/dll/States/ShutdownState.hpp | 11 - src/dll/Systems/HookingSystem.cpp | 2 +- src/dll/Systems/HookingSystem.hpp | 8 +- src/dll/Systems/LoggerSystem.cpp | 2 +- src/dll/Systems/LoggerSystem.hpp | 13 +- src/dll/Systems/PluginSystem.cpp | 40 ++- src/dll/Systems/PluginSystem.hpp | 12 +- src/dll/Systems/ScriptCompilationSystem.cpp | 11 +- src/dll/Systems/ScriptCompilationSystem.hpp | 8 +- src/dll/Systems/StateSystem.cpp | 340 +++++++++++++----- src/dll/Systems/StateSystem.hpp | 67 ++-- src/dll/Utils.cpp | 30 +- src/dll/Utils.hpp | 14 +- src/dll/v0/Plugin.cpp | 94 ----- src/dll/v0/Plugin.hpp | 32 -- src/dll/{v0 => v1}/Funcs.cpp | 43 ++- src/dll/{v0 => v1}/Funcs.hpp | 6 +- src/dll/{v0 => v1}/Logger.cpp | 63 ++-- src/dll/{v0 => v1}/Logger.hpp | 6 +- src/dll/v1/Plugin.cpp | 87 +++++ src/dll/v1/Plugin.hpp | 32 ++ src/loader/stdafx.hpp | 1 + src/playground/Main.cpp | 4 + 63 files changed, 742 insertions(+), 929 deletions(-) delete mode 100644 src/dll/GameStateHook.hpp delete mode 100644 src/dll/States/BaseInitializationState.cpp delete mode 100644 src/dll/States/BaseInitializationState.hpp delete mode 100644 src/dll/States/InitializationState.cpp delete mode 100644 src/dll/States/InitializationState.hpp delete mode 100644 src/dll/States/RunningState.cpp delete mode 100644 src/dll/States/RunningState.hpp delete mode 100644 src/dll/States/ShutdownState.cpp delete mode 100644 src/dll/States/ShutdownState.hpp delete mode 100644 src/dll/v0/Plugin.cpp delete mode 100644 src/dll/v0/Plugin.hpp rename src/dll/{v0 => v1}/Funcs.cpp (64%) rename src/dll/{v0 => v1}/Funcs.hpp (72%) rename src/dll/{v0 => v1}/Logger.cpp (80%) rename src/dll/{v0 => v1}/Logger.hpp (97%) create mode 100644 src/dll/v1/Plugin.cpp create mode 100644 src/dll/v1/Plugin.hpp diff --git a/.gitignore b/.gitignore index 4c877a16..f0f9e37b 100644 --- a/.gitignore +++ b/.gitignore @@ -364,3 +364,5 @@ FodyWeavers.xsd ### Custom ### build src/dll/Version.hpp +out +CMakeSettings.json diff --git a/deps/red4ext.sdk b/deps/red4ext.sdk index 78c3e9df..d9d953e0 160000 --- a/deps/red4ext.sdk +++ b/deps/red4ext.sdk @@ -1 +1 @@ -Subproject commit 78c3e9df2885437e4cc71cc5e49320fd34bf883b +Subproject commit d9d953e06d56cefb980b2997204a365332b6ba9e diff --git a/src/dll/Addresses.cpp b/src/dll/Addresses.cpp index 3a242736..167f3b32 100644 --- a/src/dll/Addresses.cpp +++ b/src/dll/Addresses.cpp @@ -122,7 +122,6 @@ void Addresses::LoadAddresses(const std::filesystem::path& aPath) m_addresses.emplace(static_cast(hash), address); } } - spdlog::info("{} game addresses loaded", m_addresses.size()); } diff --git a/src/dll/Addresses.hpp b/src/dll/Addresses.hpp index d11e4bc5..b4635745 100644 --- a/src/dll/Addresses.hpp +++ b/src/dll/Addresses.hpp @@ -1,11 +1,11 @@ #pragma once +#include "Paths.hpp" + #include #include #include -#include "Paths.hpp" - class Addresses { public: diff --git a/src/dll/App.cpp b/src/dll/App.cpp index 786325cf..33339454 100644 --- a/src/dll/App.cpp +++ b/src/dll/App.cpp @@ -1,10 +1,6 @@ #include "App.hpp" -#include "Addresses.hpp" -#include "DetourTransaction.hpp" -#include "Image.hpp" -#include "Utils.hpp" -#include "Version.hpp" +#include "DetourTransaction.hpp" #include "Hooks/AssertionFailed.hpp" #include "Hooks/CGameApplication.hpp" #include "Hooks/CollectSaveableSystems.hpp" @@ -14,6 +10,9 @@ #include "Hooks/Main_Hooks.hpp" #include "Hooks/ValidateScripts.hpp" #include "Hooks/gsmState_SessionActive.hpp" +#include "Image.hpp" +#include "Utils.hpp" +#include "Version.hpp" namespace { diff --git a/src/dll/App.hpp b/src/dll/App.hpp index 6393a773..0f94f7c8 100644 --- a/src/dll/App.hpp +++ b/src/dll/App.hpp @@ -35,7 +35,7 @@ class App bool AttachHooks() const; template>> - inline void AddSystem(Args&&... args) + void AddSystem(Args&&... args) { m_systems.push_back(std::make_unique(std::forward(args)...)); std::sort(m_systems.begin(), m_systems.end(), diff --git a/src/dll/Config.cpp b/src/dll/Config.cpp index ab5ddca1..d6c73457 100644 --- a/src/dll/Config.cpp +++ b/src/dll/Config.cpp @@ -1,5 +1,5 @@ -#include "stdafx.hpp" #include "Config.hpp" + #include "Utils.hpp" #define DEFAULT_TOML_EXCEPTION_MSG L"An exception occured while parsing the config file:\n\n{}\n\nFile: {}" diff --git a/src/dll/Detail/AddressHashes.hpp b/src/dll/Detail/AddressHashes.hpp index 9e05e673..fc9b7b9d 100644 --- a/src/dll/Detail/AddressHashes.hpp +++ b/src/dll/Detail/AddressHashes.hpp @@ -1,4 +1,5 @@ #pragma once + #include namespace Hashes @@ -9,6 +10,8 @@ constexpr std::uint32_t CBaseEngine_InitScripts = 2875532677UL; constexpr std::uint32_t CBaseEngine_LoadScripts = 3570081113UL; constexpr std::uint32_t CGameApplication_AddState = 4223801011UL; +constexpr std::uint32_t CGameApplication_ChangeState = 2242385233UL; +constexpr std::uint32_t CGameApplication_DoState = 804916828UL; constexpr std::uint32_t GameInstance_CollectSaveableSystems = 3230163856ul; constexpr std::uint32_t Global_ExecuteProcess = 2203918127UL; diff --git a/src/dll/DetourTransaction.cpp b/src/dll/DetourTransaction.cpp index 333dbf3d..bc5b8597 100644 --- a/src/dll/DetourTransaction.cpp +++ b/src/dll/DetourTransaction.cpp @@ -1,4 +1,5 @@ #include "DetourTransaction.hpp" + #include "Utils.hpp" DetourTransaction::DetourTransaction(const std::source_location aSource) diff --git a/src/dll/DetourTransaction.hpp b/src/dll/DetourTransaction.hpp index 810b5c05..64c46e7c 100644 --- a/src/dll/DetourTransaction.hpp +++ b/src/dll/DetourTransaction.hpp @@ -3,7 +3,7 @@ class DetourTransaction { public: - DetourTransaction(const std::source_location aSource = std::source_location::current()); + DetourTransaction(std::source_location aSource = std::source_location::current()); ~DetourTransaction(); DetourTransaction(DetourTransaction&) = delete; @@ -27,7 +27,7 @@ class DetourTransaction Failed }; - void SetState(const State aState); + void SetState(State aState); void QueueThreadsForUpdate(); const std::source_location m_source; diff --git a/src/dll/DevConsole.cpp b/src/dll/DevConsole.cpp index b2e5ff20..1f0f3316 100644 --- a/src/dll/DevConsole.cpp +++ b/src/dll/DevConsole.cpp @@ -1,4 +1,5 @@ #include "DevConsole.hpp" + #include "Utils.hpp" DevConsole::DevConsole(const Config::DevConfig& aConfig) diff --git a/src/dll/GameStateHook.hpp b/src/dll/GameStateHook.hpp deleted file mode 100644 index 6d42bf84..00000000 --- a/src/dll/GameStateHook.hpp +++ /dev/null @@ -1,180 +0,0 @@ -#pragma once - -#include "MemoryProtection.hpp" - -#include "Utils.hpp" - -template -class GameStateHook -{ -public: - using Func_t = bool (*)(T*, RED4ext::CGameApplication*); - - static_assert(std::is_base_of_v, "T should inherit IGameState"); - - GameStateHook(Func_t aOnEnter, Func_t aOnUpdate, Func_t aOnExit) - : m_isAttached(false) - , m_onEnter(aOnEnter) - , m_onUpdate(aOnUpdate) - , m_onExit(aOnExit) - { - } - - bool AttachAt(T* aState) - { - auto name = aState->GetName(); - auto vtbl = *reinterpret_cast(aState); - - spdlog::trace("Changing virtual functions for '{}' state at {}...", name, fmt::ptr(vtbl)); - - m_onEnter.orig = reinterpret_cast(vtbl[3]); - m_onUpdate.orig = reinterpret_cast(vtbl[4]); - m_onExit.orig = reinterpret_cast(vtbl[5]); - - if (SwapVFuncs(aState, m_onEnter.detour, m_onUpdate.detour, m_onExit.detour)) - { - spdlog::trace("Virtual functions for '{}' state were changed successfully", name); - return true; - } - - return false; - } - - bool DetachAt(T* aState) - { - auto name = aState->GetName(); - auto vtbl = *reinterpret_cast(aState); - - spdlog::trace("Restoring virtual functions for '{}' state at {}...", name, vtbl); - - if (SwapVFuncs(aState, m_onEnter.orig, m_onUpdate.orig, m_onExit.orig)) - { - spdlog::trace("Virtual functions for '{}' state were restored successfully", name); - return true; - } - - return false; - } - - bool OnEnter(T* aState, RED4ext::CGameApplication* aApp) - { - if (m_onEnter.shouldExecute && m_onEnter.orig) - { - auto result = m_onEnter.orig(aState, aApp); - m_onEnter.shouldExecute = !result; - - return result; - } - - return true; - } - - bool OnUpdate(T* aState, RED4ext::CGameApplication* aApp) - { - if (m_onUpdate.shouldExecute && m_onUpdate.orig) - { - auto result = m_onUpdate.orig(aState, aApp); - m_onUpdate.shouldExecute = !result; - - return result; - } - - return true; - } - - bool OnExit(T* aState, RED4ext::CGameApplication* aApp) - { - if (m_onExit.shouldExecute && m_onExit.orig) - { - auto result = m_onExit.orig(aState, aApp); - m_onExit.shouldExecute = !result; - - return result; - } - - return true; - } - -private: - struct FuncHook - { - FuncHook(Func_t aDetour) - : shouldExecute(true) - , detour(aDetour) - , orig(nullptr) - { - } - - bool shouldExecute; - Func_t detour; - Func_t orig; - }; - - bool SwapVFuncs(T* aState, Func_t aOnEnter, Func_t aOnUpdate, Func_t aOnExit) - { - auto name = aState->GetName(); - auto vtbl = *reinterpret_cast(aState); - - try - { - const auto addr = &vtbl[3]; - - constexpr auto VirtualFuncs = 3; - constexpr auto size = VirtualFuncs * sizeof(addr); - - MemoryProtection _(addr, size, PAGE_READWRITE); - - auto onEnter = &vtbl[3]; - spdlog::trace("Changing 'OnEnter' function at {} from {} to {}...", fmt::ptr(onEnter), fmt::ptr(vtbl[3]), - fmt::ptr(aOnEnter)); - - *onEnter = aOnEnter; - spdlog::trace("'OnEnter' function was changed successfully"); - - auto onUpdate = &vtbl[4]; - spdlog::trace("Changing 'OnUpdate' function at {} from {} to {}...", fmt::ptr(onUpdate), fmt::ptr(vtbl[4]), - fmt::ptr(aOnUpdate)); - - *onUpdate = aOnUpdate; - spdlog::trace("'OnUpdate' function was changed successfully"); - - auto onExit = &vtbl[5]; - spdlog::trace("Changing 'OnExit' function at {} from {} to {}...", fmt::ptr(onExit), fmt::ptr(vtbl[5]), - fmt::ptr(aOnExit)); - - *onExit = aOnExit; - spdlog::trace("'OnExit' function was changed successfully"); - } - catch (const MemoryProtection::Exception&) - { - spdlog::warn("Could not change / restoring the protection for '{}' state, the execution will continue but " - "unexpected behavior might happen", - name); - return false; - } - catch (const std::exception& e) - { - spdlog::warn("An exception occured while changing / restoring the virtual functions for '{}' state, the " - "execution will continue but unexpected behavior might happen", - name); - spdlog::warn(e.what()); - - return false; - } - catch (...) - { - spdlog::warn( - "An unknown exception occured while changing / restoring the virtual functions for '{}' state, the " - "execution will continue but unexpected behavior might happen", - name); - return false; - } - - return true; - } - - bool m_isAttached; - FuncHook m_onEnter; - FuncHook m_onUpdate; - FuncHook m_onExit; -}; diff --git a/src/dll/Hooks/AssertionFailed.cpp b/src/dll/Hooks/AssertionFailed.cpp index 417dc37a..ea165f88 100644 --- a/src/dll/Hooks/AssertionFailed.cpp +++ b/src/dll/Hooks/AssertionFailed.cpp @@ -1,9 +1,7 @@ #include "AssertionFailed.hpp" -#include "Addresses.hpp" -#include "App.hpp" + #include "Detail/AddressHashes.hpp" #include "Hook.hpp" -#include "stdafx.hpp" namespace { diff --git a/src/dll/Hooks/CGameApplication.cpp b/src/dll/Hooks/CGameApplication.cpp index 4bbe3498..d1f433ef 100644 --- a/src/dll/Hooks/CGameApplication.cpp +++ b/src/dll/Hooks/CGameApplication.cpp @@ -1,13 +1,9 @@ #include "CGameApplication.hpp" -#include "Addresses.hpp" + +#include "App.hpp" #include "Detail/AddressHashes.hpp" #include "Hook.hpp" -#include "States/BaseInitializationState.hpp" -#include "States/InitializationState.hpp" -#include "States/RunningState.hpp" -#include "States/ShutdownState.hpp" - namespace { bool isAttached = false; @@ -15,85 +11,168 @@ bool isAttached = false; bool _CGameApplication_AddState(RED4ext::CGameApplication* aThis, RED4ext::IGameState* aState); Hook CGameApplication_AddState(Hashes::CGameApplication_AddState, &_CGameApplication_AddState); +void _CGameApplication_ChangeState(RED4ext::CGameApplication* aThis, RED4ext::EGameStateType aNewState); +Hook CGameApplication_ChangeState(Hashes::CGameApplication_ChangeState, + &_CGameApplication_ChangeState); +void _CGameApplication_DoState(RED4ext::CGameApplication* aThis); +Hook CGameApplication_DoState(Hashes::CGameApplication_DoState, + &_CGameApplication_DoState); bool _CGameApplication_AddState(RED4ext::CGameApplication* aThis, RED4ext::IGameState* aState) { - bool success = true; - try + if (aThis && aState) { switch (aState->GetType()) { case RED4ext::EGameStateType::BaseInitialization: - { - success = - States::BaseInitializationState::Attach(static_cast(aState)) && - success; - break; - } + [[fallthrough]]; case RED4ext::EGameStateType::Initialization: - { - success = - States::InitializationState::Attach(static_cast(aState)) && success; - break; - } + [[fallthrough]]; case RED4ext::EGameStateType::Running: - { - success = States::RunningState::Attach(static_cast(aState)) && success; - break; - } + [[fallthrough]]; case RED4ext::EGameStateType::Shutdown: - { - success = States::ShutdownState::Attach(static_cast(aState)) && success; break; - } default: { - spdlog::warn("State '{}' ({}) is not handled", aState->GetName(), static_cast(aState->GetType())); + assert(false && "Unknown state encountered!"); + + spdlog::warn("Game tries to add state '{}' ({}) which is unknown!", aState->GetName(), + static_cast>(aState->GetType())); } } + } + else + { + assert(aThis && "CGameApplicaiton was nullptr!"); + assert(aState && "IGameState was nullptr!"); + + spdlog::error("Unexpected game state addition arguments! Arguments were: aThis = {}, aState = {}.", + fmt::ptr(aThis), fmt::ptr(aState)); + } - if (success) + return CGameApplication_AddState(aThis, aState); +} + +void _CGameApplication_ChangeState(RED4ext::CGameApplication* aThis, const RED4ext::EGameStateType aNextState) +{ + if (!aThis || aNextState < RED4ext::EGameStateType::BaseInitialization || + aNextState > RED4ext::EGameStateType::Shutdown) + { + if (!aThis) { - spdlog::trace("All virtual functions were changed successfully"); + assert(false && "CGameApplicaiton was nullptr!"); + + spdlog::error("Unexpected game state change arguments! Arguments were: aThis = {}, aNextState = {}.", + fmt::ptr(aThis), static_cast>(aNextState)); } - else + + spdlog::trace("Executing default game state change..."); + CGameApplication_ChangeState(aThis, aNextState); + + if (aThis) { + assert(false && "Unhandled state encountered!"); + spdlog::warn( - "One or more game state virtual functions could not be changed, the game will continue running " - "but unexpected behavior might happen"); + "Executed default game state change as the state '{}' ({}) is unknown!", aThis->currentState->GetName(), + static_cast>(aThis->currentState->GetType())); + } + + return; + } + + // Try to override the function. + if (App::Get()->GetStateSystem()->OnChangeState(*aThis, aNextState)) + { + // Override successful! + return; + } + + // Override failed, execute original. + spdlog::trace("Executing default game state change..."); + CGameApplication_ChangeState(aThis, aNextState); +} + +void _CGameApplication_DoState(RED4ext::CGameApplication* aThis) +{ + if (aThis) + { + // Try to override the function. + if (App::Get()->GetStateSystem()->OnDoState(*aThis)) + { + // Override successful! + return; } } - catch (const std::exception& e) + else { - spdlog::warn("An exception occurred while changing the virtual functions for the game states"); - spdlog::warn(e.what()); + assert(false && "CGameApplicaiton was nullptr!"); + + spdlog::error("Unexpected game do state arguments! Arguments were: aThis = {}.", fmt::ptr(aThis)); } - catch (...) + + // Override failed, execute original. + spdlog::trace("Executing default game do state..."); + CGameApplication_DoState(aThis); +} + +bool AttachHook(auto& aHook, const std::string_view aHookName) +{ + spdlog::trace("Trying to attach the hook for the {} at {:#x}...", aHookName, aHook.GetAddress()); + + if (const auto result = aHook.Attach(); result != NO_ERROR) { - spdlog::warn("An unknown exception occurred while changing the virtual functions for the game states"); + spdlog::error("Could not attach the hook for the {}. Detour error code: {}", aHookName, result); + return false; } - return CGameApplication_AddState(aThis, aState); + spdlog::trace("The hook for the {} was attached", aHookName); + return true; +} + +bool DetachHook(auto& aHook, const std::string_view aHookName) +{ + spdlog::trace("Trying to detach the hook for the {} at {:#x}...", aHookName, aHook.GetAddress()); + + if (const auto result = aHook.Detach(); result != NO_ERROR) + { + spdlog::error("Could not detach the hook for the {}. Detour error code: {}", aHookName, result); + return false; + } + + spdlog::trace("The hook for the {} was detached", aHookName); + return true; } + } // namespace bool Hooks::CGameApplication::Attach() { - spdlog::trace("Trying to attach the hook for the game application at {:#x}...", - CGameApplication_AddState.GetAddress()); + if (isAttached) + { + return false; + } - auto result = CGameApplication_AddState.Attach(); - if (result != NO_ERROR) + if (!AttachHook(CGameApplication_AddState, "game states addition")) { - spdlog::error("Could not attach the hook for the game application. Detour error code: {}", result); + return false; } - else + + if (!AttachHook(CGameApplication_ChangeState, "game state changes")) { - spdlog::trace("The hook for the game application was attached"); + std::ignore = DetachHook(CGameApplication_AddState, "game states addition"); + return false; } - isAttached = result == NO_ERROR; - return isAttached; + if (!AttachHook(CGameApplication_DoState, "game do state")) + { + std::ignore = DetachHook(CGameApplication_ChangeState, "game state changes"); + std::ignore = DetachHook(CGameApplication_AddState, "game states addition"); + return false; + } + + isAttached = true; + return true; } bool Hooks::CGameApplication::Detach() @@ -103,19 +182,21 @@ bool Hooks::CGameApplication::Detach() return false; } - spdlog::trace("Trying to detach the hook for the game application at {:#x}...", - CGameApplication_AddState.GetAddress()); + if (!DetachHook(CGameApplication_DoState, "game do state")) + { + return false; + } - auto result = CGameApplication_AddState.Detach(); - if (result != NO_ERROR) + if (!DetachHook(CGameApplication_ChangeState, "game state changes")) { - spdlog::error("Could not detach the hook for the game application. Detour error code: {}", result); + return false; } - else + + if (!DetachHook(CGameApplication_AddState, "game states addition")) { - spdlog::trace("The hook for the game application was detached"); + return false; } - isAttached = result != NO_ERROR; - return !isAttached; + isAttached = false; + return true; } diff --git a/src/dll/Hooks/CollectSaveableSystems.cpp b/src/dll/Hooks/CollectSaveableSystems.cpp index dc4cd760..55615eff 100644 --- a/src/dll/Hooks/CollectSaveableSystems.cpp +++ b/src/dll/Hooks/CollectSaveableSystems.cpp @@ -1,8 +1,7 @@ #include "CollectSaveableSystems.hpp" -#include "Addresses.hpp" + #include "Detail/AddressHashes.hpp" #include "Hook.hpp" -#include "stdafx.hpp" namespace { diff --git a/src/dll/Hooks/ExecuteProcess.cpp b/src/dll/Hooks/ExecuteProcess.cpp index 569aa5b6..0bdaccec 100644 --- a/src/dll/Hooks/ExecuteProcess.cpp +++ b/src/dll/Hooks/ExecuteProcess.cpp @@ -1,11 +1,12 @@ #include "ExecuteProcess.hpp" -#include "Addresses.hpp" + #include "App.hpp" #include "Detail/AddressHashes.hpp" #include "Hook.hpp" #include "ScriptCompiler/ScriptCompilerSettings.hpp" #include "Systems/ScriptCompilationSystem.hpp" -#include + +#include namespace { diff --git a/src/dll/Hooks/ExecuteProcess.hpp b/src/dll/Hooks/ExecuteProcess.hpp index 17c534bd..c3bb53b1 100644 --- a/src/dll/Hooks/ExecuteProcess.hpp +++ b/src/dll/Hooks/ExecuteProcess.hpp @@ -1,4 +1,5 @@ #pragma once + #include namespace Hooks::ExecuteProcess diff --git a/src/dll/Hooks/InitScripts.cpp b/src/dll/Hooks/InitScripts.cpp index a5263728..119373c4 100644 --- a/src/dll/Hooks/InitScripts.cpp +++ b/src/dll/Hooks/InitScripts.cpp @@ -1,4 +1,5 @@ #include "InitScripts.hpp" + #include "Addresses.hpp" #include "App.hpp" #include "Detail/AddressHashes.hpp" diff --git a/src/dll/Hooks/LoadScripts.cpp b/src/dll/Hooks/LoadScripts.cpp index 911ef9d6..8b97e627 100644 --- a/src/dll/Hooks/LoadScripts.cpp +++ b/src/dll/Hooks/LoadScripts.cpp @@ -1,4 +1,5 @@ #include "LoadScripts.hpp" + #include "Addresses.hpp" #include "App.hpp" #include "Detail/AddressHashes.hpp" diff --git a/src/dll/Hooks/Main_Hooks.cpp b/src/dll/Hooks/Main_Hooks.cpp index 9a37d4a0..ad489366 100644 --- a/src/dll/Hooks/Main_Hooks.cpp +++ b/src/dll/Hooks/Main_Hooks.cpp @@ -1,9 +1,8 @@ #include "Main_Hooks.hpp" -#include "Addresses.hpp" + #include "App.hpp" #include "Detail/AddressHashes.hpp" #include "Hook.hpp" -#include "stdafx.hpp" namespace { diff --git a/src/dll/Hooks/ValidateScripts.cpp b/src/dll/Hooks/ValidateScripts.cpp index bb7301a8..9bd22e38 100644 --- a/src/dll/Hooks/ValidateScripts.cpp +++ b/src/dll/Hooks/ValidateScripts.cpp @@ -1,4 +1,5 @@ #include "ValidateScripts.hpp" + #include "Addresses.hpp" #include "App.hpp" #include "Detail/AddressHashes.hpp" diff --git a/src/dll/Hooks/ValidateScripts.hpp b/src/dll/Hooks/ValidateScripts.hpp index 2c9064d1..98791d8a 100644 --- a/src/dll/Hooks/ValidateScripts.hpp +++ b/src/dll/Hooks/ValidateScripts.hpp @@ -1,4 +1,5 @@ #pragma once + #include "ScriptValidationError.hpp" #include "Systems/PluginSystem.hpp" diff --git a/src/dll/Hooks/gsmState_SessionActive.cpp b/src/dll/Hooks/gsmState_SessionActive.cpp index 1040a890..c25d2c2b 100644 --- a/src/dll/Hooks/gsmState_SessionActive.cpp +++ b/src/dll/Hooks/gsmState_SessionActive.cpp @@ -1,9 +1,8 @@ #include "gsmState_SessionActive.hpp" -#include "Addresses.hpp" + #include "App.hpp" #include "Detail/AddressHashes.hpp" #include "Hook.hpp" -#include "stdafx.hpp" namespace { diff --git a/src/dll/Hooks/gsmState_SessionActive.hpp b/src/dll/Hooks/gsmState_SessionActive.hpp index 6824fcd2..901913b4 100644 --- a/src/dll/Hooks/gsmState_SessionActive.hpp +++ b/src/dll/Hooks/gsmState_SessionActive.hpp @@ -1,4 +1,5 @@ #pragma once + namespace Hooks::gsmState_SessionActive { bool Attach(); diff --git a/src/dll/Image.cpp b/src/dll/Image.cpp index f4c261bb..38acc544 100644 --- a/src/dll/Image.cpp +++ b/src/dll/Image.cpp @@ -1,10 +1,11 @@ #include "Image.hpp" + #include "Utils.hpp" Image::Image() : m_isCyberpunk(false) - , m_fileVersion(RED4EXT_V0_FILEVER(0, 0, 0, 0)) - , m_productVersion(RED4EXT_V0_SEMVER(0, 0, 0)) + , m_fileVersion(RED4EXT_FILEVER(0, 0, 0, 0)) + , m_productVersion(RED4EXT_SEMVER(0, 0, 0)) { std::wstring fileName; auto hr = wil::GetModuleFileNameW(nullptr, fileName); diff --git a/src/dll/MemoryProtection.cpp b/src/dll/MemoryProtection.cpp index 1705f7f4..85bbf230 100644 --- a/src/dll/MemoryProtection.cpp +++ b/src/dll/MemoryProtection.cpp @@ -1,4 +1,5 @@ #include "MemoryProtection.hpp" + #include "Utils.hpp" MemoryProtection::MemoryProtection(void* aAddress, size_t aSize, uint32_t aProtection) diff --git a/src/dll/Paths.cpp b/src/dll/Paths.cpp index 64920d2b..40b8ffe2 100644 --- a/src/dll/Paths.cpp +++ b/src/dll/Paths.cpp @@ -1,4 +1,5 @@ #include "Paths.hpp" + #include "Utils.hpp" Paths::Paths() diff --git a/src/dll/PluginBase.cpp b/src/dll/PluginBase.cpp index 31fef8bb..381541c4 100644 --- a/src/dll/PluginBase.cpp +++ b/src/dll/PluginBase.cpp @@ -1,7 +1,26 @@ -#include "stdafx.hpp" #include "PluginBase.hpp" + #include "Utils.hpp" +namespace +{ +std::wstring_view GetReasonString(RED4ext::EMainReason aReason) +{ + switch (aReason) + { + case RED4ext::EMainReason::Load: + return L"Load"; + case RED4ext::EMainReason::Unload: + return L"Unload"; + case RED4ext::EMainReason::Run: + return L"Run"; + default: + spdlog::error(L"Unknown reason {}", static_cast>(aReason)); + return L"Unknown"; + } +} +} // namespace + PluginBase::PluginBase(const std::filesystem::path& aPath, wil::unique_hmodule aModule) : m_path(aPath) , m_module(std::move(aModule)) @@ -75,7 +94,7 @@ bool PluginBase::Main(RED4ext::EMainReason aReason) { const auto module = GetModule(); const auto name = GetName(); - const auto reasonStr = aReason == RED4ext::EMainReason::Load ? L"Load" : L"Unload"; + const auto reasonStr = GetReasonString(aReason); spdlog::trace(L"Calling 'Main' function exported by '{}' with reason '{}'...", name, reasonStr); @@ -85,7 +104,7 @@ bool PluginBase::Main(RED4ext::EMainReason aReason) { try { - auto success = mainFn(module, aReason, GetSdkStruct()); + auto success = mainFn(std::bit_cast(module), aReason, GetSdkStruct()); if (!success) { spdlog::trace(L"'Main' function returned 'false'"); @@ -103,8 +122,9 @@ bool PluginBase::Main(RED4ext::EMainReason aReason) } catch (...) { - spdlog::warn(L"An unknown exception occured while calling 'Main' function with reason '{}', exported by '{}'", - reasonStr, name); + spdlog::warn( + L"An unknown exception occured while calling 'Main' function with reason '{}', exported by '{}'", + reasonStr, name); return false; } } diff --git a/src/dll/ScriptCompiler/ScriptCompilerFailure.hpp b/src/dll/ScriptCompiler/ScriptCompilerFailure.hpp index 3136aff9..41452d7b 100644 --- a/src/dll/ScriptCompiler/ScriptCompilerFailure.hpp +++ b/src/dll/ScriptCompiler/ScriptCompilerFailure.hpp @@ -1,4 +1,5 @@ #pragma once + #include class ScriptCompilerFailure diff --git a/src/dll/ScriptCompiler/ScriptCompilerOutput.hpp b/src/dll/ScriptCompiler/ScriptCompilerOutput.hpp index 33fed78a..0dabc378 100644 --- a/src/dll/ScriptCompiler/ScriptCompilerOutput.hpp +++ b/src/dll/ScriptCompiler/ScriptCompilerOutput.hpp @@ -1,5 +1,7 @@ #pragma once + #include "ScriptCompilerSourceRef.hpp" + #include class ScriptCompilerOutput diff --git a/src/dll/ScriptCompiler/ScriptCompilerSettings.hpp b/src/dll/ScriptCompiler/ScriptCompilerSettings.hpp index 89ee404c..fb7a33a4 100644 --- a/src/dll/ScriptCompiler/ScriptCompilerSettings.hpp +++ b/src/dll/ScriptCompiler/ScriptCompilerSettings.hpp @@ -1,6 +1,8 @@ #pragma once + #include "ScriptCompilerFailure.hpp" #include "ScriptCompilerOutput.hpp" + #include class ScriptCompilerSettings diff --git a/src/dll/ScriptCompiler/ScriptCompilerSourceRef.hpp b/src/dll/ScriptCompiler/ScriptCompilerSourceRef.hpp index aa0eb54b..b38b8e1b 100644 --- a/src/dll/ScriptCompiler/ScriptCompilerSourceRef.hpp +++ b/src/dll/ScriptCompiler/ScriptCompilerSourceRef.hpp @@ -1,4 +1,5 @@ #pragma once + #include class ScriptCompilerSourceRef diff --git a/src/dll/ScriptValidationError.cpp b/src/dll/ScriptValidationError.cpp index a96758cb..7baeca18 100644 --- a/src/dll/ScriptValidationError.cpp +++ b/src/dll/ScriptValidationError.cpp @@ -1,4 +1,5 @@ #include "ScriptValidationError.hpp" + #include "App.hpp" ValidationError ValidationError::FromString(const char* str) @@ -43,7 +44,7 @@ ValidationError ValidationError::FromString(const char* str) type = ValidationErrorType::PropertyTypeMismatch; } - return { .type = type, .name = name, .parent = parent }; + return {.type = type, .name = name, .parent = parent}; } std::optional ValidationError::GetSourceRef() const diff --git a/src/dll/States/BaseInitializationState.cpp b/src/dll/States/BaseInitializationState.cpp deleted file mode 100644 index 178081c6..00000000 --- a/src/dll/States/BaseInitializationState.cpp +++ /dev/null @@ -1,64 +0,0 @@ -#include "stdafx.hpp" -#include "BaseInitializationState.hpp" -#include "App.hpp" -#include "GameStateHook.hpp" - -namespace -{ -GameStateHook CBaseInitializationState(&States::BaseInitializationState::OnEnter, - &States::BaseInitializationState::OnUpdate, - &States::BaseInitializationState::OnExit); -} - -bool States::BaseInitializationState::OnEnter(RED4ext::CBaseInitializationState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - auto result = CBaseInitializationState.OnEnter(aThis, aApp); - stateSystem->OnEnter(RED4ext::EGameStateType::BaseInitialization, aApp); - - return result; -} - -bool States::BaseInitializationState::OnUpdate(RED4ext::CBaseInitializationState* aThis, - RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - auto result = CBaseInitializationState.OnUpdate(aThis, aApp); - result = stateSystem->OnUpdate(RED4ext::EGameStateType::BaseInitialization, aApp) && result; - - /* - * Doing this because the game might call "SetState" which will also change the application status and will force - * the state to move on. - */ - if (!result) - { - aApp->status = RED4ext::EGameStateStatus::Initialized; - return false; - } - - aApp->status = RED4ext::EGameStateStatus::Ran; - return true; -} - -bool States::BaseInitializationState::OnExit(RED4ext::CBaseInitializationState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - stateSystem->OnExit(RED4ext::EGameStateType::BaseInitialization, aApp); - return CBaseInitializationState.OnExit(aThis, aApp); -} - -bool States::BaseInitializationState::Attach(RED4ext::CBaseInitializationState* aState) -{ - return CBaseInitializationState.AttachAt(aState); -} - -bool States::BaseInitializationState::Detach(RED4ext::CBaseInitializationState* aState) -{ - return CBaseInitializationState.DetachAt(aState); -} diff --git a/src/dll/States/BaseInitializationState.hpp b/src/dll/States/BaseInitializationState.hpp deleted file mode 100644 index 4dbad5bd..00000000 --- a/src/dll/States/BaseInitializationState.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -namespace States::BaseInitializationState -{ -bool OnEnter(RED4ext::CBaseInitializationState* aThis, RED4ext::CGameApplication* aApp); -bool OnUpdate(RED4ext::CBaseInitializationState* aThis, RED4ext::CGameApplication* aApp); -bool OnExit(RED4ext::CBaseInitializationState* aThis, RED4ext::CGameApplication* aApp); - -bool Attach(RED4ext::CBaseInitializationState* aState); -bool Detach(RED4ext::CBaseInitializationState* aState); -} // namespace States::BaseInitializationState diff --git a/src/dll/States/InitializationState.cpp b/src/dll/States/InitializationState.cpp deleted file mode 100644 index 0da1d36a..00000000 --- a/src/dll/States/InitializationState.cpp +++ /dev/null @@ -1,63 +0,0 @@ -#include "stdafx.hpp" -#include "InitializationState.hpp" -#include "App.hpp" -#include "GameStateHook.hpp" - -namespace -{ -GameStateHook CInitializationState(&States::InitializationState::OnEnter, - &States::InitializationState::OnUpdate, - &States::InitializationState::OnExit); -} - -bool States::InitializationState::OnEnter(RED4ext::CInitializationState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - auto result = CInitializationState.OnEnter(aThis, aApp); - stateSystem->OnEnter(RED4ext::EGameStateType::Initialization, aApp); - - return result; -} - -bool States::InitializationState::OnUpdate(RED4ext::CInitializationState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - auto result = CInitializationState.OnUpdate(aThis, aApp); - result = stateSystem->OnUpdate(RED4ext::EGameStateType::Initialization, aApp) && result; - - /* - * Doing this because the game might call "SetState" which will also change the application status and will force - * the state to move on. - */ - if (!result) - { - aApp->status = RED4ext::EGameStateStatus::Initialized; - return false; - } - - aApp->status = RED4ext::EGameStateStatus::Ran; - return true; -} - -bool States::InitializationState::OnExit(RED4ext::CInitializationState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - stateSystem->OnExit(RED4ext::EGameStateType::Initialization, aApp); - return CInitializationState.OnExit(aThis, aApp); -} - -bool States::InitializationState::Attach(RED4ext::CInitializationState* aState) -{ - return CInitializationState.AttachAt(aState); -} - -bool States::InitializationState::Detach(RED4ext::CInitializationState* aState) -{ - return CInitializationState.DetachAt(aState); -} diff --git a/src/dll/States/InitializationState.hpp b/src/dll/States/InitializationState.hpp deleted file mode 100644 index 8370a1e8..00000000 --- a/src/dll/States/InitializationState.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -namespace States::InitializationState -{ -bool OnEnter(RED4ext::CInitializationState* aThis, RED4ext::CGameApplication* aApp); -bool OnUpdate(RED4ext::CInitializationState* aThis, RED4ext::CGameApplication* aApp); -bool OnExit(RED4ext::CInitializationState* aThis, RED4ext::CGameApplication* aApp); - -bool Attach(RED4ext::CInitializationState* aState); -bool Detach(RED4ext::CInitializationState* aState); -} // namespace States::InitializationState diff --git a/src/dll/States/RunningState.cpp b/src/dll/States/RunningState.cpp deleted file mode 100644 index 599fde71..00000000 --- a/src/dll/States/RunningState.cpp +++ /dev/null @@ -1,51 +0,0 @@ -#include "stdafx.hpp" -#include "RunningState.hpp" -#include "App.hpp" -#include "GameStateHook.hpp" - -namespace -{ -GameStateHook CRunningState(&States::RunningState::OnEnter, &States::RunningState::OnUpdate, - &States::RunningState::OnExit); -} - -bool States::RunningState::OnEnter(RED4ext::CRunningState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - auto result = CRunningState.OnEnter(aThis, aApp); - stateSystem->OnEnter(RED4ext::EGameStateType::Running, aApp); - - return result; -} - -bool States::RunningState::OnUpdate(RED4ext::CRunningState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - auto result = CRunningState.OnUpdate(aThis, aApp); - stateSystem->OnUpdate(RED4ext::EGameStateType::Running, aApp); - - return result; -} - -bool States::RunningState::OnExit(RED4ext::CRunningState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - stateSystem->OnExit(RED4ext::EGameStateType::Running, aApp); - return CRunningState.OnExit(aThis, aApp); -} - -bool States::RunningState::Attach(RED4ext::CRunningState* aState) -{ - return CRunningState.AttachAt(aState); -} - -bool States::RunningState::Detach(RED4ext::CRunningState* aState) -{ - return CRunningState.DetachAt(aState); -} diff --git a/src/dll/States/RunningState.hpp b/src/dll/States/RunningState.hpp deleted file mode 100644 index b2211232..00000000 --- a/src/dll/States/RunningState.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -namespace States::RunningState -{ -bool OnEnter(RED4ext::CRunningState* aThis, RED4ext::CGameApplication* aApp); -bool OnUpdate(RED4ext::CRunningState* aThis, RED4ext::CGameApplication* aApp); -bool OnExit(RED4ext::CRunningState* aThis, RED4ext::CGameApplication* aApp); - -bool Attach(RED4ext::CRunningState* aState); -bool Detach(RED4ext::CRunningState* aState); -} // namespace States::RunningState diff --git a/src/dll/States/ShutdownState.cpp b/src/dll/States/ShutdownState.cpp deleted file mode 100644 index c686722f..00000000 --- a/src/dll/States/ShutdownState.cpp +++ /dev/null @@ -1,60 +0,0 @@ -#include "stdafx.hpp" -#include "ShutdownState.hpp" -#include "App.hpp" -#include "GameStateHook.hpp" - -namespace -{ -GameStateHook CShutdownState(&States::ShutdownState::OnEnter, &States::ShutdownState::OnUpdate, - &States::ShutdownState::OnExit); -} - -bool States::ShutdownState::OnEnter(RED4ext::CShutdownState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - stateSystem->OnEnter(RED4ext::EGameStateType::Shutdown, aApp); - return CShutdownState.OnEnter(aThis, aApp); -} - -bool States::ShutdownState::OnUpdate(RED4ext::CShutdownState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - auto result = stateSystem->OnUpdate(RED4ext::EGameStateType::Shutdown, aApp); - result = result && CShutdownState.OnUpdate(aThis, aApp); - - /* - * Doing this because the game might call "SetState" which will also change the application status and will force - * the state to move on. - */ - if (!result) - { - aApp->status = RED4ext::EGameStateStatus::Initialized; - return false; - } - - aApp->status = RED4ext::EGameStateStatus::Ran; - return true; -} - -bool States::ShutdownState::OnExit(RED4ext::CShutdownState* aThis, RED4ext::CGameApplication* aApp) -{ - auto app = App::Get(); - auto stateSystem = app->GetStateSystem(); - - stateSystem->OnExit(RED4ext::EGameStateType::Shutdown, aApp); - return CShutdownState.OnExit(aThis, aApp); -} - -bool States::ShutdownState::Attach(RED4ext::CShutdownState* aState) -{ - return CShutdownState.AttachAt(aState); -} - -bool States::ShutdownState::Detach(RED4ext::CShutdownState* aState) -{ - return CShutdownState.DetachAt(aState); -} diff --git a/src/dll/States/ShutdownState.hpp b/src/dll/States/ShutdownState.hpp deleted file mode 100644 index e1b238af..00000000 --- a/src/dll/States/ShutdownState.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -namespace States::ShutdownState -{ -bool OnEnter(RED4ext::CShutdownState* aThis, RED4ext::CGameApplication* aApp); -bool OnUpdate(RED4ext::CShutdownState* aThis, RED4ext::CGameApplication* aApp); -bool OnExit(RED4ext::CShutdownState* aThis, RED4ext::CGameApplication* aApp); - -bool Attach(RED4ext::CShutdownState* aState); -bool Detach(RED4ext::CShutdownState* aState); -} // namespace States::ShutdownState diff --git a/src/dll/Systems/HookingSystem.cpp b/src/dll/Systems/HookingSystem.cpp index fd09ed60..7e065118 100644 --- a/src/dll/Systems/HookingSystem.cpp +++ b/src/dll/Systems/HookingSystem.cpp @@ -1,5 +1,5 @@ -#include "stdafx.hpp" #include "HookingSystem.hpp" + #include "DetourTransaction.hpp" ESystemType HookingSystem::GetType() diff --git a/src/dll/Systems/HookingSystem.hpp b/src/dll/Systems/HookingSystem.hpp index fbdd786c..ac95c3b9 100644 --- a/src/dll/Systems/HookingSystem.hpp +++ b/src/dll/Systems/HookingSystem.hpp @@ -4,13 +4,13 @@ #include "ISystem.hpp" #include "PluginBase.hpp" -class HookingSystem : public ISystem +class HookingSystem final : public ISystem { public: - ESystemType GetType() final; + ESystemType GetType() override; - void Startup() final; - void Shutdown() final; + void Startup() override; + void Shutdown() override; bool Attach(std::shared_ptr aPlugin, void* aTarget, void* aDetour, void** aOriginal); bool Detach(std::shared_ptr aPlugin, void* aTarget); diff --git a/src/dll/Systems/LoggerSystem.cpp b/src/dll/Systems/LoggerSystem.cpp index 132bbe45..a8fc3d7c 100644 --- a/src/dll/Systems/LoggerSystem.cpp +++ b/src/dll/Systems/LoggerSystem.cpp @@ -1,7 +1,7 @@ #include "LoggerSystem.hpp" + #include "Config.hpp" #include "Paths.hpp" -#include "stdafx.hpp" LoggerSystem::LoggerSystem(const Paths& aPaths, const Config& aConfig, const DevConsole& aDevConsole) : m_paths(aPaths) diff --git a/src/dll/Systems/LoggerSystem.hpp b/src/dll/Systems/LoggerSystem.hpp index bd364466..fd529cc3 100644 --- a/src/dll/Systems/LoggerSystem.hpp +++ b/src/dll/Systems/LoggerSystem.hpp @@ -1,20 +1,18 @@ #pragma once -#include - #include "ISystem.hpp" #include "PluginBase.hpp" #include "Utils.hpp" -class LoggerSystem : public ISystem +class LoggerSystem final : public ISystem { public: LoggerSystem(const Paths& aPaths, const Config& aConfig, const DevConsole& aDevConsole); - ESystemType GetType() final; + ESystemType GetType() override; - void Startup() final; - void Shutdown() final; + void Startup() override; + void Shutdown() override; void RotateLogs(std::vector plugins) const; @@ -38,8 +36,7 @@ class LoggerSystem : public ISystem private: template - inline void Log(std::shared_ptr aPlugin, spdlog::level::level_enum aLevel, - std::basic_string_view aText) + void Log(std::shared_ptr aPlugin, spdlog::level::level_enum aLevel, std::basic_string_view aText) { if (!aPlugin) { diff --git a/src/dll/Systems/PluginSystem.cpp b/src/dll/Systems/PluginSystem.cpp index 2bedb425..68abc614 100644 --- a/src/dll/Systems/PluginSystem.cpp +++ b/src/dll/Systems/PluginSystem.cpp @@ -1,10 +1,12 @@ #include "PluginSystem.hpp" + +#include "App.hpp" #include "Image.hpp" #include "Utils.hpp" #include "Version.hpp" -#include "v0/Plugin.hpp" +#include "v1/Plugin.hpp" -#define MINIMUM_API_VERSION RED4EXT_API_VERSION_0 +#define MINIMUM_API_VERSION RED4EXT_API_VERSION_1 #define LATEST_API_VERSION RED4EXT_API_VERSION_LATEST #define MINIMUM_SDK_VERSION RED4EXT_SDK_0_5_0 @@ -144,10 +146,18 @@ void PluginSystem::Startup() Load(pluginInfo.path, pluginInfo.useAlteredSearchPath); } - // In the case where the exe is hosted, check if the host exe has RED4Ext exports + // In the case where the exe is hosted, check if the host exe has RED4Ext exports. Load(m_paths.GetExe(), false); spdlog::info("{} plugin(s) loaded", m_plugins.size()); + + // Install CRunningState::OnAfterEnter hook for notifying plugins about entry to Running state. + RED4ext::GameState stateHook{.OnAfterEnter = [](RED4ext::CGameApplication&) + { + App::Get()->GetPluginSystem()->EnteredRunningState(); + return RED4ext::EGameStateResult::Finished; + }}; + App::Get()->GetStateSystem()->AddHook(nullptr, RED4ext::EGameStateType::Running, stateHook); } void PluginSystem::Shutdown() @@ -168,6 +178,24 @@ void PluginSystem::Shutdown() spdlog::info("{} plugin(s) unloaded", size); } +void PluginSystem::EnteredRunningState() const +{ + if (!m_config.isEnabled) + { + return; + } + + auto size = m_plugins.size(); + spdlog::trace("Notifying {} plugin(s) about entry to Running state...", size); + + for (const auto& plugin : m_plugins | std::views::values) + { + plugin->Main(RED4ext::EMainReason::Run); + } + + spdlog::info("{} plugin(s) notified about entry to Running state", size); +} + std::shared_ptr PluginSystem::GetPlugin(HMODULE aModule) const { auto iter = m_plugins.find(aModule); @@ -190,7 +218,7 @@ std::vector PluginSystem::GetActivePlugins() const std::vector plugins; plugins.reserve(m_plugins.size()); - for (const auto& [handle, plugin] : m_plugins) + for (const auto& plugin : m_plugins | std::views::values) { plugins.emplace_back(plugin->GetName()); } @@ -357,9 +385,9 @@ std::shared_ptr PluginSystem::CreatePlugin(const std::filesystem::pa switch (apiVersion) { - case RED4EXT_API_VERSION_0: + case RED4EXT_API_VERSION_1: { - return std::make_shared(aPath, std::move(aModule)); + return std::make_shared(aPath, std::move(aModule)); } } diff --git a/src/dll/Systems/PluginSystem.hpp b/src/dll/Systems/PluginSystem.hpp index eceefddb..b026c93b 100644 --- a/src/dll/Systems/PluginSystem.hpp +++ b/src/dll/Systems/PluginSystem.hpp @@ -5,18 +5,18 @@ #include "Paths.hpp" #include "PluginBase.hpp" -class PluginSystem : public ISystem +class PluginSystem final : public ISystem { public: using PluginName = std::wstring; PluginSystem(const Config::PluginsConfig& aConfig, const Paths& aPaths); - ~PluginSystem() = default; + ~PluginSystem() override = default; - ESystemType GetType() final; + ESystemType GetType() override; - void Startup() final; - void Shutdown() final; + void Startup() override; + void Shutdown() override; std::shared_ptr GetPlugin(HMODULE aModule) const; const std::vector& GetIncompatiblePlugins() const; @@ -35,6 +35,8 @@ class PluginSystem : public ISystem void Load(const std::filesystem::path& aPath, bool aUseAlteredSearchPath); MapIter_t Unload(std::shared_ptr aPlugin); + void EnteredRunningState() const; + std::shared_ptr CreatePlugin(const std::filesystem::path& aPath, wil::unique_hmodule aModule) const; const Config::PluginsConfig& m_config; diff --git a/src/dll/Systems/ScriptCompilationSystem.cpp b/src/dll/Systems/ScriptCompilationSystem.cpp index f3568e6e..d158081a 100644 --- a/src/dll/Systems/ScriptCompilationSystem.cpp +++ b/src/dll/Systems/ScriptCompilationSystem.cpp @@ -1,4 +1,5 @@ #include "ScriptCompilationSystem.hpp" + #include "Utils.hpp" ScriptCompilationSystem::ScriptCompilationSystem(const Paths& aPaths) @@ -67,13 +68,13 @@ bool ScriptCompilationSystem::Add(std::shared_ptr aPlugin, const wch { if (std::filesystem::exists(resolvedPath)) { - spdlog::trace(L"Found absolute path: {}", resolvedPath.wstring().c_str()); + spdlog::trace(L"Found absolute path: {}", resolvedPath.native().c_str()); Add(aPlugin, resolvedPath); return true; } else { - spdlog::error(L"Could not find absolute path: {}", resolvedPath.wstring().c_str()); + spdlog::error(L"Could not find absolute path: {}", resolvedPath.native().c_str()); return false; } } @@ -82,13 +83,13 @@ bool ScriptCompilationSystem::Add(std::shared_ptr aPlugin, const wch resolvedPath = aPlugin->GetPath().parent_path() / aPath; if (std::filesystem::exists(resolvedPath)) { - spdlog::trace(L"Found path relative to plugin: {}", resolvedPath.wstring().c_str()); + spdlog::trace(L"Found path relative to plugin: {}", resolvedPath.native().c_str()); Add(aPlugin, resolvedPath); return true; } else { - spdlog::error(L"Could not find path relative to plugin: {}", resolvedPath.wstring().c_str()); + spdlog::error(L"Could not find path relative to plugin: {}", resolvedPath.native().c_str()); return false; } } @@ -112,7 +113,7 @@ std::wstring ScriptCompilationSystem::GetCompilationArgs(const FixedWString& aOr for (const auto& [plugin, path] : m_scriptPaths) { spdlog::info(L"{}: '{}'", plugin->GetName(), path); - pathsFile << path.wstring() << std::endl; + pathsFile << path.native() << std::endl; } spdlog::info(L"Paths written to: '{}'", pathsFilePath); format_to(std::back_inserter(buffer), LR"( -compilePathsFile "{}")", pathsFilePath); diff --git a/src/dll/Systems/ScriptCompilationSystem.hpp b/src/dll/Systems/ScriptCompilationSystem.hpp index a2525c94..16224bdb 100644 --- a/src/dll/Systems/ScriptCompilationSystem.hpp +++ b/src/dll/Systems/ScriptCompilationSystem.hpp @@ -13,7 +13,7 @@ struct FixedWString const wchar_t* str; }; -class ScriptCompilationSystem : public ISystem +class ScriptCompilationSystem final : public ISystem { using Map_t = std::unordered_multimap, std::filesystem::path>; using MapIter_t = Map_t::iterator; @@ -21,10 +21,10 @@ class ScriptCompilationSystem : public ISystem public: ScriptCompilationSystem(const Paths& aPaths); - ESystemType GetType() final; + ESystemType GetType() override; - void Startup() final; - void Shutdown() final; + void Startup() override; + void Shutdown() override; bool Add(std::shared_ptr aPlugin, const wchar_t* aPath); diff --git a/src/dll/Systems/StateSystem.cpp b/src/dll/Systems/StateSystem.cpp index 6e034bb4..63fa6165 100644 --- a/src/dll/Systems/StateSystem.cpp +++ b/src/dll/Systems/StateSystem.cpp @@ -1,6 +1,4 @@ -#include "stdafx.hpp" #include "StateSystem.hpp" -#include "Utils.hpp" ESystemType StateSystem::GetType() { @@ -13,148 +11,296 @@ void StateSystem::Startup() void StateSystem::Shutdown() { - spdlog::trace("Removing all game states..."); + spdlog::trace("Removing all game state hooks..."); - m_baseInitialization.onEnter.clear(); - m_baseInitialization.onUpdate.clear(); - m_baseInitialization.onExit.clear(); + m_baseInitialization.Shutdown(); + m_initialization.Shutdown(); + m_running.Shutdown(); + m_shutdown.Shutdown(); - m_initialization.onEnter.clear(); - m_initialization.onUpdate.clear(); - m_initialization.onExit.clear(); + spdlog::trace("All game state hooks were removed successfully"); +} + +bool StateSystem::OnChangeState(RED4ext::CGameApplication& aApp, const RED4ext::EGameStateType aNextState) +{ + const auto nextStateRef = aApp.states.Get(aNextState); + if (!nextStateRef) + { + assert(false && "Unable to get next state reference from states map!"); + + spdlog::error("Unable to get next state reference for state with ID {} from states map!", + static_cast>(aNextState)); + + // Override failed. + return false; + } + + const auto nextState = *nextStateRef; + if (nextState == aApp.nextState) + { + assert(aApp.nextState && "Next state was unexpectedly unset!"); - m_running.onEnter.clear(); - m_running.onUpdate.clear(); - m_running.onExit.clear(); + spdlog::trace("Already transitioned to state '{}' ({}), ignoring change of state.", aApp.nextState->GetName(), + static_cast>(aApp.nextState->GetType())); - m_shutdown.onEnter.clear(); - m_shutdown.onUpdate.clear(); - m_shutdown.onExit.clear(); + // Override successful. + return true; + } - spdlog::trace("All game states were removed successfully"); + if (aApp.nextState) + { + spdlog::warn( + "Already set up next state to '{}' ({}), ignoring change of state to '{}' ({}).", aApp.nextState->GetName(), + static_cast>(aApp.nextState->GetType()), + nextState->GetName(), static_cast>(nextState->GetType())); + } + else + { + aApp.nextState = nextState; + } + + m_shouldSwitchState = true; + + // Override successful. + return true; } -bool StateSystem::Add(std::shared_ptr aPlugin, RED4ext::EGameStateType aStateType, Func_t aOnEnter, - Func_t aOnUpdate, Func_t aOnExit) +bool StateSystem::OnDoState(RED4ext::CGameApplication& aApp) { - State* state = GetStateByType(aStateType); - if (state) + const auto switchToNextState = aApp.stateStatus == RED4ext::EGameStateStatus::NextState; + + if (!aApp.currentState || switchToNextState) { - if (aOnEnter) + if (aApp.nextState || switchToNextState) { - state->onEnter.emplace_back(aPlugin, aOnEnter); + aApp.currentState = aApp.nextState; + aApp.stateStatus = RED4ext::EGameStateStatus::Enter; + aApp.nextState = nullptr; } - - if (aOnUpdate) + else { - state->onUpdate.emplace_back(aPlugin, aOnUpdate); - } + spdlog::error("No state to execute!"); - if (aOnExit) - { - state->onExit.emplace_back(aPlugin, aOnExit); + // Override failed. + return false; } + + m_originalStateActionFinished = false; + m_shouldSwitchState = false; + + // Override successful. + return true; } - return state; -} + assert(aApp.stateStatus != RED4ext::EGameStateStatus::NextState && "State status unexpectedly equal to NextState!"); -bool StateSystem::OnEnter(RED4ext::EGameStateType aStateType, RED4ext::CGameApplication* aApp) -{ - State* state = GetStateByType(aStateType); - if (state) + static const auto runHooks = [](std::wstring_view aAction, auto& aHooks, RED4ext::CGameApplication& aApp) { - auto action = fmt::format(L"{}::OnEnter", Utils::GetStateName(aStateType)); - return Run(action, state->onEnter, aApp); - } + bool finished = true; - return true; -} + for (auto it = aHooks.begin(); it != aHooks.end();) + { + const std::wstring_view pluginName = it->plugin ? it->plugin->GetName() : L"RED4ext"; -bool StateSystem::OnUpdate(RED4ext::EGameStateType aStateType, RED4ext::CGameApplication* aApp) -{ - State* state = GetStateByType(aStateType); - if (state) + try + { + switch (const auto result = it->hook(aApp)) + { + case RED4ext::EGameStateResult::Finished: + spdlog::trace(L"Finished running '{}' registered by '{}'", aAction, pluginName); + it = aHooks.erase(it); + break; + case RED4ext::EGameStateResult::Running: + finished = false; + [[fallthrough]]; + case RED4ext::EGameStateResult::Observing: + ++it; + break; + default: + spdlog::warn(L"Unrecognized result {} for '{}' registered by '{}' - assuming the hook finished", + static_cast>(result), aAction, + pluginName); + it = aHooks.erase(it); + break; + } + } + catch (const std::exception& e) + { + spdlog::warn(L"An exception occured while executing '{}' registered by '{}'", aAction, pluginName); + spdlog::warn(e.what()); + } + catch (...) + { + spdlog::warn(L"An unknown exception occured while executing '{}' registered by '{}'", aAction, + pluginName); + } + } + + return finished; + }; + + static const std::array, + static_cast>( + RED4ext::EGameStateStatus::NextState)> + stateStatusActions = {[this](RED4ext::IGameState& aState, RED4ext::CGameApplication& aApp) + { + if (aState.OnEnter(aApp)) + { + m_nextStateStatus = RED4ext::EGameStateStatus::Tick; + } + else + { + m_nextStateStatus = RED4ext::EGameStateStatus::Exit; + } + + // Always run original OnEnter only once regardless of the result to emulate game. + m_originalStateActionFinished = true; + }, + [this](RED4ext::IGameState& aState, RED4ext::CGameApplication& aApp) + { + m_originalStateActionFinished = aState.OnTick(aApp); + + if (m_originalStateActionFinished) + m_nextStateStatus = RED4ext::EGameStateStatus::Exit; + }, + [this](RED4ext::IGameState& aState, RED4ext::CGameApplication& aApp) + { + std::ignore = aState.OnExit(aApp); + + // Always run original OnExit only once regardless of the result to emulate game. + m_originalStateActionFinished = true; + + m_nextStateStatus = RED4ext::EGameStateStatus::NextState; + }}; + + auto& state = *aApp.currentState; + auto& stateHooks = GetStateHooks(state.GetType()); + auto& actionHooks = stateHooks.GetActionHooksForStatus(aApp.stateStatus); + + const auto hooksBeforeFinished = + runHooks(fmt::format(L"{}::OnBefore{}", stateHooks.name, actionHooks.name), actionHooks.onBefore, aApp); + + if (!m_originalStateActionFinished) { - auto action = fmt::format(L"{}::OnUpdate", Utils::GetStateName(aStateType)); - return Run(action, state->onUpdate, aApp); + stateStatusActions[static_cast>(aApp.stateStatus)](state, + aApp); } - return true; -} + const auto hooksAfterFinished = + runHooks(fmt::format(L"{}::OnAfter{}", stateHooks.name, actionHooks.name), actionHooks.onAfter, aApp); -bool StateSystem::OnExit(RED4ext::EGameStateType aStateType, RED4ext::CGameApplication* aApp) -{ - State* state = GetStateByType(aStateType); - if (state) + // Transition state when current action finished execution. + if (m_originalStateActionFinished && hooksBeforeFinished && hooksAfterFinished) { - auto action = fmt::format(L"{}::OnExit", Utils::GetStateName(aStateType)); - return Run(action, state->onExit, aApp); + // Check if switch of state was enforced by original state action. + if (m_shouldSwitchState) + { + // Override state status. + aApp.stateStatus = RED4ext::EGameStateStatus::NextState; + m_shouldSwitchState = false; + } + else + { + // Normal transition. + aApp.stateStatus = m_nextStateStatus; + } + + m_originalStateActionFinished = false; } + // Override successful. return true; } -StateSystem::State* StateSystem::GetStateByType(RED4ext::EGameStateType aStateType) +bool StateSystem::AddHook(std::shared_ptr aPlugin, const RED4ext::EGameStateType aStateType, + const RED4ext::GameState& aState) { - using enum RED4ext::EGameStateType; - switch (aStateType) - { - case BaseInitialization: + auto& stateHooks = GetStateHooks(aStateType); + + if (aState.OnBeforeEnter) { - return &m_baseInitialization; + stateHooks.onEnter.onBefore.emplace_back(aPlugin, aState.OnBeforeEnter); } - case Initialization: + + if (aState.OnAfterEnter) { - return &m_initialization; + stateHooks.onEnter.onAfter.emplace_back(aPlugin, aState.OnAfterEnter); } - case Running: + + if (aState.OnBeforeTick) { - return &m_running; + stateHooks.onTick.onBefore.emplace_back(aPlugin, aState.OnBeforeTick); } - case Shutdown: + + if (aState.OnAfterTick) { - return &m_shutdown; + stateHooks.onTick.onAfter.emplace_back(aPlugin, aState.OnAfterTick); } - default: + + if (aState.OnBeforeExit) { - spdlog::warn("State with type {} is not handled", static_cast(aStateType)); - break; + stateHooks.onExit.onBefore.emplace_back(aPlugin, aState.OnBeforeExit); } + + if (aState.OnAfterExit) + { + stateHooks.onExit.onAfter.emplace_back(aPlugin, aState.OnAfterExit); } - return nullptr; + spdlog::trace(L"The request to add a '{}' state hooks for plugin '{}' has been successfully completed", + stateHooks.name, aPlugin ? aPlugin->GetName() : L"RED4ext"); + return true; } -bool StateSystem::Run(std::wstring_view aAction, std::list& aList, RED4ext::CGameApplication* aApp) +void StateSystem::StateHooks::Action::Shutdown() { - bool result = true; - for (auto it = aList.begin(); it != aList.end();) - { - auto pluginName = it->plugin->GetName(); + onBefore.clear(); + onAfter.clear(); +} - try - { - if (it->func(aApp)) - { - it = aList.erase(it); - } - else - { - ++it; - result = false; - } - } - catch (const std::exception& e) - { - spdlog::warn(L"An exception occured while executing '{}' registered by '{}'", aAction, pluginName); - spdlog::warn(e.what()); - } - catch (...) - { - spdlog::warn(L"An unknown exception occured while executing '{}' registered by '{}'", aAction, pluginName); - } +void StateSystem::StateHooks::Shutdown() +{ + onEnter.Shutdown(); + onTick.Shutdown(); + onExit.Shutdown(); +} + +#pragma warning(push) +#pragma warning(disable : 4715) // "Not all control paths return a value." -> intentional, should never happen. + +StateSystem::StateHooks::Action& StateSystem::StateHooks::GetActionHooksForStatus( + const RED4ext::EGameStateStatus aStateStatus) +{ + assert(aStateStatus >= RED4ext::EGameStateStatus::Enter && aStateStatus <= RED4ext::EGameStateStatus::Exit && + "State status unexpectedly out of range!"); + + switch (aStateStatus) + { + case RED4ext::EGameStateStatus::Enter: + return onEnter; + case RED4ext::EGameStateStatus::Tick: + return onTick; + case RED4ext::EGameStateStatus::Exit: + return onExit; } +} - return result; +StateSystem::StateHooks& StateSystem::GetStateHooks(const RED4ext::EGameStateType aStateType) +{ + assert(aStateType >= RED4ext::EGameStateType::BaseInitialization && + aStateType <= RED4ext::EGameStateType::Shutdown && "State type unexpectedly out of range!"); + + switch (aStateType) + { + case RED4ext::EGameStateType::BaseInitialization: + return m_baseInitialization; + case RED4ext::EGameStateType::Initialization: + return m_initialization; + case RED4ext::EGameStateType::Running: + return m_running; + case RED4ext::EGameStateType::Shutdown: + return m_shutdown; + } } + +#pragma warning(pop) diff --git a/src/dll/Systems/StateSystem.hpp b/src/dll/Systems/StateSystem.hpp index 0c4b900a..e7e8683a 100644 --- a/src/dll/Systems/StateSystem.hpp +++ b/src/dll/Systems/StateSystem.hpp @@ -1,45 +1,58 @@ #pragma once #include "ISystem.hpp" + #include "PluginBase.hpp" -class StateSystem : public ISystem +class StateSystem final : public ISystem { public: - using Func_t = bool (*)(RED4ext::CGameApplication*); - - ESystemType GetType() final; + ESystemType GetType() override; - void Startup() final; - void Shutdown() final; + void Startup() override; + void Shutdown() override; - bool Add(std::shared_ptr aPlugin, RED4ext::EGameStateType aStateType, Func_t aOnEnter, Func_t aOnUpdate, - Func_t aOnExit); + bool OnChangeState(RED4ext::CGameApplication& aApp, RED4ext::EGameStateType aNextState); + bool OnDoState(RED4ext::CGameApplication& aApp); - bool OnEnter(RED4ext::EGameStateType aStateType, RED4ext::CGameApplication* aApp); - bool OnUpdate(RED4ext::EGameStateType aStateType, RED4ext::CGameApplication* aApp); - bool OnExit(RED4ext::EGameStateType aStateType, RED4ext::CGameApplication* aApp); + bool AddHook(std::shared_ptr aPlugin, RED4ext::EGameStateType aStateType, + const RED4ext::GameState& aState); private: - struct StateItem + struct StateHooks { - std::shared_ptr plugin; - Func_t func; - }; + struct Action + { + struct Hook + { + std::shared_ptr plugin; + RED4ext::EGameStateResult (*hook)(RED4ext::CGameApplication&); + }; - struct State - { - std::list onEnter; - std::list onUpdate; - std::list onExit; - }; + void Shutdown(); - State* GetStateByType(RED4ext::EGameStateType aStateType); + std::wstring_view name; + std::vector onBefore; + std::vector onAfter; + }; + + void Shutdown(); + + Action& GetActionHooksForStatus(RED4ext::EGameStateStatus aStateStatus); + + std::wstring_view name; + Action onEnter = {.name = L"Enter"}; + Action onTick = {.name = L"Tick"}; + Action onExit = {.name = L"Exit"}; + }; - bool Run(std::wstring_view aAction, std::list& aList, RED4ext::CGameApplication* aApp); + StateHooks& GetStateHooks(RED4ext::EGameStateType aStateType); - State m_baseInitialization; - State m_initialization; - State m_running; - State m_shutdown; + StateHooks m_baseInitialization = {.name = L"BaseInitialization"}; + StateHooks m_initialization = {.name = L"Initialization"}; + StateHooks m_running = {.name = L"Running"}; + StateHooks m_shutdown = {.name = L"Shutdown"}; + RED4ext::EGameStateStatus m_nextStateStatus = RED4ext::EGameStateStatus::NextState; + bool m_originalStateActionFinished = false; + bool m_shouldSwitchState = false; }; diff --git a/src/dll/Utils.cpp b/src/dll/Utils.cpp index e55451b5..529a2972 100644 --- a/src/dll/Utils.cpp +++ b/src/dll/Utils.cpp @@ -1,8 +1,8 @@ #include "Utils.hpp" + #include "Config.hpp" #include "DevConsole.hpp" #include "Paths.hpp" -#include "stdafx.hpp" #include #include @@ -92,34 +92,6 @@ std::shared_ptr Utils::CreateLogger(const std::wstring_view aLog return nullptr; } -std::wstring Utils::GetStateName(RED4ext::EGameStateType aStateType) -{ - using enum RED4ext::EGameStateType; - switch (aStateType) - { - case BaseInitialization: - { - return L"BaseInitialization"; - } - case Initialization: - { - return L"Initialization"; - } - case Running: - { - return L"Running"; - } - case Shutdown: - { - return L"Shutdown"; - } - default: - { - return L"unknown"; - } - } -} - std::wstring Utils::FormatSystemMessage(uint32_t aMessageId) { wil::last_error_context last_error; diff --git a/src/dll/Utils.hpp b/src/dll/Utils.hpp index bf8feff5..ccbc070c 100644 --- a/src/dll/Utils.hpp +++ b/src/dll/Utils.hpp @@ -6,20 +6,18 @@ class Paths; namespace Utils { -std::shared_ptr CreateLogger(const std::wstring_view aLogName, const std::wstring_view aFilename, +std::shared_ptr CreateLogger(std::wstring_view aLogName, std::wstring_view aFilename, const Paths& aPaths, const Config& aConfig, const DevConsole& aDevConsole); -std::wstring GetStateName(RED4ext::EGameStateType aStateType); - std::wstring FormatSystemMessage(uint32_t aMessageId); std::wstring FormatLastError(); std::wstring FormatCurrentTimestamp(); -int32_t ShowMessageBoxEx(const std::wstring_view aCaption, const std::wstring_view aText, uint32_t aType = MB_OK); -int32_t ShowMessageBox(const std::wstring_view aText, uint32_t aType = MB_OK); +int32_t ShowMessageBoxEx(std::wstring_view aCaption, std::wstring_view aText, uint32_t aType = MB_OK); +int32_t ShowMessageBox(std::wstring_view aText, uint32_t aType = MB_OK); -std::string Narrow(const std::wstring_view aText); -std::wstring Widen(const std::string_view aText); +std::string Narrow(std::wstring_view aText); +std::wstring Widen(std::string_view aText); std::wstring ToLower(const std::wstring& acText); @@ -56,7 +54,7 @@ struct fmt::formatter : formatter auto format(const std::filesystem::path& path, FormatContext& ctx) { - return formatter, Char>::format(path.c_str(), ctx); + return formatter, Char>::format(path.native(), ctx); } }; diff --git a/src/dll/v0/Plugin.cpp b/src/dll/v0/Plugin.cpp deleted file mode 100644 index 3a64f296..00000000 --- a/src/dll/v0/Plugin.cpp +++ /dev/null @@ -1,94 +0,0 @@ -#include "stdafx.hpp" -#include "v0/Plugin.hpp" -#include "Image.hpp" -#include "v0/Funcs.hpp" -#include "v0/Logger.hpp" - -v0::Plugin::Plugin(const std::filesystem::path& aPath, wil::unique_hmodule aModule) - : PluginBase(aPath, std::move(aModule)) - , m_info{} - , m_sdk{} - , m_runtime(Image::Get()->GetProductVersion()) - , m_logger{} - , m_hooking{} - , m_gameStates{} - , m_scripts{} -{ - m_sdk.runtime = &m_runtime; - m_sdk.logger = &m_logger; - m_sdk.hooking = &m_hooking; - m_sdk.gameStates = &m_gameStates; - m_sdk.scripts = &m_scripts; - - m_logger.Trace = v0::Logger::Trace; - m_logger.TraceF = v0::Logger::TraceF; - m_logger.TraceW = v0::Logger::TraceW; - m_logger.TraceWF = v0::Logger::TraceWF; - m_logger.Debug = v0::Logger::Debug; - m_logger.DebugF = v0::Logger::DebugF; - m_logger.DebugW = v0::Logger::DebugW; - m_logger.DebugWF = v0::Logger::DebugWF; - m_logger.Info = v0::Logger::Info; - m_logger.InfoF = v0::Logger::InfoF; - m_logger.InfoW = v0::Logger::InfoW; - m_logger.InfoWF = v0::Logger::InfoWF; - m_logger.Warn = v0::Logger::Warn; - m_logger.WarnF = v0::Logger::WarnF; - m_logger.WarnWF = v0::Logger::WarnWF; - m_logger.WarnW = v0::Logger::WarnW; - m_logger.Error = v0::Logger::Error; - m_logger.ErrorF = v0::Logger::ErrorF; - m_logger.ErrorW = v0::Logger::ErrorW; - m_logger.ErrorWF = v0::Logger::ErrorWF; - m_logger.Critical = v0::Logger::Critical; - m_logger.CriticalF = v0::Logger::CriticalF; - m_logger.CriticalW = v0::Logger::CriticalW; - m_logger.CriticalWF = v0::Logger::CriticalWF; - - m_hooking.Attach = v0::Hooking::Attach; - m_hooking.Detach = v0::Hooking::Detach; - - m_gameStates.Add = v0::GameStates::Add; - - m_scripts.Add = v0::Scripts::Add; -} - -const uint32_t v0::Plugin::GetApiVersion() const -{ - return RED4EXT_API_VERSION_0; -} - -void* v0::Plugin::GetPluginInfo() -{ - return &m_info; -} - -const void* v0::Plugin::GetSdkStruct() const -{ - return &m_sdk; -} - -const std::wstring_view v0::Plugin::GetName() const -{ - return m_info.name; -} - -const std::wstring_view v0::Plugin::GetAuthor() const -{ - return m_info.author; -} - -const RED4ext::SemVer& v0::Plugin::GetVersion() const -{ - return m_info.version; -} - -const RED4ext::FileVer& v0::Plugin::GetRuntimeVersion() const -{ - return m_info.runtime; -} - -const RED4ext::SemVer& v0::Plugin::GetSdkVersion() const -{ - return m_info.sdk; -} diff --git a/src/dll/v0/Plugin.hpp b/src/dll/v0/Plugin.hpp deleted file mode 100644 index 62e74806..00000000 --- a/src/dll/v0/Plugin.hpp +++ /dev/null @@ -1,32 +0,0 @@ -#pragma once - -#include "PluginBase.hpp" - -namespace v0 -{ -class Plugin : public PluginBase -{ -public: - Plugin(const std::filesystem::path& aPath, wil::unique_hmodule aModule); - - const uint32_t GetApiVersion() const final; - void* GetPluginInfo() final; - const void* GetSdkStruct() const final; - - virtual const std::wstring_view GetName() const final; - virtual const std::wstring_view GetAuthor() const final; - virtual const RED4ext::SemVer& GetVersion() const final; - virtual const RED4ext::FileVer& GetRuntimeVersion() const final; - virtual const RED4ext::SemVer& GetSdkVersion() const final; - -private: - RED4ext::v0::PluginInfo m_info; - - RED4ext::v0::Sdk m_sdk; - RED4ext::v0::SemVer m_runtime; - RED4ext::v0::Logger m_logger; - RED4ext::v0::Hooking m_hooking; - RED4ext::v0::GameStates m_gameStates; - RED4ext::v0::Scripts m_scripts; -}; -} // namespace v0 diff --git a/src/dll/v0/Funcs.cpp b/src/dll/v1/Funcs.cpp similarity index 64% rename from src/dll/v0/Funcs.cpp rename to src/dll/v1/Funcs.cpp index 135effed..0cb19554 100644 --- a/src/dll/v0/Funcs.cpp +++ b/src/dll/v1/Funcs.cpp @@ -1,4 +1,5 @@ #include "Funcs.hpp" + #include "App.hpp" #include "Utils.hpp" @@ -12,11 +13,12 @@ std::shared_ptr GetPluginByHandle(RED4ext::PluginHandle aHandle) return nullptr; } + const auto module = std::bit_cast(aHandle); auto pluginSystem = app->GetPluginSystem(); - auto plugin = pluginSystem->GetPlugin(aHandle); + auto plugin = pluginSystem->GetPlugin(module); if (!plugin) { - spdlog::warn("Could not find a plugin with handle {}", fmt::ptr(aHandle)); + spdlog::warn("Could not find a plugin with handle {}", fmt::ptr(module)); return nullptr; } @@ -24,9 +26,12 @@ std::shared_ptr GetPluginByHandle(RED4ext::PluginHandle aHandle) } } // namespace -bool v0::Hooking::Attach(RED4ext::PluginHandle aHandle, void* aTarget, void* aDetour, void** aOriginal) +namespace v1 +{ +bool Hooking::Attach(RED4ext::PluginHandle aHandle, void* aTarget, void* aDetour, void** aOriginal) { - spdlog::trace("Attach request received from plugin with handle {}", fmt::ptr(aHandle)); + const auto module = std::bit_cast(aHandle); + spdlog::trace("Attach request received from plugin with handle {}", fmt::ptr(module)); if (aTarget == nullptr || aDetour == nullptr) { @@ -50,9 +55,10 @@ bool v0::Hooking::Attach(RED4ext::PluginHandle aHandle, void* aTarget, void* aDe return hookingSystem->Attach(plugin, aTarget, aDetour, aOriginal); } -bool v0::Hooking::Detach(RED4ext::PluginHandle aHandle, void* aTarget) +bool Hooking::Detach(RED4ext::PluginHandle aHandle, void* aTarget) { - spdlog::trace("Detach request received from plugin with handle {}", fmt::ptr(aHandle)); + const auto module = std::bit_cast(aHandle); + spdlog::trace("Detach request received from plugin with handle {}", fmt::ptr(module)); if (aTarget == nullptr) { @@ -76,15 +82,10 @@ bool v0::Hooking::Detach(RED4ext::PluginHandle aHandle, void* aTarget) return hookingSystem->Detach(plugin, aTarget); } -bool v0::GameStates::Add(RED4ext::PluginHandle aHandle, RED4ext::EGameStateType aType, RED4ext::GameState* aState) +bool GameStates::AddHook(RED4ext::PluginHandle aHandle, RED4ext::EGameStateType aType, const RED4ext::GameState& aState) { - spdlog::trace("Request to add a game state has been received from plugin with handle {}", fmt::ptr(aHandle)); - - if (!aState) - { - spdlog::warn("The state's address is NULL"); - return false; - } + const auto module = std::bit_cast(aHandle); + spdlog::trace("Request to add a game state hook has been received from plugin with handle {}", fmt::ptr(module)); auto app = App::Get(); if (!app) @@ -98,19 +99,16 @@ bool v0::GameStates::Add(RED4ext::PluginHandle aHandle, RED4ext::EGameStateType return false; } - auto stateSystem = app->GetStateSystem(); - if (stateSystem->Add(plugin, aType, aState->OnEnter, aState->OnUpdate, aState->OnExit)) + if (aType < RED4ext::EGameStateType::BaseInitialization || aType > RED4ext::EGameStateType::Shutdown) { - spdlog::trace(L"The request to add a '{}' state for '{}' has been successfully completed", - Utils::GetStateName(aType), plugin->GetName()); - return true; + return false; } - spdlog::warn(L"The request to add a '{}' state for '{}' has failed", Utils::GetStateName(aType), plugin->GetName()); - return false; + auto stateSystem = app->GetStateSystem(); + return stateSystem->AddHook(plugin, aType, aState); } -bool v0::Scripts::Add(RED4ext::PluginHandle aHandle, const wchar_t* aPath) +bool Scripts::Add(RED4ext::PluginHandle aHandle, const wchar_t* aPath) { auto app = App::Get(); if (!app) @@ -127,3 +125,4 @@ bool v0::Scripts::Add(RED4ext::PluginHandle aHandle, const wchar_t* aPath) auto scriptCompilationSystem = app->GetScriptCompilationSystem(); return scriptCompilationSystem->Add(plugin, aPath); } +} // namespace v1 diff --git a/src/dll/v0/Funcs.hpp b/src/dll/v1/Funcs.hpp similarity index 72% rename from src/dll/v0/Funcs.hpp rename to src/dll/v1/Funcs.hpp index 992a62e6..84380bca 100644 --- a/src/dll/v0/Funcs.hpp +++ b/src/dll/v1/Funcs.hpp @@ -1,6 +1,6 @@ #pragma once -namespace v0 +namespace v1 { namespace Hooking { @@ -10,7 +10,7 @@ bool Detach(RED4ext::PluginHandle aHandle, void* aTarget); namespace GameStates { -bool Add(RED4ext::PluginHandle aHandle, RED4ext::EGameStateType aType, RED4ext::GameState* aState); +bool AddHook(RED4ext::PluginHandle aHandle, RED4ext::EGameStateType aType, const RED4ext::GameState& aState); } // namespace GameStates namespace Scripts @@ -18,4 +18,4 @@ namespace Scripts bool Add(RED4ext::PluginHandle aHandle, const wchar_t* aPath); } // namespace Scripts -} // namespace v0 +} // namespace v1 diff --git a/src/dll/v0/Logger.cpp b/src/dll/v1/Logger.cpp similarity index 80% rename from src/dll/v0/Logger.cpp rename to src/dll/v1/Logger.cpp index 98d6de7f..fae0cce8 100644 --- a/src/dll/v0/Logger.cpp +++ b/src/dll/v1/Logger.cpp @@ -1,11 +1,12 @@ -#include "stdafx.hpp" #include "Logger.hpp" + #include "App.hpp" #define Log(func) \ + const auto module = std::bit_cast(aHandle); \ if (!aMessage) \ { \ - spdlog::warn("Plugin with handle {} tried to log a message with a NULL message", fmt::ptr(aHandle)); \ + spdlog::warn("Plugin with handle {} tried to log a message with a NULL message", fmt::ptr(module)); \ return; \ } \ \ @@ -16,7 +17,7 @@ } \ \ auto pluginSystem = app->GetPluginSystem(); \ - auto plugin = pluginSystem->GetPlugin(aHandle); \ + auto plugin = pluginSystem->GetPlugin(module); \ if (!plugin) \ { \ return; \ @@ -25,9 +26,10 @@ loggerSystem->func(plugin, aMessage) #define LogF(char_type, count_fn, format_fn, func) \ + const auto module = std::bit_cast(aHandle); \ if (!aFormat) \ { \ - spdlog::warn("Plugin with handle {} tried to log a message with a NULL format", fmt::ptr(aHandle)); \ + spdlog::warn("Plugin with handle {} tried to log a message with a NULL format", fmt::ptr(module)); \ return; \ } \ \ @@ -38,7 +40,7 @@ } \ \ auto pluginSystem = app->GetPluginSystem(); \ - auto plugin = pluginSystem->GetPlugin(aHandle); \ + auto plugin = pluginSystem->GetPlugin(module); \ if (!plugin) \ { \ return; \ @@ -73,122 +75,125 @@ \ va_end(args) -void v0::Logger::Trace(RED4ext::PluginHandle aHandle, const char* aMessage) +namespace v1 +{ +void Logger::Trace(RED4ext::PluginHandle aHandle, const char* aMessage) { Log(Trace); } -void v0::Logger::TraceF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) +void Logger::TraceF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) { LogF(char, ::_vscprintf, ::vsnprintf_s, Trace); } -void v0::Logger::TraceW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) +void Logger::TraceW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) { Log(Trace); } -void v0::Logger::TraceWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) +void Logger::TraceWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) { LogF(wchar_t, ::_vscwprintf, ::_vsnwprintf_s, Trace); } -void v0::Logger::Debug(RED4ext::PluginHandle aHandle, const char* aMessage) +void Logger::Debug(RED4ext::PluginHandle aHandle, const char* aMessage) { Log(Debug); } -void v0::Logger::DebugF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) +void Logger::DebugF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) { LogF(char, ::_vscprintf, ::vsnprintf_s, Debug); } -void v0::Logger::DebugW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) +void Logger::DebugW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) { Log(Debug); } -void v0::Logger::DebugWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) +void Logger::DebugWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) { LogF(wchar_t, ::_vscwprintf, ::_vsnwprintf_s, Debug); } -void v0::Logger::Info(RED4ext::PluginHandle aHandle, const char* aMessage) +void Logger::Info(RED4ext::PluginHandle aHandle, const char* aMessage) { Log(Info); } -void v0::Logger::InfoF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) +void Logger::InfoF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) { LogF(char, ::_vscprintf, ::vsnprintf_s, Info); } -void v0::Logger::InfoW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) +void Logger::InfoW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) { Log(Info); } -void v0::Logger::InfoWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) +void Logger::InfoWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) { LogF(wchar_t, ::_vscwprintf, ::_vsnwprintf_s, Info); } -void v0::Logger::Warn(RED4ext::PluginHandle aHandle, const char* aMessage) +void Logger::Warn(RED4ext::PluginHandle aHandle, const char* aMessage) { Log(Warn); } -void v0::Logger::WarnF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) +void Logger::WarnF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) { LogF(char, ::_vscprintf, ::vsnprintf_s, Warn); } -void v0::Logger::WarnW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) +void Logger::WarnW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) { Log(Warn); } -void v0::Logger::WarnWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) +void Logger::WarnWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) { LogF(wchar_t, ::_vscwprintf, ::_vsnwprintf_s, Warn); } -void v0::Logger::Error(RED4ext::PluginHandle aHandle, const char* aMessage) +void Logger::Error(RED4ext::PluginHandle aHandle, const char* aMessage) { Log(Error); } -void v0::Logger::ErrorF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) +void Logger::ErrorF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) { LogF(char, ::_vscprintf, ::vsnprintf_s, Error); } -void v0::Logger::ErrorW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) +void Logger::ErrorW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) { Log(Error); } -void v0::Logger::ErrorWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) +void Logger::ErrorWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) { LogF(wchar_t, ::_vscwprintf, ::_vsnwprintf_s, Error); } -void v0::Logger::Critical(RED4ext::PluginHandle aHandle, const char* aMessage) +void Logger::Critical(RED4ext::PluginHandle aHandle, const char* aMessage) { Log(Critical); } -void v0::Logger::CriticalF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) +void Logger::CriticalF(RED4ext::PluginHandle aHandle, const char* aFormat, ...) { LogF(char, ::_vscprintf, ::vsnprintf_s, Critical); } -void v0::Logger::CriticalW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) +void Logger::CriticalW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage) { Log(Critical); } -void v0::Logger::CriticalWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) +void Logger::CriticalWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...) { LogF(wchar_t, ::_vscwprintf, ::_vsnwprintf_s, Critical); } +} // namespace v1 diff --git a/src/dll/v0/Logger.hpp b/src/dll/v1/Logger.hpp similarity index 97% rename from src/dll/v0/Logger.hpp rename to src/dll/v1/Logger.hpp index 99f2f836..72f5d243 100644 --- a/src/dll/v0/Logger.hpp +++ b/src/dll/v1/Logger.hpp @@ -1,8 +1,6 @@ #pragma once -#pragma once - -namespace v0 +namespace v1 { namespace Logger { @@ -42,4 +40,4 @@ void CriticalF(RED4ext::PluginHandle aHandle, const char* aFormat, ...); void CriticalW(RED4ext::PluginHandle aHandle, const wchar_t* aMessage); void CriticalWF(RED4ext::PluginHandle aHandle, const wchar_t* aFormat, ...); } // namespace Logger -} // namespace v0 +} // namespace v1 diff --git a/src/dll/v1/Plugin.cpp b/src/dll/v1/Plugin.cpp new file mode 100644 index 00000000..aac7c50c --- /dev/null +++ b/src/dll/v1/Plugin.cpp @@ -0,0 +1,87 @@ +#include "Plugin.hpp" + +#include "Funcs.hpp" +#include "Image.hpp" +#include "Logger.hpp" + +namespace v1 +{ +Plugin::Plugin(const std::filesystem::path& aPath, wil::unique_hmodule aModule) + : PluginBase(aPath, std::move(aModule)) + , m_info{} + , m_runtime(Image::Get()->GetProductVersion()) + , m_logger{.Trace = Logger::Trace, + .TraceF = Logger::TraceF, + .TraceW = Logger::TraceW, + .TraceWF = Logger::TraceWF, + .Debug = Logger::Debug, + .DebugF = Logger::DebugF, + .DebugW = Logger::DebugW, + .DebugWF = Logger::DebugWF, + .Info = Logger::Info, + .InfoF = Logger::InfoF, + .InfoW = Logger::InfoW, + .InfoWF = Logger::InfoWF, + .Warn = Logger::Warn, + .WarnF = Logger::WarnF, + .WarnW = Logger::WarnW, + .WarnWF = Logger::WarnWF, + .Error = Logger::Error, + .ErrorF = Logger::ErrorF, + .ErrorW = Logger::ErrorW, + .ErrorWF = Logger::ErrorWF, + .Critical = Logger::Critical, + .CriticalF = Logger::CriticalF, + .CriticalW = Logger::CriticalW, + .CriticalWF = Logger::CriticalWF} + , m_hooking{.Attach = Hooking::Attach, .Detach = Hooking::Detach} + , m_gameStates{.AddHook = GameStates::AddHook} + , m_scripts{.Add = Scripts::Add} + , m_sdk{.runtime = &m_runtime, + .logger = &m_logger, + .hooking = &m_hooking, + .gameStates = &m_gameStates, + .scripts = &m_scripts} +{ +} + +const uint32_t Plugin::GetApiVersion() const +{ + return RED4EXT_API_VERSION_1; +} + +void* Plugin::GetPluginInfo() +{ + return &m_info; +} + +const void* Plugin::GetSdkStruct() const +{ + return &m_sdk; +} + +const std::wstring_view Plugin::GetName() const +{ + return m_info.name; +} + +const std::wstring_view Plugin::GetAuthor() const +{ + return m_info.author; +} + +const RED4ext::SemVer& Plugin::GetVersion() const +{ + return m_info.version; +} + +const RED4ext::FileVer& Plugin::GetRuntimeVersion() const +{ + return m_info.runtime; +} + +const RED4ext::SemVer& Plugin::GetSdkVersion() const +{ + return m_info.sdk; +} +} // namespace v1 diff --git a/src/dll/v1/Plugin.hpp b/src/dll/v1/Plugin.hpp new file mode 100644 index 00000000..8be6682b --- /dev/null +++ b/src/dll/v1/Plugin.hpp @@ -0,0 +1,32 @@ +#pragma once + +#include "PluginBase.hpp" + +namespace v1 +{ +class Plugin final : public PluginBase +{ +public: + Plugin(const std::filesystem::path& aPath, wil::unique_hmodule aModule); + + const uint32_t GetApiVersion() const override; + void* GetPluginInfo() override; + const void* GetSdkStruct() const override; + + const std::wstring_view GetName() const override; + const std::wstring_view GetAuthor() const override; + const RED4ext::SemVer& GetVersion() const override; + const RED4ext::FileVer& GetRuntimeVersion() const override; + const RED4ext::SemVer& GetSdkVersion() const override; + +private: + RED4ext::v1::PluginInfo m_info; + + RED4ext::v1::SemVer m_runtime; + RED4ext::v1::Logger m_logger; + RED4ext::v1::Hooking m_hooking; + RED4ext::v1::GameStates m_gameStates; + RED4ext::v1::Scripts m_scripts; + RED4ext::v1::Sdk m_sdk; +}; +} // namespace v1 diff --git a/src/loader/stdafx.hpp b/src/loader/stdafx.hpp index 77ac0e77..ec751c38 100644 --- a/src/loader/stdafx.hpp +++ b/src/loader/stdafx.hpp @@ -7,6 +7,7 @@ #include #include + #include #include diff --git a/src/playground/Main.cpp b/src/playground/Main.cpp index 89dbacc4..1659ff94 100644 --- a/src/playground/Main.cpp +++ b/src/playground/Main.cpp @@ -10,6 +10,10 @@ RED4EXT_C_EXPORT bool RED4EXT_CALL Main(RED4ext::PluginHandle aHandle, RED4ext:: { break; } + case RED4ext::EMainReason::Run: + { + break; + } case RED4ext::EMainReason::Unload: { break;