diff --git a/EGameTools/EGameTools.vcxproj b/EGameTools/EGameTools.vcxproj
index b52e0ee..74e392d 100644
--- a/EGameTools/EGameTools.vcxproj
+++ b/EGameTools/EGameTools.vcxproj
@@ -35,7 +35,6 @@
-
@@ -93,6 +92,14 @@
NotUsing
+
+ NotUsing
+ NotUsing
+
+
+ NotUsing
+ NotUsing
+
@@ -140,7 +147,6 @@
-
@@ -179,6 +185,8 @@
+
+
@@ -271,7 +279,7 @@
- pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;
+ pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;source\memscan;
$(IntDir)$(TargetName)_$(PlatformTarget).pch
@@ -301,7 +309,7 @@
Use
pch.h
stdcpplatest
- pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;%(AdditionalIncludeDirectories)
+ pch;source\spdlog\include;source\ImGui\freetype\include;source\ImGui;source\MinHook;source\memscan;%(AdditionalIncludeDirectories)
None
false
$(IntDir)$(TargetName)_$(PlatformTarget).pch
diff --git a/EGameTools/EGameTools.vcxproj.filters b/EGameTools/EGameTools.vcxproj.filters
index 454d1a9..4a72163 100644
--- a/EGameTools/EGameTools.vcxproj.filters
+++ b/EGameTools/EGameTools.vcxproj.filters
@@ -133,9 +133,6 @@
game\GamePH
-
- game\GamePH
-
game\GamePH
@@ -212,6 +209,12 @@
menu
+
+ memscan
+
+
+ memscan\util
+
@@ -364,9 +367,6 @@
game\GamePH
-
- game\GamePH
-
game\GamePH
@@ -453,6 +453,12 @@
menu
+
+ memscan
+
+
+ memscan\util
+
@@ -497,5 +503,11 @@
{122420d5-ac60-4778-80bf-1384697fcc44}
+
+ {1e82ab1f-550e-4fac-8fac-0888e1e62873}
+
+
+ {7e81ee9f-f74a-4af8-8a10-7530b26c3c86}
+
\ No newline at end of file
diff --git a/EGameTools/pch/pch.h b/EGameTools/pch/pch.h
index 501c99c..1677226 100644
--- a/EGameTools/pch/pch.h
+++ b/EGameTools/pch/pch.h
@@ -66,6 +66,7 @@
#include
#include
#include
+#include
#include "..\source\config\ini.h"
#include "..\source\game\Engine\GameSpeedHandler.h"
diff --git a/EGameTools/source/changelog.h b/EGameTools/source/changelog.h
index 7c640a4..5b8bdf0 100644
--- a/EGameTools/source/changelog.h
+++ b/EGameTools/source/changelog.h
@@ -109,7 +109,7 @@ With all of that said, here is finally what this update brings:
- Added "Teleport to Waypoint" (Teleport)
- WARNING: if the waypoint is selected to track an object/item on the map, Teleport to Waypoint will not work, if so just set the waypoint nearby instead
- WARNING: your player height won't change when teleporting, so make sure you catch yourself if you fall under the map because of the teleportation
-- Added "Saved Locations" in Teleport menu, with the ability of saving, deleting and teleporting to said locations; these locations are saved in the config file and will contain a name and a set of coordinates for each location; to reset back to the default list, remove the list from inside the config file and go back into the game
+- Added "Saved Locations" in Teleport menu, with the ability of saving, deleting and teleporting to said locations; these locations are saved in the config file and will contain a name and a set of coordinates for each location; to reset back to the default list, remove the list from inside the config file and go back into the game; thank you to @Synsteric on Discord for helping me make the default list!
- Added "Increase Data PAKs Limit" (Misc; requires game restart to apply) - you can now add more than 8 data PAKs, e.g. data8.pak, data9.pak, data10.pak, etc, up to 200 PAKs in total
- Added "Disable Data PAKs CRC Check" (Misc; requires game restart to apply) - stops the game from scanning data PAKs, which allows you to use data PAK mods in multiplayer as well
- Added "Disable Savegame CRC Check" (Misc; requires game restart to apply) - stops the game from falsely saying your savegame is corrupt whenever you modify it
@@ -132,6 +132,12 @@ NOTE: Any mods that are put inside "EGameTools\UserModFiles" as a regular file (
I'm soon starting my exams and won't really have the time to update the mod the way I did right now. I had a 2 week leave and so I had plenty of time to further develop this mod.
-If anyone is looking to help with development, I'm all eyes and ears! Thank you!)" }
+If anyone is looking to help with development, I'm all eyes and ears! Thank you!)" },
+ { "v1.2.1",
+ R"(- Added compatibility with v1.17.2 Tower Raid update
+- Added a .PDB file included by default with the mod, for debugging purposes in case the game crashes, now I can more easily detect the cause of a game crash!
+- Added a crash handler which handles game crashes and generates a "EGameTools-dump.dmp" file in the game's exe directory for debugging purposes; if you encounter a crash and this file gets generated, please send it to me anywhere you can, for example on Discord, so I can try to find out the cause of the game crash!
+- Fixed "Game Speed" (World) not getting applied with the mod menu opened while having another tab selected other than the World tab
+I have some things planned for the next updates, but time will decide when I'll be able to work on the updates. I'm almost done with my exams!)" }
};
}
\ No newline at end of file
diff --git a/EGameTools/source/core.cpp b/EGameTools/source/core.cpp
index fef118d..06399a5 100644
--- a/EGameTools/source/core.cpp
+++ b/EGameTools/source/core.cpp
@@ -20,7 +20,7 @@ namespace Core {
DWORD prev_mode = 0;
const HANDLE hInput = GetStdHandle(STD_INPUT_HANDLE);
GetConsoleMode(hInput, &prev_mode);
- SetConsoleMode(hInput, prev_mode & ENABLE_EXTENDED_FLAGS);
+ SetConsoleMode(hInput, prev_mode & ENABLE_EXTENDED_FLAGS);
}
static FILE* f = nullptr;
@@ -43,7 +43,7 @@ namespace Core {
int rendererAPI = 0;
DWORD gameVer = 0;
- static std::counting_semaphore<4> maxHookThreads(4);
+ std::counting_semaphore<4> maxHookThreads(4);
static void LoopHookRenderer() {
while (true) {
@@ -173,31 +173,37 @@ namespace Core {
GamePH::PlayerHealthModule::UpdateClassAddr();
GamePH::PlayerInfectionModule::UpdateClassAddr();
}
- /*static bool WriteMiniDump(PEXCEPTION_POINTERS pExceptionPointers) {
+#ifndef EXCP_HANDLER_DISABLE_DEBUG
+ static bool WriteMiniDump(PEXCEPTION_POINTERS pExceptionPointers) {
HANDLE hFile = CreateFileA("EGameTools-dump.dmp", GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr);
if (hFile == INVALID_HANDLE_VALUE)
return false;
MINIDUMP_EXCEPTION_INFORMATION mdei{};
- mdei.ThreadId = GetCurrentThreadId();
mdei.ExceptionPointers = pExceptionPointers;
mdei.ClientPointers = false;
- int success = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, &mdei, nullptr, nullptr);
+ int success = MiniDumpWriteDump(GetCurrentProcess(), GetCurrentProcessId(), hFile, MiniDumpNormal, (pExceptionPointers ? &mdei : nullptr), nullptr, nullptr);
CloseHandle(hFile);
return success;
- }
- static long WINAPI VectoredExceptionHandler(PEXCEPTION_POINTERS ExceptionInfo) {
- spdlog::error("VEH threw an exception with code {}. Trying to continue execution, writing mini-dump in the mean time.", ExceptionInfo->ExceptionRecord->ExceptionCode);
+ }
+ static long WINAPI CrashHandler(PEXCEPTION_POINTERS ExceptionInfo) {
+ spdlog::error("Crash Handler threw an exception with code {}. Game is exiting, writing mini-dump in the mean time.", ExceptionInfo->ExceptionRecord->ExceptionCode);
+ std::string errorMsg = "";
- if (WriteMiniDump(ExceptionInfo))
+ if (WriteMiniDump(ExceptionInfo)) {
spdlog::info("Mini-dump written to \"EGameTools-dump.dmp\". Please send this to mod author for further help!");
- else
+ errorMsg = "EGameTools encountered a fatal error that caused the game to crash.\n\nA file \"" + Utils::Files::GetCurrentProcDirectory() + "\\EGameTools-dump.dmp\" has been generated. Please send this file to the author of the mod!\n\nThe game will now close once you press OK.";
+ } else {
spdlog::error("Failed to write mini-dump.");
+ errorMsg = "EGameTools encountered a fatal error that caused the game to crash.\n\nEGameTools failed to generate a crash dump file unfortunately, which means it is harder to find the cause of the crash.\n\nThe game will now close once you press OK.";
+ }
- return EXCEPTION_CONTINUE_EXECUTION;
- }*/
+ MessageBoxA(nullptr, errorMsg.c_str(), "Fatal game error", MB_ICONERROR | MB_OK | MB_SETFOREGROUND);
+ exit(0);
+ }
+#endif
static void GameVersionCheck() {
try {
gameVer = GamePH::GetCurrentGameVersion();
@@ -216,7 +222,9 @@ namespace Core {
EnableConsole();
InitLogger();
- //AddVectoredExceptionHandler(0, &VectoredExceptionHandler);
+#ifndef EXCP_HANDLER_DISABLE_DEBUG
+ AddVectoredExceptionHandler(0, &VectoredExceptionHandler);
+#endif
spdlog::warn("Getting game version");
GameVersionCheck();
@@ -227,23 +235,16 @@ namespace Core {
CreateSymlinkForLoadingFiles();
- while (true) {
- if (!GetModuleHandle("gamedll_ph_x64_rwdi.dll") || !GetModuleHandle("engine_x64_rwdi.dll"))
- continue;
+ for (auto& hook : *Utils::Hook::HookBase::GetInstances()) {
+ std::thread([&hook]() {
+ maxHookThreads.acquire();
- for (auto& hook : *Utils::Hook::HookBase::GetInstances()) {
- std::thread([&hook]() {
- maxHookThreads.acquire();
+ spdlog::warn("Hooking \"{}\"", hook->name.data());
+ if (hook->HookLoop())
+ spdlog::info("Hooked \"{}\"!", hook->name.data());
- spdlog::warn("Hooking \"{}\"", hook->name.data());
- if (hook->HookLoop())
- spdlog::info("Hooked \"{}\"!", hook->name.data());
-
- maxHookThreads.release();
- }).detach();
- }
-
- break;
+ maxHookThreads.release();
+ }).detach();
}
spdlog::warn("Sorting Player Variables");
diff --git a/EGameTools/source/core.h b/EGameTools/source/core.h
index 16c31ea..a5ccc0c 100644
--- a/EGameTools/source/core.h
+++ b/EGameTools/source/core.h
@@ -9,6 +9,7 @@
#ifdef _DEBUG
#define LLMH_IMPL_DISABLE_DEBUG // this is for disabling low-level mouse hook in case ure trying to debug and u dont want ur pc to die lol
+#define EXCP_HANDLER_DISABLE_DEBUG // this is for disabling exception handling in case ure trying to debug and u dont want ur debugger to keep crying about errors lol
#endif
#define VK_NONE -1
@@ -16,9 +17,9 @@
#define VK_MWHEELUP 0x101
#endif
-constexpr const char* MOD_VERSION_STR = "v1.2.3";
-constexpr DWORD MOD_VERSION = 10203;
-constexpr DWORD GAME_VER_COMPAT = 11800;
+constexpr const char* MOD_VERSION_STR = "v1.2.4";
+constexpr DWORD MOD_VERSION = 10204;
+constexpr DWORD GAME_VER_COMPAT = 11900;
struct Key {
constexpr Key(std::string_view name, int code, ImGuiKey imGuiCode) : name(name), code(code), imGuiCode(imGuiCode) {}
diff --git a/EGameTools/source/dllmain.cpp b/EGameTools/source/dllmain.cpp
index f6f83eb..4569a7f 100644
--- a/EGameTools/source/dllmain.cpp
+++ b/EGameTools/source/dllmain.cpp
@@ -3,6 +3,8 @@
namespace Core {
extern void DisableConsole();
+ extern std::counting_semaphore<4> maxHookThreads;
+
extern DWORD64 WINAPI MainThread(HMODULE hModule);
extern void Cleanup();
}
@@ -22,10 +24,11 @@ BOOL APIENTRY DllMain(HMODULE hModule, DWORD64 ul_reason_for_call, LPVOID lpRese
switch (ul_reason_for_call) {
case DLL_PROCESS_ATTACH: {
MH_Initialize();
- Engine::Hooks::MountDataPaksHook.HookLoop();
- Engine::Hooks::AuthenticateDataAddNewFileHook.HookLoop();
- Engine::Hooks::FsCheckZipCrcHook.HookLoop();
- Engine::Hooks::FsOpenHook.HookLoop();
+
+ std::thread([]() { Core::maxHookThreads.acquire(); Engine::Hooks::MountDataPaksHook.HookLoop(); Core::maxHookThreads.release(); }).detach();
+ std::thread([]() { Core::maxHookThreads.acquire(); Engine::Hooks::AuthenticateDataAddNewFileHook.HookLoop(); Core::maxHookThreads.release(); }).detach();
+ std::thread([]() { Core::maxHookThreads.acquire(); Engine::Hooks::FsCheckZipCrcHook.HookLoop(); Core::maxHookThreads.release(); }).detach();
+ std::thread([]() { Core::maxHookThreads.acquire(); Engine::Hooks::FsOpenHook.HookLoop(); Core::maxHookThreads.release(); }).detach();
DisableThreadLibraryCalls(hModule);
hMainThread = CreateThread(nullptr, 0, (LPTHREAD_START_ROUTINE)Core::MainThread, hModule, 0, nullptr);
diff --git a/EGameTools/source/game/Engine/CBulletPhysicsCharacter.h b/EGameTools/source/game/Engine/CBulletPhysicsCharacter.h
index 9a74242..22a93fe 100644
--- a/EGameTools/source/game/Engine/CBulletPhysicsCharacter.h
+++ b/EGameTools/source/game/Engine/CBulletPhysicsCharacter.h
@@ -6,9 +6,9 @@ namespace Engine {
class CBulletPhysicsCharacter {
public:
union {
- buffer<0x8A0, Vector3> playerPos2;
- buffer<0x8B8, Vector3> playerPos;
- buffer<0xC38, float> playerDownwardVelocity;
+ buffer<0xA08, Vector3> playerPos2;
+ buffer<0xA20, Vector3> playerPos;
+ buffer<0xDA0, float> playerDownwardVelocity;
};
static Vector3 posBeforeFreeze;
diff --git a/EGameTools/source/game/Engine/engine_hooks.cpp b/EGameTools/source/game/Engine/engine_hooks.cpp
index 2139e8b..602417a 100644
--- a/EGameTools/source/game/Engine/engine_hooks.cpp
+++ b/EGameTools/source/game/Engine/engine_hooks.cpp
@@ -2,7 +2,6 @@
#include "..\GamePH\GameDI_PH.h"
#include "..\GamePH\LevelDI.h"
#include "..\GamePH\gameph_misc.h"
-#include "..\GamePH\gen_TPPModel.h"
#include "..\core.h"
#include "..\menu\camera.h"
#include "..\menu\misc.h"
diff --git a/EGameTools/source/game/GamePH/GameDI_PH.h b/EGameTools/source/game/GamePH/GameDI_PH.h
index fd2e8d1..7689134 100644
--- a/EGameTools/source/game/GamePH/GameDI_PH.h
+++ b/EGameTools/source/game/GamePH/GameDI_PH.h
@@ -9,7 +9,7 @@ namespace GamePH {
public:
union {
buffer<0x130, SessionCooperativeDI*> pSessionCooperativeDI;
- buffer<0x880, bool> blockPauseGameOnPlayerAfk;
+ buffer<0x908, bool> blockPauseGameOnPlayerAfk;
};
float GetGameTimeDelta();
diff --git a/EGameTools/source/game/GamePH/LocalClientDI.h b/EGameTools/source/game/GamePH/LocalClientDI.h
index 84ab55c..ffa501d 100644
--- a/EGameTools/source/game/GamePH/LocalClientDI.h
+++ b/EGameTools/source/game/GamePH/LocalClientDI.h
@@ -2,12 +2,12 @@
#include "..\buffer.h"
namespace GamePH {
- class gen_TPPModel;
+ class PlayerObjProperties;
class LocalClientDI {
public:
union {
- buffer<0x90, gen_TPPModel*> pgen_TPPModel;
+ buffer<0x90, PlayerObjProperties*> pPlayerObjProperties;
};
static LocalClientDI* Get();
diff --git a/EGameTools/source/game/GamePH/PlayerObjProperties.h b/EGameTools/source/game/GamePH/PlayerObjProperties.h
index d9e9695..8485e35 100644
--- a/EGameTools/source/game/GamePH/PlayerObjProperties.h
+++ b/EGameTools/source/game/GamePH/PlayerObjProperties.h
@@ -12,6 +12,8 @@ namespace GamePH {
buffer<0xF0, Engine::CoPhysicsProperty*> pCoPhysicsProperty;
//buffer<0x2E80, bool> isOutOfBounds;
//buffer<0x2E84, float> outOfBoundsTimer;
+ buffer<0x35E9, bool> enableTPPModel1;
+ buffer<0x35EA, bool> enableTPPModel2;
};
static PlayerObjProperties* Get();
diff --git a/EGameTools/source/game/GamePH/PlayerState.h b/EGameTools/source/game/GamePH/PlayerState.h
index 58ba884..5f6f10a 100644
--- a/EGameTools/source/game/GamePH/PlayerState.h
+++ b/EGameTools/source/game/GamePH/PlayerState.h
@@ -7,7 +7,7 @@ namespace GamePH {
class PlayerState {
public:
union {
- buffer<0x2B0, PlayerVariables*> playerVars;
+ buffer<0x300, PlayerVariables*> playerVars;
};
static PlayerState* Get();
diff --git a/EGameTools/source/game/GamePH/game_hooks.cpp b/EGameTools/source/game/GamePH/game_hooks.cpp
index d09804b..a057d77 100644
--- a/EGameTools/source/game/GamePH/game_hooks.cpp
+++ b/EGameTools/source/game/GamePH/game_hooks.cpp
@@ -11,7 +11,7 @@
#include "LevelDI.h"
#include "PlayerHealthModule.h"
#include "PlayerInfectionModule.h"
-#include "gen_TPPModel.h"
+#include "PlayerObjProperties.h"
namespace GamePH {
namespace Hooks {
@@ -161,20 +161,20 @@ namespace GamePH {
static Utils::Hook::MHook ShowTPPModelFunc3Hook{ "ShowTPPModelFunc3", &Offsets::Get_ShowTPPModelFunc3, &detourShowTPPModelFunc3 };
static void detourShowTPPModelFunc3(DWORD64 tppFunc2Addr, bool showTPPModel) {
- gen_TPPModel* pgen_TPPModel = gen_TPPModel::Get();
- if (!pgen_TPPModel) {
+ PlayerObjProperties* playerObjProperties = PlayerObjProperties::Get();
+ if (!playerObjProperties) {
ShowTPPModelFunc3Hook.pOriginal(tppFunc2Addr, showTPPModel);
return;
}
if (!showTPPModel && prevUseTPPModel) {
- pgen_TPPModel->enableTPPModel2 = true;
- pgen_TPPModel->enableTPPModel1 = true;
+ playerObjProperties->enableTPPModel2 = true;
+ playerObjProperties->enableTPPModel1 = true;
}
ShowTPPModelFunc3Hook.pOriginal(tppFunc2Addr, showTPPModel);
if (showTPPModel && prevUseTPPModel) {
- pgen_TPPModel->enableTPPModel2 = false;
- pgen_TPPModel->enableTPPModel1 = false;
+ playerObjProperties->enableTPPModel2 = false;
+ playerObjProperties->enableTPPModel1 = false;
} else
prevUseTPPModel = showTPPModel;
}
diff --git a/EGameTools/source/game/GamePH/gameph_misc.cpp b/EGameTools/source/game/GamePH/gameph_misc.cpp
index 00216a2..5415436 100644
--- a/EGameTools/source/game/GamePH/gameph_misc.cpp
+++ b/EGameTools/source/game/GamePH/gameph_misc.cpp
@@ -1,7 +1,7 @@
#include
#include "..\offsets.h"
#include "GameDI_PH.h"
-#include "gen_TPPModel.h"
+#include "PlayerObjProperties.h"
namespace GamePH {
const DWORD GetCurrentGameVersion() {
@@ -59,8 +59,8 @@ namespace GamePH {
void(*pShowTPPModelFunc3)(DWORD64 tppFunc2Addr, bool showTPPModel) = (decltype(pShowTPPModelFunc3))Offsets::Get_ShowTPPModelFunc3();
if (!pShowTPPModelFunc3)
return;
- gen_TPPModel* pgen_TPPModel = gen_TPPModel::Get();
- if (!pgen_TPPModel)
+ PlayerObjProperties* playerObjProperties = PlayerObjProperties::Get();
+ if (!playerObjProperties)
return;
pShowTPPModelFunc3(tppFunc2Addr, showTPPModel);
diff --git a/EGameTools/source/game/GamePH/gen_TPPModel.cpp b/EGameTools/source/game/GamePH/gen_TPPModel.cpp
deleted file mode 100644
index d534caf..0000000
--- a/EGameTools/source/game/GamePH/gen_TPPModel.cpp
+++ /dev/null
@@ -1,21 +0,0 @@
-#include
-#include "LocalClientDI.h"
-#include "gen_TPPModel.h"
-
-namespace GamePH {
- gen_TPPModel* gen_TPPModel::Get() {
- __try {
- LocalClientDI* pLocalClientDI = LocalClientDI::Get();
- if (!pLocalClientDI)
- return nullptr;
-
- gen_TPPModel* ptr = pLocalClientDI->pgen_TPPModel;
- if (!Utils::Memory::IsValidPtrMod(ptr, "gamedll_ph_x64_rwdi.dll"))
- return nullptr;
-
- return ptr;
- } __except (EXCEPTION_EXECUTE_HANDLER) {
- return nullptr;
- }
- }
-}
\ No newline at end of file
diff --git a/EGameTools/source/game/GamePH/gen_TPPModel.h b/EGameTools/source/game/GamePH/gen_TPPModel.h
deleted file mode 100644
index 5b8b49c..0000000
--- a/EGameTools/source/game/GamePH/gen_TPPModel.h
+++ /dev/null
@@ -1,14 +0,0 @@
- #pragma once
-#include "..\buffer.h"
-
-namespace GamePH {
- class gen_TPPModel {
- public:
- union {
- buffer<0x2DB9, bool> enableTPPModel1;
- buffer<0x2DBA, bool> enableTPPModel2;
- };
-
- static gen_TPPModel* Get();
- };
-}
\ No newline at end of file
diff --git a/EGameTools/source/memscan/memscan.c b/EGameTools/source/memscan/memscan.c
new file mode 100644
index 0000000..352dfcd
--- /dev/null
+++ b/EGameTools/source/memscan/memscan.c
@@ -0,0 +1,935 @@
+#include "memscan.h"
+
+#include // memcmp
+#include // strlen
+
+/* Types */
+
+typedef struct {
+ /* example: {0x55, 0x8B, 0xEC} */
+ const ms_ubyte_t* m_data;
+
+ /* the former will result in '3' */
+ ms_usize_t m_size;
+
+ /* the n-th instance of a pattern within range, 0 - first */
+ ms_usize_t m_match;
+} ms_bytes_t;
+
+typedef struct {
+ /* "55 8B EC" => ms_bytes_t({0x55, 0x8B, 0xEC}, 3, match)
+ * must be null terminated */
+ const char* m_data;
+
+ /* the n-th instance of a pattern within range, 0 - first */
+ ms_usize_t m_match;
+} ms_bytes_str_t;
+
+typedef struct {
+ /* example: {0x8B, 0x0D} */
+ const ms_ubyte_t* m_data;
+
+ /* the former will result in '3' */
+ ms_usize_t m_size;
+
+ /* the n-th instance of a match within range, 0 - first */
+ ms_usize_t m_match;
+
+ ms_follow_direction_t m_direction;
+} ms_find_bytes_t;
+
+typedef struct {
+ /* "8B 0C" => ms_find_bytes_t({0x8B, 0x0C}, 2, match, direction)
+ * must be null terminated */
+ const char* m_data;
+
+ /* the n-th instance of a match within range, 0 - first */
+ ms_usize_t m_match;
+
+ ms_follow_direction_t m_direction;
+} ms_find_str_t;
+
+typedef struct {
+ /* numerical form of pointer to look for */
+ ms_uptr_t m_pointer;
+
+ /* the n-th instance of a match within range, 0 - first */
+ ms_usize_t m_match;
+
+ bool m_swap_endianness;
+} ms_xref_t;
+
+typedef struct {
+ /* the string to look for */
+ const char* m_data;
+
+ /* string size */
+ ms_usize_t m_size;
+
+ /* the n-th instance of the reversed endianness address to the first
+ * character of the string, 0 - first */
+ ms_usize_t m_match;
+} ms_string_xref_t;
+
+typedef enum {
+ /* won't be reached if MEMSCAN_UNSAFE_OPTIMIZATIONS are on */
+
+ MS_FOLLOW_STATUS_NO_VALID_FOLLOW_INFO = 0,
+
+ /* */
+
+ /* overbounds on start */
+ MS_FOLLOW_STATUS_FAIL_LHS,
+
+ /* overbounds on end */
+ MS_FOLLOW_STATUS_FAIL_RHS,
+
+ /* broke before reaching match */
+ MS_FOLLOW_STATUS_INCOMPLETE,
+
+ /* follow has succeeded, state was set to OK */
+ MS_FOLLOW_STATUS_OK
+} ms_follow_status_t;
+
+typedef struct {
+ ms_uptr_t m_address;
+ ms_follow_status_t m_status;
+} ms_follow_t;
+
+/* Methods */
+
+/* Static */
+
+static ms_follow_t
+follow_until(const ms_uptr_t copy, const ms_uptr_t start, const ms_uptr_t end,
+ const ms_find_bytes_t* restrict find_bytes)
+{
+ /* data */
+
+ ms_follow_t result = {0};
+
+ ms_uptr_t address = copy;
+
+#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
+ if (find_bytes == NULL ||
+ (find_bytes != NULL &&
+ (find_bytes->m_data == NULL || find_bytes->m_size == 0))) {
+ result.m_address = 0;
+ result.m_status = MS_FOLLOW_STATUS_NO_VALID_FOLLOW_INFO;
+
+ goto return_label;
+ }
+#endif
+
+ /* the following will run at least once, so you can follow regardless of
+ * current address. it will break upon a NULL address, or after the first
+ * overbounds byte, or upon a match in memory. */
+
+ ms_usize_t match = MEMSCAN_FIRST_MATCH;
+
+ int status;
+
+retry_label:
+ status = memcmp((void*)address, find_bytes->m_data, find_bytes->m_size);
+
+ while ((void*)address != NULL && address >= (start - 1) &&
+ address <= (end + 1) && status != 0) {
+ address +=
+ find_bytes->m_direction == MS_FOLLOW_DIRECTION_BACKWARDS ? -1 : 1;
+
+ status = memcmp((void*)address, find_bytes->m_data, find_bytes->m_size);
+ }
+
+ result.m_address = address;
+
+ /* if the address is smaller than start, then it is overbounds on the lhs,
+ * otherwise, if it is smaller than end, it is overbounds no the rhs,
+ * otherwise, if status is != 0, it is OK, otherwise, it is an incomplete
+ * follow */
+
+ result.m_status = (result.m_address < start
+ ? MS_FOLLOW_STATUS_FAIL_LHS
+ : (result.m_address > end
+ ? MS_FOLLOW_STATUS_FAIL_RHS
+ : (status != 0 ? MS_FOLLOW_STATUS_INCOMPLETE
+ : MS_FOLLOW_STATUS_OK)));
+
+ /* verify match */
+
+ if (match != find_bytes->m_match &&
+ result.m_status == MS_FOLLOW_STATUS_OK) {
+ ++match;
+ address +=
+ find_bytes->m_direction == MS_FOLLOW_DIRECTION_BACKWARDS ? -1 : 1;
+ goto retry_label;
+ }
+
+return_label:
+ return result;
+}
+
+/* */
+
+static ms_result_t
+memscan_find_pattern_bb_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_bytes_t* restrict bytes,
+ const ms_find_bytes_t* restrict find_bytes)
+{
+ ms_result_t result = {0};
+
+ result.m_address = 0;
+
+#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
+ if (bytes == NULL || (bytes && bytes->m_data == NULL)) {
+ result.m_status = MS_RESULT_NO_VALID_BYTES_INFO;
+
+ goto return_label;
+ }
+#endif
+
+ /* data */
+
+ /* this value is meant to be used to deduce how many actual bytes were input
+ * to the formation, scaling to every platform's sizeof(unsigned char) */
+
+ const ms_usize_t bytes_len = bytes->m_size / sizeof *bytes->m_data;
+
+ /* here, bytes->m_size is used because we're doing pointer arithmetic */
+
+ const ms_ubyte_t* finish = (ms_ubyte_t*)(end - bytes->m_size);
+
+ ms_usize_t match = MEMSCAN_FIRST_MATCH;
+
+#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
+ if (start >= (ms_uptr_t)finish) {
+ result.m_status = MS_RESULT_NO_VALID_SPAN;
+
+ goto return_label;
+ }
+#endif
+
+ /* loop over every byte from start to end */
+
+ for (ms_ubyte_t* current = (ms_ubyte_t*)start; current < finish;
+ ++current) {
+ ms_result_status_t status = MS_RESULT_STATUS_FOUND;
+
+ /* run a scan from current byte to current byte + bytes_len to verify if
+ * we have a match */
+
+ for (ms_usize_t walker = 0; walker < bytes_len; ++walker) {
+ /* if the current byte at walker indice isn't equal to the current
+ * signature byte if it isn't our wildcard */
+
+ if (current[walker] != bytes->m_data[walker] &&
+ bytes->m_data[walker] != k_memscan_wildcard) {
+ /* declare the state as not found for the current iteration
+ * and go to the next one */
+
+ status = MS_RESULT_STATUS_NOT_FOUND;
+ break;
+ }
+ }
+
+ /* if the state wasn't altered (as in, the checks have passed and the
+ * current byte matches our signature) */
+
+ if (status == MS_RESULT_STATUS_FOUND) {
+ /* verify n-th match */
+
+ if (match != bytes->m_match) {
+ ++match;
+ continue;
+ }
+
+ /* pass our address and status to result */
+
+ result.m_address = (ms_uptr_t)current;
+ result.m_status = status;
+
+ /* attempt a follow */
+
+ const ms_follow_t follow =
+ follow_until(result.m_address, start, end, find_bytes);
+
+ switch (follow.m_status) {
+ case MS_FOLLOW_STATUS_OK: {
+ /* change the result address to the follow result,
+ * started from the original one */
+
+ result.m_address = follow.m_address;
+ } break;
+
+ case MS_FOLLOW_STATUS_FAIL_LHS:
+ case MS_FOLLOW_STATUS_FAIL_RHS: {
+ /* this is prioritized over MS_FOLLOW_STATUS_INCOMPLETE */
+
+ /* deduce if it was either a left side overbounds
+ * failure or right side overbounds failure for the
+ * possibility of still allowing a result depending on
+ * user response */
+
+ result.m_status =
+ follow.m_status == MS_FOLLOW_STATUS_FAIL_LHS
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_RHS;
+ } break;
+
+ case MS_FOLLOW_STATUS_INCOMPLETE: {
+ /* a condition to break the follow was met before there was
+ * a match in memory between the seeked byte-code and the
+ * byte-code at from it's current address for the seeked
+ * byte-code length */
+
+ result.m_status =
+ MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_INCOMPLETE;
+ } break;
+ }
+
+ goto return_label;
+ }
+ }
+
+ /* we haven't found an address for our signature, so we will set result
+ * state to NOT FOUND */
+
+ result.m_status = MS_RESULT_STATUS_NOT_FOUND;
+
+return_label:
+ return result;
+}
+
+/* */
+
+static ms_result_t
+memscan_find_pattern_bs_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_bytes_t* restrict bytes,
+ const ms_find_str_t* restrict find_bytes)
+{
+ ms_result_t result = {0};
+ result.m_address = 0;
+
+ if (find_bytes == NULL)
+ result = memscan_find_pattern_bb_impl(start, end, bytes, NULL);
+ else {
+ /* data */
+
+ ms_pattern_t pattern =
+ util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
+
+ ms_find_bytes_t find_bytes_from_str = {0};
+ find_bytes_from_str.m_data = pattern.m_data;
+ find_bytes_from_str.m_size = pattern.m_size;
+ find_bytes_from_str.m_match = find_bytes->m_match;
+ find_bytes_from_str.m_direction = find_bytes->m_direction;
+
+ /* pass converted data to base implementation */
+
+ result = memscan_find_pattern_bb_impl(start, end, bytes,
+ &find_bytes_from_str);
+
+ util_free_pattern(&pattern);
+ }
+
+ return result;
+}
+
+/* */
+
+static ms_result_t
+memscan_find_pattern_sb_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_bytes_str_t* restrict bytes,
+ const ms_find_bytes_t* restrict find_bytes)
+{
+ ms_result_t result = {0};
+ result.m_address = 0;
+
+ if (bytes == NULL || (bytes && bytes->m_data == NULL))
+ result = memscan_find_pattern_bb_impl(start, end, NULL, find_bytes);
+ else {
+ /* data */
+
+ ms_pattern_t pattern =
+ util_build_pattern(bytes->m_data, strlen(bytes->m_data));
+
+ ms_bytes_t bytes_from_str = {0};
+ bytes_from_str.m_data = pattern.m_data;
+ bytes_from_str.m_size = pattern.m_size;
+ bytes_from_str.m_match = bytes->m_match;
+
+ /* pass converted data to base implementation */
+
+ result = memscan_find_pattern_bb_impl(start, end, &bytes_from_str,
+ find_bytes);
+
+ util_free_pattern(&pattern);
+ }
+
+ return result;
+}
+
+/* */
+
+static ms_result_t
+memscan_find_pattern_ss_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_bytes_str_t* restrict bytes,
+ const ms_find_str_t* restrict find_bytes)
+{
+
+ ms_result_t result = {0};
+ result.m_address = 0;
+
+ if (find_bytes == NULL)
+ result = memscan_find_pattern_sb_impl(start, end, bytes, NULL);
+ else {
+ /* data */
+
+ ms_pattern_t find_pattern =
+ util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
+
+ ms_find_bytes_t find_bytes_from_str = {0};
+ find_bytes_from_str.m_data = find_pattern.m_data;
+ find_bytes_from_str.m_size = find_pattern.m_size;
+ find_bytes_from_str.m_match = find_bytes->m_match;
+ find_bytes_from_str.m_direction = find_bytes->m_direction;
+
+ /* pass data to our string, bytes wrapper */
+
+ result = memscan_find_pattern_sb_impl(start, end, bytes,
+ &find_bytes_from_str);
+
+ util_free_pattern(&find_pattern);
+ }
+
+ return result;
+}
+
+/* */
+
+static ms_result_t
+memscan_find_pattern_nfb_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_bytes_t* restrict bytes)
+{
+ /* pass data to our base implementation. the data being NULL
+ * will prevent a follow from happening */
+
+ return memscan_find_pattern_bb_impl(start, end, bytes, NULL);
+}
+
+/* */
+
+static ms_result_t
+memscan_find_pattern_nfs_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_bytes_str_t* restrict bytes)
+{
+ /* pass data to our string, bytes wrapper. the data being NULL
+ * will prevent a follow from happening */
+
+ return memscan_find_pattern_sb_impl(start, end, bytes, NULL);
+}
+
+/* */
+
+static ms_result_t
+memscan_find_xref_b_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_xref_t* restrict xref,
+ const ms_find_bytes_t* restrict find_bytes)
+{
+ ms_result_t result = {0};
+
+ result.m_address = 0;
+
+#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
+ if (xref == NULL /* data is handled in the pattern finder */) {
+ result.m_status = MS_RESULT_NO_VALID_BYTES_INFO;
+ goto return_label;
+ }
+#endif
+
+ /* data */
+
+ ms_bytes_t from_xref = {0};
+
+ from_xref.m_data =
+ util_ptr_to_byteset(xref->m_pointer, xref->m_swap_endianness);
+ from_xref.m_size = MEMSCAN_BYTESET_SIZE;
+ from_xref.m_match = xref->m_match;
+
+ /* pass data to our no follow, bytes wrapper */
+
+ result = memscan_find_pattern_bb_impl(start, end, &from_xref, find_bytes);
+
+return_label:
+ return result;
+}
+
+/* */
+
+static ms_result_t
+memscan_find_xref_s_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_xref_t* restrict xref,
+ const ms_find_str_t* restrict find_bytes)
+{
+ ms_result_t result = {0};
+ result.m_address = 0;
+
+ if (find_bytes == NULL)
+ result = memscan_find_xref_b_impl(start, end, xref, NULL);
+ else {
+ /* data */
+
+ ms_pattern_t pattern =
+ util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
+
+ ms_find_bytes_t find_bytes_from_str = {0};
+ find_bytes_from_str.m_data = pattern.m_data;
+ find_bytes_from_str.m_size = pattern.m_size;
+ find_bytes_from_str.m_match = find_bytes->m_match;
+ find_bytes_from_str.m_direction = find_bytes->m_direction;
+
+ result =
+ memscan_find_xref_b_impl(start, end, xref, &find_bytes_from_str);
+
+ util_free_pattern(&pattern);
+ }
+
+ return result;
+}
+
+/* */
+
+static ms_result_t
+memscan_find_xref_nf_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_xref_t* restrict xref)
+{
+ return memscan_find_xref_b_impl(start, end, xref, NULL);
+}
+
+/* */
+
+static ms_result_t
+memscan_find_string_b_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_string_xref_t* restrict string,
+ const ms_find_bytes_t* restrict find_bytes)
+{
+ ms_result_t result = {0};
+
+ result.m_address = 0;
+
+#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
+ if (string == NULL /* data is handled in the pattern finder */) {
+ result.m_status = MS_RESULT_NO_VALID_BYTES_INFO;
+ goto return_label;
+ }
+#endif
+
+ /* data */
+
+ ms_bytes_t bytes = {0};
+
+ bytes.m_data = (const ms_ubyte_t*)string->m_data;
+ bytes.m_size = string->m_size;
+ bytes.m_match = 0;
+
+ ms_result_t string_find = memscan_find_pattern_nfb_impl(start, end, &bytes);
+
+ if (string_find.m_status != MS_RESULT_STATUS_FOUND) {
+ result.m_status = string_find.m_status;
+ goto return_label;
+ }
+
+ ms_xref_t xref = {0};
+
+ xref.m_pointer = string_find.m_address;
+ xref.m_match = string->m_match;
+ xref.m_swap_endianness = true;
+
+ result = memscan_find_xref_b_impl(start, end, &xref, find_bytes);
+
+return_label:
+ return result;
+}
+
+static ms_result_t
+memscan_find_string_s_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_string_xref_t* restrict string,
+ const ms_find_str_t* restrict find_bytes)
+{
+ ms_result_t result = {0};
+ result.m_address = 0;
+
+ if (find_bytes == NULL)
+ result = memscan_find_string_b_impl(start, end, string, NULL);
+ else {
+ /* data */
+
+ ms_pattern_t pattern =
+ util_build_pattern(find_bytes->m_data, strlen(find_bytes->m_data));
+
+ ms_find_bytes_t find_bytes_from_str = {0};
+ find_bytes_from_str.m_data = pattern.m_data;
+ find_bytes_from_str.m_size = pattern.m_size;
+ find_bytes_from_str.m_match = find_bytes->m_match;
+ find_bytes_from_str.m_direction = find_bytes->m_direction;
+
+ result = memscan_find_string_b_impl(start, end, string,
+ &find_bytes_from_str);
+
+ util_free_pattern(&pattern);
+ }
+
+ return result;
+}
+
+static ms_result_t
+memscan_find_string_nf_impl(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_string_xref_t* restrict string)
+{
+ return memscan_find_string_b_impl(start, end, string, NULL);
+}
+
+/* Prototype Bodies */
+
+ms_result_t
+memscan_find_pattern_bb(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_ubyte_t* pattern,
+ const ms_usize_t pattern_size,
+ const ms_usize_t pattern_nth_match,
+ const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ /* data */
+
+ ms_bytes_t bytes = {0};
+ bytes.m_data = pattern;
+ bytes.m_size = pattern_size;
+ bytes.m_match = pattern_nth_match;
+
+ ms_find_bytes_t find = {0};
+ find.m_data = follow_pattern;
+ find.m_size = follow_pattern_size;
+ find.m_match = follow_nth_match;
+ find.m_direction = follow_direction;
+
+ /* pass structured data to manager */
+
+ return memscan_find_pattern_bb_impl(start, end, &bytes, &find);
+}
+
+/* */
+
+ms_result_t
+memscan_find_pattern_bs(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_ubyte_t* pattern,
+ const ms_usize_t pattern_size,
+ const ms_usize_t pattern_nth_match,
+ const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ /* data */
+
+ ms_bytes_t bytes = {0};
+ bytes.m_data = pattern;
+ bytes.m_size = pattern_size;
+ bytes.m_match = pattern_nth_match;
+
+ ms_find_str_t find = {0};
+ find.m_data = follow_pattern;
+ find.m_match = follow_nth_match;
+ find.m_direction = follow_direction;
+
+ /* pass structured data to manager */
+
+ return memscan_find_pattern_bs_impl(start, end, &bytes, &find);
+}
+
+/* */
+
+ms_result_t
+memscan_find_pattern_sb(const ms_uptr_t start, const ms_uptr_t end,
+ const char* pattern, const ms_usize_t pattern_nth_match,
+ const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ /* data */
+
+ ms_bytes_str_t bytes = {0};
+ bytes.m_data = pattern;
+ bytes.m_match = pattern_nth_match;
+
+ ms_find_bytes_t find = {0};
+ find.m_data = follow_pattern;
+ find.m_size = follow_pattern_size;
+ find.m_match = follow_nth_match;
+ find.m_direction = follow_direction;
+
+ /* pass structured data to manager */
+
+ return memscan_find_pattern_sb_impl(start, end, &bytes, &find);
+}
+
+/* */
+
+ms_result_t
+memscan_find_pattern_ss(const ms_uptr_t start, const ms_uptr_t end,
+ const char* pattern, const ms_usize_t pattern_nth_match,
+ const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ /* data */
+
+ ms_bytes_str_t bytes = {0};
+ bytes.m_data = pattern;
+ bytes.m_match = pattern_nth_match;
+
+ ms_find_str_t find = {0};
+ find.m_data = follow_pattern;
+ find.m_match = follow_nth_match;
+ find.m_direction = follow_direction;
+
+ /* pass structured data to manager */
+
+ return memscan_find_pattern_ss_impl(start, end, &bytes, &find);
+}
+
+/* */
+
+ms_result_t
+memscan_find_pattern_nfb(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_ubyte_t* pattern,
+ const ms_usize_t pattern_size,
+ const ms_usize_t pattern_nth_match)
+{
+ /* data */
+
+ ms_bytes_t bytes = {0};
+ bytes.m_data = pattern;
+ bytes.m_size = pattern_size;
+ bytes.m_match = pattern_nth_match;
+
+ /* pass structured data to manager */
+
+ return memscan_find_pattern_nfb_impl(start, end, &bytes);
+}
+
+/* */
+
+ms_result_t
+memscan_find_pattern_nfs(const ms_uptr_t start, const ms_uptr_t end,
+ const char* pattern,
+ const ms_usize_t pattern_nth_match)
+{
+ /* data */
+
+ ms_bytes_str_t bytes = {0};
+ bytes.m_data = pattern;
+ bytes.m_match = pattern_nth_match;
+
+ /* pass structured data to manager */
+
+ return memscan_find_pattern_nfs_impl(start, end, &bytes);
+}
+
+/* */
+
+ms_result_t
+memscan_find_xref_b(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t content, const ms_usize_t content_nth_match,
+ bool swap_endianness, const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ /* data */
+
+ ms_xref_t xref = {0};
+
+ xref.m_pointer = content;
+ xref.m_match = content_nth_match;
+ xref.m_swap_endianness = swap_endianness;
+
+ ms_find_bytes_t find_bytes = {0};
+
+ find_bytes.m_data = follow_pattern;
+ find_bytes.m_size = follow_pattern_size;
+ find_bytes.m_match = follow_nth_match;
+ find_bytes.m_direction = follow_direction;
+
+ /* pass structured data to manager */
+
+ return memscan_find_xref_b_impl(start, end, &xref, &find_bytes);
+}
+
+/* */
+
+ms_result_t
+memscan_find_xref_at_b(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness, const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ return memscan_find_xref_b(start, end, *(ms_uptr_t*)address, nth_match,
+ swap_endianness, follow_pattern,
+ follow_pattern_size, follow_nth_match,
+ follow_direction);
+}
+
+/* */
+
+ms_result_t
+memscan_find_xref_s(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t content, const ms_usize_t content_nth_match,
+ bool swap_endianness, const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ /* data */
+
+ ms_xref_t xref = {0};
+
+ xref.m_pointer = content;
+ xref.m_match = content_nth_match;
+ xref.m_swap_endianness = swap_endianness;
+
+ ms_find_str_t find = {0};
+
+ find.m_data = follow_pattern;
+ find.m_match = follow_nth_match;
+ find.m_direction = follow_direction;
+
+ /* pass structured data to manager */
+
+ return memscan_find_xref_s_impl(start, end, &xref, &find);
+}
+
+/* */
+
+ms_result_t
+memscan_find_xref_at_s(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness, const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ return memscan_find_xref_s(start, end, *(ms_uptr_t*)address, nth_match,
+ swap_endianness, follow_pattern,
+ follow_nth_match, follow_direction);
+}
+
+/* */
+
+ms_result_t
+memscan_find_xref_nf(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t content,
+ const ms_usize_t content_nth_match, bool swap_endianness)
+{
+ /* data */
+
+ ms_xref_t xref = {0};
+
+ xref.m_pointer = content;
+ xref.m_match = content_nth_match;
+ xref.m_swap_endianness = swap_endianness;
+
+ /* pass structured data to manager */
+
+ return memscan_find_xref_nf_impl(start, end, &xref);
+}
+
+/* */
+
+ms_result_t
+memscan_find_xref_at_nf(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness)
+{
+ return memscan_find_xref_nf(start, end, *(ms_uptr_t*)address, nth_match,
+ swap_endianness);
+}
+
+/* */
+
+extern ms_result_t
+memscan_find_string_b(const ms_uptr_t start, const ms_uptr_t end,
+ const char* text, const ms_usize_t text_size,
+ const ms_usize_t nth_match,
+ const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ /* data */
+
+ ms_string_xref_t xref = {0};
+
+ xref.m_data = text;
+ xref.m_size = text_size;
+ xref.m_match = nth_match;
+
+ ms_find_bytes_t find_bytes = {0};
+
+ find_bytes.m_data = follow_pattern;
+ find_bytes.m_size = follow_pattern_size;
+ find_bytes.m_match = follow_nth_match;
+ find_bytes.m_direction = follow_direction;
+
+ /* pass structured data to manager */
+
+ return memscan_find_string_b_impl(start, end, &xref, &find_bytes);
+}
+
+/* */
+
+ms_result_t
+memscan_find_string_s(const ms_uptr_t start, const ms_uptr_t end,
+ const char* text, const ms_usize_t text_size,
+ const ms_usize_t nth_match, const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction)
+{
+ /* data */
+
+ ms_string_xref_t xref = {0};
+
+ xref.m_data = text;
+ xref.m_size = text_size;
+ xref.m_match = nth_match;
+
+ ms_find_str_t find = {0};
+
+ find.m_data = follow_pattern;
+ find.m_match = follow_nth_match;
+ find.m_direction = follow_direction;
+
+ /* pass structured data to manager */
+
+ return memscan_find_string_s_impl(start, end, &xref, &find);
+}
+
+/* */
+
+ms_result_t
+memscan_find_string_nf(const ms_uptr_t start, const ms_uptr_t end,
+ const char* text, const ms_usize_t text_size,
+ const ms_usize_t nth_match)
+{
+ /* data */
+
+ ms_string_xref_t xref = {0};
+
+ xref.m_data = text;
+ xref.m_size = text_size;
+ xref.m_match = nth_match;
+
+ /* pass structured data to manager */
+
+ return memscan_find_string_nf_impl(start, end, &xref);
+}
diff --git a/EGameTools/source/memscan/memscan.h b/EGameTools/source/memscan/memscan.h
new file mode 100644
index 0000000..6eeb687
--- /dev/null
+++ b/EGameTools/source/memscan/memscan.h
@@ -0,0 +1,1037 @@
+#pragma once
+
+/* Includes */
+
+#include
+
+/* Utility */
+
+#include "util/util.h"
+
+/* Types */
+
+typedef enum {
+ /* the search goes backwards (substracts from current address) */
+ MS_FOLLOW_DIRECTION_BACKWARDS = 0,
+
+ /* the search goes forwards (adds to current address) */
+ MS_FOLLOW_DIRECTION_FORWARDS
+} ms_follow_direction_t;
+
+typedef enum {
+ /* won't be reached if MEMSCAN_UNSAFE_OPTIMIZATIONS are on */
+
+ MS_RESULT_NO_VALID_BYTES_INFO = 0,
+
+ /* finish <= start */
+ MS_RESULT_NO_VALID_SPAN,
+
+ /* */
+
+ /* generic response if not found */
+ MS_RESULT_STATUS_NOT_FOUND,
+
+ /* follow led to address being < start and thus was not applied */
+ MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS,
+
+ /* follow led to address being > end and thus was not applied */
+ MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_RHS,
+
+ /* broke before reaching a match in memory, the former 2 are pioritized */
+ MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_INCOMPLETE,
+
+ /* pattern was found, follow has succeeded, status set to FOUND */
+ MS_RESULT_STATUS_FOUND
+} ms_result_status_t;
+
+typedef struct {
+ ms_uptr_t m_address;
+ ms_result_status_t m_status;
+} ms_result_t;
+
+/* Methods */
+
+/* Patterns */
+
+/**
+ * @brief Bytes, Bytes pattern-scanning function
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + pattern_size, failure to meet this condition
+ * will be evidenciated in the return)
+ * @param pattern Array of ubytes to match in memory, may contain
+ * [k_memscan_wildcard], which allows a mismatch at the opcode's position
+ * @param pattern_size Size of pattern
+ * @param pattern_nth_match N-th pattern repetition to select, starts at
+ * MEMSCAN_FIRST_MATCH
+ * @param follow_pattern Array of ubytes to look for, from pattern scan
+ * address, in a specified direction. Spanned from start to end (failures to
+ * meet conditions will be evidentiated in the return)
+ * @param follow_pattern_size Size of follow_pattern
+ * @param follow_nth_match Same as pattern_nth_match, but with the
+ * follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_pattern_bb(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_ubyte_t* pattern,
+ const ms_usize_t pattern_size,
+ const ms_usize_t pattern_nth_match,
+ const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief Bytes, String pattern-scanning function
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + pattern_size, failure to meet this condition
+ * will be evidenciated in the return)
+ * @param pattern Array of ubytes to match in memory, may contain
+ * [k_memscan_wildcard], which allows a mismatch at the opcode's position
+ * @param pattern_size Size of pattern
+ * @param pattern_nth_match N-th pattern repetition to select, starts at
+ * MEMSCAN_FIRST_MATCH
+ * @param follow_pattern Byte-code style string that will be turned into a
+ * ms_ubyte_t array for evaluation, example: "8B 4D"
+ * @param follow_nth_match Same as pattern_nth_match, but with the
+ * follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_pattern_bs(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_ubyte_t* pattern,
+ const ms_usize_t pattern_size,
+ const ms_usize_t pattern_nth_match,
+ const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief String, Bytes pattern-scanning function
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + pattern_size, failure to meet this condition
+ * will be evidenciated in the return)
+ * @param pattern Byte-code style string that will be turned into a ms_ubyte_t
+ * array for evaluation, may contain [k_memscan_wildcard], which allows a
+ * mismatch at the opcode's position. example: "55 8B EC CC CC CC CC"
+ * @param pattern_nth_match N-th pattern repetition to select, starts at
+ * MEMSCAN_FIRST_MATCH
+ * @param follow_pattern Array of ubytes to look for, from pattern scan
+ * address, in a specified direction. Spanned from start to end (failures to
+ * meet conditions will be evidentiated in the return)
+ * @param follow_pattern_size Size of follow_pattern
+ * @param follow_nth_match Same as pattern_nth_match, but with the
+ * follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_pattern_sb(const ms_uptr_t start, const ms_uptr_t end,
+ const char* pattern, const ms_usize_t pattern_nth_match,
+ const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief String, String pattern-scanning function
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + pattern_size, failure to meet this condition
+ * will be evidenciated in the return)
+ * @param pattern Byte-code style string that will be turned into a ms_ubyte_t
+ * array for evaluation, may contain [k_memscan_wildcard], which allows a
+ * mismatch at the opcode's position. example: "55 8B EC CC CC CC CC"
+ * @param pattern_nth_match N-th pattern repetition to select, starts at
+ * MEMSCAN_FIRST_MATCH
+ * @param follow_pattern Byte-code style string that will be turned into a
+ * ms_ubyte_t array for evaluation, example: "8B 4D"
+ * @param follow_nth_match Same as pattern_nth_match, but with the
+ * follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_pattern_ss(const ms_uptr_t start, const ms_uptr_t end,
+ const char* pattern, const ms_usize_t pattern_nth_match,
+ const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief No follow, Bytes pattern-scanning function
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + pattern_size, failure to meet this condition
+ * will be evidenciated in the return)
+ * @param pattern Array of ubytes to match in memory, may contain
+ * [k_memscan_wildcard], which allows a mismatch at the opcode's position
+ * @param pattern_size Size of pattern
+ * @param pattern_nth_match N-th pattern repetition to select, starts at
+ * MEMSCAN_FIRST_MATCH
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_pattern_nfb(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_ubyte_t* pattern,
+ const ms_usize_t pattern_size,
+ const ms_usize_t pattern_nth_match);
+
+/**
+ * @brief No follow, String pattern-scanning function
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + pattern_size, failure to meet this condition
+ * will be evidenciated in the return)
+ * @param pattern Byte-code style string that will be turned into a ms_ubyte_t
+ * array for evaluation, may contain [k_memscan_wildcard], which allows a
+ * mismatch at the opcode's position. example: "55 8B EC CC CC CC CC"
+ * @param pattern_nth_match N-th pattern repetition to select, starts at
+ * MEMSCAN_FIRST_MATCH
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_pattern_nfs(const ms_uptr_t start, const ms_uptr_t end,
+ const char* pattern,
+ const ms_usize_t pattern_nth_match);
+
+/**
+ * @brief Xref Bytes Follow finder from a sequence
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this
+ * condition will be evidenciated in the return)
+ * @param content Content to find in global address space
+ * @param nth_match N-th repetition of content, starts at MEMSCAN_FIRST_MATCH
+ * @param swap_endianness Whether the content should have it's endianness
+ * swapped
+ * @param follow_pattern Array of ms_ubyte_t to look for, from xref address, in
+ * a specified direction. Spanned from start to end (failures to meet conditions
+ * will be evidentiated in the return)
+ * @param follow_pattern_size Size of follow_pattern
+ * @param follow_nth_match Same as nth_match, but with the follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_xref_b(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t content, const ms_usize_t content_nth_match,
+ bool swap_endianness, const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief Xref Bytes Follow finder from a reference to pointer
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this
+ * condition will be evidenciated in the return)
+ * @param address Reference to address to find references to in global address
+ * space
+ * @param nth_match N-th repetition of reference to select, starts at
+ * MEMSCAN_FIRST_MATCH, for clarification, matches are looked for from start,
+ * not from address
+ * @param swap_endianness Whether the contents at address to have their
+ * endianness swapped
+ * @param follow_pattern Array of ubytes to look for, from xref address, in
+ * a specified direction. Spanned from start to end (failures to meet conditions
+ * will be evidentiated in the return)
+ * @param follow_pattern_size Size of follow_pattern
+ * @param follow_nth_match Same as nth_match, but with the follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_xref_at_b(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness, const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief Xref String Follow finder from a sequence
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this
+ * condition will be evidenciated in the return)
+ * @param content Content to find in global address space
+ * @param nth_match N-th repetition of content, starts at MEMSCAN_FIRST_MATCH
+ * @param swap_endianness Whether the content should have it's endianness
+ * swapped
+ * @param follow_pattern Byte-code style string that will be turned into a
+ * ms_ubyte_t array for evaluation, example: "8B 4D"
+ * @param follow_nth_match Same as nth_match, but with the follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_xref_s(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t content, const ms_usize_t content_nth_match,
+ bool swap_endianness, const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief Xref String Follow finder from a reference to pointer
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this
+ * condition will be evidenciated in the return)
+ * @param address Reference to address to find references to in global address
+ * space
+ * @param nth_match N-th repetition of reference to select, starts at
+ * MEMSCAN_FIRST_MATCH, for clarification, matches are looked for from start,
+ * not from address
+ * @param swap_endianness Whether the contents at address to have their
+ * endianness swapped
+ * @param follow_pattern Byte-code style string that will be turned into a
+ * ms_ubyte_t array for evaluation, example: "8B 4D"
+ * @param follow_nth_match Same as nth_match, but with the follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_xref_at_s(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness, const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief Xref No follow finder from a sequence
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this
+ * condition will be evidenciated in the return)
+ * @param content Content to find in global address space
+ * @param nth_match N-th repetition of content, starts at MEMSCAN_FIRST_MATCH
+ * @param swap_endianness Whether the content should have it's endianness
+ * swapped
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_xref_nf(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t content,
+ const ms_usize_t content_nth_match, bool swap_endianness);
+
+/**
+ * @brief Xref No follow finder from a reference to pointer
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this
+ * condition will be evidenciated in the return)
+ * @param address Reference to address to find references to in global address
+ * space
+ * @param nth_match N-th repetition of reference to select, starts at
+ * MEMSCAN_FIRST_MATCH, for clarification, matches are looked for from start,
+ * not from address
+ * @param swap_endianness Whether the contents at address to have their
+ * endianness swapped
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_xref_at_nf(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness);
+
+/**
+ * @brief String, Bytes follow finder from string start pointer to reversed
+ * endianness address scan
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this
+ * condition will be evidenciated in the return)
+ * @param text Compile-time string to find (should be found in '.rdata'/it's
+ * variations for Windows, or maybe batched in '.text', etc...)
+ * @param text_size Size of the 'text'
+ * @param nth_match N-th reference to the address of the first character of the
+ * string with reversed endianness in memory
+ * @param follow_pattern Array of ubytes to look for, from string xref
+ * address, in a specified direction. Spanned from start to end (failures to
+ * meet conditions will be evidentiated in the return)
+ * @param follow_pattern_size Size of follow_pattern
+ * @param follow_nth_match Same as nth_match, but with the follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_string_b(const ms_uptr_t start, const ms_uptr_t end,
+ const char* text, const ms_usize_t text_size,
+ const ms_usize_t nth_match,
+ const ms_ubyte_t* follow_pattern,
+ const ms_usize_t follow_pattern_size,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief String, String follow finder from string start pointer to reversed
+ * endianness address scan
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this
+ * condition will be evidenciated in the return)
+ * @param text Compile-time string to find (should be found in '.rdata'/it's
+ * variations for Windows, or maybe batched in '.text', etc...)
+ * @param text_size Size of the 'text'
+ * @param nth_match N-th reference to the address of the first character of the
+ * string with reversed endianness in memory
+ * @param follow_pattern Byte-code style string that will be turned into a
+ * ms_ubyte_t array for evaluation, example: "8B 4D"
+ * @param follow_nth_match Same as nth_match, but with the follow_pattern
+ * @param follow_direction refer to ms_follow_direction_t for documentation
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_string_s(const ms_uptr_t start, const ms_uptr_t end,
+ const char* text, const ms_usize_t text_size,
+ const ms_usize_t nth_match, const char* follow_pattern,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction);
+
+/**
+ * @brief String No Follow finder from string start pointer to reversed
+ * endianness address scan
+ *
+ * @param start Position to start scanning from in global address space
+ * @param end Position to stop scanning at in global address space (expected to
+ * be, at the very least, start + MEMSCAN_BYTESET_SIZE, failure to meet this
+ * condition will be evidenciated in the return)
+ * @param text Compile-time string to find (should be found in '.rdata'/it's
+ * variations for Windows, or maybe batched in '.text', etc...)
+ * @param text_size Size of the 'text'
+ * @param nth_match N-th reference to the address of the first character of the
+ * string with reversed endianness in memory
+ * @return Refer to ms_result_t for documentation
+ */
+MEMSCAN_EXTERN ms_result_t
+memscan_find_string_nf(const ms_uptr_t start, const ms_uptr_t end,
+ const char* text, const ms_usize_t text_size,
+ const ms_usize_t nth_match);
+
+/* Detail */
+
+static const ms_ubyte_t k_memscan_wildcard = 0xAA;
+
+/* Constants */
+
+#define MEMSCAN_FIRST_MATCH (0)
+
+/* Wrapper */
+
+#if __cplusplus
+#include
+
+#ifdef MEMSCAN_CPP_EXCEPTIONS
+#include
+#endif
+
+#ifdef MEMSCAN_CPP_EXCEPTIONS
+#define MEMSCAN_CPP_NOEXCEPT noexcept(false)
+#else
+#define MEMSCAN_CPP_NOEXCEPT noexcept(true)
+#endif
+#include
+#ifdef _WIN32
+#include
+#include
+#include
+#endif
+
+namespace memscan
+{
+#ifdef _WIN32
+[[nodiscard]] inline static auto
+NtCurrentPeb() noexcept
+{
+ return NtCurrentTeb()->ProcessEnvironmentBlock;
+}
+#endif
+
+struct mapped_region_t {
+ /* constructors */
+
+ explicit mapped_region_t() = delete;
+
+ [[nodiscard]] mapped_region_t(const ms_uptr_t start,
+ const ms_uptr_t end) noexcept
+ : m_start(start),
+ m_end(end)
+ {
+ }
+
+#ifdef _WIN32
+ [[nodiscard]] mapped_region_t(const HMODULE module) MEMSCAN_CPP_NOEXCEPT
+ : m_start(reinterpret_cast(module))
+ {
+ const auto* const dos_header =
+ reinterpret_cast(m_start);
+
+#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
+ if (dos_header == nullptr) {
+#ifdef MEMSCAN_CPP_EXCEPTIONS
+ throw std::runtime_error(
+ "Failed initializing module: invalid DOS header");
+#endif
+ return;
+ }
+#endif
+
+ const auto* const nt_headers = reinterpret_cast(
+ m_start + static_cast(dos_header->e_lfanew));
+
+#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
+ if (nt_headers == nullptr) {
+#ifdef MEMSCAN_CPP_EXCEPTIONS
+ throw std::runtime_error(
+ "Failed initializing module: invalid NT headers");
+#endif
+ return;
+ }
+#endif
+
+ m_end = m_start +
+ static_cast(nt_headers->OptionalHeader.SizeOfImage);
+ }
+#endif
+
+#ifdef _WIN32
+ [[nodiscard]] mapped_region_t(const std::wstring_view module_in_list)
+ MEMSCAN_CPP_NOEXCEPT
+ {
+ const auto* const ldr = NtCurrentPeb()->Ldr;
+
+#if !MEMSCAN_UNSAFE_OPTIMIZATIONS
+ if (ldr == nullptr) {
+#ifdef MEMSCAN_CPP_EXCEPTIONS
+ throw std::runtime_error(
+ "Failed initializing module: invalid PEB Ldr");
+#endif
+ return;
+ }
+#endif
+
+ const auto* const head = reinterpret_cast(
+ reinterpret_cast(ldr) + sizeof(LIST_ENTRY));
+ const auto* curr = head->Flink;
+ while (curr != nullptr && curr != head) {
+ const auto* const data =
+ reinterpret_cast(curr);
+ if (data->FullDllName.Length == 0) {
+ curr = curr->Flink;
+ continue;
+ }
+
+ // `Length` is more rather a ptrdiff from buffer, not the actual
+ // normal definition of length.
+ const auto module_name = std::wstring_view{
+ data->FullDllName.Buffer,
+ data->FullDllName.Length / sizeof *data->FullDllName.Buffer};
+ if (module_name.ends_with(module_in_list)) {
+ (*this) =
+ mapped_region_t(static_cast(data->DllBase));
+ return;
+ }
+
+ curr = curr->Flink;
+ }
+
+#ifdef MEMSCAN_CPP_EXCEPTIONS
+ throw std::runtime_error(
+ "Failed initializing module: Unable to find module in list");
+#endif
+ }
+#endif
+
+ /* implementations */
+
+ /* C-style casts are used for automatic cast deduction */
+
+ template
+ static std::optional
+ find_pattern(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_ubyte_t (&pattern)[N], const ms_usize_t nth_match,
+ const ms_ubyte_t (&follow)[N2],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+ {
+ auto&& result =
+ memscan_find_pattern_bb(start, end, pattern, N, nth_match, follow,
+ N2, follow_nth_match, follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_pattern(const ms_uptr_t start, const ms_uptr_t end,
+ const std::string_view pattern, const ms_usize_t nth_match,
+ const ms_ubyte_t (&follow)[N],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+ {
+ auto&& result = memscan_find_pattern_sb(
+ start, end, pattern.data(), nth_match, follow, N, follow_nth_match,
+ follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_pattern(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_ubyte_t (&pattern)[N], const ms_usize_t nth_match,
+ const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+ {
+ auto&& result = memscan_find_pattern_bs(
+ start, end, pattern, N, nth_match, follow.data(), follow_nth_match,
+ follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_pattern(const ms_uptr_t start, const ms_uptr_t end,
+ const std::string_view pattern, const ms_usize_t nth_match,
+ const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+ {
+ auto&& result = memscan_find_pattern_ss(
+ start, end, pattern.data(), nth_match, follow.data(),
+ follow_nth_match, follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_pattern(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_ubyte_t (&pattern)[N],
+ const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) noexcept
+ {
+ auto&& result =
+ memscan_find_pattern_nfb(start, end, pattern, N, nth_match);
+
+ if (result.m_status == MS_RESULT_STATUS_FOUND)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_pattern(const ms_uptr_t start, const ms_uptr_t end,
+ const std::string_view pattern,
+ const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) noexcept
+ {
+ auto&& result =
+ memscan_find_pattern_nfs(start, end, pattern.data(), nth_match);
+
+ if (result.m_status == MS_RESULT_STATUS_FOUND)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_xref(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t content, const ms_usize_t content_nth_match,
+ bool swap_endianness, const ms_ubyte_t (&follow)[N],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+
+ {
+ auto&& result = memscan_find_xref_b(
+ start, end, content, content_nth_match, swap_endianness, follow, N,
+ follow_nth_match, follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_xref(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t content, const ms_usize_t content_nth_match,
+ bool swap_endianness, const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+
+ {
+ auto&& result = memscan_find_xref_s(
+ start, end, content, content_nth_match, swap_endianness,
+ follow.data(), follow_nth_match, follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_xref(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t content, const ms_usize_t content_nth_match,
+ bool swap_endianness) noexcept
+ {
+ auto&& result = memscan_find_xref_nf(
+ start, end, content, content_nth_match, swap_endianness);
+
+ if (result.m_status == MS_RESULT_STATUS_FOUND)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_xref_at(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness, const ms_ubyte_t (&follow)[N],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+
+ {
+ auto&& result = memscan_find_xref_at_b(
+ start, end, address, nth_match, swap_endianness, follow, N,
+ follow_nth_match, follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_xref_at(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness, const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+
+ {
+ auto&& result = memscan_find_xref_at_s(
+ start, end, address, nth_match, swap_endianness, follow.data(),
+ follow_nth_match, follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_xref_at(const ms_uptr_t start, const ms_uptr_t end,
+ const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness) noexcept
+ {
+ auto&& result = memscan_find_xref_at_nf(start, end, address, nth_match,
+ swap_endianness);
+
+ if (result.m_status == MS_RESULT_STATUS_FOUND)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_string(const ms_uptr_t start, const ms_uptr_t end,
+ const std::string_view string, const ms_usize_t nth_match,
+ const ms_ubyte_t (&follow)[N],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+ {
+ auto&& result = memscan_find_string_b(
+ start, end, string.data(), string.size(), nth_match, follow, N,
+ follow_nth_match, follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_string(const ms_uptr_t start, const ms_uptr_t end,
+ const std::string_view string, const ms_usize_t nth_match,
+ const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) noexcept
+ {
+ auto&& result = memscan_find_string_s(
+ start, end, string.data(), string.size(), nth_match, follow.data(),
+ follow_nth_match, follow_direction);
+
+ constexpr auto status = AcceptFailedFollow
+ ? MS_RESULT_STATUS_FOUND_FOLLOW_FAIL_LHS
+ : MS_RESULT_STATUS_FOUND;
+ if (result.m_status >= status)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ template
+ static std::optional
+ find_string(const ms_uptr_t start, const ms_uptr_t end,
+ const std::string_view string,
+ const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) noexcept
+ {
+ auto&& result = memscan_find_string_nf(start, end, string.data(),
+ string.size(), nth_match);
+
+ if (result.m_status >= MS_RESULT_STATUS_FOUND)
+ return (T)result.m_address;
+
+ return std::nullopt;
+ }
+
+ /* injections */
+
+ template
+ std::optional
+ find_pattern(const ms_ubyte_t (&pattern)[N], const ms_usize_t nth_match,
+ const ms_ubyte_t (&follow)[N2],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+ {
+ return find_pattern(
+ m_start, m_end, pattern, nth_match, follow, follow_nth_match,
+ follow_direction);
+ }
+
+ template
+ std::optional
+ find_pattern(const std::string_view pattern, const ms_usize_t nth_match,
+ const ms_ubyte_t (&follow)[N],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+ {
+ return find_pattern(
+ m_start, m_end, pattern, nth_match, follow, follow_nth_match,
+ follow_direction);
+ }
+
+ template
+ std::optional
+ find_pattern(const ms_ubyte_t (&pattern)[N], const ms_usize_t nth_match,
+ const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+ {
+ return find_pattern(
+ m_start, m_end, pattern, nth_match, follow, follow_nth_match,
+ follow_direction);
+ }
+
+ template
+ std::optional
+ find_pattern(const std::string_view pattern, const ms_usize_t nth_match,
+ const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+ {
+ return find_pattern(
+ m_start, m_end, pattern, nth_match, follow, follow_nth_match,
+ follow_direction);
+ }
+
+ template
+ std::optional
+ find_pattern(const ms_ubyte_t (&pattern)[N],
+ const ms_usize_t match = MEMSCAN_FIRST_MATCH) const noexcept
+ {
+ return find_pattern(m_start, m_end, pattern, match);
+ }
+
+ template
+ std::optional find_pattern(
+ const std::string_view pattern,
+ const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) const noexcept
+ {
+ return find_pattern(m_start, m_end, pattern, nth_match);
+ }
+
+ template
+ std::optional
+ find_xref(const ms_uptr_t content, const ms_usize_t content_nth_match,
+ bool swap_endianness, const ms_ubyte_t (&follow)[N],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+
+ {
+ return find_xref(
+ content, content_nth_match, swap_endianness, follow,
+ follow_nth_match, follow_direction);
+ }
+
+ template
+ std::optional
+ find_xref(const ms_uptr_t content, const ms_usize_t content_nth_match,
+ bool swap_endianness, const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+
+ {
+ return find_xref(
+ m_start, m_end, content, content_nth_match, swap_endianness, follow,
+ follow_nth_match, follow_direction);
+ }
+
+ template
+ std::optional find_xref(const ms_uptr_t content,
+ const ms_usize_t content_nth_match,
+ bool swap_endianness) const noexcept
+ {
+ return find_xref(m_start, m_end, content, content_nth_match,
+ swap_endianness);
+ }
+
+ template
+ std::optional
+ find_xref_at(const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness, const ms_ubyte_t (&follow)[N],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+
+ {
+ return find_xref_at(
+ m_start, m_end, address, nth_match, swap_endianness, follow,
+ follow_nth_match, follow_direction);
+ }
+
+ template
+ std::optional
+ find_xref_at(const ms_uptr_t address, const ms_usize_t nth_match,
+ bool swap_endianness, const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+
+ {
+ return find_xref_at(
+ m_start, m_end, address, nth_match, swap_endianness, follow,
+ follow_nth_match, follow_direction);
+ }
+
+ template
+ std::optional find_xref_at(const ms_uptr_t address,
+ const ms_usize_t nth_match,
+ bool swap_endianness) const noexcept
+ {
+ return find_xref_at(m_start, m_end, address, nth_match,
+ swap_endianness);
+ }
+
+ template
+ std::optional
+ find_string(const std::string_view string, const ms_usize_t nth_match,
+ const ms_ubyte_t (&follow)[N],
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+ {
+ return find_string(
+ m_start, m_end, string, nth_match, follow, follow_nth_match,
+ follow_direction);
+ }
+
+ template
+ std::optional
+ find_string(const std::string_view string, const ms_usize_t nth_match,
+ const std::string_view follow,
+ const ms_usize_t follow_nth_match,
+ const ms_follow_direction_t follow_direction) const noexcept
+ {
+ return find_string(
+ m_start, m_end, string, nth_match, follow, follow_nth_match,
+ follow_direction);
+ }
+
+ template
+ std::optional
+ find_string(const std::string_view string,
+ const ms_usize_t nth_match = MEMSCAN_FIRST_MATCH) const noexcept
+ {
+ return find_string(m_start, m_end, string, nth_match);
+ }
+
+ /* helpers */
+
+ const auto get_start() const noexcept { return m_start; }
+ const auto get_end() const noexcept { return m_end; }
+
+private:
+ ms_uptr_t m_start{};
+ ms_uptr_t m_end{};
+};
+} // namespace memscan
+#endif
\ No newline at end of file
diff --git a/EGameTools/source/memscan/util/util.c b/EGameTools/source/memscan/util/util.c
new file mode 100644
index 0000000..a1cd1b8
--- /dev/null
+++ b/EGameTools/source/memscan/util/util.c
@@ -0,0 +1,102 @@
+#include "util.h"
+#include
+#include
+
+ms_pattern_t
+util_build_pattern(const char* data, const ms_usize_t data_size)
+{
+ ms_pattern_t result = {0};
+
+ result.m_data = NULL;
+ result.m_size = 0;
+
+#if !UTIL_UNSAFE_OPTIMIZATIONS
+ if (data == NULL) {
+ result.m_status = MS_BUILD_STATUS_NO_DATA;
+
+ goto leave;
+ }
+#endif
+
+ size_t len = data_size;
+
+#if !UTIL_UNSAFE_OPTIMIZATIONS
+ if (len == 0) {
+ result.m_status = MS_BUILD_STATUS_SHORT_DATA;
+ goto leave;
+ }
+#endif
+
+ /* data */
+
+ char* start = (char*)data;
+ char* end = (char*)(data + len);
+
+ /* precompute allocation size */
+
+ ms_usize_t size = 0;
+
+ for (char* current = start; current < end; ++current)
+ ++size;
+
+ ms_ubyte_t* bytes = (ms_ubyte_t*)malloc(size * sizeof *bytes);
+
+ /* prefetched */
+
+ ms_usize_t indice = 0;
+
+ for (char* current = start; current < end; ++current) {
+ /* hex substring conversion */
+
+ bytes[indice++] = (ms_ubyte_t)strtoul(current, ¤t, 16);
+ }
+
+ result.m_data = bytes;
+ result.m_size = indice;
+ result.m_status = MS_BUILD_STATUS_OK;
+
+leave:
+ return result;
+}
+
+/* */
+
+ms_free_t
+util_free_pattern(ms_pattern_t* pattern)
+{
+#if !UTIL_UNSAFE_OPTIMIZATIONS
+ if (pattern == NULL) {
+ return MS_FREE_NO;
+ }
+#endif
+
+ ms_free_t result = MS_FREE_NO;
+
+ if (pattern->m_status == MS_BUILD_STATUS_OK) {
+ free(pattern->m_data);
+ pattern->m_data = NULL;
+
+ result = MS_FREE_YES;
+ }
+
+ pattern->m_size = 0;
+
+ return result;
+}
+
+ms_ubyte_t*
+util_ptr_to_byteset(const ms_uptr_t num, bool swap_endianness)
+{
+ /* data */
+
+ static ms_ubyte_t bytes[MEMSCAN_BYTESET_SIZE] = {0};
+
+ for (ms_usize_t i = 0; i < MEMSCAN_BYTESET_SIZE; ++i) {
+ /* shift formation to get current indice */
+
+ bytes[swap_endianness ? i : MEMSCAN_BYTESET_SIZE - i - 1] =
+ (ms_ubyte_t)(num >> (i * CHAR_BIT));
+ }
+
+ return bytes;
+}
\ No newline at end of file
diff --git a/EGameTools/source/memscan/util/util.h b/EGameTools/source/memscan/util/util.h
new file mode 100644
index 0000000..9d24139
--- /dev/null
+++ b/EGameTools/source/memscan/util/util.h
@@ -0,0 +1,88 @@
+#pragma once
+
+/* Includes */
+
+#include
+#include
+#include
+
+/* Types */
+
+typedef uint8_t ms_ubyte_t;
+typedef uintptr_t ms_uptr_t;
+typedef size_t ms_usize_t;
+
+/* Extern */
+
+#if __cplusplus
+#define MEMSCAN_EXTERN extern "C"
+#else
+#define MEMSCAN_EXTERN extern
+#endif
+
+typedef enum {
+ /* nothing to free, or there's a condition preventing the process */
+ MS_FREE_NO = 0,
+
+ /* the data was found present and then freed */
+ MS_FREE_YES
+} ms_free_t;
+
+typedef enum {
+ /* won't be reached unless UTIL_UNSAFE_OPTIMIZATIONS is off */
+
+ /* passed data was NULL */
+ MS_BUILD_STATUS_NO_DATA = 0,
+
+ /* data len was 0 */
+ MS_BUILD_STATUS_SHORT_DATA,
+
+ /* */
+
+ /* generation has succeeded, status was set to OK */
+ MS_BUILD_STATUS_OK
+} ms_build_status_t;
+
+typedef struct {
+ ms_ubyte_t* m_data;
+ ms_usize_t m_size;
+ ms_build_status_t m_status;
+} ms_pattern_t;
+
+/* Methods */
+
+/**
+ * @brief Generate byte code array from byte-code style string
+ *
+ * @param data Example: "AA BB CC DD EE FF", equivalent to
+ * (ms_ubyte_t*)"\xAA\xBB\xCC\xDD\xEE\xFF"
+ * @param data_size Size of 'data'
+ * @return Refer to ms_pattern_t for documentation
+ */
+MEMSCAN_EXTERN ms_pattern_t
+util_build_pattern(const char* data, const ms_usize_t data_size);
+
+/**
+ * @brief Deallocate pattern array after usage
+ *
+ * @param pattern Reference to the pattern construct
+ * @return Refer to ms_free_t for documentation
+ */
+MEMSCAN_EXTERN ms_free_t
+util_free_pattern(ms_pattern_t* pattern);
+
+/**
+ * @brief Convert pointer in numerical form to byteset of MEMSCAN_BYTESET_SIZE
+ * bytes
+ *
+ * @param num Value to convert
+ * @param swap_endianness Whether to swap endianness or not
+ * @return Value as a MEMSCAN_BYTESET_SIZE bytes array
+ */
+MEMSCAN_EXTERN ms_ubyte_t*
+util_ptr_to_byteset(const ms_uptr_t num, bool swap_endianness);
+
+/* Constants */
+
+#define MEMSCAN_BYTESET_SIZE (sizeof(ms_uptr_t) / sizeof(ms_ubyte_t))
+#define MEMSCAN_POINTER_BITS (sizeof(ms_uptr_t) * CHAR_BIT)
\ No newline at end of file
diff --git a/EGameTools/source/menu/camera.cpp b/EGameTools/source/menu/camera.cpp
index 53cc232..5667914 100644
--- a/EGameTools/source/menu/camera.cpp
+++ b/EGameTools/source/menu/camera.cpp
@@ -6,8 +6,8 @@
#include "..\game\GamePH\GameDI_PH.h"
#include "..\game\GamePH\LevelDI.h"
#include "..\game\GamePH\PlayerVariables.h"
+#include "..\game\GamePH\PlayerObjProperties.h"
#include "..\game\GamePH\gameph_misc.h"
-#include "..\game\GamePH\gen_TPPModel.h"
#include "..\offsets.h"
#include "camera.h"
#include "menu.h"
@@ -149,8 +149,8 @@ namespace Menu {
if (!iLevel || !iLevel->IsLoaded())
return;
- GamePH::gen_TPPModel* pgen_TPPModel = GamePH::gen_TPPModel::Get();
- if (pgen_TPPModel) {
+ GamePH::PlayerObjProperties* playerObjProperties = GamePH::PlayerObjProperties::Get();
+ if (playerObjProperties) {
if (Menu::Camera::freeCam.GetValue() && !iLevel->IsTimerFrozen())
GamePH::ShowTPPModel(true);
else if (Menu::Camera::freeCam.GetValue() && iLevel->IsTimerFrozen() && !photoMode.GetValue())
diff --git a/EGameTools/source/menu/debug.cpp b/EGameTools/source/menu/debug.cpp
index c04a3c1..a82631a 100644
--- a/EGameTools/source/menu/debug.cpp
+++ b/EGameTools/source/menu/debug.cpp
@@ -16,7 +16,6 @@
#include "..\game\GamePH\SessionCooperativeDI.h"
#include "..\game\GamePH\TPPCameraDI.h"
#include "..\game\GamePH\TimeWeather\CSystem.h"
-#include "..\game\GamePH\gen_TPPModel.h"
#include "..\game\Engine\CBulletPhysicsCharacter.h"
#include "..\game\Engine\CGSObject.h"
@@ -37,7 +36,6 @@ namespace Menu {
{ "FreeCamera", reinterpret_cast(&GamePH::FreeCamera::Get) },
{ "GameDI_PH", reinterpret_cast(&GamePH::GameDI_PH::Get) },
{ "GameDI_PH2", reinterpret_cast(&GamePH::GameDI_PH2::Get) },
- { "gen_TPPModel", reinterpret_cast(&GamePH::gen_TPPModel::Get) },
{ "LevelDI", reinterpret_cast(&GamePH::LevelDI::Get) },
{ "LocalClientDI", reinterpret_cast(&GamePH::LocalClientDI::Get) },
{ "LogicalPlayer", reinterpret_cast(&GamePH::LogicalPlayer::Get) },
diff --git a/EGameTools/source/menu/menu.cpp b/EGameTools/source/menu/menu.cpp
index fe0f763..ed70058 100644
--- a/EGameTools/source/menu/menu.cpp
+++ b/EGameTools/source/menu/menu.cpp
@@ -22,6 +22,8 @@ namespace Menu {
Option firstTimeRunning{};
Option hasSeenChangelog{};
+ int currentTabIndex = 0;
+
void Render() {
ImGui::StyleScaleAllSizes(&ImGui::GetStyle(), scale, &defStyle);
ImGui::GetIO().FontGlobalScale = scale;
@@ -47,6 +49,7 @@ namespace Menu {
ImGui::SetNextWindowBgAlpha(static_cast(opacity) / 100.0f);
ImGui::SetNextWindowSizeConstraints(ImVec2(std::fmax(minWndSize.x - GImGui->Style.WindowPadding.x * 2.0f, ImGui::CalcTextSize(Core::gameVer > GAME_VER_COMPAT ? "Please wait for a new mod update." : "Upgrade your game version to one that the mod supports.").x), remainingHeight), ImVec2(maxWndSize.x - GImGui->Style.WindowPadding.x * 2.0f, remainingHeight));
if (ImGui::BeginChild("##TabChild", ImVec2(0.0f, 0.0f), ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeX | ImGuiChildFlags_Border)) {
+ currentTabIndex = tab.first;
childWidth = ImGui::GetItemRectSize().x;
tab.second->Render();
ImGui::EndChild();
diff --git a/EGameTools/source/menu/menu.h b/EGameTools/source/menu/menu.h
index d99d0c0..4806025 100644
--- a/EGameTools/source/menu/menu.h
+++ b/EGameTools/source/menu/menu.h
@@ -12,7 +12,6 @@ namespace Menu {
virtual void Update() {};
std::string_view tabName{};
- private:
int tabIndex{};
};
@@ -27,5 +26,7 @@ namespace Menu {
extern Option firstTimeRunning;
extern Option hasSeenChangelog;
+ extern int currentTabIndex;
+
extern void Render();
}
\ No newline at end of file
diff --git a/EGameTools/source/menu/world.cpp b/EGameTools/source/menu/world.cpp
index 0c0ec71..599cb16 100644
--- a/EGameTools/source/menu/world.cpp
+++ b/EGameTools/source/menu/world.cpp
@@ -89,7 +89,7 @@ namespace Menu {
if (freezeTime.GetValue() && !Utils::Values::are_samef(time, timeBeforeFreeze, 0.0095f))
dayNightCycle->SetDaytime(timeBeforeFreeze);
- if (!menuToggle.GetValue()) {
+ if (!menuToggle.GetValue() || Menu::currentTabIndex != World::Tab::tabIndex) {
if (!slowMotion.GetValue() && !slowMotion.HasChanged() && !Utils::Values::are_samef(gameSpeed, 1.0f))
iLevel->TimerSetSpeedUp(gameSpeed);
actualGameSpeed = iLevel->TimerGetSpeedUp();
diff --git a/EGameTools/source/offsets.h b/EGameTools/source/offsets.h
index 932cb67..55fbf36 100644
--- a/EGameTools/source/offsets.h
+++ b/EGameTools/source/offsets.h
@@ -6,7 +6,7 @@
static retType Get_## name () {\
static retType name = NULL;\
static int i = 0;\
- if (Utils::Memory::IsValidPtr(name) || i >= 10) return name;\
+ if (Utils::Memory::IsValidPtr(name) || !GetModuleHandleA(moduleName) || i >= 10) return name;\
i++;\
return name=reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(moduleName, {pattern, type}));\
}
@@ -20,14 +20,14 @@ static DWORD64 Get_## name () {\
static DWORD64 Get_## name () {\
static DWORD64 name = 0;\
if (Utils::Memory::IsValidPtr(name)) return name;\
- return name=reinterpret_cast(GetModuleHandle(moduleName)) + static_cast(off);\
+ return name=reinterpret_cast(GetModuleHandleA(moduleName)) + static_cast(off);\
}
#define AddVTOffset(name, moduleName, rttiName, retType)\
static retType GetVT_## name () {\
static retType VT_## name = NULL;\
static int i = 0;\
- if (Utils::Memory::IsValidPtr(VT_## name)) return VT_## name;\
+ if (Utils::Memory::IsValidPtr(VT_## name)|| !GetModuleHandleA(moduleName) || i >= 10) return VT_## name;\
i++;\
return VT_## name=reinterpret_cast(Utils::RTTI::GetVTablePtr(moduleName, rttiName));\
}
@@ -42,7 +42,7 @@ struct Offsets {
AddVTOffset(TypedFieldMetaFloatPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VFloatPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", LPVOID)
AddVTOffset(TypedFieldMetaBoolPlayerVariable, "gamedll_ph_x64_rwdi.dll", "?$TypedFieldMeta@VBoolPlayerVariable@@@?$FieldsCollection@VPlayerVariables@@@constds", LPVOID)
AddOffset(LoadPlayerVars, "gamedll_ph_x64_rwdi.dll", "48 89 4C 24 ?? B8 ?? ?? ?? ?? E8 ?? ?? ?? ?? 48 2B E0 48 8B 8C 24", Utils::SigScan::PatternType::Address, LPVOID)
- AddOffset(PlayerState, "gamedll_ph_x64_rwdi.dll", "4C 8B 35 [?? ?? ?? ?? 4C 8B E2", Utils::SigScan::PatternType::RelativePointer, LPVOID)
+ AddOffset(PlayerState, "gamedll_ph_x64_rwdi.dll", "48 8B 35 [?? ?? ?? ?? 4C 8B F2 48 8B F9", Utils::SigScan::PatternType::RelativePointer, LPVOID)
// Game related
AddVTOffset(CBulletPhysicsCharacter, "engine_x64_rwdi.dll", "CBulletPhysicsCharacter", LPVOID)
diff --git a/EGameTools/source/utils/hook.h b/EGameTools/source/utils/hook.h
index 8bfa94f..fce2e43 100644
--- a/EGameTools/source/utils/hook.h
+++ b/EGameTools/source/utils/hook.h
@@ -40,11 +40,11 @@ namespace Utils {
bool HookLoop() override {
if (hooked || (optionRef && !optionRef->GetValue()))
return true;
- timeSpentHooking = Utils::Time::Timer(160000);
+ timeSpentHooking = Utils::Time::Timer(120000);
while (true) {
if (timeSpentHooking.DidTimePass()) {
- spdlog::error("Failed hooking \"{}\" after 160 seconds", name);
+ spdlog::error("Failed hooking \"{}\" after 120 seconds", name);
return false;
}
if (!pGetOffsetFunc || !pGetOffsetFunc())
@@ -62,6 +62,8 @@ namespace Utils {
VirtualProtect(pGetOffsetFunc(), bytesAmount, originalProtection, &oldProtection);
hooked = true;
return true;
+
+ Sleep(10);
}
}
@@ -111,7 +113,7 @@ namespace Utils {
bool hooked = false;
- Utils::Time::Timer timeSpentHooking{ 160000 };
+ Utils::Time::Timer timeSpentHooking{ 120000 };
};
template
class MHook : HookBase {
@@ -121,11 +123,11 @@ namespace Utils {
bool HookLoop() override {
if (pOriginal)
return true;
- timeSpentHooking = Utils::Time::Timer(160000);
+ timeSpentHooking = Utils::Time::Timer(120000);
while (true) {
if (timeSpentHooking.DidTimePass()) {
- spdlog::error("Failed hooking function \"{}\" after 160 seconds", name);
+ spdlog::error("Failed hooking function \"{}\" after 120 seconds", name);
return false;
}
if (!pGetOffsetFunc)
@@ -137,6 +139,8 @@ namespace Utils {
MH_EnableHook(pTarget);
return true;
}
+
+ Sleep(10);
}
}
@@ -146,7 +150,7 @@ namespace Utils {
GetTargetOffsetRetType(*pGetOffsetFunc)() = nullptr;
OrigType pDetour = nullptr;
- Utils::Time::Timer timeSpentHooking{ 160000 };
+ Utils::Time::Timer timeSpentHooking{ 120000 };
};
template
class VTHook : HookBase {
@@ -156,11 +160,11 @@ namespace Utils {
bool HookLoop() override {
if (pOriginal)
return true;
- timeSpentHooking = Utils::Time::Timer(160000);
+ timeSpentHooking = Utils::Time::Timer(120000);
while (true) {
if (timeSpentHooking.DidTimePass()) {
- spdlog::error("Failed hooking function \"{}\" after 160 seconds", name);
+ spdlog::error("Failed hooking function \"{}\" after 120 seconds", name);
return false;
}
if (!pGetOffsetFunc)
@@ -172,6 +176,8 @@ namespace Utils {
HookVT(pInstance, pDetour, reinterpret_cast(&pOriginal), offset);
return true;
}
+
+ Sleep(10);
}
}
@@ -181,7 +187,7 @@ namespace Utils {
LPVOID pInstance = nullptr;
OrigType pDetour = nullptr;
- Utils::Time::Timer timeSpentHooking{ 160000 };
+ Utils::Time::Timer timeSpentHooking{ 120000 };
DWORD offset = 0x0;
};
diff --git a/EGameTools/source/utils/memory.cpp b/EGameTools/source/utils/memory.cpp
index 9599b47..f9125c8 100644
--- a/EGameTools/source/utils/memory.cpp
+++ b/EGameTools/source/utils/memory.cpp
@@ -37,13 +37,13 @@ namespace Utils {
return ptr && (ptr <= moduleEndPoint && ptr >= moduleEntryPoint);
}
- static std::string bytesToIDAPattern(BYTE* bytes, size_t size) {
+ std::string BytesToIDAPattern(BYTE* bytes, size_t size) {
std::stringstream idaPattern;
idaPattern << std::hex << std::uppercase << std::setfill('0');
for (size_t i = 0; i < size; i++) {
const int currentByte = bytes[i];
- if (currentByte != 255)
+ if (currentByte != SigScanWildCard)
idaPattern << std::setw(2) << currentByte;
else
idaPattern << "??";
@@ -54,6 +54,34 @@ namespace Utils {
return idaPattern.str();
}
+ std::string ConvertSigToScannerSig(const char* pattern, int* offsetToAddrInSig) {
+ size_t len = strlen(pattern);
+
+ std::string patt{};
+ int bytesCounted = 0;
+
+ for (size_t i = 0; i < len;) {
+ if (pattern[i] == ' ' || i == len - 1)
+ bytesCounted++;
+
+ if (pattern[i] == '[') {
+ i++;
+ if (offsetToAddrInSig)
+ *offsetToAddrInSig = static_cast(bytesCounted);
+ continue;
+ }
+
+ if (pattern[i] == '?') {
+ patt += SigScanWildCardStr;
+ i += (pattern[static_cast(i) + 1] == '?') ? 2 : 1;
+ } else {
+ patt.push_back(pattern[i]);
+ i++;
+ }
+ }
+
+ return patt;
+ }
DWORD64 CalcTargetAddrOfRelInst(DWORD64 addrOfInst, size_t opSize) {
int offset = *reinterpret_cast(addrOfInst + opSize);
@@ -62,7 +90,7 @@ namespace Utils {
std::vector GetXrefsTo(DWORD64 address, DWORD64 start, size_t size) {
std::vector xrefs = {};
- const std::string idaPattern = bytesToIDAPattern(reinterpret_cast(&address), 8);
+ const std::string idaPattern = BytesToIDAPattern(reinterpret_cast(&address), 8);
const DWORD64 end = start + size;
while (start && start < end) {
diff --git a/EGameTools/source/utils/memory.h b/EGameTools/source/utils/memory.h
index 384c863..45c38b7 100644
--- a/EGameTools/source/utils/memory.h
+++ b/EGameTools/source/utils/memory.h
@@ -5,11 +5,17 @@
namespace Utils {
namespace Memory {
+ static const BYTE SigScanWildCard = 0xAA;
+ static const std::string_view SigScanWildCardStr = "AA";
+
extern const MODULEINFO GetModuleInfo(const char* szModule);
extern const FARPROC GetProcAddr(const std::string_view& module, const std::string_view& funcName);
extern const bool IsAddressValidMod(const DWORD64 ptr, const char* moduleName);
+ extern std::string BytesToIDAPattern(BYTE* bytes, size_t size);
+ extern std::string ConvertSigToScannerSig(const char* pattern, int* offsetToAddrInSig = nullptr);
+
extern DWORD64 CalcTargetAddrOfRelInst(DWORD64 addrOfInst, size_t opSize);
std::vector GetXrefsTo(DWORD64 address, DWORD64 start, size_t size);
diff --git a/EGameTools/source/utils/rtti.cpp b/EGameTools/source/utils/rtti.cpp
index f4b2a62..9b79493 100644
--- a/EGameTools/source/utils/rtti.cpp
+++ b/EGameTools/source/utils/rtti.cpp
@@ -1,45 +1,30 @@
-#include "pch.h"
+#include
namespace Utils {
namespace RTTI {
- static std::string bytesToIDAPattern(BYTE* bytes, size_t size) {
- std::stringstream idaPattern;
- idaPattern << std::hex << std::uppercase << std::setfill('0');
-
- for (size_t i = 0; i < size; i++) {
- const int currentByte = bytes[i];
- if (currentByte != 255)
- idaPattern << std::setw(2) << currentByte;
- else
- idaPattern << "??";
-
- if (i != size - 1)
- idaPattern << " ";
- }
-
- return idaPattern.str();
- }
static std::vector getXrefsTo(DWORD64 moduleBaseAddr, DWORD64 address, DWORD64 start, size_t size) {
std::vector xrefs = {};
const DWORD64 finalOffset = address - moduleBaseAddr;
- const std::string idaPattern = bytesToIDAPattern(reinterpret_cast(const_cast(&finalOffset)), 4);
+ const std::string idaPattern = Memory::BytesToIDAPattern(reinterpret_cast(const_cast(&finalOffset)), 4);
+ std::string patt = Memory::ConvertSigToScannerSig(idaPattern.c_str());
// Get the end of the section (in our case the end of the .rdata section)
const DWORD64 end = start + size;
while (start && start < end) {
- DWORD64 xref = reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast(start), size, { idaPattern.c_str(), Utils::SigScan::PatternType::Address}));
+ const auto scanner = memscan::mapped_region_t(start, end);
+ auto patternFind = scanner.find_pattern(patt);
// If the xref is 0 it means that there either were no xrefs, or there are no remaining xrefs.
// So we should break out of our loop, otherwise it will keep on trying to look for xrefs.
- if (!xref)
+ if (!patternFind.has_value())
break;
// We've found an xref, save it in the vector, and add 8 to start, so it wil now search for xrefs
// from the previously found xref untill we're at the end of the section, or there aren't any xrefs left.
- xrefs.push_back(xref);
- start = xref + 8;
+ xrefs.push_back(patternFind.value());
+ start = patternFind.value() + 8;
}
return xrefs;
@@ -83,7 +68,7 @@ namespace Utils {
const std::string typeDescriptorName = ".?AV" + std::string(tableName) + "@@";
// Convert the string to an IDA pattern so that we can pattern scan it
- std::string idaPattern = bytesToIDAPattern(reinterpret_cast(const_cast(typeDescriptorName.data())), typeDescriptorName.size());
+ std::string idaPattern = Memory::BytesToIDAPattern(reinterpret_cast(const_cast(typeDescriptorName.data())), typeDescriptorName.size());
DWORD64 rttiTypeDescriptor = reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(moduleName, { idaPattern.c_str(), Utils::SigScan::PatternType::Address }));
if (!rttiTypeDescriptor)
@@ -113,7 +98,7 @@ namespace Utils {
// Now we need to get an xref to the object locator, as that's where the vtable is located
{
// Convert the object locator address to an IDA pattern
- idaPattern = bytesToIDAPattern(reinterpret_cast(const_cast(&objectLocator)), 8);
+ idaPattern = Memory::BytesToIDAPattern(reinterpret_cast(const_cast(&objectLocator)), 8);
const DWORD64 vtableAddr = reinterpret_cast(Utils::SigScan::PatternScanner::FindPattern(reinterpret_cast(rdataStart), rdataSize, { idaPattern.c_str(), Utils::SigScan::PatternType::Address })) + 0x8;
diff --git a/EGameTools/source/utils/sigscan.cpp b/EGameTools/source/utils/sigscan.cpp
index f4ba66f..35807ca 100644
--- a/EGameTools/source/utils/sigscan.cpp
+++ b/EGameTools/source/utils/sigscan.cpp
@@ -38,109 +38,16 @@ namespace Utils {
}
LPVOID PatternScanner::FindPattern(LPVOID startAddress, DWORD64 searchSize, const Pattern& pattern) {
- size_t len = strlen(pattern.pattern);
- if (len == 0)
- return nullptr;
-
- DWORD64 pos = 0;
-
- size_t byteCount = 1;
- uint32_t i = 0;
- while (i < len - 1) {
- if (pattern.pattern[i] == ' ')
- byteCount++;
- i++;
- }
-
- BYTE* patt = reinterpret_cast(malloc(byteCount + 1));
- if (!patt)
- return nullptr;
-
- BYTE* mask = reinterpret_cast(malloc(byteCount + 1));
- if (!mask) {
- free(patt);
- return nullptr;
- }
-
int offset = 0;
- int bytesCounted = 0;
- i = 0;
- while (i < len - 1) {
- if (pattern.pattern[i] == '[') {
- i++;
- offset = bytesCounted;
- }
+ std::string patt = Memory::ConvertSigToScannerSig(pattern.pattern, &offset);
- if (pattern.pattern[i] == '\0')
- break;
-
- if (pattern.pattern[i] == '?' && pattern.pattern[i + 1] == '?') {
- mask[bytesCounted] = '?';
- patt[bytesCounted] = '\0';
- } else {
- BYTE hn = pattern.pattern[i] > '9' ? pattern.pattern[i] - 'A' + 10 : pattern.pattern[i] - '0';
- BYTE ln = pattern.pattern[i + 1] > '9' ? pattern.pattern[i + 1] - 'A' + 10 : pattern.pattern[i + 1] - '0';
- BYTE n = (hn << 4) | ln;
-
- mask[bytesCounted] = 'x';
- patt[bytesCounted] = n;
- }
-
- bytesCounted++;
-
- i += 2;
- while (i < len && (pattern.pattern[i] == ' ' || pattern.pattern[i] == '\t' || pattern.pattern[i] == '\r' || pattern.pattern[i] == '\n'))
- i++;
- }
- if (bytesCounted <= byteCount)
- mask[bytesCounted] = '\0';
+ const auto scanner = memscan::mapped_region_t(reinterpret_cast(startAddress), reinterpret_cast(startAddress) + searchSize);
+ auto patternFind = scanner.find_pattern(patt);
LPVOID ret = nullptr;
- const DWORD64 retAddress = reinterpret_cast(startAddress);
- const DWORD64 endAddress = retAddress + searchSize;
- size_t searchLen = bytesCounted;
- BYTE* retAddressPtr = reinterpret_cast(retAddress);
- BYTE* endAddressPtr = reinterpret_cast(endAddress);
-
- while (retAddressPtr < endAddressPtr) {
- MEMORY_BASIC_INFORMATION mbi;
- if (VirtualQuery(retAddressPtr, &mbi, sizeof(mbi))) {
- // Check if the memory region is readable
- if (mbi.Protect & (PAGE_EXECUTE_READ | PAGE_EXECUTE_READWRITE | PAGE_READONLY | PAGE_READWRITE)) {
- // Adjust the end address to the end of this memory region
- BYTE* regionEnd = reinterpret_cast(mbi.BaseAddress) + mbi.RegionSize;
-
- // Only scan within this region
- while (retAddressPtr < regionEnd && retAddressPtr < endAddressPtr) {
- bool found = true;
-
- for (size_t j = 0; j < searchLen; j++) {
- if (mask[j] == 'x' && retAddressPtr[j] != patt[j]) {
- found = false;
- break;
- }
- }
-
- if (found) {
- ret = reinterpret_cast(retAddressPtr + offset);
- break;
- }
-
- retAddressPtr++;
- }
-
- if (ret != nullptr)
- break;
- } else
- // Skip the non-readable memory region
- retAddressPtr = reinterpret_cast(mbi.BaseAddress) + mbi.RegionSize;
- } else
- retAddressPtr++;
- }
-
- free(patt);
- free(mask);
+ if (patternFind.has_value())
+ ret = reinterpret_cast(patternFind.value() + offset);
switch (pattern.type) {
case PatternType::Pointer: