Permalink
Name already in use
A tag already exists with the provided branch name. Many Git commands accept both tag and branch names, so creating this branch may cause unexpected behavior. Are you sure you want to create this branch?
scripthookvdotnet/source/core/NativeMemory.cpp
Go to fileThis commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
1080 lines (944 sloc)
34.5 KB
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| #include "NativeMemory.hpp" | |
| #include "ScriptDomain.hpp" | |
| #include <Main.h> | |
| #include <Psapi.h> | |
| using namespace System; | |
| using namespace System::Collections::Generic; | |
| using namespace System::Collections::ObjectModel; | |
| using namespace System::Runtime::InteropServices; | |
| const char* const _cellEmailBcon = "CELL_EMAIL_BCON"; | |
| const char* const _string = "STRING"; | |
| const char* const _nullStr = ""; | |
| namespace GTA | |
| { | |
| namespace Native | |
| { | |
| namespace | |
| { | |
| [StructLayout(LayoutKind::Explicit)] | |
| private value class EntityPool | |
| { | |
| public: | |
| [FieldOffset(0x10)]UInt32 num1; | |
| [FieldOffset(0x20)]UInt32 num2; | |
| inline bool Full() | |
| { | |
| return num1 - (num2 & 0x3FFFFFFF) <= 256; | |
| } | |
| }; | |
| [StructLayout(LayoutKind::Explicit)] | |
| private value class VehiclePool | |
| { | |
| public: | |
| [FieldOffset(0x00)]UInt64 *poolAddress; | |
| [FieldOffset(0x08)]UInt32 size; | |
| [FieldOffset(0x30)]UInt32* bitArray; | |
| [FieldOffset(0x60)]UInt32 itemCount; | |
| inline bool isValid(UInt32 i) | |
| { | |
| return (bitArray[i >> 5] >> (i & 0x1F)) & 1; | |
| } | |
| inline UInt64 getAddress(UInt32 i) | |
| { | |
| return poolAddress[i]; | |
| } | |
| }; | |
| [StructLayout(LayoutKind::Explicit)] | |
| private value class GenericPool{ | |
| public: | |
| [FieldOffset(0x00)] UInt64 poolStartAddress; | |
| [FieldOffset(0x08)] Byte* byteArray; | |
| [FieldOffset(0x10)] UInt32 size; | |
| [FieldOffset(0x14)] 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); | |
| } | |
| }; | |
| private ref struct EntityPoolTask : IScriptTask | |
| { | |
| enum class Type | |
| { | |
| Ped=1, | |
| Object=2, | |
| Vehicle=4, | |
| PickupObject=8 | |
| }; | |
| EntityPoolTask(Type type) : _type(type) { } | |
| inline bool CheckEntity(uintptr_t address) | |
| { | |
| if (_posCheck) | |
| { | |
| float position[3]; | |
| MemoryAccess::_entityPositionFunc(address, position); | |
| if(_position.DistanceToSquared(Math::Vector3(position[0], position[1], position[2])) > _radiusSquared) | |
| { | |
| return false; | |
| } | |
| } | |
| if (_modelCheck) | |
| { | |
| UINT32 v0 = *reinterpret_cast<UINT32 *>(MemoryAccess::_entityModel1Func(*reinterpret_cast<UINT64 *>(address + 32))); | |
| UINT32 v1 = v0 & 0xFFFF; | |
| UINT32 v2 = ((v1 ^ v0) & 0x0FFF0000 ^ v1) & 0xDFFFFFFF; | |
| UINT32 v3 = ((v2 ^ v0) & 0x10000000 ^ v2) & 0x3FFFFFFF; | |
| const uintptr_t v5 = MemoryAccess::_entityModel2Func(reinterpret_cast<uintptr_t>(&v3)); | |
| if (!v5) | |
| { | |
| return false; | |
| } | |
| for each(int hash in _modelHashes) | |
| { | |
| if (*reinterpret_cast<int *>(v5 + 24) == hash) | |
| { | |
| return true; | |
| } | |
| } | |
| return false; | |
| } | |
| return true; | |
| } | |
| virtual void Run() | |
| { | |
| if(*MemoryAccess::_entityPoolAddress == 0) | |
| { | |
| return; | |
| } | |
| EntityPool* entityPool = reinterpret_cast<EntityPool*>(*MemoryAccess::_entityPoolAddress); | |
| if(_type.HasFlag(Type::Vehicle)) | |
| { | |
| if(*MemoryAccess::_vehiclePoolAddress) | |
| { | |
| VehiclePool* vehiclePool = *reinterpret_cast<VehiclePool**>(*MemoryAccess::_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->Add(MemoryAccess::_addEntityToPoolFunc(address)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if(_type.HasFlag(Type::Ped)) | |
| { | |
| if(*MemoryAccess::_pedPoolAddress) | |
| { | |
| GenericPool* pedPool = reinterpret_cast<GenericPool*>(*MemoryAccess::_pedPoolAddress); | |
| 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->Add(MemoryAccess::_addEntityToPoolFunc(address)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if(_type.HasFlag(Type::Object)) | |
| { | |
| if(*MemoryAccess::_objectPoolAddress) | |
| { | |
| GenericPool* propPool = reinterpret_cast<GenericPool*>(*MemoryAccess::_objectPoolAddress); | |
| 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->Add(MemoryAccess::_addEntityToPoolFunc(address)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if(_type.HasFlag(Type::PickupObject)) | |
| { | |
| if(*MemoryAccess::_pickupObjectPoolAddress) | |
| { | |
| GenericPool* pickupPool = reinterpret_cast<GenericPool*>(*MemoryAccess::_pickupObjectPoolAddress); | |
| 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(_position.DistanceToSquared(Math::Vector3(position[0], position[1], position[2])) > _radiusSquared) | |
| { | |
| continue; | |
| } | |
| } | |
| _handles->Add(MemoryAccess::_addEntityToPoolFunc(address)); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| Type _type; | |
| List<int> ^_handles = gcnew List<int>(); | |
| bool _posCheck, _modelCheck; | |
| Math::Vector3 _position; | |
| float _radiusSquared; | |
| array<int> ^_modelHashes; | |
| }; | |
| private ref struct EuphoriaMessageTask : IScriptTask | |
| { | |
| EuphoriaMessageTask(int target, String ^message, Dictionary<String ^, Object ^> ^arguments) : _targetHandle(target), _message(message), _arguments(arguments) { } | |
| virtual void Run() | |
| { | |
| __int64 NativeFunc = Native::MemoryAccess::CreateNmMessageFunc; | |
| __int64 MessageAddress = reinterpret_cast<__int64(*)(__int64)>(*reinterpret_cast<int*>(NativeFunc + 0x22) + NativeFunc + 0x26)(4632); | |
| if (MessageAddress == 0) | |
| { | |
| return; | |
| } | |
| reinterpret_cast<__int64(*)(__int64, __int64, int)>(*reinterpret_cast<int*>(NativeFunc + 0x3C) + NativeFunc + 0x40)(MessageAddress, MessageAddress + 24, 64); | |
| for each (auto argument in _arguments) | |
| { | |
| IntPtr name = ScriptDomain::CurrentDomain->PinString(argument.Key); | |
| if (argument.Value->GetType() == Boolean::typeid) | |
| { | |
| MemoryAccess::SetNmBoolAddress(MessageAddress, name.ToInt64(), (bool)argument.Value ? 1 : 0); | |
| } | |
| if (argument.Value->GetType() == Int32::typeid) | |
| { | |
| MemoryAccess::SetNmIntAddress(MessageAddress, name.ToInt64(), (int)argument.Value); | |
| } | |
| if (argument.Value->GetType() == Single::typeid) | |
| { | |
| MemoryAccess::SetNmFloatAddress(MessageAddress, name.ToInt64(), (float)argument.Value); | |
| } | |
| if (argument.Value->GetType() == Math::Vector3::typeid) | |
| { | |
| auto value = (Math::Vector3)argument.Value; | |
| MemoryAccess::SetNmVec3Address(MessageAddress, name.ToInt64(), value.X, value.Y, value.Z); | |
| } | |
| if (argument.Value->GetType() == String::typeid) | |
| { | |
| MemoryAccess::SetNmStringAddress(MessageAddress, name.ToInt64(), ScriptDomain::CurrentDomain->PinString((String ^)argument.Value).ToInt64()); | |
| } | |
| } | |
| __int64 BaseFunc = Native::MemoryAccess::GiveNmMessageFunc; | |
| __int64 ByteAddr = *reinterpret_cast<int*>(BaseFunc + 0xBC) + BaseFunc + 0xC0; | |
| __int64 UnkStrAddr = *reinterpret_cast<int*>(BaseFunc + 0xCE) + BaseFunc + 0xD2; | |
| __int64 _PedAddress = Native::MemoryAccess::GetEntityAddress(_targetHandle).ToInt64(); | |
| __int64 PedNmAddress; | |
| bool v5 = false; | |
| __int8 v7; | |
| __int64 v11; | |
| __int64 v12; | |
| if (_PedAddress == 0) | |
| return; | |
| if (*reinterpret_cast<__int64*>(_PedAddress + 48) == 0) | |
| return; | |
| int AddrOff = getGameVersion() < VER_1_0_573_1_NOSTEAM ? 0 : 16; | |
| PedNmAddress = *reinterpret_cast<__int64*>(_PedAddress + 5016 + AddrOff); // | |
| if (*reinterpret_cast<__int64*>(_PedAddress + 48) == PedNmAddress && *reinterpret_cast<float*>(_PedAddress + 5232 + AddrOff) <= *reinterpret_cast<float*>(_PedAddress + 640)) | |
| { | |
| if ((*reinterpret_cast<int(**)(__int64)> (*reinterpret_cast<__int64*>(PedNmAddress) + 152))(PedNmAddress) != -1) | |
| { | |
| if (*(short *)(reinterpret_cast<__int64(*)(__int64)>(*reinterpret_cast<int*>(BaseFunc + 0xA2) + BaseFunc + 0xA6)(*(__int64 *)(*(__int64 *)(_PedAddress + 4208 + AddrOff) + 864)) + 52) == 401) | |
| { | |
| v5 = true; | |
| } | |
| else | |
| { | |
| v7 = *(__int8*)ByteAddr; | |
| if (v7) | |
| { | |
| reinterpret_cast<void(*)(__int64)>(*reinterpret_cast<int*>(BaseFunc + 0xD3) + BaseFunc + 0xD7)(UnkStrAddr); | |
| v7 = *(__int8*)ByteAddr; | |
| } | |
| int count = *reinterpret_cast<int*>(*reinterpret_cast<__int64*>(_PedAddress + 4208 + AddrOff) + 1064); | |
| if (v7) | |
| { | |
| reinterpret_cast<void(*)(__int64)>(*reinterpret_cast<int*>(BaseFunc + 0xF0) + BaseFunc + 0xF4)(UnkStrAddr); | |
| } | |
| for (int i = 0; i < count; i++) | |
| { | |
| v11 = *reinterpret_cast<__int64*>(*reinterpret_cast<__int64*>(_PedAddress + 4208 + AddrOff) + 8 * ((i + *reinterpret_cast<int*>(*reinterpret_cast<__int64*>(_PedAddress + 4208 + AddrOff) + 1060) + 1) % 16) + 928); | |
| if (v11) | |
| { | |
| if ((*(int(__fastcall **)(__int64))(*reinterpret_cast<__int64*>(v11) + 24))(v11) == 132) | |
| { | |
| v12 = *reinterpret_cast<__int64*>(v11 + 40); | |
| if (v12) | |
| { | |
| if (*reinterpret_cast<short*>(v12 + 52) == 401) | |
| v5 = true; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| if (v5 && (*reinterpret_cast<int(**)(__int64)>(*reinterpret_cast<__int64*>(PedNmAddress) + 152))(PedNmAddress) != -1) | |
| { | |
| reinterpret_cast<void(*)(__int64, __int64, __int64)>(*reinterpret_cast<int*>(BaseFunc + 0x1AA) + BaseFunc + 0x1AE)(PedNmAddress, ScriptDomain::CurrentDomain->PinString(_message).ToInt64(), MessageAddress);//Send Message To Ped | |
| } | |
| reinterpret_cast<void(*)(__int64)>(*reinterpret_cast<int*>(BaseFunc + 0x1BB) + BaseFunc + 0x1BF)(MessageAddress);//Free Message Memory | |
| } | |
| } | |
| } | |
| int _targetHandle; | |
| String ^_message; | |
| Dictionary<String ^, Object ^> ^_arguments; | |
| }; | |
| private ref struct GenericTask : IScriptTask | |
| { | |
| public: | |
| typedef UInt64(*func)(UInt64); | |
| GenericTask(func pFunc, UInt64 Arg) : _toRun(pFunc), _arg(Arg) | |
| { | |
| } | |
| virtual void Run() | |
| { | |
| _res = _toRun(_arg); | |
| } | |
| UInt64 GetResult() | |
| { | |
| return _res; | |
| } | |
| private: | |
| func _toRun; | |
| UInt64 _arg; | |
| UInt64 _res; | |
| }; | |
| } | |
| static MemoryAccess::MemoryAccess() | |
| { | |
| uintptr_t 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. | |
| address = FindPattern("\x33\xFF\xE8\x00\x00\x00\x00\x48\x85\xC0\x74\x58", "xxx????xxxxx"); | |
| _entityAddressFunc = reinterpret_cast<uintptr_t(*)(int)>(*reinterpret_cast<int *>(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<uintptr_t(*)(int)>(*reinterpret_cast<int *>(address + 3) + address + 7); | |
| address = FindPattern("\x74\x21\x48\x8B\x48\x20\x48\x85\xC9\x74\x18\x48\x8B\xD6\xE8", "xxxxxxxxxxxxxxx") - 10; | |
| _ptfxAddressFunc = reinterpret_cast<uintptr_t(*)(int)>(*reinterpret_cast<int*>(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<int(*)(uintptr_t)>(address - 0x68); | |
| 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<UINT64(*)(UINT64, float *)>(*reinterpret_cast<int *>(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<UINT64(*)(UINT64)>(*reinterpret_cast<int *>(address - 61) + address - 57); | |
| _entityModel2Func = reinterpret_cast<UINT64(*)(UINT64)>(*reinterpret_cast<int *>(address + 10) + address + 14); | |
| address = FindPattern("\x4C\x8B\x0D\x00\x00\x00\x00\x44\x8B\xC1\x49\x8B\x41\x08", "xxx????xxxxxxx"); | |
| _entityPoolAddress = reinterpret_cast<uintptr_t *>(*reinterpret_cast<int *>(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<uintptr_t *>(*reinterpret_cast<int *>(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<uintptr_t *>(*reinterpret_cast<int *>(address + 3) + address + 7); | |
| address = FindPattern("\x48\x8B\x05\x00\x00\x00\x00\x8B\x78\x10\x85\xFF", "xxx????xxxxx"); | |
| _objectPoolAddress = reinterpret_cast<uintptr_t *>(*reinterpret_cast<int *>(address + 3) + address + 7); | |
| address = FindPattern("\x8B\xF0\x48\x8B\x05\x00\x00\x00\x00\xF3\x0F\x59\xF6", "xxxxx????xxxx"); | |
| _pickupObjectPoolAddress = reinterpret_cast<uintptr_t *>(*reinterpret_cast<int *>(address + 5) + address + 9); | |
| CreateNmMessageFunc = FindPattern("\x33\xDB\x48\x89\x1D\x00\x00\x00\x00\x85\xFF", "xxxxx????xx") - 0x42; | |
| GiveNmMessageFunc = FindPattern("\x48\x8b\xc4\x48\x89\x58\x08\x48\x89\x68\x10\x48\x89\x70\x18\x48\x89\x78\x20\x41\x55\x41\x56\x41\x57\x48\x83\xec\x20\xe8\x00\x00\x00\x00\x48\x8b\xd8\x48\x85\xc0\x0f", "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxx????xxxxxxx"); | |
| 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<unsigned char(*)(__int64, __int64, unsigned char)>(address); | |
| address = FindPattern("\x40\x53\x48\x83\xEC\x30\x48\x8B\xD9\x48\x63\x49\x0C", "xxxxxxxxxxxxx"); | |
| SetNmFloatAddress = reinterpret_cast<unsigned char(*)(__int64, __int64, float)>(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<unsigned char(*)(__int64, __int64, int)>(address); | |
| address = FindPattern("\x57\x48\x83\xEC\x20\x48\x8B\xD9\x48\x63\x49\x0C\x49\x8B\xE8", "xxxxxxxxxxxxxxx") - 15; | |
| SetNmStringAddress = reinterpret_cast<unsigned char(*)(__int64, __int64, __int64)>(address); | |
| address = FindPattern("\x40\x53\x48\x83\xEC\x40\x48\x8B\xD9\x48\x63\x49\x0C", "xxxxxxxxxxxxx"); | |
| SetNmVec3Address = reinterpret_cast<unsigned char(*)(__int64, __int64, float, float, float)>(address); | |
| address = FindPattern("\x8A\x4C\x24\x60\x8B\x50\x10\x44\x8A\xCE", "xxxxxxxxxx"); | |
| CheckpointBaseAddr = reinterpret_cast<UINT64(*)()>(*reinterpret_cast<int*>(address - 19) + address - 15); | |
| CheckpointHandleAddr = reinterpret_cast<UINT64(*)(UINT64, int)>(*reinterpret_cast<int*>(address - 9) + address - 5); | |
| checkpointPoolAddress = reinterpret_cast<uintptr_t *>(*reinterpret_cast<int *>(address + 17) + address + 21); | |
| address = FindPattern("\x48\x8B\x0B\x33\xD2\xE8\x00\x00\x00\x00\x89\x03", "xxxxxx????xx"); | |
| _getHashKey = reinterpret_cast<unsigned int(*)(char*, unsigned int)>(*reinterpret_cast<int*>(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????"); | |
| _writeWorldGravityAddr = reinterpret_cast<float *>(*reinterpret_cast<int *>(address + 6) + address + 10); | |
| _readWorldGravityAddr = reinterpret_cast<float *>(*reinterpret_cast<int *>(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<int *>(*reinterpret_cast<int*>(address - 4) + address); | |
| address = FindPattern("\x48\x8B\xC7\xF3\x0F\x10\x0D", "xxxxxxx") - 0x1D; | |
| address = address + *reinterpret_cast<int*>(address) + 4; | |
| _gamePlayCameraAddr = reinterpret_cast<UInt64*>(*reinterpret_cast<int*>(address + 3) + address + 7); | |
| address = FindPattern("\x48\x8B\xC8\xEB\x02\x33\xC9\x48\x85\xC9\x74\x26", "xxxxxxxxxxxx") - 9; | |
| _cameraPoolAddress = reinterpret_cast<UInt64*>(*reinterpret_cast<int*>(address) + address + 4); | |
| GenerateVehicleModelList(); | |
| _cellEmailBconPtr = IntPtr((void*)_cellEmailBcon); | |
| _stringPtr = IntPtr((void*)_string); | |
| _nullString = IntPtr((void*)_nullStr); | |
| } | |
| IntPtr MemoryAccess::CellEmailBcon::get() | |
| { | |
| return _cellEmailBconPtr; | |
| } | |
| IntPtr MemoryAccess::StringPtr::get() | |
| { | |
| return _stringPtr; | |
| } | |
| IntPtr MemoryAccess::NullString::get() | |
| { | |
| return _nullString; | |
| } | |
| struct HashNode | |
| { | |
| int hash; | |
| UINT16 data; | |
| UINT16 padding; | |
| HashNode* next; | |
| }; | |
| inline bool bittest(int data, unsigned char index) | |
| { | |
| return (data & (1 << index)) != 0; | |
| } | |
| void MemoryAccess::GenerateVehicleModelList() | |
| { | |
| uintptr_t address = FindPattern("\x66\x81\xF9\x00\x00\x74\x10\x4D\x85\xC0", "xxx??xxxxx") - 0x21; | |
| UINT64 baseFuncAddr = address + *reinterpret_cast<int*>(address)+4; | |
| modelHashEntries = *reinterpret_cast<PUINT16>(baseFuncAddr + *reinterpret_cast<int*>(baseFuncAddr + 3) + 7); | |
| modelNum1 = *reinterpret_cast<int*>(*reinterpret_cast<int*>(baseFuncAddr + 0x52) + baseFuncAddr + 0x56); | |
| modelNum2 = *reinterpret_cast<PUINT64>(*reinterpret_cast<int*>(baseFuncAddr + 0x63) + baseFuncAddr + 0x67); | |
| modelNum3 = *reinterpret_cast<PUINT64>(*reinterpret_cast<int*>(baseFuncAddr + 0x7A) + baseFuncAddr + 0x7E); | |
| modelNum4 = *reinterpret_cast<PUINT64>(*reinterpret_cast<int*>(baseFuncAddr + 0x81) + baseFuncAddr + 0x85); | |
| modelHashTable = *reinterpret_cast<PUINT64>(*reinterpret_cast<int*>(baseFuncAddr + 0x24) + baseFuncAddr + 0x28); | |
| int vehClassOff = *reinterpret_cast<int*>(address + 0x31); | |
| HashNode** HashMap = reinterpret_cast<HashNode**>(modelHashTable); | |
| array<List<int>^> ^hashes = gcnew array<List<int>^>(0x20); | |
| for (int i = 0; i<0x20; i++) | |
| { | |
| hashes[i] = gcnew List<int>(); | |
| } | |
| for (int i = 0; i < modelHashEntries; i++) | |
| { | |
| for (HashNode* cur = HashMap[i]; cur; cur = cur->next) | |
| { | |
| UINT16 data = cur->data; | |
| if ((int)data < modelNum1 && bittest(*reinterpret_cast<int*>(modelNum2 + (4 * data >> 5)), data & 0x1F)) | |
| { | |
| UINT64 addr1 = modelNum4 + modelNum3 * data; | |
| if (addr1) | |
| { | |
| UINT64 addr2 = *reinterpret_cast<PUINT64>(addr1); | |
| if (addr2) | |
| { | |
| if ((*reinterpret_cast<PBYTE>(addr2 + 157) & 0x1F) == 5) | |
| { | |
| hashes[*reinterpret_cast<PBYTE>(addr2 + vehClassOff) & 0x1F]->Add(cur->hash); | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| array<ReadOnlyCollection<int> ^> ^result = gcnew array<ReadOnlyCollection<int> ^>(0x20); | |
| for (int i = 0; i<0x20; i++) | |
| { | |
| result[i] = Array::AsReadOnly(hashes[i]->ToArray()); | |
| } | |
| vehicleModels = Array::AsReadOnly(result); | |
| } | |
| bool MemoryAccess::IsModelAPed(int modelHash) | |
| { | |
| HashNode** HashMap = reinterpret_cast<HashNode**>(modelHashTable); | |
| for (HashNode* cur = HashMap[static_cast<unsigned int>(modelHash) % modelHashEntries]; cur; cur = cur->next) | |
| { | |
| if (cur->hash != modelHash) | |
| { | |
| continue; | |
| } | |
| UINT16 data = cur->data; | |
| if ((int)data < modelNum1 && bittest(*reinterpret_cast<int*>(modelNum2 + (4 * data >> 5)), data & 0x1F)) | |
| { | |
| UINT64 addr1 = modelNum4 + modelNum3 * data; | |
| if (addr1) | |
| { | |
| UINT64 addr2 = *reinterpret_cast<PUINT64>(addr1); | |
| if (addr2) | |
| { | |
| return (*reinterpret_cast<PBYTE>(addr2 + 157) & 0x1F) == 6; | |
| } | |
| } | |
| } | |
| } | |
| return false; | |
| } | |
| int MemoryAccess::GetGameVersion() | |
| { | |
| return getGameVersion(); | |
| } | |
| char MemoryAccess::ReadSByte(IntPtr address) | |
| { | |
| const auto data = static_cast<const char *>(address.ToPointer()); | |
| return *data; | |
| } | |
| unsigned char MemoryAccess::ReadByte(IntPtr address) | |
| { | |
| const auto data = static_cast<const unsigned char *>(address.ToPointer()); | |
| return *data; | |
| } | |
| short MemoryAccess::ReadShort(IntPtr address) | |
| { | |
| const auto data = static_cast<const short *>(address.ToPointer()); | |
| return *data; | |
| } | |
| unsigned short MemoryAccess::ReadUShort(IntPtr address) | |
| { | |
| const auto data = static_cast<const unsigned short *>(address.ToPointer()); | |
| return *data; | |
| } | |
| int MemoryAccess::ReadInt(IntPtr address) | |
| { | |
| const auto data = static_cast<const int *>(address.ToPointer()); | |
| return *data; | |
| } | |
| unsigned int MemoryAccess::ReadUInt(IntPtr address) | |
| { | |
| const auto data = static_cast<const unsigned int *>(address.ToPointer()); | |
| return *data; | |
| } | |
| float MemoryAccess::ReadFloat(IntPtr address) | |
| { | |
| const auto data = static_cast<const float *>(address.ToPointer()); | |
| return *data; | |
| } | |
| Math::Vector3 MemoryAccess::ReadVector3(IntPtr address) | |
| { | |
| const auto data = static_cast<const float *>(address.ToPointer()); | |
| return Math::Vector3(data[0], data[1], data[2]); | |
| } | |
| String ^MemoryAccess::ReadString(IntPtr address) | |
| { | |
| const auto data = static_cast<const char *>(address.ToPointer()); | |
| return gcnew System::String(data); | |
| } | |
| IntPtr MemoryAccess::ReadPtr(IntPtr address) | |
| { | |
| const auto data = static_cast<void **>(address.ToPointer()); | |
| return IntPtr(*data); | |
| } | |
| Math::Matrix MemoryAccess::ReadMatrix(System::IntPtr address) | |
| { | |
| const auto data = static_cast<const Math::Matrix*>(address.ToPointer()); | |
| return Math::Matrix(*data); | |
| } | |
| long long MemoryAccess::ReadLong(IntPtr address) | |
| { | |
| const auto data = static_cast<const long long *>(address.ToPointer()); | |
| return *data; | |
| } | |
| unsigned long long MemoryAccess::ReadULong(IntPtr address) | |
| { | |
| const auto data = static_cast<const unsigned long long *>(address.ToPointer()); | |
| return *data; | |
| } | |
| void MemoryAccess::WriteSByte(System::IntPtr address, char value) | |
| { | |
| const auto data = static_cast<char *>(address.ToPointer()); | |
| *data = value; | |
| } | |
| void MemoryAccess::WriteByte(System::IntPtr address, unsigned char value) | |
| { | |
| const auto data = static_cast<unsigned char *>(address.ToPointer()); | |
| *data = value; | |
| } | |
| void MemoryAccess::WriteShort(System::IntPtr address, short value) | |
| { | |
| const auto data = static_cast<short *>(address.ToPointer()); | |
| *data = value; | |
| } | |
| void MemoryAccess::WriteUShort(System::IntPtr address, unsigned short value) | |
| { | |
| const auto data = static_cast<unsigned short *>(address.ToPointer()); | |
| *data = value; | |
| } | |
| void MemoryAccess::WriteInt(IntPtr address, int value) | |
| { | |
| const auto data = static_cast<int *>(address.ToPointer()); | |
| *data = value; | |
| } | |
| void MemoryAccess::WriteUInt(IntPtr address, unsigned int value) | |
| { | |
| const auto data = static_cast<unsigned int *>(address.ToPointer()); | |
| *data = value; | |
| } | |
| void MemoryAccess::WriteFloat(IntPtr address, float value) | |
| { | |
| const auto data = static_cast<float *>(address.ToPointer()); | |
| *data = value; | |
| } | |
| void MemoryAccess::WriteVector3(IntPtr address, Math::Vector3 value) | |
| { | |
| const auto data = static_cast<float *>(address.ToPointer()); | |
| data[0] = value.X; | |
| data[1] = value.Y; | |
| data[2] = value.Z; | |
| } | |
| void MemoryAccess::WriteMatrix(IntPtr address, Math::Matrix value) | |
| { | |
| const auto data = static_cast<float *>(address.ToPointer()); | |
| auto arr = value.ToArray(); | |
| for (int i = 0; i < arr->Length; i++) | |
| { | |
| data[i] = arr[i]; | |
| } | |
| } | |
| void MemoryAccess::WriteLong(IntPtr address, long long value) | |
| { | |
| const auto data = static_cast<long long *>(address.ToPointer()); | |
| *data = value; | |
| } | |
| void MemoryAccess::WriteULong(IntPtr address, unsigned long long value) | |
| { | |
| const auto data = static_cast<unsigned long long *>(address.ToPointer()); | |
| *data = value; | |
| } | |
| void MemoryAccess::SetBit(IntPtr address, int bit) | |
| { | |
| if (bit < 0 || bit >31) | |
| { | |
| throw gcnew ArgumentOutOfRangeException("bit", "The bit index has to be between 0 and 31"); | |
| } | |
| const int mask = 1 << bit; | |
| const auto data = static_cast<int *>(address.ToPointer()); | |
| *data |= mask; | |
| } | |
| void MemoryAccess::ClearBit(IntPtr address, int bit) | |
| { | |
| if (bit < 0 || bit >31) | |
| { | |
| throw gcnew ArgumentOutOfRangeException("bit", "The bit index has to be between 0 and 31"); | |
| } | |
| const int mask = 1 << bit; | |
| const auto data = static_cast<int *>(address.ToPointer()); | |
| *data &= ~mask; | |
| } | |
| bool MemoryAccess::IsBitSet(IntPtr address, int bit) | |
| { | |
| if (bit < 0 || bit >31) | |
| { | |
| throw gcnew ArgumentOutOfRangeException("bit", "The bit index has to be between 0 and 31"); | |
| } | |
| const int mask = 1 << bit; | |
| const auto data = static_cast<int *>(address.ToPointer()); | |
| return (*data & mask) != 0; | |
| } | |
| unsigned int MemoryAccess::GetHashKey(String^ toHash) | |
| { | |
| IntPtr handle = ScriptDomain::CurrentDomain->PinString(toHash); | |
| return _getHashKey((char*)handle.ToPointer(), 0); | |
| } | |
| IntPtr MemoryAccess::GetEntityAddress(int handle) | |
| { | |
| return IntPtr((long long)_entityAddressFunc(handle)); | |
| } | |
| IntPtr MemoryAccess::GetPlayerAddress(int handle) | |
| { | |
| return IntPtr((long long)_playerAddressFunc(handle)); | |
| } | |
| UInt64 _getCheckpointAddress(UInt64 Data) | |
| { | |
| int handle = *(int*)(&Data); | |
| UInt64 addr = MemoryAccess::CheckpointHandleAddr(MemoryAccess::CheckpointBaseAddr(), handle); | |
| if (addr != 0) | |
| { | |
| return (UInt64)((UInt64)(MemoryAccess::checkpointPoolAddress) + 96 * *reinterpret_cast<int *>(addr + 16)); | |
| } | |
| return 0; | |
| } | |
| IntPtr MemoryAccess::GetCheckpointAddress(int handle) | |
| { | |
| GenericTask ^task = gcnew GenericTask(_getCheckpointAddress, handle); | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return IntPtr((long long)task->GetResult()); | |
| } | |
| IntPtr MemoryAccess::GetPtfxAddress(int handle) | |
| { | |
| return IntPtr((long long)_ptfxAddressFunc(handle)); | |
| } | |
| int MemoryAccess::GetEntityBoneCount(int handle) | |
| { | |
| auto fragSkeletonData = GetEntitySkeletonData(handle); | |
| return fragSkeletonData ? *reinterpret_cast<int*>(fragSkeletonData + 32) : 0; | |
| } | |
| IntPtr MemoryAccess::GetEntityBoneMatrixAddress(int handle, int boneIndex) | |
| { | |
| if ((boneIndex & 0x80000000) != 0)//boneIndex cant be negative | |
| return IntPtr::Zero; | |
| auto fragSkeletonData = GetEntitySkeletonData(handle); | |
| if (!fragSkeletonData) return IntPtr::Zero; | |
| if (boneIndex < *reinterpret_cast<int*>(fragSkeletonData + 32))// boneIndex < max bones? | |
| { | |
| return IntPtr((long long)(*(UInt64*)(fragSkeletonData + 24) + (boneIndex * 0x40))); | |
| } | |
| return IntPtr::Zero; | |
| } | |
| IntPtr MemoryAccess::GetEntityBonePoseAddress(int handle, int boneIndex) | |
| { | |
| if ((boneIndex & 0x80000000) != 0)//boneIndex cant be negative | |
| return IntPtr::Zero; | |
| auto fragSkeletonData = GetEntitySkeletonData(handle); | |
| if (!fragSkeletonData) return IntPtr::Zero; | |
| if (boneIndex < *reinterpret_cast<int*>(fragSkeletonData + 32))// boneIndex < max bones? | |
| { | |
| return IntPtr((long long)(*(UInt64*)(fragSkeletonData + 16) + (boneIndex * 0x40))); | |
| } | |
| return IntPtr::Zero; | |
| } | |
| unsigned long long MemoryAccess::GetEntitySkeletonData(int handle) | |
| { | |
| UInt64 MemAddress = _entityAddressFunc(handle); | |
| UInt64 Addr2 = (*(UInt64(__thiscall **)(__int64))(*(UInt64 *)MemAddress + 88i64))(MemAddress); | |
| UInt64 Addr3; | |
| if (!Addr2) | |
| { | |
| Addr3 = *(UInt64*)(MemAddress + 80); | |
| if (!Addr3) | |
| { | |
| return 0; | |
| } | |
| else | |
| { | |
| Addr3 = *(UInt64*)(Addr3 + 40); | |
| } | |
| } | |
| else | |
| { | |
| Addr3 = *(UInt64*)(Addr2 + 104); | |
| if (!Addr3 || !*(UInt64*)(Addr2 + 120)) | |
| { | |
| return 0; | |
| } | |
| else | |
| { | |
| Addr3 = *(UInt64*)(Addr3 + 376); | |
| } | |
| } | |
| if (!Addr3) | |
| { | |
| return 0; | |
| } | |
| return Addr3; | |
| } | |
| float MemoryAccess::ReadWorldGravity() | |
| { | |
| return *_readWorldGravityAddr; | |
| } | |
| void MemoryAccess::WriteWorldGravity(float value) | |
| { | |
| *_writeWorldGravityAddr = value; | |
| } | |
| int MemoryAccess::ReadCursorSprite() | |
| { | |
| return *_cursorSpriteAddr; | |
| } | |
| IntPtr MemoryAccess::GetGameplayCameraAddress() | |
| { | |
| return IntPtr((long long)*_gamePlayCameraAddr); | |
| } | |
| IntPtr MemoryAccess::GetCameraAddress(int handle) | |
| { | |
| unsigned int index = (unsigned int)(handle >> 8); | |
| UInt64 poolAddr = *_cameraPoolAddress; | |
| if(*(Byte *)(index + *(Int64*)(poolAddr + 8)) == (Byte)(handle & 0xFF)) | |
| { | |
| return IntPtr(*(Int64*)poolAddr + (unsigned int)(index * *(UInt32 *)(poolAddr + 20))); | |
| } | |
| return IntPtr::Zero; | |
| } | |
| array<int> ^MemoryAccess::GetEntityHandles() | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::Ped | EntityPoolTask::Type::Object | EntityPoolTask::Type::Vehicle); | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| array<int> ^MemoryAccess::GetEntityHandles(Math::Vector3 position, float radius) | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::Ped | EntityPoolTask::Type::Object | EntityPoolTask::Type::Vehicle); | |
| task->_position = position; | |
| task->_radiusSquared = radius * radius; | |
| task->_posCheck = true; | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| array<int> ^MemoryAccess::GetVehicleHandles(array<int> ^modelhashes) | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::Vehicle); | |
| task->_modelHashes = modelhashes; | |
| task->_modelCheck = modelhashes != nullptr && modelhashes->Length > 0; | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| array<int> ^MemoryAccess::GetVehicleHandles(Math::Vector3 position, float radius, array<int> ^modelhashes) | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::Vehicle); | |
| task->_position = position; | |
| task->_radiusSquared = radius * radius; | |
| task->_posCheck = true; | |
| task->_modelHashes = modelhashes; | |
| task->_modelCheck = modelhashes != nullptr && modelhashes->Length > 0; | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| array<int> ^MemoryAccess::GetPedHandles(array<int> ^modelhashes) | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::Ped); | |
| task->_modelHashes = modelhashes; | |
| task->_modelCheck = modelhashes != nullptr && modelhashes->Length > 0; | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| array<int> ^MemoryAccess::GetPedHandles(Math::Vector3 position, float radius, array<int> ^modelhashes) | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::Ped); | |
| task->_position = position; | |
| task->_radiusSquared = radius * radius; | |
| task->_posCheck = true; | |
| task->_modelHashes = modelhashes; | |
| task->_modelCheck = modelhashes != nullptr && modelhashes->Length > 0; | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| array<int> ^MemoryAccess::GetPropHandles(array<int> ^modelhashes) | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::Object); | |
| task->_modelHashes = modelhashes; | |
| task->_modelCheck = modelhashes != nullptr && modelhashes->Length > 0; | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| array<int> ^MemoryAccess::GetPropHandles(Math::Vector3 position, float radius, array<int> ^modelhashes) | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::Object); | |
| task->_position = position; | |
| task->_radiusSquared = radius * radius; | |
| task->_posCheck = true; | |
| task->_modelHashes = modelhashes; | |
| task->_modelCheck = modelhashes != nullptr && modelhashes->Length > 0; | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| struct checkpoint | |
| { | |
| char padding[0xC]; | |
| int handle; | |
| char padding1[0x08]; | |
| checkpoint* next; | |
| }; | |
| UInt64 _getCheckpoinHandles(UInt64 ArrayPtr) | |
| { | |
| int* handles = (int*)ArrayPtr; | |
| UInt64 count = 0; | |
| for (checkpoint* item = *reinterpret_cast<checkpoint**>(MemoryAccess::CheckpointBaseAddr() + 48); item && count < 64; item = item->next) | |
| { | |
| handles[count++] = item->handle; | |
| } | |
| return count; | |
| } | |
| array<int> ^MemoryAccess::GetCheckpointHandles() | |
| { | |
| int Handles[64]; | |
| GenericTask ^task = gcnew GenericTask(_getCheckpoinHandles, (UInt64)Handles); | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| int count = (int)task->GetResult(); | |
| array<int>^ data_array = gcnew array<int>(count); | |
| pin_ptr<int> ptrBuffer = &data_array[0]; | |
| memcpy(ptrBuffer, Handles, count * 4); | |
| return data_array; | |
| } | |
| array<int> ^MemoryAccess::GetPickupObjectHandles() | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::PickupObject); | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| array<int> ^MemoryAccess::GetPickupObjectHandles(Math::Vector3 position, float radius) | |
| { | |
| auto task = gcnew EntityPoolTask(EntityPoolTask::Type::PickupObject); | |
| task->_position = position; | |
| task->_radiusSquared = radius * radius; | |
| task->_posCheck = true; | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| return task->_handles->ToArray(); | |
| } | |
| int MemoryAccess::GetNumberOfVehicles() | |
| { | |
| if (*_vehiclePoolAddress) | |
| { | |
| VehiclePool* pool = *reinterpret_cast<VehiclePool**>(*_vehiclePoolAddress); | |
| return pool->itemCount; | |
| } | |
| return 0; | |
| } | |
| void MemoryAccess::SendEuphoriaMessage(int targetHandle, String ^message, Dictionary<String ^, Object ^> ^arguments) | |
| { | |
| auto task = gcnew EuphoriaMessageTask(targetHandle, message, arguments); | |
| ScriptDomain::CurrentDomain->ExecuteTask(task); | |
| } | |
| int MemoryAccess::CreateTexture(System::String ^filename) | |
| { | |
| return createTexture(static_cast<const char *>(ScriptDomain::CurrentDomain->PinString(filename).ToPointer())); | |
| } | |
| void MemoryAccess::DrawTexture(int id, int index, int level, int time, float sizeX, float sizeY, float centerX, float centerY, float posX, float posY, float rotation, float scaleFactor, Drawing::Color color) | |
| { | |
| drawTexture(id, index, level, time, sizeX, sizeY, centerX, centerY, posX, posY, rotation, scaleFactor, color.R / 255.0f, color.G / 255.0f, color.B / 255.0f, color.A / 255.0f); | |
| } | |
| uintptr_t MemoryAccess::FindPattern(const char *pattern, const char *mask) | |
| { | |
| MODULEINFO module = { }; | |
| GetModuleInformation(GetCurrentProcess(), GetModuleHandle(nullptr), &module, sizeof(MODULEINFO)); | |
| auto *address = reinterpret_cast<const char *>(module.lpBaseOfDll), *address_end = address + module.SizeOfImage; | |
| const auto mask_length = static_cast<size_t>(strlen(mask) - 1); | |
| for (size_t i = 0; address < address_end; address++) | |
| { | |
| if (*address == pattern[i] || mask[i] == '?') | |
| { | |
| if (mask[i + 1] == '\0') | |
| { | |
| return reinterpret_cast<uintptr_t>(address) - mask_length; | |
| } | |
| i++; | |
| } | |
| else | |
| { | |
| i = 0; | |
| } | |
| } | |
| return 0; | |
| } | |
| } | |
| } |