Merge branch 'enhanced' of https://github.com/YimMenu/YimMenuV2 into enhanced

This commit is contained in:
maybegreat48
2025-03-30 17:53:58 -04:00
16 changed files with 305 additions and 7 deletions
@@ -0,0 +1,54 @@
#include "GiveVehicleReward.hpp"
#include "core/backend/ScriptMgr.hpp"
#include "game/backend/Self.hpp"
#include "game/gta/Natives.hpp"
#include "game/gta/ScriptFunction.hpp"
#include "game/gta/ScriptLocal.hpp"
#include "types/script/locals/VehicleRewardData.hpp"
namespace YimMenu
{
static ScriptFunction giveVehicleReward("GVR", "AM_MP_VEHICLE_REWARD"_J, "2D 0C 1E 00 00");
bool GiveVehicleReward::IsSafeToRunScript()
{
return Self::GetVehicle().IsValid() && *Pointers.IsSessionStarted && SCRIPT::GET_NUMBER_OF_THREADS_RUNNING_THE_SCRIPT_WITH_THIS_HASH("AM_MP_VEHICLE_REWARD"_J) > 0;
}
void GiveVehicleReward::RunScriptImpl()
{
while (g_Running)
{
if (m_ShouldRunScript)
{
if (IsSafeToRunScript())
{
if (auto thread = Scripts::FindScriptThread("AM_MP_VEHICLE_REWARD"_J))
{
if (auto VehicleRewardData = VEHICLE_REWARD_DATA::Get(thread))
{
auto VehicleMenuData = ScriptLocal(thread, 176).As<PINT>(); // TO-DO: add struct for this?
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)
{
VehicleRewardData->TransactionStatus = 0;
VehicleRewardData->Garage = 0;
VehicleRewardData->GarageOffset = 0;
VehicleRewardData->ControlStatus = 0;
m_ShouldRunScript = false;
}
}
}
}
}
else
{
m_ShouldRunScript = false;
}
}
ScriptMgr::Yield();
}
}
}
@@ -0,0 +1,30 @@
#pragma once
namespace YimMenu
{
class GiveVehicleReward
{
static GiveVehicleReward& GetInstance()
{
static GiveVehicleReward instance;
return instance;
}
void RunScriptImpl();
bool m_ShouldRunScript = false;
public:
static bool IsSafeToRunScript();
static void RunScript()
{
GetInstance().RunScriptImpl();
}
static void SetShouldRunScript(bool run)
{
GetInstance().m_ShouldRunScript = run;
}
};
}
+2
View File
@@ -11,6 +11,7 @@
#include "submenus/Teleport.hpp"
#include "submenus/Network.hpp"
#include "submenus/Players.hpp"
#include "submenus/Recovery.hpp"
#include "submenus/Settings.hpp"
#include "submenus/Debug.hpp"
#include "submenus/World.hpp"
@@ -25,6 +26,7 @@ namespace YimMenu
UIManager::AddSubmenu(std::make_shared<Submenus::World>());
UIManager::AddSubmenu(std::make_shared<Submenus::Network>());
UIManager::AddSubmenu(std::make_shared<Submenus::Players>());
UIManager::AddSubmenu(std::make_shared<Submenus::Recovery>());
UIManager::AddSubmenu(std::make_shared<Submenus::Settings>());
UIManager::AddSubmenu(std::make_shared<Submenus::Debug>());
+26
View File
@@ -0,0 +1,26 @@
#include "Recovery.hpp"
#include "game/frontend/items/Items.hpp"
#include "game/features/recovery/GiveVehicleReward.hpp"
namespace YimMenu::Submenus
{
Recovery::Recovery() :
Submenu::Submenu("Recovery")
{
auto shopping = std::make_shared<Category>("Shopping");
auto vehiclesGroup = std::make_shared<Group>("Vehicles");
vehiclesGroup->AddItem(std::make_unique<ImGuiItem>([] {
if (ImGui::Button("Save This Vehicle as Personal Vehicle"))
{
if (GiveVehicleReward::IsSafeToRunScript())
{
GiveVehicleReward::SetShouldRunScript(true);
}
}
}));
shopping->AddItem(vehiclesGroup);
AddCategory(std::move(shopping));
}
}
+11
View File
@@ -0,0 +1,11 @@
#pragma once
#include "core/frontend/manager/UIManager.hpp"
namespace YimMenu::Submenus
{
class Recovery : public Submenu
{
public:
Recovery();
};
}
+30
View File
@@ -0,0 +1,30 @@
#include "ScriptFunction.hpp"
namespace YimMenu
{
ScriptFunction::ScriptFunction(const std::string& name, const joaat_t script, const SimplePattern& pattern) :
m_Name(name),
m_Script(script),
m_Pattern(pattern),
m_PC(0)
{
}
std::uint32_t ScriptFunction::GetPC(rage::scrProgram* program)
{
if (m_PC != 0)
return m_PC;
if (auto location = Scripts::GetCodeLocationByPattern(program, m_Pattern))
{
m_PC = location.value();
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;
}
return m_PC;
}
}
+68
View File
@@ -0,0 +1,68 @@
#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"
namespace YimMenu
{
class ScriptFunction
{
joaat_t m_Script;
const SimplePattern m_Pattern;
std::uint32_t m_PC;
std::string m_Name;
std::uint32_t GetPC(rage::scrProgram* program);
template<typename Arg>
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);
}
public:
ScriptFunction(const std::string& name, const joaat_t script, const SimplePattern& pattern);
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;
if constexpr (!std::is_same_v<Ret, void>)
{
return *reinterpret_cast<Ret*>(stack + topStack);
}
}
};
}
+5
View File
@@ -6,4 +6,9 @@ namespace YimMenu
{
return reinterpret_cast<uintptr_t*>((uintptr_t)m_StackPtr + (m_Index * sizeof(uintptr_t)));
}
bool ScriptLocal::CanAccess() const
{
return m_StackPtr != nullptr;
}
}
+7 -5
View File
@@ -1,6 +1,7 @@
#pragma once
#include <cstddef>
#include <type_traits>
#include <types/script/scrThread.hpp>
namespace YimMenu
{
@@ -25,12 +26,11 @@ namespace YimMenu
}
// TODO: Uncomment when GTAV-Classes have been added
// constexpr ScriptLocal(rage::scrThread* thread, std::size_t index) :
// ScriptLocal(thread->m_stack, index)
// {
constexpr ScriptLocal(rage::scrThread* thread, std::size_t index) :
ScriptLocal(thread->m_Stack, index)
{
// }
}
constexpr ScriptLocal At(std::ptrdiff_t offset) const
{
@@ -65,6 +65,8 @@ namespace YimMenu
return *static_cast<std::add_pointer_t<std::remove_reference_t<T>>>(Get());
}
bool CanAccess() const;
private:
void* Get() const;
+30
View File
@@ -3,6 +3,7 @@
#include "types/rage/atArray.hpp"
#include "types/rage/tlsContext.hpp"
#include "types/script/GtaThread.hpp"
#include "types/script/scrProgram.hpp"
#include "types/script/CGameScriptHandler.hpp"
#include "types/script/CGameScriptHandlerNetComponent.hpp"
#include "types/script/CGameScriptId.hpp"
@@ -10,6 +11,7 @@
#include "game/gta/Packet.hpp"
#include "game/backend/Players.hpp"
#include "game/backend/Self.hpp"
#include "core/memory/Pattern.hpp"
namespace YimMenu::Scripts
{
@@ -26,6 +28,16 @@ namespace YimMenu::Scripts
return nullptr;
}
rage::scrProgram* FindScriptProgram(joaat_t hash)
{
for (int i = 0; i < 176; i++)
{
if (Pointers.ScriptPrograms[i] && Pointers.ScriptPrograms[i]->m_NameHash == hash)
return Pointers.ScriptPrograms[i];
}
return nullptr;
}
void RunAsScript(rage::scrThread* thread, std::function<void()> callback)
{
@@ -82,6 +94,24 @@ namespace YimMenu::Scripts
return false;
}
std::optional<std::uint32_t> GetCodeLocationByPattern(rage::scrProgram* program, SimplePattern pattern)
{
uint32_t codeSize = program->m_CodeSize;
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())
if (pattern.m_Bytes[j].value() != *program->GetCodeAddress(i + j))
goto incorrect;
return i;
incorrect:
continue;
}
return std::nullopt;
}
void ForceScriptHost(rage::scrThread* thread)
{
if (auto net_component = reinterpret_cast<GtaThread*>(thread)->m_NetComponent)
+4
View File
@@ -7,11 +7,15 @@ namespace rage
class scrProgram;
}
class SimplePattern;
namespace YimMenu::Scripts
{
extern rage::scrThread* FindScriptThread(joaat_t hash);
extern rage::scrProgram* FindScriptProgram(joaat_t hash);
extern void RunAsScript(rage::scrThread* thread, std::function<void()> callback);
extern void RunWithSpoofedThreadName(std::uint32_t name, std::function<void()> callback); // consider using RunAsScript if possible
extern bool SafeToModifyFreemodeBroadcastGlobals(); // TODO: find a better method for this that works for all scripts
extern std::optional<std::uint32_t> GetCodeLocationByPattern(rage::scrProgram* program, SimplePattern pattern);
extern void ForceScriptHost(rage::scrThread* thread); // likely unstable, beware!
}
+1 -1
View File
@@ -204,7 +204,7 @@ namespace YimMenu
constexpr auto scriptVMPtrn = Pattern<"49 63 41 1C">("ScriptVM");
scanner.Add(scriptVMPtrn, [this](PointerCalculator ptr) {
ScriptVM = ptr.Sub(0x24).As<PVOID>();
ScriptVM = ptr.Sub(0x24).As<Functions::ScriptVM>();
});
constexpr auto prepareMetricForSendingPtrn = Pattern<"48 89 F9 FF 50 20 48 8D 15">("PrepareMetricForSending");
+2 -1
View File
@@ -39,6 +39,7 @@ namespace YimMenu
using RequestControl = void(*)(rage::netObject* object);
using EventAck = bool(*)(uintptr_t data, CNetGamePlayer* target_player, uint32_t event_index, uint32_t event_handled_bitset);
using SendEventAck = void(*)(rage::netEventMgr* event_manager, CNetGamePlayer* source_player);
using ScriptVM = int (*)(uint64_t* stack, int64_t** scr_globals, rage::scrProgram* program, void* ctx);
}
struct PointerData
@@ -85,7 +86,7 @@ namespace YimMenu
Functions::SendEventAck SendEventAck;
PVOID QueueDependency;
PVOID SigScanMemory;
PVOID ScriptVM;
Functions::ScriptVM ScriptVM;
PVOID PrepareMetricForSending;
int* BERestartStatus;
bool* NeedsBERestart;
+2
View File
@@ -16,6 +16,7 @@
#include "game/backend/NativeHooks.hpp"
#include "game/frontend/GUI.hpp"
#include "game/pointers/Pointers.hpp"
#include "game/features/recovery/GiveVehicleReward.hpp"
namespace YimMenu
{
@@ -55,6 +56,7 @@ namespace YimMenu
FiberPool::Init(16);
ScriptMgr::AddScript(std::make_unique<Script>(&HotkeySystem::RunScript));
ScriptMgr::AddScript(std::make_unique<Script>(&Commands::RunScript));
ScriptMgr::AddScript(std::make_unique<Script>(&GiveVehicleReward::RunScript));
Notifications::Show("YimMenuV2", "Loaded succesfully", NotificationType::Success);
+13
View File
@@ -0,0 +1,13 @@
#include "VehicleRewardData.hpp"
#include "game/gta/ScriptLocal.hpp"
#define DEFINE_LOCAL_ACCESSOR(str, local) \
str* str::Get(rage::scrThread* thread) \
{ \
auto lcl = YimMenu::ScriptLocal(thread, local); \
if (!lcl.CanAccess()) \
return nullptr; \
return lcl.As<str*>(); \
}
DEFINE_LOCAL_ACCESSOR(VEHICLE_REWARD_DATA, 129);
@@ -0,0 +1,20 @@
#pragma once
#include "../types.hpp"
namespace rage
{
class scrThread;
}
struct VEHICLE_REWARD_DATA
{
uint64_t PAD_0000[3];
SCR_INT TransactionStatus; // 4
SCR_INT Garage; // 5
SCR_INT GarageOffset; // 6
SCR_INT ControlStatus; // 7
uint64_t PAD_0008[40];
static VEHICLE_REWARD_DATA* Get(rage::scrThread* thread);
};
static_assert(sizeof(VEHICLE_REWARD_DATA) == 47 * 8);