mirror of
https://github.com/YimMenu/YimMenuV2.git
synced 2026-10-10 03:43:15 +08:00
Cache Script Pointers (#211)
* Cache Script Pointers * Rewrite script pointers
This commit is contained in:
@@ -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<int32_t> 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<int32_t> ScriptPatches::Patch::GetPC()
|
||||
std::optional<uint32_t> 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<std::uint32_t>();
|
||||
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<uint8_t> patch) :
|
||||
m_Pattern(pattern),
|
||||
m_Offset(offset),
|
||||
ScriptPatches::Patch::Patch(joaat_t script, ScriptPointer pointer, std::vector<uint8_t> 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::Patch> ScriptPatches::AddPatchImpl(joaat_t script, const std::string& pattern, int32_t offset, std::vector<uint8_t> patch)
|
||||
std::shared_ptr<ScriptPatches::Patch> ScriptPatches::AddPatchImpl(joaat_t script, ScriptPointer pointer, std::vector<uint8_t> patch)
|
||||
{
|
||||
auto scr_patch = std::make_shared<Patch>(script, SimplePattern(pattern), offset, patch);
|
||||
auto scr_patch = std::make_shared<Patch>(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<Data>(program)));
|
||||
m_Datas.emplace(program->m_NameHash, std::move(std::make_unique<ScriptData>(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();
|
||||
|
||||
@@ -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<uint8_t> m_OriginalBytes;
|
||||
std::vector<uint8_t> m_PatchedBytes;
|
||||
std::optional<int32_t> m_Pc;
|
||||
SimplePattern m_Pattern;
|
||||
int32_t m_Offset;
|
||||
std::optional<uint32_t> m_Pc;
|
||||
ScriptPointer m_Pointer;
|
||||
bool m_Enabled;
|
||||
std::uint32_t m_Hash;
|
||||
|
||||
std::optional<int32_t> GetPC();
|
||||
std::optional<uint32_t> GetPC();
|
||||
void Apply();
|
||||
void Restore();
|
||||
|
||||
public:
|
||||
Patch(joaat_t script, SimplePattern pattern, int32_t offset, std::vector<uint8_t> patch);
|
||||
Patch(joaat_t script, ScriptPointer pointer, std::vector<uint8_t> patch);
|
||||
void Enable();
|
||||
void Disable();
|
||||
void Update();
|
||||
bool InScope(std::uint32_t hash);
|
||||
};
|
||||
|
||||
static std::shared_ptr<Patch> AddPatch(joaat_t script, const std::string& pattern, int32_t offset, std::vector<uint8_t> patch)
|
||||
static std::shared_ptr<Patch> AddPatch(joaat_t script, ScriptPointer pointer, std::vector<uint8_t> 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<Patch> AddPatchImpl(joaat_t script, const std::string& pattern, int32_t offset, std::vector<uint8_t> patch);
|
||||
std::shared_ptr<Patch> AddPatchImpl(joaat_t script, ScriptPointer pointer, std::vector<uint8_t> 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<std::shared_ptr<Patch>> m_Patches;
|
||||
std::unordered_map<joaat_t, std::unique_ptr<Data>> m_Datas;
|
||||
std::unordered_map<joaat_t, std::unique_ptr<ScriptData>> m_Datas;
|
||||
std::uint8_t** m_CurrentlyReplacedBytecode;
|
||||
};
|
||||
|
||||
|
||||
@@ -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<uint8_t[]>(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.";
|
||||
}
|
||||
}
|
||||
@@ -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<joaat_t, std::uint32_t> 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);
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<int*>(); // TO-DO: add struct for this?
|
||||
static ScriptFunction giveVehicleReward("AM_MP_VEHICLE_REWARD"_J, ScriptPointer("GiveVehicleReward", "2D 0C 1E 00 00"));
|
||||
if (giveVehicleReward.Call<bool>(Self::GetVehicle().GetHandle(), VehicleMenuData, &VehicleRewardData->TransactionStatus, &VehicleRewardData->Garage, &VehicleRewardData->GarageOffset, &VehicleRewardData->ControlStatus, false, true, true, false, 0, -1))
|
||||
{
|
||||
if (VehicleRewardData->ControlStatus != 3)
|
||||
|
||||
@@ -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<std::uint8_t>(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<std::uint8_t>(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});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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<void>(team, true);
|
||||
});
|
||||
}
|
||||
|
||||
@@ -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<ScriptPatch> 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<int>(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<GtaThread*>(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<void>(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<void>(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Notifications::Show("Random Events", "Event script is not active. Are you a participant?", NotificationType::Error);
|
||||
}
|
||||
}
|
||||
|
||||
std::shared_ptr<Category> 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<GtaThread*>(eventThread)->m_NetComponent)
|
||||
{
|
||||
if (NetComponent->IsLocalPlayerHost())
|
||||
{
|
||||
ScriptFunction setFMContentScriptServerState("SetFMContentScriptServerState", randomEventScripts[(int)selectedEvent], "5D ? ? ? 55 2E 00 5D", 1, true);
|
||||
setFMContentScriptServerState.Call<void>(3);
|
||||
}
|
||||
else
|
||||
{
|
||||
ScriptFunction setFMContentScriptClientState("SetFMContentScriptClientState", randomEventScripts[(int)selectedEvent], "5D ? ? ? 55 08 00 74", 1, true);
|
||||
setFMContentScriptClientState.Call<void>(3);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
Notifications::Show("Random Events", "Event script is not active. Are you a participant?", NotificationType::Error);
|
||||
}
|
||||
KillActiveEvent();
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -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<void>();
|
||||
sendToClouds.Call<void>();
|
||||
*join_type_global.As<JoinType*>() = to_launch;
|
||||
}
|
||||
|
||||
|
||||
@@ -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<std::uint32_t> 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;
|
||||
}
|
||||
}
|
||||
@@ -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<uint32_t> GetCodeLocationByPattern(ScriptData* data, const SimplePattern& pattern);
|
||||
};
|
||||
}
|
||||
@@ -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<uint64_t>& 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<std::uint32_t>();
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -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<typename Arg>
|
||||
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<std::remove_cv_t<std::remove_reference_t<Arg>>*>(reinterpret_cast<std::uint64_t*>(stack) + (stackPtr++)) = std::forward<Arg>(value);
|
||||
}
|
||||
|
||||
std::uint32_t ReadThreeByte(std::uint8_t* arr)
|
||||
{
|
||||
return arr[0] + (arr[1] << 8) + (arr[2] << 16);
|
||||
}
|
||||
void CallImpl(const std::vector<uint64_t>& 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<typename Ret, typename... Args>
|
||||
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>(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<std::uint64_t> params(sizeof...(Args));
|
||||
(PushArg(params.data(), index, std::forward<Args>(args)), ...);
|
||||
if constexpr (!std::is_same_v<Ret, void>)
|
||||
{
|
||||
return *reinterpret_cast<Ret*>(stack + topStack);
|
||||
Ret returnValue;
|
||||
CallImpl(params, &returnValue, sizeof(returnValue));
|
||||
return returnValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
CallImpl(params);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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<typename T>
|
||||
T As()
|
||||
{
|
||||
return (T)m_Address;
|
||||
}
|
||||
|
||||
std::string GetName() const
|
||||
{
|
||||
return m_Name;
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -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<Script>(&NativeHooks::RunScript)); // runs once
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
#include "GlobalPlayerBD.hpp"
|
||||
#include "GPBD_FM.hpp"
|
||||
#include "GPBD_FM_2.hpp"
|
||||
#include "GPBD_FM_3.hpp"
|
||||
@@ -10,17 +9,19 @@
|
||||
#include "GSBD_FM_Events.hpp"
|
||||
#include "GSBD_Kicking.hpp"
|
||||
#include "GSBD_PropertyInstances.hpp"
|
||||
#include "g_AMC_playerBD.hpp"
|
||||
#include "GSBD_RandomEvents.hpp"
|
||||
#include "GlobalPlayerBD.hpp"
|
||||
#include "g_AMC_playerBD.hpp"
|
||||
#include "game/gta/ScriptGlobal.hpp"
|
||||
|
||||
#define DEFINE_GLOBAL_ACCESSOR(str, global) str* str::Get()\
|
||||
{ \
|
||||
auto glb = YimMenu::ScriptGlobal(global); \
|
||||
if (!glb.CanAccess()) \
|
||||
return nullptr; \
|
||||
return glb.As<str*>(); \
|
||||
}
|
||||
#define DEFINE_GLOBAL_ACCESSOR(str, global) \
|
||||
str* str::Get() \
|
||||
{ \
|
||||
auto glb = YimMenu::ScriptGlobal(global); \
|
||||
if (!glb.CanAccess()) \
|
||||
return nullptr; \
|
||||
return glb.As<str*>(); \
|
||||
}
|
||||
|
||||
DEFINE_GLOBAL_ACCESSOR(GlobalPlayerBD, 2657994);
|
||||
DEFINE_GLOBAL_ACCESSOR(GPBD_FM, 1845270);
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#include "VehicleRewardData.hpp"
|
||||
#include "FMRandomEvents.hpp"
|
||||
#include "VehicleRewardData.hpp"
|
||||
#include "game/gta/ScriptLocal.hpp"
|
||||
|
||||
#define DEFINE_LOCAL_ACCESSOR(str, local) \
|
||||
|
||||
Reference in New Issue
Block a user