diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..54e5768 --- /dev/null +++ b/.gitignore @@ -0,0 +1,12 @@ +.vscode +.vs +build/ +*.suo +*.db +*.db-shm +*.db-wal +*.opendb +*.sln +*.vcxproj +*.user +*.filters \ No newline at end of file diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..c2ca894 --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,31 @@ +cmake_minimum_required(VERSION 3.11) + +project(RDR-Classes) + +include(CheckIncludeFileCXX) + +set(CMAKE_CXX_EXTENSIONS OFF) +set(CMAKE_CXX_STANDARD 20) +set(CMAKE_CXX_STANDARD_REQUIRED ON) + +set(CMAKE_REQUIRED_QUIET ON) + +set(OK TRUE) + +file(GLOB_RECURSE HEADERS "**.hpp") +file(GLOB_RECURSE SRC_MAIN "classes.cpp") +source_group(FILES ${SRC_MAIN}) +add_library(RDR-Classes MODULE "${SRC_MAIN}") + +add_compile_definitions(_CRT_SECURE_NO_WARNINGS) + +message(STATUS "") + +target_include_directories(RDR-Classes PRIVATE + "${SRC_DIR}" +) + +if(NOT OK) + file(READ "${CMAKE_CURRENT_BINARY_DIR}/CMakeFiles/CMakeError.log" LOG) + message(STATUS ${LOG}) +endif() \ No newline at end of file diff --git a/classes.cpp b/classes.cpp new file mode 100644 index 0000000..80072d8 --- /dev/null +++ b/classes.cpp @@ -0,0 +1,17 @@ +#include "network/CNetGamePlayer.hpp" +#include "network/CNetworkPlayerMgr.hpp" +#include "network/netPeerAddress.hpp" +#include "network/netPlayer.hpp" +#include "network/netPlayerMgrBase.hpp" +#include "network/rlGamerHandle.hpp" +#include "network/rlGamerInfoBase.hpp" +#include "network/rlGamerInfo.hpp" +#include "player/CPlayerInfo.hpp" +#include "rage/joaat.hpp" +#include "rage/vector.hpp" +#include "script/scriptHandlerNetComponent.hpp" +#include "script/scrNativeHandler.hpp" +#include "script/scrThread.hpp" +#include "script/scrThreadContext.hpp" +#include "script/scrVector.hpp" +#include "script/types.hpp" \ No newline at end of file diff --git a/network/CNetGamePlayer.hpp b/network/CNetGamePlayer.hpp new file mode 100644 index 0000000..0991265 --- /dev/null +++ b/network/CNetGamePlayer.hpp @@ -0,0 +1,11 @@ +#pragma once +#include "netPlayer.hpp" + +class CPlayerInfo; +class CNetGamePlayer : public rage::netPlayer +{ +public: + CPlayerInfo* m_PlayerInfo; //0x0128 + char pad_0130[2432]; //0x0130 +}; //Size: 0x0AB0 +static_assert(sizeof(CNetGamePlayer) == 0xAB0); \ No newline at end of file diff --git a/network/CNetworkPlayerMgr.hpp b/network/CNetworkPlayerMgr.hpp new file mode 100644 index 0000000..8020d7c --- /dev/null +++ b/network/CNetworkPlayerMgr.hpp @@ -0,0 +1,16 @@ +#pragma once +#include "netPlayerMgrBase.hpp" +#include "CNetGamePlayer.hpp" + +#include +#pragma pack(push, 8) +class CNetworkPlayerMgr : public rage::netPlayerMgrBase +{ +public: + CNetGamePlayer m_PlayerStorage[32]; // 0x08E0 + CNetGamePlayer* m_FirstPlayer; // 0x15EE0 + CNetGamePlayer* m_LastPlayer; // 0x15EE8 + std::uint64_t m_UnkCounter; // 0x15EF0 +}; +static_assert(sizeof(CNetworkPlayerMgr) == 0x15EF8); +#pragma pack(pop) \ No newline at end of file diff --git a/network/netPeerAddress.hpp b/network/netPeerAddress.hpp new file mode 100644 index 0000000..c35df68 --- /dev/null +++ b/network/netPeerAddress.hpp @@ -0,0 +1,32 @@ +#pragma once +#include + +union netAddress { + uint32_t m_packed; //0x0000 + struct { + uint8_t m_field4; //0x0000 + uint8_t m_field3; //0x0001 + uint8_t m_field2; //0x0002 + uint8_t m_field1; //0x0003 + }; +}; //Size: 0x0004 +static_assert(sizeof(netAddress) == 0x04); + +namespace rage +{ +#pragma pack(push, 4) + class netPeerAddress + { + public: + netAddress m_internal_ip; //0x0000 + uint16_t m_internal_port; //0x0004 + netAddress m_external_ip; //0x0008 + uint16_t m_external_port; //0x000C + uint64_t m_peer_id; //0x0010 + netAddress m_relay_address; //0x0018 + uint16_t m_relay_port; //0x001C + uint8_t m_connection_type; //0x001E + }; + static_assert(sizeof(netPeerAddress) == 0x20); +#pragma pack(pop) +} \ No newline at end of file diff --git a/network/netPlayer.hpp b/network/netPlayer.hpp new file mode 100644 index 0000000..532ebf4 --- /dev/null +++ b/network/netPlayer.hpp @@ -0,0 +1,35 @@ +#pragma once +#include + +namespace rage +{ + class rlGamerInfo; + class netPlayer + { + public: + virtual ~netPlayer() = default; // 0x00 + virtual void Initialize(void* unk, int message_id, int unk2) {}; // 0x08 + virtual void Initialize2(int unk) {}; // 0x10 + virtual void Reset() {}; // 0x18 + virtual bool IsValid() { return false; }; // 0x20 + virtual const char* GetName() { return nullptr; }; // 0x28 + virtual void* GetUnk() { return nullptr; }; // 0x30 + virtual void SetTeam(int new_team) {}; // 0x38 + virtual bool IsSameTeam(netPlayer* other) { return false; }; // 0x40 + virtual void _0x48() {}; // 0x48 some posse stuff + virtual void RefreshData() {}; // 0x50 + virtual bool IsHost() { return true; }; // 0x58 + virtual rage::rlGamerInfo* GetGamerInfo() { return nullptr; }; // 0x60 + + char pad_0008[12]; //0x0008 + uint32_t m_MessageId; //0x0014 + uint8_t m_ActiveIndex; //0x0018 + uint8_t m_PlayerIndex; //0x0019 + char pad_001A[10]; //0x001A + uint32_t m_JoinTime; //0x0024 + char pad_0028[4]; //0x0028 + uint32_t m_Team; //0x002C + char pad_0030[248]; //0x0030 + }; //Size: 0x0128 + static_assert(sizeof(rage::netPlayer) == 0x128); +} \ No newline at end of file diff --git a/network/netPlayerMgrBase.hpp b/network/netPlayerMgrBase.hpp new file mode 100644 index 0000000..aa59e89 --- /dev/null +++ b/network/netPlayerMgrBase.hpp @@ -0,0 +1,39 @@ +#pragma once +#include + +class CNetGamePlayer; +class CNetGamePlayerDataMsg; +class CNonPhysicalPlayerData; + +namespace rage +{ + class rlGamerInfo; + class netConnectionManager; +#pragma pack(push, 8) + // the same as GTA 1.63 + class netPlayerMgrBase + { + public: + virtual ~netPlayerMgrBase() = default; + virtual void Initialize() = 0; + virtual void Shutdown() = 0; + virtual void unk_0x18() = 0; + virtual CNetGamePlayer* AddPlayer_raw(rage::rlGamerInfo* gamer_info, uint32_t a2, CNetGamePlayerDataMsg* player_data, CNonPhysicalPlayerData* non_physical_player_data) = 0; + virtual void RemovePlayer(CNetGamePlayer* net_game_player) = 0; + virtual void UpdatePlayerListsForPlayer(CNetGamePlayer* net_game_player) = 0; + virtual CNetGamePlayer* AddPlayer(rage::rlGamerInfo* gamer_info, uint32_t a3, CNetGamePlayerDataMsg* player_data, CNonPhysicalPlayerData* non_physical_player_data) = 0; + + rage::netConnectionManager* m_NetConnectionManager; //0x0008 + uint64_t* m_NetBandwidthManager; //0x0010 + char pad_0018[208]; //0x0018 + CNetGamePlayer* m_LocalPlayer; //0x00E8 + char pad_00F0[144]; //0x00F0 + CNetGamePlayer* m_PlayerList[32]; //0x0180 (TODO: is this used?) + uint16_t m_PlayerLimit; //0x0280 + char pad_0282[10]; //0x0282 + uint16_t m_PlayerCount; //0x028C + char pad_0290[1618]; //0x0290 + }; //Size: 0x08E0 + static_assert(sizeof(netPlayerMgrBase) == 0x8E0); +#pragma pack(pop) +} \ No newline at end of file diff --git a/network/rlGamerHandle.hpp b/network/rlGamerHandle.hpp new file mode 100644 index 0000000..4c19087 --- /dev/null +++ b/network/rlGamerHandle.hpp @@ -0,0 +1,26 @@ +#pragma once + +#include + +namespace rage +{ +#pragma pack(push,8) + class rlGamerHandle + { + public: + uint64_t m_rockstar_id; //0x0000 + uint8_t m_platform; //0x0008 + uint8_t unk_0009; //0x0009 + + inline rlGamerHandle() = default; + + inline rlGamerHandle(uint64_t rockstar_id) : + m_rockstar_id(rockstar_id), + m_platform(3), + unk_0009(0) + { + } + }; //Size: 0x0010 + static_assert(sizeof(rlGamerHandle) == 0x10); +#pragma pack(pop) +} \ No newline at end of file diff --git a/network/rlGamerInfo.hpp b/network/rlGamerInfo.hpp new file mode 100644 index 0000000..294e869 --- /dev/null +++ b/network/rlGamerInfo.hpp @@ -0,0 +1,19 @@ +#pragma once +#include +#include "rlGamerInfoBase.hpp" + +#pragma pack(push, 8) +namespace rage +{ + class rlGamerInfo : public rage::rlGamerInfoBase + { + uint64_t m_HostToken; + uint64_t m_PeerId2; + rage::rlGamerHandle m_GamerHandle2; + uint32_t m_ProfileIndex; + char m_Name[21]; + uint8_t m_Flags; + }; + static_assert(sizeof(rage::rlGamerInfo) == 0xD8); +} +#pragma pack(pop) \ No newline at end of file diff --git a/network/rlGamerInfoBase.hpp b/network/rlGamerInfoBase.hpp new file mode 100644 index 0000000..4b150e6 --- /dev/null +++ b/network/rlGamerInfoBase.hpp @@ -0,0 +1,26 @@ +#pragma once +#include +#include "rlGamerHandle.hpp" + +#pragma pack(push, 8) +namespace rage +{ + class rlGamerInfoBase + { + uint64_t m_PeerId; + rage::rlGamerHandle m_GamerHandle; + char m_AesKey[33]; + netAddress m_InternalAddress; + uint16_t m_InternalPort; + netAddress m_UnkAddress; + uint16_t m_UnkPort; + netAddress m_ExternalAddress; + uint16_t m_ExternalPort; + netAddress m_RelayAddress; + uint16_t m_RelayPort; + char gap5A[54]; // TODO: reverse this + uint32_t m_RelayState; + }; + static_assert(sizeof(rage::rlGamerInfoBase) == 0x98); +} +#pragma pack(pop) \ No newline at end of file diff --git a/player/CPlayerInfo.hpp b/player/CPlayerInfo.hpp new file mode 100644 index 0000000..6d5ca35 --- /dev/null +++ b/player/CPlayerInfo.hpp @@ -0,0 +1,13 @@ +#pragma once +#include "../network/rlGamerInfo.hpp" + +class CPlayerInfo +{ +public: + char pad_0000[336]; //0x0000 + rage::rlGamerInfo m_GamerInfo; //0x0150 + char pad_0158[4000]; //0x0228 + void* m_Ped; //0x11C8 + char pad_11D0[3640]; //0x11D0 +}; //Size: 0x2008 +static_assert(sizeof(CPlayerInfo) == 0x2008); \ No newline at end of file diff --git a/rage/joaat.hpp b/rage/joaat.hpp new file mode 100644 index 0000000..e631b88 --- /dev/null +++ b/rage/joaat.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include +#include + +namespace rage +{ + using joaat_t = std::uint32_t; + inline constexpr char joaat_to_lower(char c) + { + return c >= 'A' && c <= 'Z' ? c | 1 << 5 : c; + } + + inline constexpr joaat_t joaat(const std::string_view str) + { + joaat_t hash = 0; + for (auto c : str) + { + hash += joaat_to_lower(c); + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } +}; \ No newline at end of file diff --git a/rage/vector.hpp b/rage/vector.hpp new file mode 100644 index 0000000..c88c715 --- /dev/null +++ b/rage/vector.hpp @@ -0,0 +1,87 @@ +#pragma once + +namespace rage +{ + template + union vector2 + { + T data[2]; + struct { T x, y; }; + + vector2(T x, T y) : + x(x), + y(y) + { + } + + vector2() : + x(), + y() + { + } + }; + + template + union vector3 + { + T data[3]; + struct { T x, y, z; }; + + vector3(T x, T y, T z) : + x(x), + y(y), + z(z) + { + } + + vector3() : + x(), + y(), + z() + { + } + }; + + template + union vector4 + { + T data[4]; + struct { T x, y, z, w; }; + + vector4(T x, T y, T z, T w) : + x(x), + y(y), + z(z), + w(w) + { + } + + vector4() : + x(), + y(), + z(), + w() + { + } + }; + + template + union matrix34 + { + T data[3][4]; + struct { struct { T x, y, z, w; } rows[3]; }; + }; + + template + union matrix44 + { + T data[4][4]; + struct { struct { T x, y, z, w; } rows[4]; }; + }; + + typedef vector2 fvector2; + typedef vector3 fvector3; + typedef vector4 fvector4; + typedef matrix34 fmatrix34; + typedef matrix44 fmatrix44; +} \ No newline at end of file diff --git a/rdr2.rcnet b/rdr2.rcnet new file mode 100644 index 0000000..e44c271 Binary files /dev/null and b/rdr2.rcnet differ diff --git a/script/scrNativeHandler.hpp b/script/scrNativeHandler.hpp new file mode 100644 index 0000000..6cf1ff3 --- /dev/null +++ b/script/scrNativeHandler.hpp @@ -0,0 +1,61 @@ +#pragma once +#include +#include +#include + +namespace rage +{ + class scrNativeCallContext + { + public: + constexpr void reset() + { + m_arg_count = 0; + m_data_count = 0; + } + + template + constexpr void push_arg(T&& value) + { + static_assert(sizeof(T) <= sizeof(std::uint64_t)); + *reinterpret_cast>*>(reinterpret_cast(m_args) + (m_arg_count++)) = std::forward(value); + } + + template + constexpr T& get_arg(std::size_t index) + { + static_assert(sizeof(T) <= sizeof(std::uint64_t)); + return *reinterpret_cast(reinterpret_cast(m_args) + index); + } + + template + constexpr void set_arg(std::size_t index, T&& value) + { + static_assert(sizeof(T) <= sizeof(std::uint64_t)); + *reinterpret_cast>*>(reinterpret_cast(m_args) + index) = std::forward(value); + } + + template + constexpr T* get_return_value() + { + return reinterpret_cast(m_return_value); + } + + template + constexpr void set_return_value(T&& value) + { + *reinterpret_cast>*>(m_return_value) = std::forward(value); + } + protected: + void* m_return_value; + std::uint32_t m_arg_count; + void* m_args; + std::int32_t m_data_count; + std::uint32_t m_data[48]; + }; + static_assert(sizeof(scrNativeCallContext) == 0xE0); + + using scrNativeHash = std::int64_t; + using scrNativePair = std::pair; + using scrNativeHandler = void(*)(scrNativeCallContext*); +} \ No newline at end of file diff --git a/script/scrThread.hpp b/script/scrThread.hpp new file mode 100644 index 0000000..50b0873 --- /dev/null +++ b/script/scrThread.hpp @@ -0,0 +1,27 @@ +#pragma once +#include "scrThreadContext.hpp" + +namespace rage +{ + class scrThread + { + public: + virtual ~scrThread() = default; // 0 (0x00) + virtual void Reset(std::uint32_t script_hash, void* args, std::uint32_t arg_count) = 0; // 1 (0x08) + virtual eThreadState Run() = 0; // 2 (0x10) + virtual eThreadState Tick(std::uint32_t ops_to_execute) = 0; // 3 (0x18) + virtual void Kill() = 0; // 4 (0x20) + + public: + scrThreadContext m_Context; // 0x008 + void* m_Stack; // 0x6B8 + char m_padding[0x8]; // 0x6C0 + uint32_t m_ArgsSize; // 0x6C8 + uint32_t m_ArgsPointer; // 0x6CC + const char* m_ExitMessage; // 0x6D0 + char m_padding2[0x8]; // 0x6D8 + void* m_Handler; // 0x6E0 + void* m_HandlerNetComponent; // 0x6E8 + }; + static_assert(sizeof(rage::scrThread) == 0x6F0); +} \ No newline at end of file diff --git a/script/scrThreadContext.hpp b/script/scrThreadContext.hpp new file mode 100644 index 0000000..c409dd8 --- /dev/null +++ b/script/scrThreadContext.hpp @@ -0,0 +1,42 @@ +#pragma once +#include + +#include "../rage/joaat.hpp" + +namespace rage +{ + enum class eThreadState : std::uint32_t + { + idle, + running, + killed, + unk_3, + unk_4, + }; + + class scrThreadContext + { + public: + std::uint32_t m_ThreadId; // 0x00 + joaat_t m_ScriptHash; // 0x04 + eThreadState m_State; // 0x08 + std::uint32_t m_ProgramCounter; // 0x0C + std::uint32_t m_FramePointer; // 0x10 + std::uint32_t m_StackPointer; // 0x14 + float m_TimerA; // 0x18 + float m_TimerB; // 0x1C + float m_TimerC; // 0x20 + char m_padding1[0x2C]; // 0x24 + std::uint32_t m_StackSize; // 0x50 + uint32_t m_SetjmpPC; // 0x54 + uint32_t m_SetjmpFrame; // 0x58 + uint32_t m_SetjmpStack; // 0x5C + uint32_t m_SetjmpUnk; // 0x60 + uint32_t m_StackFrameCount; // 0x64 actually used in RDR2 + uint32_t m_StackFrames[16]; // 0x68 + void* m_SecureFrames[96]; // 0xA8 + void* m_SecureStatics[96]; // 0x3A8 + void* m_UnkSecureVar; // 0x6A8 + }; + static_assert(sizeof(scrThreadContext) == 0x6B0); +} \ No newline at end of file diff --git a/script/scrVector.hpp b/script/scrVector.hpp new file mode 100644 index 0000000..cbbe0c1 --- /dev/null +++ b/script/scrVector.hpp @@ -0,0 +1,119 @@ +#pragma once +#include "../rage/vector.hpp" +#include +#include +#include + +namespace rage +{ + class scrVector + { + public: + scrVector() = default; + + scrVector(rage::fvector3 vec) : + x(vec.x), y(vec.y), z(vec.z) + { + } + + scrVector(float x, float y, float z) : + x(x), y(y), z(z) + { + } + + scrVector operator+(const scrVector& other) + { + scrVector vec; + vec.x = this->x + other.x; + vec.y = this->y + other.y; + vec.z = this->z + other.z; + return vec; + } + + scrVector operator-(const scrVector& other) + { + scrVector vec; + vec.x = this->x - other.x; + vec.y = this->y - other.y; + vec.z = this->z - other.z; + return vec; + } + + scrVector operator*(const scrVector& other) + { + scrVector vec; + vec.x = this->x * other.x; + vec.y = this->y * other.y; + vec.z = this->z * other.z; + return vec; + } + + scrVector operator*(const float& other) + { + scrVector vec; + vec.x = this->x * other; + vec.y = this->y * other; + vec.z = this->z * other; + return vec; + } + + bool operator==(const scrVector& other) + { + return this->x == other.x && this->y == other.y && this->z == other.z; + } + + bool operator!=(const scrVector& other) + { + return this->x != other.x || this->y != other.y || this->z != other.z; + } + + std::string to_string() const + { + std::stringstream ss; + ss << *this; + return ss.str(); + } + + friend std::ostream& operator<<(std::ostream& os, const scrVector& vec) + { + os << "(" << vec.x << ", " << vec.y << ", " << vec.z << ")"; + return os; + } + + alignas(8) float x{}; + alignas(8) float y{}; + alignas(8) float z{}; + }; +} + +class Vector2 final +{ +public: + Vector2() = default; + + Vector2(float x, float y) + : x(x), y(y) + { + } + +public: + alignas(8) float x; + alignas(8) float y; +}; + +class Vector4 final +{ +public: + Vector4() = default; + + Vector4(float x, float y, float z, float w) + : x(x), y(y), z(z), w(w) + { + } + +public: + alignas(8) float x; + alignas(8) float y; + alignas(8) float z; + alignas(8) float w; +}; \ No newline at end of file diff --git a/script/scriptHandlerNetComponent.hpp b/script/scriptHandlerNetComponent.hpp new file mode 100644 index 0000000..4a700e9 --- /dev/null +++ b/script/scriptHandlerNetComponent.hpp @@ -0,0 +1,40 @@ +#pragma once + +class CNetGamePlayer; + +class CScriptParticipant +{ +public: + char pad_0000[16]; //0x0000 + class CNetGamePlayer* m_net_game_player;//0x0010 + char pad_0018[2]; //0x0018 + int16_t m_participant_index; //0x001A + char pad_001C[12]; //0x001C +}; +static_assert(sizeof(CScriptParticipant) == 0x28); + +#pragma pack(push, 8) +namespace rage +{ + class scriptHandler; + class scriptHandlerNetComponent + { + public: + virtual ~scriptHandlerNetComponent() = default; + virtual bool _0x08(void*) = 0; + virtual void _0x10(CNetGamePlayer*) = 0; + virtual void* HandlePlayerLeft(CNetGamePlayer* player) = 0; + virtual void* SendHostMigrationEvent(CNetGamePlayer* player) = 0; + + public: + scriptHandler* m_ScriptHandler; // 0x08 + char m_Pad[0x28]; // 0x10 + std::uint8_t m_LocalParticipantId; // 0x38 + void* m_Pad2[4]; // 0x40 + CScriptParticipant* m_Participants[32]; // 0x60 + int m_Pad3[3]; // 0x160 + std::uint8_t m_NumParticipants; // 0x168 + }; + static_assert(sizeof(rage::scriptHandlerNetComponent) == 0x170); +} +#pragma pack(pop) \ No newline at end of file diff --git a/script/types.hpp b/script/types.hpp new file mode 100644 index 0000000..a0dff1c --- /dev/null +++ b/script/types.hpp @@ -0,0 +1,119 @@ +#pragma once +#include +#include "scrVector.hpp" + +#ifndef BOOL +#define BOOL int +#endif + +using Vector3 = rage::scrVector; +typedef int Void; +typedef int Any; +typedef int uint; +typedef int Hash; +typedef int Entity; +typedef int Player; +typedef int FireId; +typedef int Ped; +typedef int Vehicle; +typedef int Cam; +typedef int CarGenerator; +typedef int Group; +typedef int Train; +typedef int Pickup; +typedef int Object; +typedef int Weapon; +typedef int Interior; +typedef int Blip; +typedef int Texture; +typedef int TextureDict; +typedef int CoverPoint; +typedef int Camera; +typedef int TaskSequence; +typedef int ColourIndex; +typedef int Sphere; +typedef int INT, ScrHandle; +typedef int AnimScene; +typedef int Volume; +typedef int ItemSet; +typedef int PropSet; +typedef int PopZone; +typedef int PersChar; +typedef int Prompt; + +#define PLAYER_INDEX alignas(8) Player +#define ENTITY_INDEX alignas(8) Entity +#define PED_INDEX alignas(8) Ped +#define VEHICLE_INDEX alignas(8) Vehicle +#define INTERIOR_INDEX alignas(8) Interior +#define NETWORK_INDEX alignas(8) int + +#define SCR_HASH alignas(8) Hash +#define SCR_INT alignas(8) int +#define SCR_BOOL alignas(8) BOOL +#define SCR_FLOAT alignas(8) float +#define SCR_VEC3 Vector3 + +template +struct SCR_TEXT_LABEL +{ + alignas(8) char Data[SIZE]; +private: + alignas(8) char _PAD[SIZE]; +public: + operator char* () { return Data; } +}; + +#define TEXT_LABEL_15 SCR_TEXT_LABEL<16> +#define TEXT_LABEL_23 SCR_TEXT_LABEL<24> +#define TEXT_LABEL_31 SCR_TEXT_LABEL<32> +#define TEXT_LABEL_63 SCR_TEXT_LABEL<64> + +template +struct SCR_ARRAY +{ + SCR_INT Size; + alignas(8) T Data[SIZE]; + + T& operator [](int index) + { + return Data[index]; + } +}; + +template +struct SCR_BITSET +{ + alignas(8) int Value; + + bool IsSet(T val) + { + return Value & (1 << (int)val); + } + + void Set(T val) + { + Value |= (1 << (int)val); + } + + void Clear(T val) + { + Value &= ~(1 << (int)val); + } +}; + +struct Color3 +{ + SCR_INT R; + SCR_INT G; + SCR_INT B; +}; +static_assert(sizeof(Color3) == 3 * 8); + +// serialized bitbuffer data of rage::rlGamerHandle + some padding for last gen compatibility +struct GAMER_HANDLE +{ +private: + uint64_t Data[13]; +}; +static_assert(sizeof(GAMER_HANDLE) == 13 * 8);