diff --git a/.github/FUNDING.yml b/.github/FUNDING.yml
new file mode 100644
index 00000000..d9446e56
--- /dev/null
+++ b/.github/FUNDING.yml
@@ -0,0 +1,15 @@
+# These are supported funding model platforms
+
+github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]
+patreon: # Replace with a single Patreon username
+open_collective: # Replace with a single Open Collective username
+ko_fi: # Replace with a single Ko-fi username
+tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
+community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
+liberapay: # Replace with a single Liberapay username
+issuehunt: # Replace with a single IssueHunt username
+lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
+polar: # Replace with a single Polar username
+buy_me_a_coffee: itsjustcurtis # Replace with a single Buy Me a Coffee username
+thanks_dev: # Replace with a single thanks.dev username
+custom: https://paypal.me/curtisre # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md
new file mode 100644
index 00000000..5031ff1a
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/bug_report.md
@@ -0,0 +1,23 @@
+---
+name: Bug report
+about: Create a report to help us improve
+title: "[Bug Report]"
+labels: bug
+assignees: ''
+
+---
+
+**Describe the bug**
+A clear and concise description of what the bug is.
+
+**To Reproduce**
+Steps to reproduce the behavior
+
+**Expected behavior**
+A clear and concise description of what you expected to happen.
+
+**Screenshots**
+If applicable, add screenshots to help explain your problem.
+
+**Log**
+Change your loglevel in the Menyoo .ini file to 3 and try again. Share your menyoolog.txt file with your report.
diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md
new file mode 100644
index 00000000..12fef876
--- /dev/null
+++ b/.github/ISSUE_TEMPLATE/feature_request.md
@@ -0,0 +1,20 @@
+---
+name: Feature request
+about: Suggest an idea for this project
+title: "[Feature Request]"
+labels: enhancement
+assignees: ''
+
+---
+
+**Is your feature request related to a problem? Please describe.**
+A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+
+**Describe the solution you'd like**
+A clear and concise description of what you want to happen.
+
+**Describe alternatives you've considered**
+A clear and concise description of any alternative solutions or features you've considered.
+
+**Additional context**
+Add any other context, screenshots, or examples of the feature that would help it get implemented.
diff --git a/.github/workflows/master_build.yml b/.github/workflows/master_build.yml
index 207a24a9..69ad6c86 100644
--- a/.github/workflows/master_build.yml
+++ b/.github/workflows/master_build.yml
@@ -108,11 +108,9 @@ jobs:
- name: Set output
id: set_output
- if: env.CANCELJOB != 'true'
run: echo "CANCELJOB=${{ env.CANCELJOB }}" >> $GITHUB_OUTPUT
build:
- if: github.ref == 'refs/heads/main' || (github.ref == 'refs/heads/Development' && needs.check-version-and-merge.outputs.CANCELJOB != 'true')
runs-on: windows-latest
needs: check-version-and-merge
@@ -152,6 +150,7 @@ jobs:
pre-release:
+ if: github.ref == 'refs/heads/main' || (github.ref == 'refs/heads/Development' && needs.check-version-and-merge.outputs.CANCELJOB != 'true')
needs: build
permissions: write-all
name: "Pre Release"
@@ -196,4 +195,3 @@ jobs:
prerelease: ${{ github.ref_name != 'main' }}
title: ${{ steps.version.outputs.version}}
files: MenyooSP.zip
- dry-run: true
diff --git a/README.md b/README.md
index 8f433ada..b4ae1cf5 100644
--- a/README.md
+++ b/README.md
@@ -3,7 +3,7 @@
[](https://github.com/itsjustcurtis/MenyooSP/releases/latest)
[](https://github.com/itsjustcurtis/MenyooSP/releases)
-
+
# Menyoo PC - [DOWNLOAD LATEST RELEASE](https://github.com/itsjustcurtis/MenyooSP/releases/latest/download/MenyooSP.zip)
@@ -13,17 +13,18 @@
---
## Community
+
- [Discord](https://discord.gg/v29AwqAemT)
## Requirements
- [OpenIV and its ASI Loader](https://openiv.com/)
- [ScriptHookV by Alexander Blade](http://www.dev-c.com/gtav/scripthookv/)
- [Battleye disabled in the Rockstar Launcher Settings](https://staticg.sportskeeda.com/editor/2024/11/bdc35-17305620610616-1920.jpg)
-- GTA V Version 3095.0 or newer.
+- GTA V Legacy Version 3095.0 or newer OR GTA V Enhanced
## Build
Visual Studio 2022 required
-- I used v17.0.4
+- I used v17.14.19
## Install
Copy the Menyoo.asi file along with the menyooStuff folder to the Grand Theft Auto V game directory.
diff --git a/Solution/source/Memory/GTAmemory.cpp b/Solution/source/Memory/GTAmemory.cpp
index 132fa7d7..0e093337 100644
--- a/Solution/source/Memory/GTAmemory.cpp
+++ b/Solution/source/Memory/GTAmemory.cpp
@@ -46,25 +46,47 @@ MODULEINFO g_MainModuleInfo = { 0 };
ScriptTable* scriptTable;
ScriptHeader* shopController;
-typedef CVehicleModelInfo*(*GetModelInfo_t)(unsigned int modelHash, int* index);
-typedef CVehicleModelInfo*(*InitVehicleArchetype_t)(const char*, bool, unsigned int);
+typedef CVehicleModelInfo* (*GetModelInfo_t)(unsigned int modelHash, int* index);
+typedef CVehicleModelInfo* (*InitVehicleArchetype_t)(const char*, bool, unsigned int);
+typedef CVehicleModelInfo* (*InitVehicleArchetypeEnhanced_t)(uint32_t, const char*);
GetModelInfo_t GetModelInfo;
std::unordered_map g_vehicleHashes;
-CallHook * g_InitVehicleArchetype = nullptr;
+CallHook* g_InitVehicleArchetype = nullptr;
CVehicleModelInfo* initVehicleArchetype_stub(const char* name, bool a2, unsigned int a3) {
+ addlog(ige::LogType::LOG_DEBUG, "getting hashkey for " + std::string(name), __FILENAME__);
g_vehicleHashes.insert({ GET_HASH_KEY(name), boost::to_lower_copy(name) });
return g_InitVehicleArchetype->fn(name, a2, a3);
}
+
+// InitVehicleArchetype has been inlined in Enhanced, instead we hook one of functions which are called after it and take its first parameter.
+CallHook* g_InitVehicleArchetypeEnhanced = nullptr;
+CVehicleModelInfo* initVehicleArchetypeEnhanced_stub(uint32_t a1, const char* name) {
+ g_vehicleHashes.insert({ GET_HASH_KEY(name), name });
+ return g_InitVehicleArchetypeEnhanced->fn(a1, name);
+}
+
void setupHooks() {
- auto addr = GTAmemory::FindPattern("\xE8\x00\x00\x00\x00\x48\x8B\x4D\xE0\x48\x8B\x11", "x????xxxxxxx");
- if (!addr) {
- addlog(ige::LogType::LOG_ERROR, "Couldn't find InitVehicleArchetype", __FILENAME__);
- return;
+
+ if (g_isEnhanced) {
+ auto addr = MemryScan::PatternScanner::FindPattern("e8 ? ? ? ? 43 89 44 2c");
+ if (!addr) {
+ addlog(ige::LogType::LOG_ERROR, "Couldn't find InitVehicleArchetypeEnahnced", __FILENAME__);
+ return;
+ }
+ addlog(ige::LogType::LOG_INFO, "Found InitVehicleArchetypeEnhanced at " + std::to_string(addr), __FILENAME__);
+ g_InitVehicleArchetypeEnhanced = HookManager::SetCall(addr, initVehicleArchetypeEnhanced_stub);
+ }
+ else {
+ auto addr = GTAmemory::FindPattern("\xE8\x00\x00\x00\x00\x48\x8B\x4D\xE0\x48\x8B\x11", "x????xxxxxxx");
+ if (!addr) {
+ addlog(ige::LogType::LOG_ERROR, "Couldn't find InitVehicleArchetype", __FILENAME__);
+ return;
+ }
+ addlog(ige::LogType::LOG_INFO, "Found InitVehicleArchetype at " + std::to_string(addr), __FILENAME__);
+ g_InitVehicleArchetype = HookManager::SetCall(addr, initVehicleArchetype_stub);
}
- addlog(ige::LogType::LOG_INFO, "Found InitVehicleArchetype at "+std::to_string(addr), __FILENAME__);
- g_InitVehicleArchetype = HookManager::SetCall(addr, initVehicleArchetype_stub);
}
void removeHooks() {
@@ -72,6 +94,10 @@ void removeHooks() {
delete g_InitVehicleArchetype;
g_InitVehicleArchetype = nullptr;
}
+ if (g_InitVehicleArchetypeEnhanced) {
+ delete g_InitVehicleArchetypeEnhanced;
+ g_InitVehicleArchetypeEnhanced = nullptr;
+ }
}
template R GetMultilayerPointer(void* base, const std::vector& offsets)
{
@@ -159,6 +185,8 @@ namespace MemryScan
}
}
+ addlog(ige::LogType::LOG_ERROR, "Pattern " + s + " not found", __FILENAME__);
+
return NULL;
}
DWORD64 Pattern::Scan(MODULEINFO mi, const std::string& s)
@@ -166,7 +194,7 @@ namespace MemryScan
return Scan((DWORD64)mi.lpBaseOfDll, (DWORD64)mi.SizeOfImage, s);
}
- bool PatternScanner::CompareMemory(const BYTE *data, const BYTE *pattern, const char *mask)
+ bool PatternScanner::CompareMemory(const BYTE* data, const BYTE* pattern, const char* mask)
{
for (; *mask; ++mask, ++data, ++pattern)
{
@@ -206,7 +234,7 @@ namespace MemryScan
for (i = 0; i < size; ++i)
{
- if (CompareMemory((BYTE *)(address + i), (BYTE *)search.data(), mask.data()))
+ if (CompareMemory((BYTE*)(address + i), (BYTE*)search.data(), mask.data()))
{
return (DWORD64)(address + i);
}
@@ -289,25 +317,25 @@ UINT64(*GTAmemory::_entityAddressFunc)(int handle);
UINT64(*GTAmemory::_playerAddressFunc)(int handle);
UINT64(*GTAmemory::_ptfxAddressFunc)(int handle);
int(*GTAmemory::_addEntityToPoolFunc)(UINT64 address);
-UINT64(*GTAmemory::_entityPositionFunc)(UINT64 address, float *position);
+UINT64(*GTAmemory::_entityPositionFunc)(UINT64 address, float* position);
UINT64(*GTAmemory::_entityModel1Func)(UINT64 address), (*GTAmemory::_entityModel2Func)(UINT64 address);
-UINT64 *GTAmemory::_entityPoolAddress, *GTAmemory::_vehiclePoolAddress, *GTAmemory::_pedPoolAddress, *GTAmemory::_objectPoolAddress, *GTAmemory::_cameraPoolAddress, *GTAmemory::_pickupObjectPoolAddress;
unsigned char(*GTAmemory::SetNmBoolAddress)(__int64, __int64, unsigned char);
unsigned char(*GTAmemory::SetNmIntAddress)(__int64, __int64, int);
unsigned char(*GTAmemory::SetNmFloatAddress)(__int64, __int64, float);
unsigned char(*GTAmemory::SetNmVec3Address)(__int64, __int64, float, float, float);
unsigned char(*GTAmemory::SetNmStringAddress)(__int64, __int64, __int64);
-UINT64 GTAmemory::CreateNmMessageFunc, GTAmemory::GiveNmMessageFunc;
+UINT64(*GTAmemory::CreateNmMessageFunc)(uint64_t, uint64_t, int);
+void(*GTAmemory::GiveNmMessageFunc)(uint64_t, void*, uint64_t);
UINT64(*GTAmemory::CheckpointBaseAddr)();
UINT64(*GTAmemory::CheckpointHandleAddr)(UINT64 baseAddr, int Handle);
-UINT64 *GTAmemory::checkpointPoolAddress;
-float *GTAmemory::_readWorldGravityAddress, *GTAmemory::_writeWorldGravityAddress;
-UINT64 *GTAmemory::_gamePlayCameraAddr;
-int*GTAmemory:: _cursorSpriteAddr;
+UINT64* GTAmemory::checkpointPoolAddress;
+float* GTAmemory::_readWorldGravityAddress, * GTAmemory::_writeWorldGravityAddress;
+UINT64* GTAmemory::_gamePlayCameraAddr;
+int* GTAmemory::_cursorSpriteAddr;
INT32* GTAmemory::_transitionStatus = nullptr;
UINT64 GTAmemory::_gxtLabelFromHashAddr1;
-char*(__fastcall *GTAmemory::_gxtLabelFromHashFuncAddr)(UINT64 address, unsigned int hash);
+char* (__fastcall* GTAmemory::_gxtLabelFromHashFuncAddr)(UINT64 address, unsigned int hash);
/*struct EntityPoolTask
{
@@ -499,11 +527,49 @@ class EntityPool
return num1 - (num2 & 0x3FFFFFFF) <= 256;
}
};
+
+class EntityPoolEnhanced
+{
+public:
+ char _padding0[24];
+ UINT32 num1;
+ char _padding1[12];
+ UINT32 num2;
+
+ inline bool Full()
+ {
+ return num1 - (num2 & 0x3FFFFFFF) <= 256;
+ }
+};
+
class VehiclePool
{
public:
//char _padding0[0];
- UINT64 *poolAddress;
+ UINT64* poolAddress;
+ //char _padding1[0];
+ UINT32 size;
+ char _padding2[36];
+ UINT32* bitArray;
+ char _padding3[40];
+ UINT32 itemCount;
+
+ inline bool isValid(UINT32 i)
+ {
+ return (bitArray[i >> 5] >> (i & 0x1F)) & 1;
+ }
+
+ inline UINT64 getAddress(UINT32 i)
+ {
+ return poolAddress[i];
+ }
+};
+class VehiclePoolEnhanced
+{
+public:
+ void* vptr;
+ //char _padding0[0];
+ UINT64* poolAddress;
//char _padding1[0];
UINT32 size;
char _padding2[36];
@@ -529,7 +595,37 @@ class GenericPool {
BYTE* byteArray;
//char _padding2[0];
UINT32 size;
+ //char _padding3[0];
+ UINT32 itemSize;
+
+
+ inline bool isValid(UINT32 i)
+ {
+ return mask(i) != 0;
+ }
+
+ inline UINT64 getAddress(UINT32 i)
+ {
+ return mask(i) & (poolStartAddress + i * itemSize);
+ }
+private:
+ inline long long mask(UINT32 i)
+ {
+ long long num1 = byteArray[i] & 0x80;
+ return ~((num1 | -num1) >> 63);
+ }
+};
+// TODO
+class GenericPoolEnhanced {
+public:
+ void* vptr;
+ //char _padding0[0];
+ UINT64 poolStartAddress;
+ //char _padding1[0];
+ BYTE* byteArray;
//char _padding2[0];
+ UINT32 size;
+ //char _padding3[0];
UINT32 itemSize;
@@ -567,12 +663,48 @@ struct EntityPoolTask
{
}
+ static uintptr_t GetModelInfo(uintptr_t entityAddress)
+ {
+ if (entityAddress)
+ {
+ return entityAddress + 0x20;
+ }
+
+ return (uintptr_t)(0);
+ }
+ // Unchanged in Enhanced.
+ static int GetModelHashFromFwArcheType(uintptr_t fwArcheTypeAddress)
+ {
+ if (fwArcheTypeAddress)
+ {
+ return *reinterpret_cast(fwArcheTypeAddress + 0x18);
+ }
+
+ return 0;
+ }
+ static int GetModelHashFromEntity(uintptr_t entityAddress)
+ {
+ if (!entityAddress)
+ {
+ return 0;
+ }
+
+ auto modelInfoAddress = GetModelInfo(entityAddress);
+ if (modelInfoAddress)
+ {
+ return GetModelHashFromFwArcheType(modelInfoAddress);
+ }
+
+ return 0;
+ }
+
+
inline bool CheckEntity(uintptr_t address)
{
if (_posCheck)
{
float position[3];
- GTAmemory::_entityPositionFunc(address, position);
+ GTAmemory::GetEntityPos(address, position); // replaced _getEntityPos with a call to GetEntityPos which implements the same function for enhanced (because it was inlined), and calls the old function for legacy.
if (Vector3::Subtract(_position, Vector3(position[0], position[1], position[2])).LengthSquared() > _radiusSquared)
{
@@ -582,25 +714,38 @@ struct EntityPoolTask
if (_modelCheck)
{
- UINT32 v0 = *reinterpret_cast(GTAmemory::_entityModel1Func(*reinterpret_cast(address + 32)));
- UINT32 v1 = v0 & 0xFFFF;
- UINT32 v2 = ((v1 ^ v0) & 0x0FFF0000 ^ v1) & 0xDFFFFFFF;
- UINT32 v3 = ((v2 ^ v0) & 0x10000000 ^ v2) & 0x3FFFFFFF;
- const uintptr_t v5 = GTAmemory::_entityModel2Func(reinterpret_cast(&v3));
-
-
- if (!v5)
- {
+ if (g_isEnhanced) {
+ // This should be equivalent to what we have in legacy.
+ int modelHash = GetModelHashFromEntity(address);
+ for (int hash : _modelHashes) {
+ if (modelHash == hash)
+ {
+ return true;
+ }
+ }
return false;
}
- for (int hash : _modelHashes)
- {
- if (*reinterpret_cast(v5 + 24) == hash)
+ else {
+ UINT32 v0 = *reinterpret_cast(GTAmemory::_entityModel1Func(*reinterpret_cast(address + 32)));
+ UINT32 v1 = v0 & 0xFFFF;
+ UINT32 v2 = ((v1 ^ v0) & 0x0FFF0000 ^ v1) & 0xDFFFFFFF;
+ UINT32 v3 = ((v2 ^ v0) & 0x10000000 ^ v2) & 0x3FFFFFFF;
+ const uintptr_t v5 = GTAmemory::_entityModel2Func(reinterpret_cast(&v3));
+
+
+ if (!v5)
+ {
+ return false;
+ }
+ for (int hash : _modelHashes)
{
- return true;
+ if (*reinterpret_cast(v5 + 24) == hash)
+ {
+ return true;
+ }
}
+ return false;
}
- return false;
}
return true;
@@ -608,27 +753,64 @@ struct EntityPoolTask
void Run(std::vector& _handles)
{
- if (*GTAmemory::_entityPoolAddress == 0)
- {
- return;
+ if (g_isEnhanced) {
+ if (GTAmemory::_entityPoolAddress == 0)
+ {
+ return;
+ }
+ }
+ else {
+ if (*GTAmemory::_entityPoolAddress == 0)
+ {
+ return;
+ }
+ }
+
+ EntityPool* entityPool;
+ EntityPoolEnhanced* entityPoolEnhanced;
+
+ if (g_isEnhanced) {
+ entityPoolEnhanced = reinterpret_cast(*GTAmemory::_entityPoolAddress);
+ }
+ else {
+ entityPool = reinterpret_cast(*GTAmemory::_entityPoolAddress);
}
- EntityPool* entityPool = reinterpret_cast(*GTAmemory::_entityPoolAddress);
if (TypeHasFlag(Type::Vehicle))
{
if (*GTAmemory::_vehiclePoolAddress)
{
- VehiclePool* vehiclePool = *reinterpret_cast(*GTAmemory::_vehiclePoolAddress);
+ // could definitely be refactored to avoid the duplicate code
+ if (g_isEnhanced) {
- for (UINT32 i = 0; i < vehiclePool->size; i++)
- {
- //if (entityPool->Full()) break;
- if (vehiclePool->isValid(i))
+ VehiclePoolEnhanced* vehiclePool = *reinterpret_cast(*GTAmemory::_vehiclePoolAddress);
+
+ for (UINT32 i = 0; i < vehiclePool->size; i++)
{
- UINT64 address = vehiclePool->getAddress(i);
- if (address && CheckEntity(address))
+ //if (entityPoolEnhanced->Full()) break;
+ if (vehiclePool->isValid(i))
{
- _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ UINT64 address = vehiclePool->getAddress(i);
+ if (address && CheckEntity(address))
+ {
+ _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ }
+ }
+ }
+ }
+ else {
+ VehiclePool* vehiclePool = *reinterpret_cast(*GTAmemory::_vehiclePoolAddress);
+
+ for (UINT32 i = 0; i < vehiclePool->size; i++)
+ {
+ //if (entityPool->Full()) break;
+ if (vehiclePool->isValid(i))
+ {
+ UINT64 address = vehiclePool->getAddress(i);
+ if (address && CheckEntity(address))
+ {
+ _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ }
}
}
}
@@ -636,39 +818,82 @@ struct EntityPoolTask
}
if (TypeHasFlag(Type::Ped))
{
- if (*GTAmemory::_pedPoolAddress)
- {
- GenericPool* pedPool = reinterpret_cast(*GTAmemory::_pedPoolAddress);
+ if (g_isEnhanced) {
+ if (GTAmemory::_pedPoolAddress)
+ {
+ GenericPoolEnhanced* pedPool = reinterpret_cast(GTAmemory::_pedPoolAddress);
- for (UINT32 i = 0; i < pedPool->size; i++)
+ for (UINT32 i = 0; i < pedPool->size; i++)
+ {
+ //if (entityPool->Full()) break;
+ if (pedPool->isValid(i))
+ {
+ UINT64 address = pedPool->getAddress(i);
+ if (address && CheckEntity(address))
+ {
+ _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (*GTAmemory::_pedPoolAddress)
{
- //if (entityPool->Full()) break;
- if (pedPool->isValid(i))
+ GenericPool* pedPool = reinterpret_cast(*GTAmemory::_pedPoolAddress);
+
+ for (UINT32 i = 0; i < pedPool->size; i++)
{
- UINT64 address = pedPool->getAddress(i);
- if (address && CheckEntity(address))
+ //if (entityPool->Full()) break;
+ if (pedPool->isValid(i))
{
- _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ UINT64 address = pedPool->getAddress(i);
+ if (address && CheckEntity(address))
+ {
+ _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ }
}
}
}
}
+
}
if (TypeHasFlag(Type::Prop))
{
- if (*GTAmemory::_objectPoolAddress)
- {
- GenericPool* propPool = reinterpret_cast(*GTAmemory::_objectPoolAddress);
+ if (g_isEnhanced) {
+ if (GTAmemory::_objectPoolAddress)
+ {
+ GenericPoolEnhanced* propPool = reinterpret_cast(GTAmemory::_objectPoolAddress);
- for (UINT32 i = 0; i < propPool->size; i++)
+ for (UINT32 i = 0; i < propPool->size; i++)
+ {
+ //if (entityPool->Full()) break;
+ if (propPool->isValid(i))
+ {
+ UINT64 address = propPool->getAddress(i);
+ if (address && CheckEntity(address))
+ {
+ _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (*GTAmemory::_objectPoolAddress)
{
- //if (entityPool->Full()) break;
- if (propPool->isValid(i))
+ GenericPool* propPool = reinterpret_cast(*GTAmemory::_objectPoolAddress);
+
+ for (UINT32 i = 0; i < propPool->size; i++)
{
- UINT64 address = propPool->getAddress(i);
- if (address && CheckEntity(address))
+ //if (entityPool->Full()) break;
+ if (propPool->isValid(i))
{
- _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ UINT64 address = propPool->getAddress(i);
+ if (address && CheckEntity(address))
+ {
+ _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ }
}
}
}
@@ -676,27 +901,56 @@ struct EntityPoolTask
}
if (TypeHasFlag(Type::PickupObject))
{
- if (*GTAmemory::_pickupObjectPoolAddress)
- {
- GenericPool* pickupPool = reinterpret_cast(*GTAmemory::_pickupObjectPoolAddress);
+ if (g_isEnhanced) {
+ if (GTAmemory::_pickupObjectPoolAddress)
+ {
+ GenericPoolEnhanced* pickupPool = reinterpret_cast(GTAmemory::_pickupObjectPoolAddress);
- for (UINT32 i = 0; i < pickupPool->size; i++)
+ for (UINT32 i = 0; i < pickupPool->size; i++)
+ {
+ //if (entityPool->Full()) break;
+ if (pickupPool->isValid(i))
+ {
+ UINT64 address = pickupPool->getAddress(i);
+ if (address)
+ {
+ if (_posCheck)
+ {
+ float* position = (float*)(address + 0x90);
+ if (Vector3::Subtract(_position, Vector3(position[0], position[1], position[2])).LengthSquared() > _radiusSquared)
+ {
+ continue;
+ }
+ }
+ _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
+ }
+ }
+ }
+ }
+ }
+ else {
+ if (*GTAmemory::_pickupObjectPoolAddress)
{
- //if (entityPool->Full()) break;
- if (pickupPool->isValid(i))
+ GenericPool* pickupPool = reinterpret_cast(*GTAmemory::_pickupObjectPoolAddress);
+
+ for (UINT32 i = 0; i < pickupPool->size; i++)
{
- UINT64 address = pickupPool->getAddress(i);
- if (address)
+ //if (entityPool->Full()) break;
+ if (pickupPool->isValid(i))
{
- if (_posCheck)
+ UINT64 address = pickupPool->getAddress(i);
+ if (address)
{
- float* position = (float*)(address + 0x90);
- if (Vector3::Subtract(_position, Vector3(position[0], position[1], position[2])).LengthSquared() > _radiusSquared)
+ if (_posCheck)
{
- continue;
+ float* position = (float*)(address + 0x90);
+ if (Vector3::Subtract(_position, Vector3(position[0], position[1], position[2])).LengthSquared() > _radiusSquared)
+ {
+ continue;
+ }
}
+ _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
}
- _handles.push_back(GTAmemory::_addEntityToPoolFunc(address));
}
}
}
@@ -719,161 +973,769 @@ struct GenericTask
GenericTask(func pFunc, UINT64 Arg) : _toRun(pFunc), _arg(Arg)
{
}
- virtual void Run()
- {
- _res = _toRun(_arg);
+ virtual void Run()
+ {
+ _res = _toRun(_arg);
+ }
+
+ UINT64 GetResult()
+ {
+ return _res;
+ }
+
+private:
+ func _toRun;
+ UINT64 _arg;
+ UINT64 _res;
+};
+
+#pragma region -- Helpers needed for Enhanced --
+
+uint64_t(*s_getScriptEntity)(int handle);
+uint64_t s_ptfxEntityVPtr;
+uint8_t(*s_isPtfxEntityUsableVFunc)(uint64_t entity, uint64_t arg2, uint64_t vptr);
+
+uint64_t s_PtfxVfuncSecondArgumentFuncAddr;
+uint64_t* s_PtfxHashTableCount;
+uint64_t* s_PtfxHashTableBuckets;
+
+static void* GetPtfxAddressFunc(int handle);
+static bool IsPtfxEntityUsable(uint64_t entity);
+
+static bool IsPtfxEntityUsable(uint64_t entity)
+{
+ if (entity == 0)
+ return false;
+
+ // Cache vptr and the vfunc at 0x28
+ if (!s_isPtfxEntityUsableVFunc || s_ptfxEntityVPtr == 0)
+ {
+ s_ptfxEntityVPtr = *reinterpret_cast(entity);
+ if (s_ptfxEntityVPtr == 0)
+ return false;
+
+ uintptr_t funcPtr = *reinterpret_cast(s_ptfxEntityVPtr + 0x28);
+ if (funcPtr == 0)
+ return false;
+
+ s_isPtfxEntityUsableVFunc = reinterpret_cast<
+ uint8_t(*)(uint64_t, uint64_t, uint64_t)
+ >(funcPtr);
+ }
+
+ int result = s_isPtfxEntityUsableVFunc(
+ entity,
+ s_PtfxVfuncSecondArgumentFuncAddr,
+ s_ptfxEntityVPtr
+ );
+
+ return result != 0;
+}
+
+static void* GetPtfxAddressFunc(int handle)
+{
+ uint64_t entity = s_getScriptEntity(handle);
+ if (entity == 0 || !IsPtfxEntityUsable(entity))
+ return nullptr;
+
+ if (!s_PtfxHashTableCount || !s_PtfxHashTableBuckets)
+ return nullptr;
+ if (*s_PtfxHashTableCount == 0 || *s_PtfxHashTableBuckets == 0)
+ return nullptr;
+
+ uint32_t keyed = static_cast(handle + 0x10000000);
+ uint32_t count = static_cast(*s_PtfxHashTableCount);
+ uint32_t index = keyed % count;
+
+ auto** bucketsBase = reinterpret_cast(*s_PtfxHashTableBuckets);
+ uint64_t* node = reinterpret_cast(bucketsBase[index]);
+
+ while (node != nullptr)
+ {
+ uint64_t key = *node;
+ if (key == keyed)
+ {
+ uint64_t A = *(node + 1);
+ if (A == 0)
+ return nullptr;
+ return reinterpret_cast(A);
+ }
+ node = *reinterpret_cast(node + 2);
+ }
+
+ return nullptr;
+}
+
+uint64_t s_entityVPtr;
+uint8_t(*s_isEntityUsableVFunc)(uint64_t entity, uint64_t arg2);
+uint64_t s_entityPosVFuncSecondArgument;
+
+uintptr_t s_pedEntityPosSecondCheckOffset;
+uintptr_t s_entityPosFloatsOffset;
+uintptr_t s_entityInternalTypeOffset;
+uintptr_t s_pedEntityInVehicleCheckOffset;
+
+enum EntityTypeInternal
+{
+ TypeInvalid = 0,
+ TypeBuilding = 1,
+ TypeAnimatedBuilding = 2,
+ TypeVehicle = 3,
+ TypePed = 4,
+ TypeObject = 5,
+ TypeParticleEffect = 48
+};
+
+static bool IsEntityUsable(uint64_t entity);
+static EntityTypeInternal GetEntityTypeInternal(uint64_t address);
+static uint64_t GetVehiclePedIsIn(uint64_t address);
+static void GetEntityPos(uint64_t address, float* position);
+
+static bool IsEntityUsable(uint64_t entity)
+{
+ if (entity == 0)
+ return false;
+
+ if (!s_isEntityUsableVFunc || s_entityVPtr == 0)
+ {
+ s_entityVPtr = *reinterpret_cast(entity);
+ if (s_entityVPtr == 0)
+ return false;
+
+ uintptr_t funcPtr = *reinterpret_cast(s_entityVPtr + 0x28);
+ if (funcPtr == 0)
+ return false;
+
+ s_isEntityUsableVFunc = reinterpret_cast(funcPtr);
+ }
+
+ int result = s_isEntityUsableVFunc(entity, s_entityPosVFuncSecondArgument);
+ return result != 0;
+}
+
+void GTAmemory::GetEntityPos(uint64_t address, float* position)
+{
+ if (address == 0)
+ {
+ position[0] = position[1] = position[2] = 0.0f;
+ return;
+ }
+
+ if (g_isEnhanced)
+ {
+ if (IsEntityUsable(address))
+ {
+ if (GetEntityTypeInternal(address) == EntityTypeInternal::TypePed)
+ {
+ if (*(uint8_t*)(address + s_pedEntityPosSecondCheckOffset) != 0x40)
+ {
+ uint64_t vehicleAddress = GetVehiclePedIsIn(address);
+ if (vehicleAddress != 0)
+ address = vehicleAddress;
+ }
+ }
+
+ position[0] = *(float*)(address + s_entityPosFloatsOffset);
+ position[1] = *(float*)(address + s_entityPosFloatsOffset + 4);
+ position[2] = *(float*)(address + s_entityPosFloatsOffset + 8);
+ }
+ else
+ {
+ position[0] = position[1] = position[2] = 0.0f;
+ }
+ }
+ else
+ {
+ GTAmemory::_entityPositionFunc(address, position);
+ }
+}
+
+static EntityTypeInternal GetEntityTypeInternal(uint64_t address)
+{
+ uint8_t type = *(uint8_t*)(address + s_entityInternalTypeOffset);
+ return static_cast(type);
+}
+
+static uint64_t GetVehiclePedIsIn(uint64_t address)
+{
+ return *(uint64_t*)(address + s_pedEntityInVehicleCheckOffset);
+}
+
+static uint64_t Rol(uint64_t value, int count)
+{
+ count &= 63;
+ return (value << count) | (value >> (64 - count));
+}
+
+#pragma endregion
+
+void GTAmemory::Init()
+{
+ UINT64 address;
+
+ // Get relative address and add it to the instruction address.
+ // 3 bytes equal the size of the opcode and its first argument. 7 bytes are the length of opcode and all its parameters.
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("48 8d 3d ? ? ? ? 48 8b 04 f7");
+ }
+ else {
+ address = FindPattern("\x4C\x8D\x05\x00\x00\x00\x00\x0F\xB7\xC1", "xxx????xxx");
+ }
+ if (address) _blipList = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("41 57 41 56 56 57 53 48 83 ec ? 89 d7 49 89 ce");
+ }
+ else {
+ address = FindPattern("\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x18\x89\x54\x24\x10\x56\x57\x41\x56\x48\x83\xEC\x20\x48\x8B\xD9", "xxxxxxxxxxxxxxxxxxxxxxxxx");
+ }
+ if (address) _gxtLabelFromHashFuncAddr = reinterpret_cast(address);
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("85 c0 74 ? 48 8d 0d ? ? ? ? 48 89 da e8");
+ }
+ else {
+ address = FindPattern("\x84\xC0\x74\x34\x48\x8D\x0D\x00\x00\x00\x00\x48\x8B\xD3", "xxxxxxx????xxx");
+ }
+ if (address) _gxtLabelFromHashAddr1 = *reinterpret_cast(address + 7) + address + 11;
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("41 8b 4c 1c ? e8");
+ // This function is not 100% the same as in legacy. The one from legacy was inlined in Enhanced,
+ // so that we can only call the function that returns an entitys address from its guid, but need to implement the vfunc call that comes after it on our own.
+ // That vfunc doesn't always take the same parameters, and it seems to not be necessary for the correct functioning of this function,
+ // so we will leave it out in general, and implement it for some specific functions (see GetPtfxAddress).
+ _entityAddressFunc = reinterpret_cast(*reinterpret_cast(address + 6) + address + 10);
+
+ address = MemryScan::PatternScanner::FindPattern("89 f1 b2 ? e8 ? ? ? ? 31 c9 48");
+ _playerAddressFunc = reinterpret_cast(*reinterpret_cast(address + 5) + address + 9);
+
+ // entityPositionFunc was inlined in Enhanced. We reimplement it ourselves.
+ address = MemryScan::PatternScanner::FindPattern("0f 84 ? ? ? ? f3 0f 10 bc 24 30 01 00 00");
+ s_entityPosVFuncSecondArgument = (uint64_t)(*(int*)(address - 12) + address - 8);
+
+ address = MemryScan::PatternScanner::FindPattern("31 c0 80 7f ? ? 48 0f 45 f8 0f 85 ? ? ? ? e9");
+ address = *(int*)(address + 17) + address + 21;
+ s_pedEntityPosSecondCheckOffset = *(int*)(address + 2);
+ address = *(int*)(address + 14) + address + 18;
+ s_pedEntityInVehicleCheckOffset = *(int*)(address + 3);
+
+ address = MemryScan::PatternScanner::FindPattern("0f 28 8f ? ? ? ? f3 0f 10 b7 ? ? ? ? f3 0f 16 c1 e9");
+ s_entityPosFloatsOffset = *(int*)(address + 3);
+
+ address = MemryScan::PatternScanner::FindPattern("48 89 d6 0f b6 42 ? 04 ? 3c ? 0f 87 ? ? ? ? e9");
+ address = *(int*)(address + 13) + address + 17;
+ address = *(int*)(address + 1) + address + 5;
+ s_entityInternalTypeOffset = *(byte*)(address + 2);
+
+
+ address = MemryScan::PatternScanner::FindPattern("0c ? 88 46 ? 48 8b 4e ? e8");
+ _entityModel1Func = reinterpret_cast(*reinterpret_cast(address + 10) + address + 14);
+
+ }
+ else {
+ if (GTAmemory::GetGameVersion() >= eGameVersion::VER_1_0_1290_1_STEAM)
+ {
+ address = FindPattern("\xE8\x00\x00\x00\x00\x48\x8B\xD8\x48\x85\xC0\x74\x2E\x48\x83\x3D", "x????xxxxxxxxxxx");
+ _entityAddressFunc = reinterpret_cast(*reinterpret_cast(address + 1) + address + 5);
+ address = FindPattern("\xB2\x01\xE8\x00\x00\x00\x00\x48\x85\xC0\x74\x1C\x8A\x88", "xxx????xxxxxxx");
+ _playerAddressFunc = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+
+ address = FindPattern("\x48\x8B\xDA\xE8\x00\x00\x00\x00\xF3\x0F\x10\x44\x24", "xxxx????xxxxx");
+ _entityPositionFunc = reinterpret_cast(address - 6);
+ address = FindPattern("\x0F\x85\x00\x00\x00\x00\x48\x8B\x4B\x20\xE8\x00\x00\x00\x00\x48\x8B\xC8", "xx????xxxxx????xxx");
+ _entityModel1Func = reinterpret_cast(*reinterpret_cast(address + 11) + address + 15);
+ address = FindPattern("\x45\x33\xC9\x3B\x05", "xxxxx");
+ _entityModel2Func = reinterpret_cast(address - 0x46);
+
+ address = FindPattern("\x4C\x8B\x05\x00\x00\x00\x00\x40\x8A\xF2\x8B\xE9", "xxx????xxxxx");
+ _pickupObjectPoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+ }
+ else
+ {
+ address = FindPattern("\x33\xFF\xE8\x00\x00\x00\x00\x48\x85\xC0\x74\x58", "xxx????xxxxx");
+ _entityAddressFunc = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+ address = FindPattern("\xB2\x01\xE8\x00\x00\x00\x00\x33\xC9\x48\x85\xC0\x74\x3B", "xxx????xxxxxxx");
+ _playerAddressFunc = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+
+ address = FindPattern("\x48\x8B\xC8\xE8\x00\x00\x00\x00\xF3\x0F\x10\x54\x24\x00\xF3\x0F\x10\x4C\x24\x00\xF3\x0F\x10", "xxxx????xxxxx?xxxxx?xxx");
+ _entityPositionFunc = reinterpret_cast(*reinterpret_cast(address + 4) + address + 8);
+ address = FindPattern("\x25\xFF\xFF\xFF\x3F\x89\x44\x24\x38\xE8\x00\x00\x00\x00\x48\x85\xC0\x74\x03", "xxxxxxxxxx????xxxxx");
+ _entityModel1Func = reinterpret_cast(*reinterpret_cast(address - 61) + address - 57);
+ _entityModel2Func = reinterpret_cast(*reinterpret_cast(address + 10) + address + 14);
+
+ address = FindPattern("\x8B\xF0\x48\x8B\x05\x00\x00\x00\x00\xF3\x0F\x59\xF6", "xxxxx????xxxx");
+ _pickupObjectPoolAddress = reinterpret_cast(*reinterpret_cast(address + 5) + address + 9);
+ }
+ }
+
+ // This function was inlined in Enhanced, so we reimplement it ourselves.
+ if (!g_isEnhanced) {
+ address = FindPattern("\x74\x21\x48\x8B\x48\x20\x48\x85\xC9\x74\x18\x48\x8B\xD6\xE8", "xxxxxxxxxxxxxxx") - 10;
+ _ptfxAddressFunc = reinterpret_cast(*reinterpret_cast(address) + address + 4);
+ }
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("56 57 48 83 ec ? 48 89 cf 48 8d 71 ? 8b 15");
+ }
+ else {
+ address = FindPattern("\x48\xF7\xF9\x49\x8B\x48\x08\x48\x63\xD0\xC1\xE0\x08\x0F\xB6\x1C\x11\x03\xD8", "xxxxxxxxxxxxxxxxxxx");
+ }
+ if (address) {
+ _addEntityToPoolFunc = reinterpret_cast(address - (g_isEnhanced ? 0 : 0x68));
+ }
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("48 8b 05 ? ? ? ? 48 85 c0 74 ? 4c 8b 00");
+ }
+ else {
+ address = FindPattern("\x48\x8B\x05\x00\x00\x00\x00\xF3\x0F\x59\xF6\x48\x8B\x08", "xxx????xxxxxxx");
+ }
+ _vehiclePoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+
+ if (!g_isEnhanced) {
+ address = FindPattern("\x4C\x8B\x0D\x00\x00\x00\x00\x44\x8B\xC1\x49\x8B\x41\x08", "xxx????xxxxxxx");
+ _entityPoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+
+ address = FindPattern("\x48\x8B\x05\x00\x00\x00\x00\x41\x0F\xBF\xC8\x0F\xBF\x40\x10", "xxx????xxxxxxxx");
+ _pedPoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+
+ address = FindPattern("\x48\x8B\x05\x00\x00\x00\x00\x8B\x78\x10\x85\xFF", "xxx????xxxxx");
+ _objectPoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+
+ address = FindPattern("\x48\x8B\xC8\xEB\x02\x33\xC9\x48\x85\xC9\x74\x26", "xxxxxxxxxxxx") - 9;
+ _cameraPoolAddress = reinterpret_cast(*reinterpret_cast(address) + address + 4);
+ }
+
+ if (g_isEnhanced)
+ {
+ address = MemryScan::PatternScanner::FindPattern("56 48 83 ec ? 48 89 ce 48 89 11 44 89 41");
+ }
+ else
+ {
+ address = MemryScan::PatternScanner::FindPattern("\x40\x53\x48\x83\xEC\x20\x83\x61\x0C\x00\x44\x89\x41\x08\x49\x63\xC0", "xxxxxxxxxxxxxxxxx");
+ }
+ if (address != null)
+ {
+ CreateNmMessageFunc = reinterpret_cast(address);
+ }
+
+
+ if (g_isEnhanced)
+ {
+ address = MemryScan::PatternScanner::FindPattern("44 8b 89 ? ? ? ? b9");
+ if (address != null)
+ {
+ GiveNmMessageFunc = reinterpret_cast(address);
+ }
+ }
+ else
+ {
+ address = MemryScan::PatternScanner::FindPattern("\x0F\x84\x8B\x00\x00\x00\x48\x8B\x47\x30\x48\x8B\x48\x10\x48\x8B\x51\x20\x80\x7A\x10\x0A", "xxxxxxxxxxxxxxxxxxxxxx");
+ if (address != null)
+ {
+ GiveNmMessageFunc = reinterpret_cast((UINT64*)(*(int*)(address - 0x1E) + address - 0x1A));
+ }
+ }
+
+ if (g_isEnhanced)
+ {
+ address = MemryScan::PatternScanner::FindPattern("7d ? 45 89 c6 48 89 d7");
+ if (address != null)
+ {
+ SetNmIntAddress = reinterpret_cast(address - 20);
+ }
+ }
+ else {
+ address = MemryScan::PatternScanner::FindPattern("\x48\x89\x5C\x24\x00\x57\x48\x83\xEC\x20\x48\x8B\xD9\x48\x63\x49\x0C\x41\x8B\xF8", "xxxx?xxxxxxxxxxxxxxx");
+ if (address != null)
+ {
+ SetNmIntAddress = reinterpret_cast(address);
+ }
+ }
+
+ if (g_isEnhanced)
+ {
+ address = MemryScan::PatternScanner::FindPattern("7d ? 45 89 c6 48 89 d3");
+ if (address != null)
+ {
+ SetNmBoolAddress = reinterpret_cast(address - 20);
+ }
+ }
+ else
+ {
+ address = MemryScan::PatternScanner::FindPattern("\x48\x89\x5C\x24\x00\x57\x48\x83\xEC\x20\x48\x8B\xD9\x48\x63\x49\x0C\x41\x8A\xF8", "xxxx?xxxxxxxxxxxxxxx");
+ if (address != null)
+ {
+ SetNmBoolAddress = reinterpret_cast(address);
+ }
+ }
+
+ if (g_isEnhanced)
+ {
+ address = MemryScan::PatternScanner::FindPattern("41 56 56 57 53 48 83 ec ? 0f 29 74 24 20 48 89 ce 48 63 79");
+ }
+ else {
+ address = MemryScan::PatternScanner::FindPattern("\x40\x53\x48\x83\xEC\x30\x48\x8B\xD9\x48\x63\x49\x0C", "xxxxxxxxxxxxx");
+ }
+ if (address != null)
+ {
+ SetNmFloatAddress = reinterpret_cast(address);
+ }
+
+ if (g_isEnhanced)
+ {
+ address = MemryScan::PatternScanner::FindPattern("41 56 56 57 53 48 83 ec ? 48 89 ce 48 63 79");
+ if (address != null)
+ {
+ SetNmStringAddress = reinterpret_cast(address);
+ }
+ }
+ else {
+ address = MemryScan::PatternScanner::FindPattern("\x57\x48\x83\xEC\x20\x48\x8B\xD9\x48\x63\x49\x0C\x49\x8B\xE8", "xxxxxxxxxxxxxxx");
+ if (address != null)
+ {
+ SetNmStringAddress = reinterpret_cast(address - 15);
+ }
+ }
+
+ if (g_isEnhanced)
+ {
+ address = MemryScan::PatternScanner::FindPattern("0f 29 7c 24 30 0f 29 74 24 20 48 89 ce 48 63 79");
+ if (address != null)
+ {
+ SetNmVec3Address = reinterpret_cast(address - 15);
+ }
+ }
+ else
+ {
+ address = MemryScan::PatternScanner::FindPattern("\x40\x53\x48\x83\xEC\x40\x48\x8B\xD9\x48\x63\x49\x0C", "xxxxxxxxxxxxx");
+ if (address != null)
+ {
+ SetNmVec3Address = reinterpret_cast(address);
+ }
+ }
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("83 fe ? 75 ? b8 ? ? ? ? 48 8d 0d");
+ if (address)
+ {
+ checkpointPoolAddress = reinterpret_cast(*reinterpret_cast(address + 13) + address + 17);
+ }
+ address = MemryScan::PatternScanner::FindPattern("48 83 ec ? e8 ? ? ? ? 48 85 c0 74 ? e8 ? ? ? ? 48 8b 80");
+ if (address)
+ {
+ CheckpointBaseAddr = reinterpret_cast(address);
+ }
+ address = MemryScan::PatternScanner::FindPattern("74 ? 66 83 78 ? ? 75 ? 48 63 78");
+ if (address)
+ {
+ CheckpointHandleAddr = reinterpret_cast(*reinterpret_cast(address - 7) + address - 3);
+ }
+ }
+ else {
+ address = FindPattern("\x8A\x4C\x24\x60\x8B\x50\x10\x44\x8A\xCE", "xxxxxxxxxx");
+ CheckpointBaseAddr = reinterpret_cast(*reinterpret_cast(address - 19) + address - 15);
+ CheckpointHandleAddr = reinterpret_cast(*reinterpret_cast(address - 9) + address - 5);
+ checkpointPoolAddress = reinterpret_cast(*reinterpret_cast(address + 17) + address + 21);
+ }
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("e8 ? ? ? ? 41 80 bd ? ? ? ? ? 45 0f b7 85");
+ _getHashKey = reinterpret_cast(*reinterpret_cast(address + 1) + address + 5);
+ }
+ else {
+ address = FindPattern("\x48\x8B\x0B\x33\xD2\xE8\x00\x00\x00\x00\x89\x03", "xxxxxx????xx");
+ _getHashKey = reinterpret_cast(*reinterpret_cast(address + 6) + address + 10);
+ }
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("89 c8 48 8d 0d ? ? ? ? f3 0f 10 04 81 f3 0f 11 05");
+ if (address)
+ {
+ _readWorldGravityAddress = reinterpret_cast(*reinterpret_cast(address + 18) + address + 22);
+ _writeWorldGravityAddress = reinterpret_cast(*reinterpret_cast(address + 5) + address + 9);
+ }
+ }
+ else {
+ address = FindPattern("\x48\x63\xC1\x48\x8D\x0D\x00\x00\x00\x00\xF3\x0F\x10\x04\x81\xF3\x0F\x11\x05\x00\x00\x00\x00", "xxxxxx????xxxxxxxxx????");
+ if (address) {
+ _writeWorldGravityAddress = reinterpret_cast(*reinterpret_cast(address + 6) + address + 10);
+ _readWorldGravityAddress = reinterpret_cast(*reinterpret_cast(address + 19) + address + 23);
+ }
+ }
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("89 0d ? ? ? ? 48 8d 0d ? ? ? ? e8 ? ? ? ? 84 c0");
+ if (address)
+ {
+ _cursorSpriteAddr = reinterpret_cast(*reinterpret_cast(address + 2) + address + 6);
+ }
+ }
+ else {
+ address = FindPattern("\x74\x11\x8B\xD1\x48\x8D\x0D\x00\x00\x00\x00\x45\x33\xC0", "xxxxxxx????xxx");
+ _cursorSpriteAddr = reinterpret_cast(*reinterpret_cast(address - 4) + address);
+ }
+
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("88 05 ? ? ? ? e8 ? ? ? ? 88 05 ? ? ? ? e8");
+ if (address)
+ {
+ address = (*reinterpret_cast(address + 18) + address + 22);
+ _gamePlayCameraAddr = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+ }
+ }
+ else {
+ address = FindPattern("\x48\x8B\xC7\xF3\x0F\x10\x0D", "xxxxxxx") - 0x1D;
+ address = address + *reinterpret_cast(address) + 4;
+ _gamePlayCameraAddr = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+ }
+
+
+
+ // Bypass model requests block
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("44 0f b6 b4 24 ? ? ? ? 41 20 f6");
+ if (address) memset(reinterpret_cast(address - 20), 0x90, 20);
+ }
+ else {
+ // new pattern that lands outside of the patched bytes, to ensure compatibility with other mods that patch them.
+ address = MemryScan::PatternScanner::FindPattern("45 84 e4 74 ? e8 ? ? ? ? 48 85 c0");
+ if (address) memset(reinterpret_cast(address - 24), 0x90, 24);
}
- UINT64 GetResult()
- {
- return _res;
+ // Bypass is player model allowed to spawn checks
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("74 ? e8 ? ? ? ? 48 8b b0");
+ if (address) memset(reinterpret_cast(address + 28), 0x90, 6);
+ }
+ else {
+ // new pattern that lands outside of the patched bytes, to ensure compatibility with other mods that patch them.
+ address = MemryScan::PatternScanner::FindPattern("40 53 48 83 ec ? e8 ? ? ? ? 48 8b d8 48 85 c0 74 ? 48 8b 10 48 8b c8 ff 52");
+ if (address) memset(reinterpret_cast(address + 28), 0x90, 4);
}
-private:
- func _toRun;
- UINT64 _arg;
- UINT64 _res;
-};
+ //GetModelInfo
-void GTAmemory::Init()
-{
- UINT64 address;
+ if (g_isEnhanced) {
+ address = MemryScan::PatternScanner::FindPattern("45 85 d2 74 ? 49 89 d0 4c 8b 1d");
- // Get relative address and add it to the instruction address.
- // 3 bytes equal the size of the opcode and its first argument. 7 bytes are the length of opcode and all its parameters.
+ if (address) {
+ address = address - 8;
+ }
+ else {
+ addlog(ige::LogType::LOG_ERROR, "Couldn't find GetModelInfo (Enhanced)", __FILENAME__);
+ }
+ }
+ else {
+ if (getGameVersion() <= 57) {
+ address = FindPattern(
+ "\x0F\xB7\x05\x00\x00\x00\x00"
+ "\x45\x33\xC9\x4C\x8B\xDA\x66\x85\xC0"
+ "\x0F\x84\x00\x00\x00\x00"
+ "\x44\x0F\xB7\xC0\x33\xD2\x8B\xC1\x41\xF7\xF0\x48"
+ "\x8B\x05\x00\x00\x00\x00"
+ "\x4C\x8B\x14\xD0\xEB\x09\x41\x3B\x0A\x74\x54",
+ "xxx????"
+ "xxxxxxxxx"
+ "xx????"
+ "xxxxxxxxxxxx"
+ "xx????"
+ "xxxxxxxxxxx");
+
+ if (!address) {
+ addlog(ige::LogType::LOG_ERROR, "Couldn't find GetModelInfo", __FILENAME__);
+ }
+ }
+ else {
+ address = FindPattern("\xEB\x09\x41\x3B\x0A\x74\x54", "xxxxxxx");
- address = FindPattern("\x4C\x8D\x05\x00\x00\x00\x00\x0F\xB7\xC1", "xxx????xxx");
- if (address) _blipList = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+ if (address) {
+ address = address - 0x2C;
+ }
+ else {
+ addlog(ige::LogType::LOG_ERROR, "Couldn't find GetModelInfo (v58+)", __FILENAME__);
+ }
+ }
+ }
+ if (address) {
+ GetModelInfo = (GetModelInfo_t)(address);
+ }
- address = FindPattern("\x48\x89\x5C\x24\x08\x48\x89\x6C\x24\x18\x89\x54\x24\x10\x56\x57\x41\x56\x48\x83\xEC\x20", "xxxxxxxxxxxxxxxxxxxxxx");
- _gxtLabelFromHashFuncAddr = reinterpret_cast(address);
- address = FindPattern("\x84\xC0\x74\x34\x48\x8D\x0D\x00\x00\x00\x00\x48\x8B\xD3", "xxxxxxx????xxx");
- _gxtLabelFromHashAddr1 = *reinterpret_cast(address + 7) + address + 11;
+ _SpSnow = SpSnow();
+ addlog(ige::LogType::LOG_INIT, "GTAMemory Init Done", __FILENAME__);
+}
- if (GTAmemory::GetGameVersion() >= eGameVersion::VER_1_0_1290_1_STEAM)
- {
- address = FindPattern("\xE8\x00\x00\x00\x00\x48\x8B\xD8\x48\x85\xC0\x74\x2E\x48\x83\x3D", "x????xxxxxxxxxxx");
- _entityAddressFunc = reinterpret_cast(*reinterpret_cast(address + 1) + address + 5);
- address = FindPattern("\xB2\x01\xE8\x00\x00\x00\x00\x48\x85\xC0\x74\x1C\x8A\x88", "xxx????xxxxxxx");
- _playerAddressFunc = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
+void GTAmemory::InitEnhancedPools() {
+ if (g_isEnhanced) {
- address = FindPattern("\x48\x8B\xDA\xE8\x00\x00\x00\x00\xF3\x0F\x10\x44\x24", "xxxx????xxxxx");
- _entityPositionFunc = reinterpret_cast(address - 6);
- address = FindPattern("\x0F\x85\x00\x00\x00\x00\x48\x8B\x4B\x20\xE8\x00\x00\x00\x00\x48\x8B\xC8", "xx????xxxxx????xxx");
- _entityModel1Func = reinterpret_cast(*reinterpret_cast(address + 11) + address + 15);
- address = FindPattern("\x45\x33\xC9\x3B\x05", "xxxxx");
- _entityModel2Func = reinterpret_cast(address - 0x46);
+ // In Enhanced, the pools are encrypted/obfuscated. Instead of having their pointer stored somewhere, it is constructed at init from different values,
+ // using bit operations. The problem is that menyoo is loaded a bit too early for the initialization to have happened, and so we can't retrieve those values
+ // during GTAmemory init. For that reason, we leave that to the very end, and we keep waiting until the game has started (GameState == 0 (PLAYING)).
+ // This was not a problem with SHVDNE, since NativeMemroy init is only done after the game has started.
- address = FindPattern("\x4C\x8B\x05\x00\x00\x00\x00\x40\x8A\xF2\x8B\xE9", "xxx????xxxxx");
- _pickupObjectPoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
- }
- else
- {
- address = FindPattern("\x33\xFF\xE8\x00\x00\x00\x00\x48\x85\xC0\x74\x58", "xxx????xxxxx");
- _entityAddressFunc = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
- address = FindPattern("\xB2\x01\xE8\x00\x00\x00\x00\x33\xC9\x48\x85\xC0\x74\x3B", "xxx????xxxxxxx");
- _playerAddressFunc = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
-
- address = FindPattern("\x48\x8B\xC8\xE8\x00\x00\x00\x00\xF3\x0F\x10\x54\x24\x00\xF3\x0F\x10\x4C\x24\x00\xF3\x0F\x10", "xxxx????xxxxx?xxxxx?xxx");
- _entityPositionFunc = reinterpret_cast(*reinterpret_cast(address + 4) + address + 8);
- address = FindPattern("\x25\xFF\xFF\xFF\x3F\x89\x44\x24\x38\xE8\x00\x00\x00\x00\x48\x85\xC0\x74\x03", "xxxxxxxxxx????xxxxx");
- _entityModel1Func = reinterpret_cast(*reinterpret_cast(address - 61) + address - 57);
- _entityModel2Func = reinterpret_cast(*reinterpret_cast(address + 10) + address + 14);
-
- address = FindPattern("\x8B\xF0\x48\x8B\x05\x00\x00\x00\x00\xF3\x0F\x59\xF6", "xxxxx????xxxx");
- _pickupObjectPoolAddress = reinterpret_cast(*reinterpret_cast(address + 5) + address + 9);
- }
-
- address = FindPattern("\x74\x21\x48\x8B\x48\x20\x48\x85\xC9\x74\x18\x48\x8B\xD6\xE8", "xxxxxxxxxxxxxxx") - 10;
- _ptfxAddressFunc = reinterpret_cast(*reinterpret_cast(address) + address + 4);
-
- address = FindPattern("\x48\xF7\xF9\x49\x8B\x48\x08\x48\x63\xD0\xC1\xE0\x08\x0F\xB6\x1C\x11\x03\xD8", "xxxxxxxxxxxxxxxxxxx");
- _addEntityToPoolFunc = reinterpret_cast(address - 0x68);
-
- address = FindPattern("\x4C\x8B\x0D\x00\x00\x00\x00\x44\x8B\xC1\x49\x8B\x41\x08", "xxx????xxxxxxx");
- _entityPoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
- address = FindPattern("\x48\x8B\x05\x00\x00\x00\x00\xF3\x0F\x59\xF6\x48\x8B\x08", "xxx????xxxxxxx");
- _vehiclePoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
- address = FindPattern("\x48\x8B\x05\x00\x00\x00\x00\x41\x0F\xBF\xC8\x0F\xBF\x40\x10", "xxx????xxxxxxxx");
- _pedPoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
- address = FindPattern("\x48\x8B\x05\x00\x00\x00\x00\x8B\x78\x10\x85\xFF", "xxx????xxxxx");
- _objectPoolAddress = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
-
- CreateNmMessageFunc = FindPattern("\x33\xDB\x48\x89\x1D\x00\x00\x00\x00\x85\xFF", "xxxxx????xx") - 0x42;
- GiveNmMessageFunc = FindPattern("\x0F\x84\x00\x00\x00\x00\x48\x8B\x01\xFF\x90\x00\x00\x00\x00\x41\x3B\xC5", "xx????xxxxx????xxx") - 0x78;
- address = FindPattern("\x48\x89\x5C\x24\x00\x57\x48\x83\xEC\x20\x48\x8B\xD9\x48\x63\x49\x0C\x41\x8A\xF8", "xxxx?xxxxxxxxxxxxxxx");
- SetNmBoolAddress = reinterpret_cast(address);
- address = FindPattern("\x40\x53\x48\x83\xEC\x30\x48\x8B\xD9\x48\x63\x49\x0C", "xxxxxxxxxxxxx");
- SetNmFloatAddress = reinterpret_cast(address);
- address = FindPattern("\x48\x89\x5C\x24\x00\x57\x48\x83\xEC\x20\x48\x8B\xD9\x48\x63\x49\x0C\x41\x8B\xF8", "xxxx?xxxxxxxxxxxxxxx");
- SetNmIntAddress = reinterpret_cast(address);
- address = FindPattern("\x57\x48\x83\xEC\x20\x48\x8B\xD9\x48\x63\x49\x0C\x49\x8B\xE8", "xxxxxxxxxxxxxxx") - 15;
- SetNmStringAddress = reinterpret_cast(address);
- address = FindPattern("\x40\x53\x48\x83\xEC\x40\x48\x8B\xD9\x48\x63\x49\x0C", "xxxxxxxxxxxxx");
- SetNmVec3Address = reinterpret_cast(address);
-
- address = FindPattern("\x8A\x4C\x24\x60\x8B\x50\x10\x44\x8A\xCE", "xxxxxxxxxx");
- CheckpointBaseAddr = reinterpret_cast(*reinterpret_cast(address - 19) + address - 15);
- CheckpointHandleAddr = reinterpret_cast(*reinterpret_cast(address - 9) + address - 5);
- checkpointPoolAddress = reinterpret_cast(*reinterpret_cast(address + 17) + address + 21);
-
- address = FindPattern("\x48\x8B\x0B\x33\xD2\xE8\x00\x00\x00\x00\x89\x03", "xxxxxx????xx");
- _getHashKey = reinterpret_cast(*reinterpret_cast(address + 6) + address + 10);
-
- address = FindPattern("\x48\x63\xC1\x48\x8D\x0D\x00\x00\x00\x00\xF3\x0F\x10\x04\x81\xF3\x0F\x11\x05\x00\x00\x00\x00", "xxxxxx????xxxxxxxxx????");
- _writeWorldGravityAddress = reinterpret_cast(*reinterpret_cast(address + 6) + address + 10);
- _readWorldGravityAddress = reinterpret_cast(*reinterpret_cast(address + 19) + address + 23);
-
- address = FindPattern("\x74\x11\x8B\xD1\x48\x8D\x0D\x00\x00\x00\x00\x45\x33\xC0", "xxxxxxx????xxx");
- _cursorSpriteAddr = reinterpret_cast(*reinterpret_cast(address - 4) + address);
-
- address = FindPattern("\x48\x8B\xC7\xF3\x0F\x10\x0D", "xxxxxxx") - 0x1D;
- address = address + *reinterpret_cast(address) + 4;
- _gamePlayCameraAddr = reinterpret_cast(*reinterpret_cast(address + 3) + address + 7);
- address = FindPattern("\x48\x8B\xC8\xEB\x02\x33\xC9\x48\x85\xC9\x74\x26", "xxxxxxxxxxxx") - 9;
- _cameraPoolAddress = reinterpret_cast(*reinterpret_cast(address) + address + 4);
+ UINT64 address = MemryScan::PatternScanner::FindPattern("83 f9 ? 74 ? 41 89 c8");
+ if (address)
+ {
+ bool isInitialized = (*(byte*)(*(int*)(address + 11) + address + 15) & 1) != 0;
+ UINT64 firstValue = *(UINT64*)(*(int*)(address + 29) + address + 33);
+ UINT64 secondValue = *(UINT64*)(*(int*)(address + 18) + address + 22);
+ auto firstRol = *(byte*)(address + 25); // 0x1E
+ auto secondRol = *(byte*)(address + 39); // 0x20
+ auto andValue = *(byte*)(address + 42); // 0x1F
+ auto addValue = *(byte*)(address + 45); // 2
+ GTAmemory::_entityPoolAddress = (UINT64*)0;
+ if (isInitialized)
+ {
+ auto rcx = secondValue;
+ rcx = Rol(rcx, firstRol);
+ auto rdx = firstValue;
+ rdx = rdx ^ rcx;
+ rdx = Rol(rdx, secondRol);
+ auto cl = (byte)(rcx & 0xFF);
+ cl = (byte)(cl & andValue);
+ cl = (byte)(cl + addValue);
+ rdx = Rol(rdx, cl);
+ rdx = ~rdx;
+ GTAmemory::_entityPoolAddress = (UINT64*)rdx;
+ }
- // Bypass model requests block
- address = MemryScan::PatternScanner::FindPattern("48 85 C0 0F 84 ? ? ? ? 8B 48 50");
- if (address) memset(reinterpret_cast(address), 0x90, 24);
+ }
- // Bypass is player model allowed to spawn checks
- address = MemryScan::PatternScanner::FindPattern("48 8B C8 FF 52 30 84 C0 74 05 48");
- if (address) memset(reinterpret_cast(address + 0x8), 0x90, 2);
+ address = MemryScan::PatternScanner::FindPattern("48 83 ec ? 89 ce 0f b6 05");
+ if (address)
+ {
+ bool isInitialized = (*(byte*)(*(int*)(address + 9) + address + 13) & 1) != 0;
+ uint64_t firstValue = *(uint64_t*)(*(int*)(address + 27) + address + 31);
+ uint64_t secondValue = *(uint64_t*)(*(int*)(address + 16) + address + 20);
+ auto firstRol = *(byte*)(address + 23); // 0x1F
+ auto secondRol = *(byte*)(address + 49); // 0x20
+ auto andValue = *(byte*)(address + 36); // 0x1F
+ auto addValue = *(byte*)(address + 39); // 5
+ GTAmemory::_pickupObjectPoolAddress = (uint64_t*)0UL;
+ if (isInitialized)
+ {
+ auto rcx = secondValue;
+ rcx = Rol(rcx, firstRol);
+ auto rax = firstValue;
+ rax = rax ^ rcx;
+ auto cl = (byte)(rcx & 0xFF);
+ cl = (byte)(cl & andValue);
+ cl = (byte)(cl + addValue);
+ rax = Rol(rax, cl);
+ rax = Rol(rax, secondRol);
+ rax = ~rax;
+ GTAmemory::_pickupObjectPoolAddress = (uint64_t*)rax;
+ }
+ }
- //GetModelInfo
- if (getGameVersion() <= 57) {
- address = FindPattern(
- "\x0F\xB7\x05\x00\x00\x00\x00"
- "\x45\x33\xC9\x4C\x8B\xDA\x66\x85\xC0"
- "\x0F\x84\x00\x00\x00\x00"
- "\x44\x0F\xB7\xC0\x33\xD2\x8B\xC1\x41\xF7\xF0\x48"
- "\x8B\x05\x00\x00\x00\x00"
- "\x4C\x8B\x14\xD0\xEB\x09\x41\x3B\x0A\x74\x54",
- "xxx????"
- "xxxxxxxxx"
- "xx????"
- "xxxxxxxxxxxx"
- "xx????"
- "xxxxxxxxxxx");
-
- if (!address) {
- addlog(ige::LogType::LOG_ERROR, "Couldn't find GetModelInfo", __FILENAME__);
+ address = MemryScan::PatternScanner::FindPattern("48 83 ec ? 83 3d ? ? ? ? ? 0f 84 ? ? ? ? 0f b6 05");
+ if (address) {
+ bool isInitialized = (*(byte*)(*(int*)(address + 20) + address + 24) & 1) != 0;
+ UINT64 firstValue = *(UINT64*)(*(int*)(address + 38) + address + 42);
+ UINT64 secondValue = *(UINT64*)(*(int*)(address + 27) + address + 31);
+ auto firstRol = *(byte*)(address + 34); // 0x1E
+ auto secondRol = *(byte*)(address + 48); // 0x20
+ auto andValue = *(byte*)(address + 51); // 0x1F
+ auto addValue = *(byte*)(address + 54); // 2
+ GTAmemory::_pedPoolAddress = (UINT64*)0UL;
+ if (isInitialized)
+ {
+ auto rcx = secondValue;
+ rcx = Rol(rcx, firstRol);
+ auto rax = firstValue;
+ rax = rax ^ rcx;
+ rax = Rol(rax, secondRol);
+ auto cl = (byte)(rcx & 0xFF);
+ cl = (byte)(cl & andValue);
+ cl = (byte)(cl + addValue);
+ rax = Rol(rax, cl);
+ rax = ~rax;
+ GTAmemory::_pedPoolAddress = (UINT64*)rax;
+ }
}
- }
- else {
- address = FindPattern("\xEB\x09\x41\x3B\x0A\x74\x54", "xxxxxxx");
- if (!address) {
- addlog(ige::LogType::LOG_ERROR, "Couldn't find GetModelInfo (v58+)", __FILENAME__);
+
+ address = MemryScan::PatternScanner::FindPattern("53 48 81 ec ? ? ? ? 0f 29 b4 ? ? ? ? ? 48 89 ce 0f b6 05");
+ if (address)
+ {
+ bool isInitialized = (*(byte*)(*(int*)(address + 22) + address + 26) & 1) != 0;
+ UINT64 firstValue = *(UINT64*)(*(int*)(address + 40) + address + 44);
+ UINT64 secondValue = *(UINT64*)(*(int*)(address + 29) + address + 33);
+ auto firstRol = *(byte*)(address + 36); // 0x1E
+ auto secondRol = *(byte*)(address + 59); // 0x20
+ auto andValue = *(byte*)(address + 49); // 0x1F
+ auto addValue = *(byte*)(address + 52); // 3
+ GTAmemory::_objectPoolAddress = (UINT64*)0UL;
+ if (isInitialized)
+ {
+ auto rcx = secondValue;
+ rcx = Rol(rcx, firstRol);
+ auto rdx = firstValue;
+ rdx = rdx ^ rcx;
+ auto cl = (byte)(rcx & 0xFF);
+ cl = (byte)(cl & andValue);
+ cl = (byte)(cl + addValue);
+ rdx = Rol(rdx, cl);
+ rdx = Rol(rdx, secondRol);
+ rdx = ~rdx;
+ GTAmemory::_objectPoolAddress = (UINT64*)rdx;
+ }
+ }
+
+ address = MemryScan::PatternScanner::FindPattern("48 89 cb 48 8b 41 ? 8b 10 f2 0f 10 3d");
+ if (address)
+ {
+ // The pattern is inside a function, which calls a function, which accesses the pool.
+ address = (*reinterpret_cast(address + 34) + address + 38);
+ if (address)
+ {
+ bool isInitialized = (*reinterpret_cast(*reinterpret_cast(address + 3) + address + 7) & 1) != 0;
+ UINT64 firstValue = *reinterpret_cast(*reinterpret_cast(address + 21) + address + 25);
+ UINT64 secondValue = *reinterpret_cast(*reinterpret_cast(address + 10) + address + 14);
+ auto firstRol = *reinterpret_cast(address + 17); // 0x1b
+ auto secondRol = *reinterpret_cast(address + 31); // 0x20
+ auto andValue = *reinterpret_cast(address + 33); // 0x1f
+ auto addValue = *reinterpret_cast(address + 36); // 0x1
+ auto xorValue = *reinterpret_cast(address + 44); // 0x3f
+ GTAmemory::_cameraPoolAddress = (UINT64*)0UL;
+ if (isInitialized)
+ {
+ auto rax = secondValue;
+ rax = Rol(rax, firstRol);
+ auto rsi = firstValue;
+ rsi = rsi ^ rax;
+ rsi = Rol(rsi, secondRol);
+ auto al = (byte)(rax & 0xFF);
+ al = (byte)(al & andValue);
+ rax = (rax & 0xFFFFFFFFFFFFFF00UL) | (UINT64)al;
+ auto ecx = (int)((rax + (UINT64)addValue) & 0xFFFFFFFF);
+ auto rdi = rsi;
+ auto cl = (byte)(ecx & 0xFF);
+ rdi = rdi << cl;
+ al = (byte)(al ^ xorValue);
+ rax = (rax & 0xFFFFFFFFFFFFFF00UL) | (UINT64)al;
+ auto eax = (int)(rax & 0xFFFFFFFF);
+ ecx = eax;
+ cl = (byte)(ecx & 0xFF);
+ rsi = rsi >> cl;
+ rsi = rsi | rdi;
+ rsi = ~rsi;
+ GTAmemory::_cameraPoolAddress = (UINT64*)rsi;
+ }
+ }
}
address = address - 0x2C;
- }
- GetModelInfo = (GetModelInfo_t)(address);
-
- _SpSnow = SpSnow();
+ addlog(ige::LogType::LOG_TRACE, "Found Pattern: " + std::to_string(address), __FILENAME__);
+ GetModelInfo = (GetModelInfo_t)(address);
+
+ _SpSnow = SpSnow();
+ }
}
+
+
Vector3 GTAmemory::ReadVector3(UINT64 address)
{
const float* data = (float*)address;
@@ -896,52 +1758,85 @@ struct HashNode
};
void GTAmemory::GenerateVehicleModelList()
{
+ addlog(ige::LogType::LOG_DEBUG, "Generating Vehicle Model List. isEnhanced = " + std::to_string(g_isEnhanced), __FILENAME__);
+
+ int classOffset;
+ uintptr_t address;
+ HashNode** HashMap;
//Zorg
- uintptr_t address = FindPattern("\x66\x81\xF9\x00\x00\x74\x10\x4D\x85\xC0", "xxx??xxxxx");
- if (address)
+ if (g_isEnhanced) {
+ addlog(ige::LogType::LOG_TRACE, "Scanning Enhanced Address", __FILENAME__);
+ address = MemryScan::PatternScanner::FindPattern("0f b6 88 ? ? ? ? 83 e1 ? e9");
+ if (address)
+ {
+ addlog(ige::LogType::LOG_TRACE, "Found Address, scanning for Hashes", __FILENAME__);
+ classOffset = *(uint*)(address + 3);
+
+ address = MemryScan::PatternScanner::FindPattern("74 ? 49 89 d0 4c 8b 1d");
+ if (address)
+ {
+ modelHashTable = *reinterpret_cast(*(int*)(address + 8) + address + 12);
+ modelHashEntries = *reinterpret_cast(address + *(int*)(address - 7) - 3);
+ // Pattern scan to avoid having offsets accross labels.
+ address = MemryScan::PatternScanner::FindPattern("\x3B\x05\x00\x00\x00\x00\x7D\x00\x48\x8B\x0D", "xx????x?xxx", address, 200); // TODO: use the findpattern with legacy patterns, because it supports a start address.
+ modelNum1 = *reinterpret_cast(*(int*)(address + 2) + address + 6);
+ modelNum2 = *reinterpret_cast(*(int*)(address + 11) + address + 15);
+ modelNum3 = *reinterpret_cast(*(int*)(address + 48) + address + 52);
+ modelNum4 = *reinterpret_cast(*(int*)(address + 33) + address + 37);
+ }
+ }
+ }
+ else {
+ address = FindPattern("\x66\x81\xF9\x00\x00\x74\x10\x4D\x85\xC0", "xxx??xxxxx");
+ if (address) {
+ addlog(ige::LogType::LOG_TRACE, "Found Address, scanning for Hashes", __FILENAME__);
+ address = address - 0x21;
+ //UINT64 baseFuncAddr = *reinterpret_cast(address - 0x21) + address - 0x1D;
+ UINT64 baseFuncAddr = address + *reinterpret_cast(address) + 0x4;
+ //int classOffset = *reinterpret_cast(address + 0x10);
+ classOffset = *reinterpret_cast(address + 0x31);
+ modelHashEntries = *reinterpret_cast(baseFuncAddr + *reinterpret_cast(baseFuncAddr + 3) + 7);
+ modelNum1 = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x52) + baseFuncAddr + 0x56); //cmp
+ modelNum2 = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x63) + baseFuncAddr + 0x67); //mov
+ modelNum3 = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x7A) + baseFuncAddr + 0x7E); //mul
+ modelNum4 = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x81) + baseFuncAddr + 0x85); //add
+
+ modelHashTable = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x24) + baseFuncAddr + 0x28);
+ }
+ }
+
+ addlog(ige::LogType::LOG_TRACE, "Patterns Scanned Success", __FILENAME__);
+ HashMap = reinterpret_cast(modelHashTable);
+ //I know 0x20 items are defined but there are only 0x16 vehicle classes.
+ //But keeping it at 0x20 is just being safe as the & 0x1F in theory supports up to 0x20
+ auto& vehicleHashes = GTAmemory::vehicleModels;
+ for (auto& vec : vehicleHashes)
+ vec.clear();
+ for (int i = 0; i < modelHashEntries; i++)
{
- address = address - 0x21;
- //UINT64 baseFuncAddr = *reinterpret_cast(address - 0x21) + address - 0x1D;
- UINT64 baseFuncAddr = address + *reinterpret_cast(address) + 0x4;
- //int classOffset = *reinterpret_cast(address + 0x10);
- int classOffset = *reinterpret_cast(address + 0x31);
- modelHashEntries = *reinterpret_cast(baseFuncAddr + *reinterpret_cast(baseFuncAddr + 3) + 7);
- modelNum1 = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x52) + baseFuncAddr + 0x56); //cmp
- modelNum2 = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x63) + baseFuncAddr + 0x67); //mov
- modelNum3 = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x7A) + baseFuncAddr + 0x7E); //mul
- modelNum4 = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x81) + baseFuncAddr + 0x85); //add
-
- modelHashTable = *reinterpret_cast(*reinterpret_cast(baseFuncAddr + 0x24) + baseFuncAddr + 0x28);
- HashNode** HashMap = reinterpret_cast(modelHashTable);
- //I know 0x20 items are defined but there are only 0x16 vehicle classes.
- //But keeping it at 0x20 is just being safe as the & 0x1F in theory supports up to 0x20
- auto& vehicleHashes = GTAmemory::vehicleModels;
- for (auto& vec : vehicleHashes)
- vec.clear();
- for (int i = 0; i < modelHashEntries; i++)
- {
- for (HashNode* cur = HashMap[i]; cur; cur = cur->next)
+ for (HashNode* cur = HashMap[i]; cur; cur = cur->next)
+ {
+ UINT16 data = cur->data;
+ if ((int)data < modelNum1 && bittest(*reinterpret_cast(modelNum2 + (4 * data >> 5)), data & 0x1F))
{
- UINT16 data = cur->data;
- if ((int)data < modelNum1 && bittest(*reinterpret_cast(modelNum2 + (4 * data >> 5)), data & 0x1F))
+ UINT64 addr1 = modelNum4 + modelNum3 * data;
+ if (addr1)
{
- UINT64 addr1 = modelNum4 + modelNum3 * data;
- if (addr1)
+ UINT64 addr2 = *reinterpret_cast