diff --git a/src/game/backend/ScriptPatches.cpp b/src/game/backend/ScriptPatches.cpp index 2bfc7ac..e58e63f 100644 --- a/src/game/backend/ScriptPatches.cpp +++ b/src/game/backend/ScriptPatches.cpp @@ -1,67 +1,13 @@ #include "ScriptPatches.hpp" +#include "game/backend/ScriptPointers.hpp" #include "game/pointers/Pointers.hpp" #include "types/script/scrProgram.hpp" namespace YimMenu { - static uint8_t* GetCodeLocation(ScriptPatches::Data* data, int idx) - { - return &data->GetData()[idx >> 14][idx & 0x3FFF]; - } - - // TODO: refactor this and cache results - static std::optional GetCodeLocationByPattern(ScriptPatches::Data* data, const SimplePattern& pattern) - { - uint32_t code_size = data->GetSize(); - for (uint32_t i = 0; i < (code_size - pattern.m_Bytes.size()); i++) - { - for (uint32_t j = 0; j < pattern.m_Bytes.size(); j++) - { - if (pattern.m_Bytes[j].has_value()) - { - int loc = i + j; - if (pattern.m_Bytes[j] != *GetCodeLocation(data, loc)) - goto incorrect; - } - } - - return i; - incorrect: - continue; - } - - return std::nullopt; - } - - ScriptPatches::Data::Data(rage::scrProgram* program) - { - auto pages = new uint8_t*[program->GetNumCodePages()]; - - for (auto i = 0u; i < program->GetNumCodePages(); i++) - { - pages[i] = new uint8_t[program->GetCodePageSize(i)]; - std::memcpy(pages[i], program->GetCodePage(i), program->GetCodePageSize(i)); - } - - m_Data = pages; - m_Pages = program->GetNumCodePages(); - m_Size = program->GetFullCodeSize(); - } - - ScriptPatches::Data::~Data() - { - for (auto i = 0u; i < m_Pages; i++) - { - delete[] m_Data[i]; - } - - delete[] m_Data; - m_Data = nullptr; - } - - std::optional ScriptPatches::Patch::GetPC() + std::optional ScriptPatches::Patch::GetPC() { if (m_Pc.has_value()) return m_Pc; @@ -71,15 +17,14 @@ namespace YimMenu if (!data) return std::nullopt; - auto location = GetCodeLocationByPattern(data, m_Pattern); - - if (!location.has_value()) + if (auto address = ScriptPointers::GetPointer(Joaat(m_Pointer.GetName()))) { - LOG(FATAL) << "Failed to find pattern for script " << HEX(m_Hash); + m_Pc = address; } else { - m_Pc = location.value() + m_Offset; + m_Pc = m_Pointer.Scan(data).As(); + ScriptPointers::CachePointer(Joaat(m_Pointer.GetName()), m_Pc.value()); } return m_Pc; @@ -96,10 +41,10 @@ namespace YimMenu if (m_OriginalBytes.size() == 0) { m_OriginalBytes.resize(m_PatchedBytes.size()); - memcpy(m_OriginalBytes.data(), GetCodeLocation(data, pc.value()), m_PatchedBytes.size()); + memcpy(m_OriginalBytes.data(), ScriptData::GetCodeLocation(data, pc.value()), m_PatchedBytes.size()); } - memcpy(GetCodeLocation(data, pc.value()), m_PatchedBytes.data(), m_PatchedBytes.size()); + memcpy(ScriptData::GetCodeLocation(data, pc.value()), m_PatchedBytes.data(), m_PatchedBytes.size()); } void ScriptPatches::Patch::Restore() @@ -113,13 +58,12 @@ namespace YimMenu if (!pc || !data) return; - memcpy(GetCodeLocation(data, pc.value()), m_OriginalBytes.data(), m_OriginalBytes.size()); + memcpy(ScriptData::GetCodeLocation(data, pc.value()), m_OriginalBytes.data(), m_OriginalBytes.size()); } - ScriptPatches::Patch::Patch(joaat_t script, SimplePattern pattern, int32_t offset, std::vector patch) : - m_Pattern(pattern), - m_Offset(offset), + ScriptPatches::Patch::Patch(joaat_t script, ScriptPointer pointer, std::vector patch) : m_PatchedBytes(patch), + m_Pointer(pointer), m_Enabled(false), m_Pc(std::nullopt), m_OriginalBytes({}), @@ -162,9 +106,9 @@ namespace YimMenu return m_Hash == hash; } - std::shared_ptr ScriptPatches::AddPatchImpl(joaat_t script, const std::string& pattern, int32_t offset, std::vector patch) + std::shared_ptr ScriptPatches::AddPatchImpl(joaat_t script, ScriptPointer pointer, std::vector patch) { - auto scr_patch = std::make_shared(script, SimplePattern(pattern), offset, patch); + auto scr_patch = std::make_shared(script, pointer, patch); // add patch to map m_Patches.push_back(scr_patch); @@ -202,7 +146,7 @@ namespace YimMenu if (!in_scope) return; // avoid polluting memory with unused program bytecode - m_Datas.emplace(program->m_NameHash, std::move(std::make_unique(program))); + m_Datas.emplace(program->m_NameHash, std::move(std::make_unique(program))); // refresh patches so they can enable themselves for (auto& patch : m_Patches) @@ -248,7 +192,7 @@ namespace YimMenu } } - ScriptPatches::Data* ScriptPatches::GetDataImpl(joaat_t script) + ScriptData* ScriptPatches::GetDataImpl(joaat_t script) { if (auto it = m_Datas.find(script); it != m_Datas.end()) return it->second.get(); diff --git a/src/game/backend/ScriptPatches.hpp b/src/game/backend/ScriptPatches.hpp index 9a79245..3f1b73d 100644 --- a/src/game/backend/ScriptPatches.hpp +++ b/src/game/backend/ScriptPatches.hpp @@ -1,6 +1,7 @@ #pragma once -#include "core/memory/Pattern.hpp" #include "core/util/Joaat.hpp" +#include "game/gta/ScriptData.hpp" +#include "game/gta/ScriptPointer.hpp" namespace rage { @@ -12,52 +13,29 @@ namespace YimMenu class ScriptPatches { public: - class Data - { - std::uint8_t** m_Data; - std::uint32_t m_Pages; - std::uint32_t m_Size; - - public: - inline std::uint8_t** GetData() const - { - return m_Data; - } - - inline std::uint32_t GetSize() const - { - return m_Size; - } - - Data(rage::scrProgram* program); - ~Data(); - }; - class Patch { std::vector m_OriginalBytes; std::vector m_PatchedBytes; - std::optional m_Pc; - SimplePattern m_Pattern; - int32_t m_Offset; + std::optional m_Pc; + ScriptPointer m_Pointer; bool m_Enabled; std::uint32_t m_Hash; - - std::optional GetPC(); + std::optional GetPC(); void Apply(); void Restore(); public: - Patch(joaat_t script, SimplePattern pattern, int32_t offset, std::vector patch); + Patch(joaat_t script, ScriptPointer pointer, std::vector patch); void Enable(); void Disable(); void Update(); bool InScope(std::uint32_t hash); }; - static std::shared_ptr AddPatch(joaat_t script, const std::string& pattern, int32_t offset, std::vector patch) + static std::shared_ptr AddPatch(joaat_t script, ScriptPointer pointer, std::vector patch) { - return GetInstance().AddPatchImpl(script, pattern, offset, patch); + return GetInstance().AddPatchImpl(script, pointer, patch); } static void RegisterProgram(rage::scrProgram* program) @@ -90,15 +68,15 @@ namespace YimMenu return Instance; } - std::shared_ptr AddPatchImpl(joaat_t script, const std::string& pattern, int32_t offset, std::vector patch); + std::shared_ptr AddPatchImpl(joaat_t script, ScriptPointer pointer, std::vector patch); void RegisterProgramImpl(rage::scrProgram* program); void UnregisterProgramImpl(rage::scrProgram* program); void OnScriptVMEnterImpl(rage::scrProgram* program); void OnScriptVMLeaveImpl(rage::scrProgram* program); - Data* GetDataImpl(joaat_t script); + ScriptData* GetDataImpl(joaat_t script); std::vector> m_Patches; - std::unordered_map> m_Datas; + std::unordered_map> m_Datas; std::uint8_t** m_CurrentlyReplacedBytecode; }; diff --git a/src/game/backend/ScriptPointers.cpp b/src/game/backend/ScriptPointers.cpp new file mode 100644 index 0000000..19a7b42 --- /dev/null +++ b/src/game/backend/ScriptPointers.cpp @@ -0,0 +1,75 @@ +#include "ScriptPointers.hpp" +#include "core/filemgr/FileMgr.hpp" +#include "core/memory/ModuleMgr.hpp" + +namespace YimMenu +{ + ScriptPointers::ScriptPointers() : + m_CacheFile(FileMgr::GetProjectFile("./scr_pointers.bin")) + { + } + + void ScriptPointers::InitImpl() + { + m_CacheFile.Load(); + if (m_CacheFile.UpToDate(ModuleMgr.Get("GTA5_Enhanced.exe"_J)->GetNtHeader()->FileHeader.TimeDateStamp)) + { + Load(); + } + } + + void ScriptPointers::CachePointerImpl(joaat_t nameHash, std::uint32_t address) + { + m_ScriptPointers.emplace(nameHash, address); + Save(); + } + + std::uint32_t ScriptPointers::GetPointerImpl(joaat_t nameHash) + { + if (auto it = m_ScriptPointers.find(nameHash); it != m_ScriptPointers.end()) + { + return it->second; + } + + return 0; + } + + void ScriptPointers::Save() + { + auto dataSize = sizeof(std::uint32_t) + sizeof(scrPointerSaveStruct) * m_ScriptPointers.size(); + auto data = std::make_unique(dataSize); + auto dataPtr = data.get(); + + *(std::uint32_t*)dataPtr = m_ScriptPointers.size(); + dataPtr += sizeof(std::uint32_t); + + for (auto& [hash, val] : m_ScriptPointers) + { + auto saveStruct = (scrPointerSaveStruct*)dataPtr; + saveStruct->m_NameHash = hash; + saveStruct->m_Address = val; + dataPtr += sizeof(scrPointerSaveStruct); + } + + m_CacheFile.SetHeaderVersion(ModuleMgr.Get("GTA5_Enhanced.exe"_J)->GetNtHeader()->FileHeader.TimeDateStamp); + m_CacheFile.SetData(std::move(data), dataSize); + m_CacheFile.Write(); + } + + void ScriptPointers::Load() + { + auto data = m_CacheFile.Data(); + + auto numPointers = *(std::uint32_t*)data; + data += sizeof(std::uint32_t); + + for (int i = 0; i < numPointers; i++) + { + auto saveStruct = (scrPointerSaveStruct*)data; + m_ScriptPointers.emplace(saveStruct->m_NameHash, saveStruct->m_Address); + data += sizeof(scrPointerSaveStruct); + } + + LOG(INFO) << "Loaded " << m_ScriptPointers.size() << " script pointers from cache."; + } +} \ No newline at end of file diff --git a/src/game/backend/ScriptPointers.hpp b/src/game/backend/ScriptPointers.hpp new file mode 100644 index 0000000..5907a6a --- /dev/null +++ b/src/game/backend/ScriptPointers.hpp @@ -0,0 +1,50 @@ +#pragma once +#include "core/util/Joaat.hpp" +#include "core/filemgr/CacheFile.hpp" + +namespace YimMenu +{ +#pragma pack(push, 1) + struct scrPointerSaveStruct + { + joaat_t m_NameHash; + std::uint32_t m_Address; + }; +#pragma pack(pop) + + class ScriptPointers + { + static ScriptPointers& GetInstance() + { + static ScriptPointers instance; + return instance; + } + + void InitImpl(); + void CachePointerImpl(joaat_t nameHash, std::uint32_t address); + std::uint32_t GetPointerImpl(joaat_t nameHash); + void Save(); + void Load(); + + CacheFile m_CacheFile; + std::unordered_map m_ScriptPointers{}; + + public: + ScriptPointers(); + + static void Init() + { + GetInstance().InitImpl(); + } + + static void CachePointer(joaat_t nameHash, std::uint32_t address) + { + GetInstance().CachePointerImpl(nameHash, address); + } + + static std::uint32_t GetPointer(joaat_t nameHash) + { + return GetInstance().GetPointerImpl(nameHash); + } + }; +} \ No newline at end of file diff --git a/src/game/features/network/DisableDeathBarriers.cpp b/src/game/features/network/DisableDeathBarriers.cpp index 573ba54..19df9f4 100644 --- a/src/game/features/network/DisableDeathBarriers.cpp +++ b/src/game/features/network/DisableDeathBarriers.cpp @@ -13,7 +13,7 @@ namespace YimMenu::Features { if (!m_DeathBarriersPatch) { - m_DeathBarriersPatch = ScriptPatches::AddPatch("freemode"_J, "2D 01 09 00 00 5D ? ? ? 56 ? ? 3A", 5, {0x2E, 0x01, 0x00}); + m_DeathBarriersPatch = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("DeathBarriersPatch", "2D 01 09 00 00 5D ? ? ? 56 ? ? 3A").Add(5), {0x2E, 0x01, 0x00}); } m_DeathBarriersPatch->Enable(); } diff --git a/src/game/features/recovery/DLCVehicles.cpp b/src/game/features/recovery/DLCVehicles.cpp index 98706c9..12605f0 100644 --- a/src/game/features/recovery/DLCVehicles.cpp +++ b/src/game/features/recovery/DLCVehicles.cpp @@ -15,19 +15,19 @@ namespace YimMenu::Features { if (!m_VehiclePOSIXPatch) { - m_VehiclePOSIXPatch = ScriptPatches::AddPatch("appinternet"_J, "59 ? ? 72 2E 02 01", 0, {0x2B, 0x00, 0x00}); + m_VehiclePOSIXPatch = ScriptPatches::AddPatch("appinternet"_J, ScriptPointer("VehiclePOSIXPatch", "59 ? ? 72 2E 02 01"), {0x2B, 0x00, 0x00}); } m_VehiclePOSIXPatch->Enable(); if (!m_GetVehiclePricePatch) { - m_GetVehiclePricePatch = ScriptPatches::AddPatch("appinternet"_J, "56 ? ? 70 2E 04 01 38 01", 0, {0x55}); + m_GetVehiclePricePatch = ScriptPatches::AddPatch("appinternet"_J, ScriptPointer("GetVehiclePricePatch", "56 ? ? 70 2E 04 01 38 01"), {0x55}); } m_GetVehiclePricePatch->Enable(); if (!m_BuyVehiclePatch) { - m_BuyVehiclePatch = ScriptPatches::AddPatch("appinternet"_J, "5D ? ? ? 06 56 ? ? 38 00 25 ? 50", 5, {0x55}); + m_BuyVehiclePatch = ScriptPatches::AddPatch("appinternet"_J, ScriptPointer("BuyVehiclePatch", "5D ? ? ? 06 56 ? ? 38 00 25 ? 50").Add(5), {0x55}); } m_BuyVehiclePatch->Enable(); } diff --git a/src/game/features/recovery/GiveVehicleReward.cpp b/src/game/features/recovery/GiveVehicleReward.cpp index 1bac5a3..e4a7269 100644 --- a/src/game/features/recovery/GiveVehicleReward.cpp +++ b/src/game/features/recovery/GiveVehicleReward.cpp @@ -2,14 +2,13 @@ #include "core/backend/ScriptMgr.hpp" #include "game/backend/Self.hpp" #include "game/gta/Natives.hpp" +#include "game/gta/Scripts.hpp" #include "game/gta/ScriptFunction.hpp" #include "game/gta/ScriptLocal.hpp" #include "types/script/locals/VehicleRewardData.hpp" namespace YimMenu { - static ScriptFunction giveVehicleReward("GiveVehicleReward", "AM_MP_VEHICLE_REWARD"_J, "2D 0C 1E 00 00"); - bool GiveVehicleReward::IsSafeToRunScript() { return Self::GetVehicle().IsValid() && *Pointers.IsSessionStarted; @@ -67,6 +66,7 @@ namespace YimMenu if (auto VehicleRewardData = VEHICLE_REWARD_DATA::Get(m_Thread)) { auto VehicleMenuData = ScriptLocal(m_Thread, 176).As(); // TO-DO: add struct for this? + static ScriptFunction giveVehicleReward("AM_MP_VEHICLE_REWARD"_J, ScriptPointer("GiveVehicleReward", "2D 0C 1E 00 00")); if (giveVehicleReward.Call(Self::GetVehicle().GetHandle(), VehicleMenuData, &VehicleRewardData->TransactionStatus, &VehicleRewardData->Garage, &VehicleRewardData->GarageOffset, &VehicleRewardData->ControlStatus, false, true, true, false, 0, -1)) { if (VehicleRewardData->ControlStatus != 3) diff --git a/src/game/features/recovery/PlayAllMissionsSolo.cpp b/src/game/features/recovery/PlayAllMissionsSolo.cpp index f092f7e..c5b2e63 100644 --- a/src/game/features/recovery/PlayAllMissionsSolo.cpp +++ b/src/game/features/recovery/PlayAllMissionsSolo.cpp @@ -30,62 +30,62 @@ namespace YimMenu::Features { if (!m_CanLaunchJobPatch) { - m_CanLaunchJobPatch = ScriptPatches::AddPatch("fmmc_launcher"_J, "2D 05 0C 00 00 71", 5, {0x72, 0x2E, 0x05, 0x01}); // the main patch that bypasses the not enough players alert + m_CanLaunchJobPatch = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("CanLaunchJobPatch", "2D 05 0C 00 00 71").Add(5), {0x72, 0x2E, 0x05, 0x01}); // the main patch that bypasses the not enough players alert } m_CanLaunchJobPatch->Enable(); // these patches allow the mission request system to launch fm_mission_controller with insufficent players if (!m_MissionRequestPatch1) { - m_MissionRequestPatch1 = ScriptPatches::AddPatch("freemode"_J, "38 00 72 5D ? ? ? 38 00 5D ? ? ? 2E 02 00 38 00 38 01", 0, std::vector(14, 0x0)); + m_MissionRequestPatch1 = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("MissionRequestPatch1", "38 00 72 5D ? ? ? 38 00 5D ? ? ? 2E 02 00 38 00 38 01"), std::vector(14, 0x0)); } m_MissionRequestPatch1->Enable(); if (!m_MissionRequestPatch2) { - m_MissionRequestPatch2 = ScriptPatches::AddPatch("freemode"_J, "25 ? 37 07 33 37 07 5D ? ? ? 39 11", 20, {0x72}); + m_MissionRequestPatch2 = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("MissionRequestPatch2", "25 ? 37 07 33 37 07 5D ? ? ? 39 11").Add(20), {0x72}); } m_MissionRequestPatch2->Enable(); if (!m_MissionRequestPatch3) { - m_MissionRequestPatch3 = ScriptPatches::AddPatch("freemode"_J, "2D 03 1A 00 00 38 00 5D ? ? ? 39 05", 5, {0x72, 0x2E, 0x03, 0x01}); + m_MissionRequestPatch3 = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("MissionRequestPatch3", "2D 03 1A 00 00 38 00 5D ? ? ? 39 05").Add(5), {0x72, 0x2E, 0x03, 0x01}); } m_MissionRequestPatch3->Enable(); if (!m_MissionRequestPatch4) { - m_MissionRequestPatch4 = ScriptPatches::AddPatch("freemode"_J, "5D ? ? ? 39 06 38 01 5D ? ? ? 25", 0, {0x72, 0x2E, 0x04, 0x01}); + m_MissionRequestPatch4 = ScriptPatches::AddPatch("freemode"_J, ScriptPointer("MissionRequestPatch4", "5D ? ? ? 39 06 38 01 5D ? ? ? 25"), {0x72, 0x2E, 0x04, 0x01}); } m_MissionRequestPatch4->Enable(); if (!m_PlayerCountWatchdogPatch1) { - m_PlayerCountWatchdogPatch1 = ScriptPatches::AddPatch("fmmc_launcher"_J, "2D 02 09 00 00 25 A4", 5, {0x71, 0x2E, 0x02, 0x01}); // instant kick if this check fails + m_PlayerCountWatchdogPatch1 = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("PlayerCountWatchdogPatch1", "2D 02 09 00 00 25 A4").Add(5), {0x71, 0x2E, 0x02, 0x01}); // instant kick if this check fails } m_PlayerCountWatchdogPatch1->Enable(); if (!m_PlayerCountWatchdogPatch2) { - m_PlayerCountWatchdogPatch2 = ScriptPatches::AddPatch("fmmc_launcher"_J, "2D 04 0B 00 00 2C ? ? ? 71 5D", 5, {0x71, 0x2E, 0x04, 0x01}); + m_PlayerCountWatchdogPatch2 = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("PlayerCountWatchdogPatch2", "2D 04 0B 00 00 2C ? ? ? 71 5D").Add(5), {0x71, 0x2E, 0x04, 0x01}); } m_PlayerCountWatchdogPatch2->Enable(); if (!m_HeistTeamsPatch1) { - m_HeistTeamsPatch1 = ScriptPatches::AddPatch("fmmc_launcher"_J, "47 ? ? 5B 7B 00 38 04", 0, {0x2B, 0x2B, 0x00, 0x55}); // TODO: doesn't work for doomsday heist + m_HeistTeamsPatch1 = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("HeistTeamsPatch1", "47 ? ? 5B 7B 00 38 04"), {0x2B, 0x2B, 0x00, 0x55}); // TODO: doesn't work for doomsday heist } m_HeistTeamsPatch1->Enable(); if (!m_HeistTeamsPatch2) { - m_HeistTeamsPatch2 = ScriptPatches::AddPatch("fmmc_launcher"_J, "2D 01 05 00 00 25 5D", 5, {0x72, 0x2E, 0x01, 0x01}); + m_HeistTeamsPatch2 = ScriptPatches::AddPatch("fmmc_launcher"_J, ScriptPointer("HeistTeamsPatch2", "2D 01 05 00 00 25 5D").Add(5), {0x72, 0x2E, 0x01, 0x01}); } m_HeistTeamsPatch2->Enable(); if (!m_ShouldFailMissionPatch1) { - m_ShouldFailMissionPatch1 = ScriptPatches::AddPatch("fm_mission_controller"_J, "50 ? ? 78 82 06 1F 56 ? ? 38 02", 0, {0x00, 0x00, 0x00, 0x72, 0x00}); + m_ShouldFailMissionPatch1 = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("ShouldFailMissionPatch1", "50 ? ? 78 82 06 1F 56 ? ? 38 02"), {0x00, 0x00, 0x00, 0x72, 0x00}); } m_ShouldFailMissionPatch1->Enable(); @@ -94,7 +94,10 @@ namespace YimMenu::Features // TODO: this is a very bad idea that can break anytime // for some reason, Rockstar thought it's a good idea to copy-paste the same failure check in four different branches, "just in case" for (int i = 0; i < 4; i++) - m_ShouldFailMissionPatches.push_back(ScriptPatches::AddPatch("fm_mission_controller"_J, "38 02 5D ? ? ? 71 09 2A 56 07 00 71 5D ? ? ? 06 1F 56", 0, {0x71, 0x00, 0x00, 0x00, 0x00, 0x00})); + { + std::string ptrName = "ShouldFailMissionPatches" + std::to_string(i); + m_ShouldFailMissionPatches.push_back(ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer(ptrName, "38 02 5D ? ? ? 71 09 2A 56 07 00 71 5D ? ? ? 06 1F 56"), {0x71, 0x00, 0x00, 0x00, 0x00, 0x00})); + } } for (auto& patch : m_ShouldFailMissionPatches) @@ -102,37 +105,37 @@ namespace YimMenu::Features if (!m_ShouldFailMissionPatch2) { - m_ShouldFailMissionPatch2 = ScriptPatches::AddPatch("fm_mission_controller"_J, "56 07 00 38 02 5D ? ? ? 20 56 BC 01", 0, {0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x71}); // some heist check, more or less same as above + m_ShouldFailMissionPatch2 = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("ShouldFailMissionPatch2", "56 07 00 38 02 5D ? ? ? 20 56 BC 01"), {0x2B, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x2B, 0x71}); // some heist check, more or less same as above } m_ShouldFailMissionPatch2->Enable(); if (!m_ShouldFailMissionPatch3) { - m_ShouldFailMissionPatch3 = ScriptPatches::AddPatch("fm_mission_controller"_J, "50 ? ? 78 82 06 1F 56 2E 01", 0, {0x00, 0x00, 0x00, 0x00, 0x72}); + m_ShouldFailMissionPatch3 = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("ShouldFailMissionPatch3", "50 ? ? 78 82 06 1F 56 2E 01"), {0x00, 0x00, 0x00, 0x00, 0x72}); } m_ShouldFailMissionPatch3->Enable(); if (!m_NotEnoughTeamsPatch) { - m_NotEnoughTeamsPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, "2D 00 02 00 00 61 ? ? ? 47 ? ? 72 57 04 00 72 2E 00 01 4F", 5, {0x71, 0x2E, 0x00, 0x01}); + m_NotEnoughTeamsPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("NotEnoughTeamsPatch", "2D 00 02 00 00 61 ? ? ? 47 ? ? 72 57 04 00 72 2E 00 01 4F").Add(5), {0x71, 0x2E, 0x00, 0x01}); } m_NotEnoughTeamsPatch->Enable(); if (!m_IsTeamValidPatch) { - m_IsTeamValidPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, "2D 01 03 00 00 38 00 71 57 1D 00", 5, {0x72, 0x2E, 0x01, 0x01}); + m_IsTeamValidPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("IsTeamValidPatch", "2D 01 03 00 00 38 00 71 57 1D 00").Add(5), {0x72, 0x2E, 0x01, 0x01}); } m_IsTeamValidPatch->Enable(); if (!m_EnsureMissionPassedPatch) { - m_EnsureMissionPassedPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, "55 C8 FF 61", 3, {0x2E, 0x00, 0x00}); // the game does one final check to ensure all players exist before the mission is passed + m_EnsureMissionPassedPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("EnsureMissionPassedPatch", "55 C8 FF 61").Add(3), {0x2E, 0x00, 0x00}); // the game does one final check to ensure all players exist before the mission is passed } m_EnsureMissionPassedPatch->Enable(); if (!m_ProcessPhoneHackingPatch) { - m_ProcessPhoneHackingPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, "58 13 00 38 00 4F ? ? 48", 0, {0x2B, 0x00, 0x00}); + m_ProcessPhoneHackingPatch = ScriptPatches::AddPatch("fm_mission_controller"_J, ScriptPointer("ProcessPhoneHackingPatch", "58 13 00 38 00 4F ? ? 48"), {0x2B, 0x00, 0x00}); } } diff --git a/src/game/features/system/DespawnBypass.cpp b/src/game/features/system/DespawnBypass.cpp index 9edc831..9035ac3 100644 --- a/src/game/features/system/DespawnBypass.cpp +++ b/src/game/features/system/DespawnBypass.cpp @@ -13,7 +13,7 @@ namespace YimMenu::Features { if (!m_ShopPatch) { - m_ShopPatch = ScriptPatches::AddPatch("shop_controller"_J, "2D 01 04 00 00 2C ? ? ? 56 ? ? 71", 5, {0x71, 0x2E, 0x01, 0x01}); // disable the kick out subroutine + m_ShopPatch = ScriptPatches::AddPatch("shop_controller"_J, ScriptPointer("ShopPatch", "2D 01 04 00 00 2C ? ? ? 56 ? ? 71").Add(5), {0x71, 0x2E, 0x01, 0x01}); // disable the kick out subroutine } m_ShopPatch->Enable(); } diff --git a/src/game/features/system/LSCCustomsBypass.cpp b/src/game/features/system/LSCCustomsBypass.cpp index b3615ad..ef9103e 100644 --- a/src/game/features/system/LSCCustomsBypass.cpp +++ b/src/game/features/system/LSCCustomsBypass.cpp @@ -15,13 +15,13 @@ namespace YimMenu::Features { if (!m_CanUseVehiclePatch) { - m_CanUseVehiclePatch = ScriptPatches::AddPatch("carmod_shop"_J, "2D ? ? ? ? 38 ? 5D ? ? ? 56 ? ? 71 2E ? ? 5D", 5, {0x72, 0x2E, 0x03, 0x01}); + m_CanUseVehiclePatch = ScriptPatches::AddPatch("carmod_shop"_J, ScriptPointer("CanUseVehiclePatch", "2D ? ? ? ? 38 ? 5D ? ? ? 56 ? ? 71 2E ? ? 5D").Add(5), {0x72, 0x2E, 0x03, 0x01}); } m_CanUseVehiclePatch->Enable(); if (!m_BlockMenuOptionPatch) { - m_BlockMenuOptionPatch = ScriptPatches::AddPatch("carmod_shop"_J, "2D ? ? ? ? 38 ? 5D ? ? ? 5D ? ? ? 56", 5, {0x71, 0x2E, 0x01, 0x01}); + m_BlockMenuOptionPatch = ScriptPatches::AddPatch("carmod_shop"_J, ScriptPointer("BlockMenuOptionPatch", "2D ? ? ? ? 38 ? 5D ? ? ? 5D ? ? ? 56").Add(5), {0x71, 0x2E, 0x01, 0x01}); } m_BlockMenuOptionPatch->Enable(); } diff --git a/src/game/features/world/Weather.cpp b/src/game/features/world/Weather.cpp index 54e61ef..15a7ef7 100644 --- a/src/game/features/world/Weather.cpp +++ b/src/game/features/world/Weather.cpp @@ -105,7 +105,7 @@ namespace YimMenu::Features // ensure shop controller can't control the weather if (!m_ShopControllerWeatherPatch) { - m_ShopControllerWeatherPatch = ScriptPatches::AddPatch("shop_controller"_J, "2D 00 07 00 00 71 39 02 71", 5, {0x2E, 0x00, 0x00}); + m_ShopControllerWeatherPatch = ScriptPatches::AddPatch("shop_controller"_J, ScriptPointer("ShopControllerWeatherPatch", "2D 00 07 00 00 71 39 02 71").Add(5), {0x2E, 0x00, 0x00}); } m_ShopControllerWeatherPatch->Enable(); } diff --git a/src/game/frontend/submenus/Debug/Misc.cpp b/src/game/frontend/submenus/Debug/Misc.cpp index 9a1c118..dd098ca 100644 --- a/src/game/frontend/submenus/Debug/Misc.cpp +++ b/src/game/frontend/submenus/Debug/Misc.cpp @@ -18,7 +18,7 @@ namespace YimMenu::Submenus if (ImGui::Button("fm_mission_controller DoTeamSwap")) { FiberPool::Push([] { - static ScriptFunction DoTeamSwap("DoTeamSwap", "fm_mission_controller"_J, "2D 02 04 00 00 38 00 50"); + static ScriptFunction DoTeamSwap("fm_mission_controller"_J, ScriptPointer("DoTeamSwap", "2D 02 04 00 00 38 00 50")); DoTeamSwap.Call(team, true); }); } diff --git a/src/game/frontend/submenus/network/RandomEvents.cpp b/src/game/frontend/submenus/network/RandomEvents.cpp index 0cf3d83..e6ad5d7 100644 --- a/src/game/frontend/submenus/network/RandomEvents.cpp +++ b/src/game/frontend/submenus/network/RandomEvents.cpp @@ -8,7 +8,6 @@ #include "game/gta/data/RandomEvents.hpp" #include "game/gta/Scripts.hpp" #include "game/gta/ScriptFunction.hpp" -#include "game/pointers/Pointers.hpp" #include "types/script/globals/GPBD_FM_2.hpp" #include "types/script/globals/GSBD_RandomEvents.hpp" #include "types/script/locals/FMRandomEvents.hpp" @@ -47,7 +46,6 @@ namespace YimMenu::Submenus }; static std::vector sendUpdateRECoordsTSECooldownPatches{}; - static ScriptFunction getNumFMMCVariations("GetNumFMMCVariations", "freemode"_J, "5D ? ? ? 01 72 02 39 04", 1, true); static GPBD_FM_2* GPBDFM2 = nullptr; static GSBD_RandomEvents* GSBDRandomEvents = nullptr; static RANDOM_EVENTS_FREEMODE_DATA* FMRandomEvents = nullptr; @@ -105,17 +103,51 @@ namespace YimMenu::Submenus static void OnComboChange() { - selectedSubvariation = 0; + static ScriptFunction getNumFMMCVariations("freemode"_J, ScriptPointer("GetNumFMMCVariations", "5D ? ? ? 01 72 02 39 04").Add(1).Rip()); numSubvariations = getNumFMMCVariations.Call(FMRandomEvents->MissionData.FMMCData[selectedEvent].FMMCType, 0) - 1; + selectedSubvariation = 0; ResetEventTunables(selectedEvent); } + static void KillActiveEvent() + { + if (auto eventThread = Scripts::FindScriptThread(randomEventScripts[(int)selectedEvent])) + { + if (auto NetComponent = reinterpret_cast(eventThread)->m_NetComponent) + { + if (auto eventProgram = Scripts::FindScriptProgram(randomEventScripts[(int)selectedEvent])) + { + // TO-DO: Cache these too? + if (NetComponent->IsLocalPlayerHost()) + { + std::string ptrName = "SetFMContentScriptServerState" + std::to_string(selectedEvent); + ScriptFunction setFMContentScriptServerState(randomEventScripts[(int)selectedEvent], ScriptPointer(ptrName, "5D ? ? ? 55 2E 00 5D").Add(1).Rip()); + setFMContentScriptServerState.Call(3); + } + else + { + std::string ptrName = "SetFMContentScriptClientState" + std::to_string(selectedEvent); + ScriptFunction setFMContentScriptClientState(randomEventScripts[(int)selectedEvent], ScriptPointer(ptrName, "5D ? ? ? 55 08 00 74").Add(1).Rip()); + setFMContentScriptClientState.Call(3); + } + } + } + } + else + { + Notifications::Show("Random Events", "Event script is not active. Are you a participant?", NotificationType::Error); + } + } + std::shared_ptr BuildRandomEventsMenu() { if (sendUpdateRECoordsTSECooldownPatches.empty()) { for (int event = DRUG_VEHICLE; event < MAX_EVENTS; event++) - sendUpdateRECoordsTSECooldownPatches.push_back(ScriptPatches::AddPatch(randomEventScripts[event], "43 88 13 2E 00 01", 0, {0x71, 0x00, 0x00})); + { + std::string ptrName = "SendUpdateRECoordsTSECooldownPatch" + std::to_string(event); + sendUpdateRECoordsTSECooldownPatches.push_back(ScriptPatches::AddPatch(randomEventScripts[event], ScriptPointer(ptrName, "43 88 13 2E 00 01"), {0x71, 0x00, 0x00})); + } } for (auto& patch : sendUpdateRECoordsTSECooldownPatches) @@ -224,26 +256,7 @@ namespace YimMenu::Submenus } else if (GSBDRandomEvents->EventData[selectedEvent].State == eRandomEventState::ACTIVE) { - if (auto eventThread = Scripts::FindScriptThread(randomEventScripts[(int)selectedEvent])) - { - if (auto NetComponent = reinterpret_cast(eventThread)->m_NetComponent) - { - if (NetComponent->IsLocalPlayerHost()) - { - ScriptFunction setFMContentScriptServerState("SetFMContentScriptServerState", randomEventScripts[(int)selectedEvent], "5D ? ? ? 55 2E 00 5D", 1, true); - setFMContentScriptServerState.Call(3); - } - else - { - ScriptFunction setFMContentScriptClientState("SetFMContentScriptClientState", randomEventScripts[(int)selectedEvent], "5D ? ? ? 55 08 00 74", 1, true); - setFMContentScriptClientState.Call(3); - } - } - } - else - { - Notifications::Show("Random Events", "Event script is not active. Are you a participant?", NotificationType::Error); - } + KillActiveEvent(); } else { diff --git a/src/game/gta/Network.cpp b/src/game/gta/Network.cpp index e7116f0..a42cf34 100644 --- a/src/game/gta/Network.cpp +++ b/src/game/gta/Network.cpp @@ -18,10 +18,10 @@ namespace YimMenu::Network { void LaunchJoinType(JoinType to_launch) { - static ScriptFunction send_to_clouds("SendToClouds", "shop_controller"_J, "2D 00 02 00 00 72 5D ? ? ? 72"); + static ScriptFunction sendToClouds("shop_controller"_J, ScriptPointer("SendToClouds", "2D 00 02 00 00 72 5D ? ? ? 72")); static ScriptGlobal join_type_global(1575038); - send_to_clouds.Call(); + sendToClouds.Call(); *join_type_global.As() = to_launch; } diff --git a/src/game/gta/ScriptData.cpp b/src/game/gta/ScriptData.cpp new file mode 100644 index 0000000..def94ec --- /dev/null +++ b/src/game/gta/ScriptData.cpp @@ -0,0 +1,61 @@ +#include "ScriptData.hpp" +#include "types/script/scrProgram.hpp" + +namespace YimMenu +{ + ScriptData::ScriptData(rage::scrProgram* program) + { + auto pages = new uint8_t*[program->GetNumCodePages()]; + + for (auto i = 0u; i < program->GetNumCodePages(); i++) + { + pages[i] = new uint8_t[program->GetCodePageSize(i)]; + std::memcpy(pages[i], program->GetCodePage(i), program->GetCodePageSize(i)); + } + + m_Data = pages; + m_Pages = program->GetNumCodePages(); + m_Size = program->GetFullCodeSize(); + m_Name = program->m_Name; + } + + ScriptData::~ScriptData() + { + for (auto i = 0u; i < m_Pages; i++) + { + delete[] m_Data[i]; + } + + delete[] m_Data; + m_Data = nullptr; + m_Name = nullptr; + } + + std::uint8_t* ScriptData::GetCodeLocation(ScriptData* data, int idx) + { + return &data->GetData()[idx >> 14][idx & 0x3FFF]; + } + + std::optional ScriptData::GetCodeLocationByPattern(ScriptData* data, const SimplePattern& pattern) + { + std::uint32_t codeSize = data->GetSize(); + for (uint32_t i = 0; i < (codeSize - pattern.m_Bytes.size()); i++) + { + for (uint32_t j = 0; j < pattern.m_Bytes.size(); j++) + { + if (pattern.m_Bytes[j].has_value()) + { + int loc = i + j; + if (pattern.m_Bytes[j] != *GetCodeLocation(data, loc)) + goto incorrect; + } + } + + return i; + incorrect: + continue; + } + + return std::nullopt; + } +} \ No newline at end of file diff --git a/src/game/gta/ScriptData.hpp b/src/game/gta/ScriptData.hpp new file mode 100644 index 0000000..f6e744a --- /dev/null +++ b/src/game/gta/ScriptData.hpp @@ -0,0 +1,40 @@ +#pragma once +#include "core/memory/Pattern.hpp" + +namespace rage +{ + class scrProgram; +} + +namespace YimMenu +{ + class ScriptData + { + std::uint8_t** m_Data; + std::uint32_t m_Pages; + std::uint32_t m_Size; + const char* m_Name; + + public: + inline std::uint8_t** GetData() const + { + return m_Data; + } + + inline std::uint32_t GetSize() const + { + return m_Size; + } + + inline const char* GetName() const + { + return m_Name; + } + + ScriptData(rage::scrProgram* program); + ~ScriptData(); + + static uint8_t* GetCodeLocation(ScriptData* data, int idx); + static std::optional GetCodeLocationByPattern(ScriptData* data, const SimplePattern& pattern); + }; +} \ No newline at end of file diff --git a/src/game/gta/ScriptFunction.cpp b/src/game/gta/ScriptFunction.cpp index 9875e4a..66050ee 100644 --- a/src/game/gta/ScriptFunction.cpp +++ b/src/game/gta/ScriptFunction.cpp @@ -1,34 +1,66 @@ #include "ScriptFunction.hpp" +#include "game/backend/ScriptPointers.hpp" +#include "game/pointers/Pointers.hpp" +#include "game/gta/Scripts.hpp" +#include "game/gta/ScriptPointer.hpp" +#include "types/script/scrThread.hpp" +#include "types/script/scrProgram.hpp" +#include "types/rage/tlsContext.hpp" namespace YimMenu { - ScriptFunction::ScriptFunction(const std::string& name, const joaat_t script, const SimplePattern& pattern, std::int32_t offset, bool rip) : - m_Name(name), - m_Script(script), - m_Pattern(pattern), - m_Offset(offset), - m_Rip(rip), - m_PC(0) + ScriptFunction::ScriptFunction(const joaat_t script, ScriptPointer ptr) : + m_Script(script), + m_Pointer(ptr), + m_Pc(0) { } - std::uint32_t ScriptFunction::GetPC(rage::scrProgram* program) + void ScriptFunction::CallImpl(const std::vector& args, void* returnValue, std::uint32_t returnSize) { - if (m_PC != 0) - return m_PC; + auto thread = Scripts::FindScriptThread(m_Script); + auto program = Scripts::FindScriptProgram(m_Script); - if (auto location = Scripts::GetCodeLocationByPattern(program, m_Pattern)) + if (!thread || !program) + return; + + if (!m_Pc) { - m_PC = location.value() + m_Offset; - if (m_Rip) - m_PC = ReadThreeByte(program->GetCodeAddress(m_PC)); - LOG(INFO) << "Found pattern for " << m_Name << " at " << HEX(m_PC) << " in script " << program->m_Name; - } - else - { - LOG(FATAL) << "Failed to find pattern for " << m_Name << " in script " << program->m_Name; + if (auto address = ScriptPointers::GetPointer(Joaat(m_Pointer.GetName()))) + { + m_Pc = address; + } + else + { + m_Pc = m_Pointer.Scan(program).As(); + ScriptPointers::CachePointer(Joaat(m_Pointer.GetName()), m_Pc); + } + + if (!m_Pc) // if still not valid + return; } - return m_PC; + auto tlsCtx = rage::tlsContext::Get(); + auto stack = (std::uint64_t*)thread->m_Stack; + auto ogThread = tlsCtx->m_CurrentScriptThread; + tlsCtx->m_CurrentScriptThread = thread; + tlsCtx->m_ScriptThreadActive = true; + rage::scrThread::Context ctx = thread->m_Context; + auto topStack = ctx.m_StackPointer; + + for (auto& arg : args) + stack[ctx.m_StackPointer++] = arg; + + stack[ctx.m_StackPointer++] = 0; + ctx.m_ProgramCounter = m_Pc; + ctx.m_State = rage::scrThread::State::IDLE; + + Pointers.ScriptVM(stack, Pointers.ScriptGlobals, program, &ctx); + + tlsCtx->m_CurrentScriptThread = ogThread; + tlsCtx->m_ScriptThreadActive = ogThread != nullptr; + + if (returnValue) + std::memcpy(returnValue, stack + topStack, returnSize); } } \ No newline at end of file diff --git a/src/game/gta/ScriptFunction.hpp b/src/game/gta/ScriptFunction.hpp index 96c3b37..fec445d 100644 --- a/src/game/gta/ScriptFunction.hpp +++ b/src/game/gta/ScriptFunction.hpp @@ -1,74 +1,41 @@ #pragma once #include "core/util/Joaat.hpp" -#include "core/memory/Pattern.hpp" -#include "game/pointers/Pointers.hpp" -#include "game/gta/Scripts.hpp" -#include "types/script/scrThread.hpp" -#include "types/script/scrProgram.hpp" -#include "types/rage/tlsContext.hpp" +#include "game/gta/ScriptPointer.hpp" namespace YimMenu { class ScriptFunction { joaat_t m_Script; - const SimplePattern m_Pattern; - std::int32_t m_Offset; - bool m_Rip; - std::uint32_t m_PC; - std::string m_Name; - - std::uint32_t GetPC(rage::scrProgram* program); + ScriptPointer m_Pointer; + std::uint32_t m_Pc; template - void PushArg(std::uint64_t* stack, std::uint32_t& stackPtr, Arg&& value) + static void PushArg(std::uint64_t* stack, std::uint32_t& stackPtr, Arg&& value) { *reinterpret_cast>*>(reinterpret_cast(stack) + (stackPtr++)) = std::forward(value); } - std::uint32_t ReadThreeByte(std::uint8_t* arr) - { - return arr[0] + (arr[1] << 8) + (arr[2] << 16); - } + void CallImpl(const std::vector& args, void* returnValue = 0, std::uint32_t returnSize = 0); public: - ScriptFunction(const std::string& name, const joaat_t script, const SimplePattern& pattern, std::int32_t offset = 0, bool rip = false); + ScriptFunction(const joaat_t script, ScriptPointer ptr); template Ret Call(Args... args) { - auto thread = Scripts::FindScriptThread(m_Script); - auto program = Scripts::FindScriptProgram(m_Script); - - if (!thread || !program) - return Ret(); - - auto pc = GetPC(program); - if (!pc) - return Ret(); - - auto tlsCtx = rage::tlsContext::Get(); - auto stack = (std::uint64_t*)thread->m_Stack; - auto ogThread = tlsCtx->m_CurrentScriptThread; - tlsCtx->m_CurrentScriptThread = thread; - tlsCtx->m_ScriptThreadActive = true; - rage::scrThread::Context ctx = thread->m_Context; - auto topStack = ctx.m_StackPointer; - - (PushArg(stack, ctx.m_StackPointer, std::forward(args)), ...); - - stack[ctx.m_StackPointer++] = 0; - ctx.m_ProgramCounter = pc; - ctx.m_State = rage::scrThread::State::IDLE; - - Pointers.ScriptVM(stack, Pointers.ScriptGlobals, program, &ctx); - - tlsCtx->m_CurrentScriptThread = ogThread; - tlsCtx->m_ScriptThreadActive = ogThread != nullptr; - + std::uint32_t index{}; + std::vector params(sizeof...(Args)); + (PushArg(params.data(), index, std::forward(args)), ...); if constexpr (!std::is_same_v) { - return *reinterpret_cast(stack + topStack); + Ret returnValue; + CallImpl(params, &returnValue, sizeof(returnValue)); + return returnValue; + } + else + { + CallImpl(params); } } }; diff --git a/src/game/gta/ScriptPointer.cpp b/src/game/gta/ScriptPointer.cpp new file mode 100644 index 0000000..6de4320 --- /dev/null +++ b/src/game/gta/ScriptPointer.cpp @@ -0,0 +1,67 @@ +#include "ScriptPointer.hpp" +#include "game/gta/Scripts.hpp" +#include "types/script/scrProgram.hpp" + +namespace YimMenu +{ + static std::uint32_t ReadThreeByte(std::uint8_t* arr) + { + return arr[0] + (arr[1] << 8) + (arr[2] << 16); + } + + ScriptPointer::ScriptPointer(const std::string& name, const SimplePattern& pattern, std::int32_t offset, bool rip, std::uint32_t address) : + m_Name(name), + m_Pattern(pattern), + m_Offset(offset), + m_Rip(rip), + m_Address(address) + { + } + + ScriptPointer ScriptPointer::Add(std::uint32_t offset) + { + return ScriptPointer(m_Name, m_Pattern, m_Offset + offset, m_Rip, m_Address); + } + + ScriptPointer ScriptPointer::Sub(std::uint32_t offset) + { + return ScriptPointer(m_Name, m_Pattern, m_Offset - offset, m_Rip, m_Address); + } + + ScriptPointer ScriptPointer::Rip() + { + return ScriptPointer(m_Name, m_Pattern, m_Offset, true, m_Address); + } + + ScriptPointer ScriptPointer::Scan(rage::scrProgram* program) + { + if (auto location = Scripts::GetCodeLocationByPattern(program, m_Pattern)) + { + m_Address = location.value() + m_Offset; + if (m_Rip) + m_Address = ReadThreeByte(program->GetCodeAddress(m_Address)); + + LOG(INFO) << "Found pattern for " << m_Name << " at " << HEX(m_Address) << " in script " << program->m_Name; + return ScriptPointer(m_Name, m_Pattern, m_Offset, m_Rip, m_Address); + } + + LOG(FATAL) << "Failed to find pattern for " << m_Name; + return ScriptPointer(m_Name, m_Pattern, m_Offset, m_Rip, 0); + } + + ScriptPointer ScriptPointer::Scan(ScriptData* data) + { + if (auto location = ScriptData::GetCodeLocationByPattern(data, m_Pattern)) + { + m_Address = location.value() + m_Offset; + if (m_Rip) + m_Address = ReadThreeByte(ScriptData::GetCodeLocation(data, m_Address)); + + LOG(INFO) << "Found pattern for " << m_Name << " at " << HEX(m_Address) << " in script " << data->GetName(); + return ScriptPointer(m_Name, m_Pattern, m_Offset, m_Rip, m_Address); + } + + LOG(FATAL) << "Failed to find pattern for " << m_Name; + return ScriptPointer(m_Name, m_Pattern, m_Offset, m_Rip, 0); + } +} \ No newline at end of file diff --git a/src/game/gta/ScriptPointer.hpp b/src/game/gta/ScriptPointer.hpp new file mode 100644 index 0000000..b3edf6c --- /dev/null +++ b/src/game/gta/ScriptPointer.hpp @@ -0,0 +1,34 @@ +#pragma once +#include "game/gta/ScriptData.hpp" + +namespace YimMenu +{ + class ScriptPointer + { + const std::string m_Name; + SimplePattern m_Pattern; + std::int32_t m_Offset; + bool m_Rip; + std::uint32_t m_Address; + + public: + ScriptPointer(const std::string& name, const SimplePattern& pattern, std::int32_t offset = 0, bool rip = false, std::uint32_t address = 0); + + ScriptPointer Add(std::uint32_t offset); + ScriptPointer Sub(std::uint32_t offset); + ScriptPointer Rip(); + ScriptPointer Scan(rage::scrProgram* program); + ScriptPointer Scan(ScriptData* data); // For script patches + + template + T As() + { + return (T)m_Address; + } + + std::string GetName() const + { + return m_Name; + } + }; +} \ No newline at end of file diff --git a/src/main.cpp b/src/main.cpp index 958eaa9..4bb402e 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -17,6 +17,7 @@ #include "game/backend/Self.hpp" #include "game/backend/NativeHooks.hpp" #include "game/backend/Tunables.hpp" +#include "game/backend/ScriptPointers.hpp" #include "game/frontend/GUI.hpp" #include "game/pointers/Pointers.hpp" #include "game/features/recovery/GiveVehicleReward.hpp" @@ -50,6 +51,8 @@ namespace YimMenu ScriptMgr::Init(); LOG(INFO) << "ScriptMgr initialized"; + ScriptPointers::Init(); + GUI::Init(); ScriptMgr::AddScript(std::make_unique