From 0c6d1f4e79d31de6fde4979a72afacdd6d13ca60 Mon Sep 17 00:00:00 2001 From: Emma Miler Date: Mon, 14 Nov 2022 01:11:59 +0100 Subject: New syntax for reversed structs (#324) * Initial commit Co-Authored-By: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> * Format change * Revert `VPKData` definition * Add `FIELDS` macro Co-authored-by: BobTheBob <32057864+BobTheBob9@users.noreply.github.com> --- NorthstarDLL/NorthstarDLL.vcxproj | 1 + NorthstarDLL/NorthstarDLL.vcxproj.filters | 3 ++ NorthstarDLL/filesystem.h | 29 ++++++++------ NorthstarDLL/pch.h | 1 + NorthstarDLL/r2engine.h | 40 +++++--------------- NorthstarDLL/r2server.h | 24 ++++++------ NorthstarDLL/structs.h | 63 +++++++++++++++++++++++++++++++ 7 files changed, 106 insertions(+), 55 deletions(-) create mode 100644 NorthstarDLL/structs.h diff --git a/NorthstarDLL/NorthstarDLL.vcxproj b/NorthstarDLL/NorthstarDLL.vcxproj index 9d66b854..c1532157 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj +++ b/NorthstarDLL/NorthstarDLL.vcxproj @@ -558,6 +558,7 @@ + diff --git a/NorthstarDLL/NorthstarDLL.vcxproj.filters b/NorthstarDLL/NorthstarDLL.vcxproj.filters index 024274cf..8ff5f461 100644 --- a/NorthstarDLL/NorthstarDLL.vcxproj.filters +++ b/NorthstarDLL/NorthstarDLL.vcxproj.filters @@ -1494,6 +1494,9 @@ Header Files + + Header Files + Header Files\Console diff --git a/NorthstarDLL/filesystem.h b/NorthstarDLL/filesystem.h index c326b419..9a22893a 100644 --- a/NorthstarDLL/filesystem.h +++ b/NorthstarDLL/filesystem.h @@ -5,25 +5,32 @@ typedef void* FileHandle_t; #pragma pack(push, 1) -struct VPKFileEntry + +// clang-format off +OFFSET_STRUCT(VPKFileEntry) { - char* directory; - char* filename; - char* extension; - unsigned char unknown[0x38]; + STRUCT_SIZE(0x44); + FIELDS(0x0, + char* directory; + char* filename; + char* extension; + ) }; +// clang-format on #pragma pack(pop) #pragma pack(push, 1) +// clang-format off struct VPKData { - unsigned char unknown[5]; - char path[255]; - unsigned char unknown2[0x134]; - int32_t numEntries; - unsigned char unknown3[12]; - VPKFileEntry* entries; + STRUCT_SIZE(0x50); + FIELDS(0x0, + char* directory; + char* filename; + char* extension; + ) }; +// clang-format on #pragma pack(pop) enum SearchPathAdd_t diff --git a/NorthstarDLL/pch.h b/NorthstarDLL/pch.h index e07c5598..824aaee8 100644 --- a/NorthstarDLL/pch.h +++ b/NorthstarDLL/pch.h @@ -20,6 +20,7 @@ namespace fs = std::filesystem; +#include "structs.h" #include "color.h" #include "spdlog/spdlog.h" #include "logging.h" diff --git a/NorthstarDLL/r2engine.h b/NorthstarDLL/r2engine.h index e428e1cc..2614b4cc 100644 --- a/NorthstarDLL/r2engine.h +++ b/NorthstarDLL/r2engine.h @@ -157,39 +157,17 @@ namespace R2 READY_REMOTE }; -#pragma pack(push, 1) - struct CBaseClient // 0x2D728 bytes + // clang-format off + OFFSET_STRUCT(CBaseClient) { - char pad0[0x16]; - - // +0x16 - char m_Name[64]; - - // +0x56 - char pad1[0x202]; - - KeyValues* m_ConVars; // this is a KeyValues* object but not got that struct mapped out atm - - char pad2[0x240]; - - // +0x4A0 - ePersistenceReady m_iPersistenceReady; - // +0x4A1 - - char pad3[0x59]; - - // +0x4FA - char m_PersistenceBuffer[PERSISTENCE_MAX_SIZE]; - - char pad4[0x1239]; - - // +0xF500 - char m_UID[32]; - // +0xF520 - - char pad5[0x1E208]; + STRUCT_SIZE(0x2D728) + FIELD(0x16, char m_Name[64]) + FIELD(0x258, KeyValues* m_ConVars) + FIELD(0x4A0, ePersistenceReady m_iPersistenceReady) + FIELD(0x4FA, char m_PersistenceBuffer[PERSISTENCE_MAX_SIZE]) + FIELD(0xF500, char m_UID[32]) }; -#pragma pack(pop) + // clang-format on extern CBaseClient* g_pClientArray; diff --git a/NorthstarDLL/r2server.h b/NorthstarDLL/r2server.h index 235dde7f..aadfdefe 100644 --- a/NorthstarDLL/r2server.h +++ b/NorthstarDLL/r2server.h @@ -9,22 +9,20 @@ namespace R2 class CBaseEntity; extern CBaseEntity* (*Server_GetEntityByIndex)(int index); -#pragma pack(push, 1) - struct CBasePlayer + // clang-format off + OFFSET_STRUCT(CBasePlayer) { - char pad[0x58]; - uint32_t m_nPlayerIndex; + STRUCT_SIZE(0x1D02); + FIELD(0x58, uint32_t m_nPlayerIndex) - // +0x5C - char pad1[0x1C34]; - bool m_hasBadReputation; // 0x1C90 - char m_communityName[64]; // 0x1C91 - char m_communityClanTag[16]; // 0x1CD1 - char m_factionName[16]; // 0x1CE1 - char m_hardwareIcon[16]; // 0x1CF1 - bool m_happyHourActive; // 0x1D01 + FIELD(0x1C90, bool m_hasBadReputation) + FIELD(0x1C91, char m_communityName[64]) + FIELD(0x1CD1, char m_communityClanTag[16]) + FIELD(0x1CE1, char m_factionName[16]) + FIELD(0x1CF1, char m_hardwareIcon[16]) + FIELD(0x1D01, bool m_happyHourActive) }; -#pragma pack(pop) + // clang-format on extern CBasePlayer*(__fastcall* UTIL_PlayerByIndex)(int playerIndex); } // namespace R2 diff --git a/NorthstarDLL/structs.h b/NorthstarDLL/structs.h new file mode 100644 index 00000000..939fd302 --- /dev/null +++ b/NorthstarDLL/structs.h @@ -0,0 +1,63 @@ +#pragma once +//clang-format off +// About this file: +// This file contains several macros used to define reversed structs +// The reason we use these macros is to make it easier to update existing structs +// when new fields are reversed +// This means we dont have to manually add padding, and recalculate when updating + +// Technical note: +// While functionally, these structs act like a regular struct, they are actually +// defined as unions with anonymous structs in them. +// This means that each field is essentially an offset into a union. +// We acknowledge that this goes against C++'s active-member guideline for unions +// However, this is not such a big deal here as these structs will not be constructed + +// Usage: +// To use these macros, define a struct like so: +/* +OFFSET_STRUCT(Name) +{ + STRUCT_SIZE(0x100) // Total in-memory struct size + FIELD(0x0, int field) // offset, signature +} +*/ + +#define OFFSET_STRUCT(name) union name +#define STRUCT_SIZE(size) char __size[size]; +#define STRUCT_FIELD_OFFSET(offset, signature) \ + struct \ + { \ + char CONCAT2(pad, __LINE__)[offset]; \ + signature; \ + }; + +// Special case for a 0-offset field +#define STRUCT_FIELD_NOOFFSET(offset, signature) signature; + +// Based on: https://stackoverflow.com/questions/11632219/c-preprocessor-macro-specialisation-based-on-an-argument +// Yes, this is hacky, but it works quite well actually +// This basically makes sure that when the offset is 0x0, no padding field gets generated +#define OFFSET_0x0 () + +#define IIF(c) CONCAT2(IIF_, c) +#define IIF_0(t, ...) __VA_ARGS__ +#define IIF_1(t, ...) t + +#define PROBE(x) x, 1 + +#define MSVC_VA_ARGS_WORKAROUND(define, args) define args +#define CHECK(...) MSVC_VA_ARGS_WORKAROUND(CHECK_N, (__VA_ARGS__, 0)) +#define CHECK_N(x, n, ...) n + +#define DO_PROBE(offset) PROBE_PROXY(OFFSET_##offset) // concatenate prefix with offset +#define PROBE_PROXY(...) PROBE_PRIMITIVE(__VA_ARGS__) // expand arguments +#define PROBE_PRIMITIVE(x) PROBE_COMBINE_##x // merge +#define PROBE_COMBINE_(...) PROBE(~) // if merge successful, expand to probe + +#define IS_0(offset) CHECK(DO_PROBE(offset)) + +#define FIELD(offset, signature) IIF(IS_0(offset))(STRUCT_FIELD_NOOFFSET, STRUCT_FIELD_OFFSET)(offset, signature) +#define FIELDS FIELD + +//clang-format on -- cgit v1.2.3