From 6c9e28ff4237987a0a80cdcb7d4aa2100622f2cd Mon Sep 17 00:00:00 2001 From: /dev/root Date: Mon, 16 Jan 2017 17:42:50 +0100 Subject: [PATCH] [LineEndings] Fix crlf lineendings - added gitattributes file to force crlf for hpp/cpp/lua --- .gitattributes | 3 + src/Components/Modules/AntiCheat.cpp | 1040 +-- src/Components/Modules/AntiCheat.hpp | 140 +- src/Components/Modules/ArenaLength.cpp | 232 +- src/Components/Modules/ArenaLength.hpp | 40 +- src/Components/Modules/AssetHandler.cpp | 878 +- src/Components/Modules/AssetHandler.hpp | 198 +- .../Modules/AssetInterfaces/IComWorld.cpp | 124 +- .../Modules/AssetInterfaces/IFxEffectDef.cpp | 8 +- .../Modules/AssetInterfaces/IGameWorldMp.cpp | 152 +- .../Modules/AssetInterfaces/IGameWorldSp.cpp | 620 +- .../Modules/AssetInterfaces/IGfxImage.cpp | 362 +- .../Modules/AssetInterfaces/IGfxLightDef.cpp | 80 +- .../Modules/AssetInterfaces/IGfxWorld.cpp | 2654 +++--- .../Modules/AssetInterfaces/IGfxWorld.hpp | 44 +- .../Modules/AssetInterfaces/ILoadedSound.cpp | 90 +- .../Modules/AssetInterfaces/IMapEnts.cpp | 264 +- .../Modules/AssetInterfaces/IMaterial.cpp | 930 +- .../Modules/AssetInterfaces/IRawFile.cpp | 120 +- .../Modules/AssetInterfaces/ISndCurve.cpp | 48 +- .../Modules/AssetInterfaces/IXAnimParts.cpp | 708 +- .../Modules/AssetInterfaces/IXModel.cpp | 696 +- .../Modules/AssetInterfaces/IclipMap_t.cpp | 1734 ++-- .../AssetInterfaces/Isnd_alias_list_t.cpp | 338 +- src/Components/Modules/Auth.cpp | 1028 +-- src/Components/Modules/Auth.hpp | 104 +- src/Components/Modules/Bans.cpp | 424 +- src/Components/Modules/Bans.hpp | 62 +- src/Components/Modules/Bots.cpp | 108 +- src/Components/Modules/Bots.hpp | 36 +- src/Components/Modules/Colors.cpp | 508 +- src/Components/Modules/Colors.hpp | 80 +- src/Components/Modules/Command.hpp | 146 +- src/Components/Modules/ConnectProtocol.cpp | 484 +- src/Components/Modules/ConnectProtocol.hpp | 48 +- src/Components/Modules/Console.cpp | 1236 +-- src/Components/Modules/Console.hpp | 138 +- src/Components/Modules/D3D9Ex.hpp | 348 +- src/Components/Modules/Dedicated.cpp | 952 +-- src/Components/Modules/Dedicated.hpp | 86 +- src/Components/Modules/Discovery.cpp | 216 +- src/Components/Modules/Discovery.hpp | 42 +- src/Components/Modules/Download.cpp | 1190 +-- src/Components/Modules/Download.hpp | 194 +- src/Components/Modules/Dvar.cpp | 414 +- src/Components/Modules/Dvar.hpp | 120 +- src/Components/Modules/Exception.cpp | 352 +- src/Components/Modules/Exception.hpp | 46 +- src/Components/Modules/FastFiles.cpp | 1036 +-- src/Components/Modules/FastFiles.hpp | 130 +- src/Components/Modules/FileSystem.cpp | 648 +- src/Components/Modules/FileSystem.hpp | 234 +- src/Components/Modules/Flags.cpp | 98 +- src/Components/Modules/Flags.hpp | 40 +- src/Components/Modules/FrameTime.cpp | 262 +- src/Components/Modules/FrameTime.hpp | 44 +- src/Components/Modules/Gametypes.cpp | 224 +- src/Components/Modules/Gametypes.hpp | 38 +- src/Components/Modules/IPCPipe.cpp | 466 +- src/Components/Modules/IPCPipe.hpp | 156 +- src/Components/Modules/Lean.cpp | 138 +- src/Components/Modules/Lean.hpp | 56 +- src/Components/Modules/Localization.cpp | 400 +- src/Components/Modules/Localization.hpp | 60 +- src/Components/Modules/Logger.cpp | 650 +- src/Components/Modules/Logger.hpp | 88 +- src/Components/Modules/Maps.cpp | 1250 +-- src/Components/Modules/Maps.hpp | 118 +- src/Components/Modules/Materials.cpp | 378 +- src/Components/Modules/Materials.hpp | 68 +- src/Components/Modules/Menus.cpp | 1418 +-- src/Components/Modules/Menus.hpp | 120 +- src/Components/Modules/ModList.cpp | 212 +- src/Components/Modules/ModList.hpp | 54 +- src/Components/Modules/ModelSurfs.cpp | 696 +- src/Components/Modules/ModelSurfs.hpp | 70 +- src/Components/Modules/MusicalTalent.cpp | 94 +- src/Components/Modules/MusicalTalent.hpp | 38 +- src/Components/Modules/Network.cpp | 756 +- src/Components/Modules/Network.hpp | 192 +- src/Components/Modules/News.cpp | 398 +- src/Components/Modules/News.hpp | 48 +- src/Components/Modules/Node.hpp | 190 +- src/Components/Modules/Party.cpp | 918 +- src/Components/Modules/Party.hpp | 106 +- src/Components/Modules/PlayerName.cpp | 66 +- src/Components/Modules/PlayerName.hpp | 36 +- src/Components/Modules/Playlist.cpp | 368 +- src/Components/Modules/Playlist.hpp | 64 +- src/Components/Modules/QuickPatch.hpp | 76 +- src/Components/Modules/RCon.cpp | 338 +- src/Components/Modules/RCon.hpp | 62 +- src/Components/Modules/RawFiles.cpp | 106 +- src/Components/Modules/RawFiles.hpp | 28 +- src/Components/Modules/Renderer.cpp | 298 +- src/Components/Modules/Renderer.hpp | 84 +- src/Components/Modules/Script.cpp | 486 +- src/Components/Modules/Script.hpp | 72 +- src/Components/Modules/ServerInfo.cpp | 590 +- src/Components/Modules/ServerInfo.hpp | 86 +- src/Components/Modules/ServerList.cpp | 1486 ++-- src/Components/Modules/ServerList.hpp | 270 +- src/Components/Modules/Singleton.cpp | 64 +- src/Components/Modules/Singleton.hpp | 34 +- src/Components/Modules/Slowmotion.cpp | 172 +- src/Components/Modules/Slowmotion.hpp | 44 +- src/Components/Modules/StringTable.cpp | 196 +- src/Components/Modules/StringTable.hpp | 40 +- src/Components/Modules/StructuredData.cpp | 436 +- src/Components/Modules/StructuredData.hpp | 74 +- src/Components/Modules/Theatre.cpp | 766 +- src/Components/Modules/Theatre.hpp | 140 +- src/Components/Modules/Threading.cpp | 94 +- src/Components/Modules/Threading.hpp | 32 +- src/Components/Modules/Toast.cpp | 280 +- src/Components/Modules/Toast.hpp | 64 +- src/Components/Modules/UIFeeder.cpp | 616 +- src/Components/Modules/UIFeeder.hpp | 112 +- src/Components/Modules/UIScript.cpp | 262 +- src/Components/Modules/UIScript.hpp | 88 +- src/Components/Modules/Weapon.cpp | 62 +- src/Components/Modules/Weapon.hpp | 30 +- src/Components/Modules/Window.cpp | 328 +- src/Components/Modules/Window.hpp | 72 +- src/Components/Modules/ZoneBuilder.cpp | 1744 ++-- src/Components/Modules/ZoneBuilder.hpp | 230 +- src/Components/Modules/Zones.cpp | 3318 +++---- src/Components/Modules/Zones.hpp | 136 +- src/Game/Functions.cpp | 1338 +-- src/Game/Functions.hpp | 1428 ++-- src/Game/Structs.hpp | 7604 ++++++++--------- src/Main.cpp | 168 +- src/STDInclude.hpp | 324 +- src/Steam/Interfaces/SteamMatchmaking.cpp | 394 +- src/Steam/Interfaces/SteamUser.cpp | 208 +- src/Steam/Interfaces/SteamUtils.cpp | 202 +- src/Steam/Proxy.cpp | 340 +- src/Steam/Proxy.hpp | 712 +- src/Steam/Steam.cpp | 362 +- src/Steam/Steam.hpp | 220 +- src/Utils/CSV.cpp | 288 +- src/Utils/Chain.hpp | 294 +- src/Utils/Compression.cpp | 148 +- src/Utils/Cryptography.cpp | 570 +- src/Utils/Cryptography.hpp | 672 +- src/Utils/Hooking.cpp | 502 +- src/Utils/IO.cpp | 148 +- src/Utils/IO.hpp | 22 +- src/Utils/InfoString.cpp | 132 +- src/Utils/InfoString.hpp | 46 +- src/Utils/Library.cpp | 58 +- src/Utils/Library.hpp | 56 +- src/Utils/Memory.cpp | 134 +- src/Utils/Memory.hpp | 264 +- src/Utils/Stream.cpp | 708 +- src/Utils/Stream.hpp | 16 +- src/Utils/String.cpp | 414 +- src/Utils/String.hpp | 64 +- src/Utils/Time.cpp | 34 +- src/Utils/Time.hpp | 34 +- src/Utils/Utils.cpp | 90 +- src/Utils/Utils.hpp | 166 +- src/Utils/WebIO.cpp | 1084 +-- src/Utils/WebIO.hpp | 216 +- 164 files changed, 32585 insertions(+), 32582 deletions(-) create mode 100644 .gitattributes diff --git a/.gitattributes b/.gitattributes new file mode 100644 index 00000000..a4254333 --- /dev/null +++ b/.gitattributes @@ -0,0 +1,3 @@ +*.hpp eol=crlf +*.cpp eol=crlf +*.lua eol=crlf diff --git a/src/Components/Modules/AntiCheat.cpp b/src/Components/Modules/AntiCheat.cpp index 31c1fa43..3f8844ea 100644 --- a/src/Components/Modules/AntiCheat.cpp +++ b/src/Components/Modules/AntiCheat.cpp @@ -1,520 +1,520 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Utils::Time::Interval AntiCheat::LastCheck; - std::string AntiCheat::Hash; - Utils::Hook AntiCheat::LoadLibHook[4]; - unsigned long AntiCheat::Flags = NO_FLAG; - - // This function does nothing, it only adds the two passed variables and returns the value - // The only important thing it does is to clean the first parameter, and then return - // By returning, the crash procedure will be called, as it hasn't been cleaned from the stack - __declspec(naked) void AntiCheat::NullSub() - { - __asm - { - push ebp - push ecx - mov ebp, esp - - xor eax, eax - mov eax, [ebp + 8h] - mov ecx, [ebp + 0Ch] - add eax, ecx - - pop ecx - pop ebp - retn 4 - } - } - - void AntiCheat::CrashClient() - { -#ifdef DEBUG_DETECTIONS - Logger::Flush(); - MessageBoxA(0, "Check the log for more information!", "AntiCheat triggered", MB_ICONERROR); - ExitProcess(0xFFFFFFFF); -#else - static std::thread triggerThread; - if (!triggerThread.joinable()) - { - triggerThread = std::thread([] () - { - std::this_thread::sleep_for(43s); - Utils::Hook::Set(0x41BA2C, 0xEB); - }); - } -#endif - } - - void AntiCheat::AssertCalleeModule(void* callee) - { - HMODULE hModuleSelf = nullptr, hModuleTarget = nullptr, hModuleProcess = GetModuleHandleA(NULL); - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(callee), &hModuleTarget); - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(AntiCheat::AssertCalleeModule), &hModuleSelf); - - if (!hModuleSelf || !hModuleTarget || !hModuleProcess || (hModuleTarget != hModuleSelf && hModuleTarget != hModuleProcess)) - { -#ifdef DEBUG_DETECTIONS - char buffer[MAX_PATH] = { 0 }; - GetModuleFileNameA(hModuleTarget, buffer, sizeof buffer); - - Logger::Print(Utils::String::VA("AntiCheat: Callee assertion failed: %X %s", reinterpret_cast(callee), buffer)); -#endif - - AntiCheat::CrashClient(); - } - } - - void AntiCheat::InitLoadLibHook() - { - static uint8_t loadLibStub[] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 }; // xor eax, eax; retn 04h - static uint8_t loadLibExStub[] = { 0x33, 0xC0, 0xC2, 0x0C, 0x00 }; // xor eax, eax; retn 0Ch - - static uint8_t kernel32Str[] = { 0xB4, 0x9A, 0x8D, 0xB1, 0x9A, 0x93, 0xCC, 0xCD, 0xD1, 0x9B, 0x93, 0x93 }; // KerNel32.dll - static uint8_t loadLibAStr[] = { 0xB3, 0x90, 0x9E, 0x9B, 0xB3, 0x96, 0x9D, 0x8D, 0x9E, 0x8D, 0x86, 0xBE }; // LoadLibraryA - static uint8_t loadLibWStr[] = { 0xB3, 0x90, 0x9E, 0x9B, 0xB3, 0x96, 0x9D, 0x8D, 0x9E, 0x8D, 0x86, 0xA8 }; // LoadLibraryW - - HMODULE kernel32 = GetModuleHandleA(Utils::String::XOR(std::string(reinterpret_cast(kernel32Str), sizeof kernel32Str), -1).data()); - - if (kernel32) - { - FARPROC loadLibA = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast(loadLibAStr), sizeof loadLibAStr), -1).data()); - FARPROC loadLibW = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast(loadLibWStr), sizeof loadLibWStr), -1).data()); - - if (loadLibA && loadLibW) - { -#ifdef DEBUG_LOAD_LIBRARY - AntiCheat::LoadLibHook[0].initialize(loadLibA, LoadLibaryAStub, HOOK_JUMP); - AntiCheat::LoadLibHook[1].initialize(loadLibW, LoadLibaryWStub, HOOK_JUMP); -#else - AntiCheat::LoadLibHook[0].initialize(loadLibA, loadLibStub, HOOK_JUMP); - AntiCheat::LoadLibHook[1].initialize(loadLibW, loadLibStub, HOOK_JUMP); -#endif - //AntiCheat::LoadLibHook[2].initialize(LoadLibraryExA, loadLibExStub, HOOK_JUMP); - //AntiCheat::LoadLibHook[3].initialize(LoadLibraryExW, loadLibExStub, HOOK_JUMP); - } - } - } - - void AntiCheat::ReadIntegrityCheck() - { - static Utils::Time::Interval check; - - if(check.elapsed(20s)) - { - check.update(); - - if (HANDLE h = OpenProcess(PROCESS_VM_READ, TRUE, GetCurrentProcessId())) - { -#ifdef DEBUG_DETECTIONS - Logger::Print("AntiCheat: Process integrity check failed"); -#endif - - CloseHandle(h); - AntiCheat::CrashClient(); - } - } - - // Set the integrity flag - AntiCheat::Flags |= AntiCheat::IntergrityFlag::READ_INTEGRITY_CHECK; - } - - void AntiCheat::FlagIntegrityCheck() - { - static Utils::Time::Interval check; - - if (check.elapsed(30s)) - { - check.update(); - - unsigned long flags = ((AntiCheat::IntergrityFlag::MAX_FLAG - 1) << 1) - 1; - - if (AntiCheat::Flags != flags) - { -#ifdef DEBUG_DETECTIONS - Logger::Print(Utils::String::VA("AntiCheat: Flag integrity check failed: %X", AntiCheat::Flags)); -#endif - - AntiCheat::CrashClient(); - } - } - } - - void AntiCheat::ScanIntegrityCheck() - { - // If there was no check within the last 40 seconds, crash! - if (AntiCheat::LastCheck.elapsed(40s)) - { -#ifdef DEBUG_DETECTIONS - Logger::Print("AntiCheat: Integrity check failed"); -#endif - - AntiCheat::CrashClient(); - } - - // Set the integrity flag - AntiCheat::Flags |= AntiCheat::IntergrityFlag::SCAN_INTEGRITY_CHECK; - } - - void AntiCheat::PerformScan() - { - // Perform check only every 10 seconds - if (!AntiCheat::LastCheck.elapsed(10s)) return; - AntiCheat::LastCheck.update(); - - // Hash .text segment - // Add 1 to each value, so searching in memory doesn't reveal anything - size_t textSize = 0x2D6001; - uint8_t* textBase = reinterpret_cast(0x401001); - std::string hash = Utils::Cryptography::SHA512::Compute(textBase - 1, textSize - 1, false); - - // Set the hash, if none is set - if (AntiCheat::Hash.empty()) - { - AntiCheat::Hash = hash; - } - // Crash if the hashes don't match - else if (AntiCheat::Hash != hash) - { -#ifdef DEBUG_DETECTIONS - Logger::Print("AntiCheat: Memory scan failed"); -#endif - - AntiCheat::CrashClient(); - } - - // Set the memory scan flag - AntiCheat::Flags |= AntiCheat::IntergrityFlag::MEMORY_SCAN; - } - -#ifdef DEBUG_LOAD_LIBRARY - HANDLE AntiCheat::LoadLibary(std::wstring library, void* callee) - { - HMODULE module; - char buffer[MAX_PATH] = { 0 }; - - GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(callee), &module); - GetModuleFileNameA(module, buffer, sizeof buffer); - - MessageBoxA(0, Utils::String::VA("Loading library %s via %s %X", std::string(library.begin(), library.end()).data(), buffer, reinterpret_cast(callee)), 0, 0); - - return LoadLibraryExW(library.data(), NULL, 0); - } - - HANDLE WINAPI AntiCheat::LoadLibaryAStub(const char* library) - { - std::string lib(library); - return AntiCheat::LoadLibary(std::wstring(lib.begin(), lib.end()), _ReturnAddress()); - } - - HANDLE WINAPI AntiCheat::LoadLibaryWStub(const wchar_t* library) - { - return AntiCheat::LoadLibary(library, _ReturnAddress()); - } -#endif - - void AntiCheat::UninstallLibHook() - { - for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i) - { - AntiCheat::LoadLibHook[i].uninstall(); - } - } - - void AntiCheat::InstallLibHook() - { - AntiCheat::LoadLibHook[0].install(); - AntiCheat::LoadLibHook[1].install(); - //AntiCheat::LoadLibHook[2].install(); - //AntiCheat::LoadLibHook[3].install(); - } - - void AntiCheat::PatchWinAPI() - { - AntiCheat::UninstallLibHook(); - - // Initialize directx - Utils::Hook::Call(0x5078C0)(); - - AntiCheat::InstallLibHook(); - } - - void AntiCheat::SoundInitStub(int a1, int a2, int a3) - { - AntiCheat::UninstallLibHook(); - - Game::SND_Init(a1, a2, a3); - - AntiCheat::InstallLibHook(); - } - - void AntiCheat::SoundInitDriverStub() - { - AntiCheat::UninstallLibHook(); - - Game::SND_InitDriver(); - - AntiCheat::InstallLibHook(); - } - - void AntiCheat::LostD3DStub() - { - AntiCheat::UninstallLibHook(); - - // Reset directx - Utils::Hook::Call(0x508070)(); - - AntiCheat::InstallLibHook(); - } - - __declspec(naked) void AntiCheat::CinematicStub() - { - __asm - { - pushad - call AntiCheat::UninstallLibHook - popad - - call Game::R_Cinematic_StartPlayback_Now - - pushad - call AntiCheat::InstallLibHook - popad - - retn - } - } - - __declspec(naked) void AntiCheat::DObjGetWorldTagPosStub() - { - __asm - { - pushad - push [esp + 20h] - - call AntiCheat::AssertCalleeModule - - pop esi - popad - - push ecx - mov ecx, [esp + 10h] - - push 426585h - retn - } - } - - __declspec(naked) void AntiCheat::AimTargetGetTagPosStub() - { - __asm - { - pushad - push [esp + 20h] - - call AntiCheat::AssertCalleeModule - - pop esi - popad - - sub esp, 14h - cmp dword ptr[esi + 0E0h], 1 - push 56AC6Ah - ret - } - } - - unsigned long AntiCheat::ProtectProcess() - { - Utils::Memory::Allocator allocator; - - HANDLE hToken = nullptr; - if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) - { - if (!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hToken)) - { - return GetLastError(); - } - } - - auto freeSid = [] (void* sid) - { - if (sid) - { - FreeSid(reinterpret_cast(sid)); - } - }; - - allocator.reference(hToken, [] (void* hToken) - { - if (hToken) - { - CloseHandle(hToken); - } - }); - - DWORD dwSize = 0; - PVOID pTokenInfo = nullptr; - if (GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) return GetLastError(); - - if (dwSize) - { - pTokenInfo = allocator.allocate(dwSize); - if (!pTokenInfo) return GetLastError(); - } - - if (!GetTokenInformation(hToken, TokenUser, pTokenInfo, dwSize, &dwSize) || !pTokenInfo) return GetLastError(); - - PSID psidCurUser = reinterpret_cast(pTokenInfo)->User.Sid; - - PSID psidEveryone = nullptr; - SID_IDENTIFIER_AUTHORITY sidEveryone = SECURITY_WORLD_SID_AUTHORITY; - if (!AllocateAndInitializeSid(&sidEveryone, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidEveryone) || !psidEveryone) return GetLastError(); - allocator.reference(psidEveryone, freeSid); - - PSID psidSystem = nullptr; - SID_IDENTIFIER_AUTHORITY sidSystem = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&sidSystem, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidSystem) || !psidSystem) return GetLastError(); - allocator.reference(psidSystem, freeSid); - - PSID psidAdmins = nullptr; - SID_IDENTIFIER_AUTHORITY sidAdministrators = SECURITY_NT_AUTHORITY; - if (!AllocateAndInitializeSid(&sidAdministrators, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmins) || !psidAdmins) return GetLastError(); - allocator.reference(psidAdmins, freeSid); - - const PSID psidArray[] = - { - psidEveryone, /* Deny most rights to everyone */ - psidCurUser, /* Allow what was not denied */ - psidSystem, /* Full control */ - psidAdmins, /* Full control */ - }; - - // Determine required size of the ACL - dwSize = sizeof(ACL); - - // First the DENY, then the ALLOW - dwSize += GetLengthSid(psidArray[0]); - dwSize += sizeof(ACCESS_DENIED_ACE) - sizeof(DWORD); - - for (UINT i = 1; i < _countof(psidArray); ++i) - { - // DWORD is the SidStart field, which is not used for absolute format - dwSize += GetLengthSid(psidArray[i]); - dwSize += sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD); - } - - PACL pDacl = reinterpret_cast(allocator.allocate(dwSize)); - if (!pDacl || !InitializeAcl(pDacl, dwSize, ACL_REVISION)) return GetLastError(); - - // Mimic Protected Process - // http://www.microsoft.com/whdc/system/vista/process_vista.mspx - // Protected processes allow PROCESS_TERMINATE, which is - // probably not appropriate for high integrity software. - static const DWORD dwPoison = - /*READ_CONTROL |*/ WRITE_DAC | WRITE_OWNER | - PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD | - PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | - PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION | - PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | - // In addition to protected process - PROCESS_SUSPEND_RESUME | PROCESS_TERMINATE; - - if (!AddAccessDeniedAce(pDacl, ACL_REVISION, dwPoison, psidArray[0])) return GetLastError(); - - // Standard and specific rights not explicitly denied - static const DWORD dwAllowed = ~dwPoison & 0x1FFF; - if (!AddAccessAllowedAce(pDacl, ACL_REVISION, dwAllowed, psidArray[1])) return GetLastError(); - - // Because of ACE ordering, System will effectively have dwAllowed even - // though the ACE specifies PROCESS_ALL_ACCESS (unless software uses - // SeDebugPrivilege or SeTcbName and increases access). - // As an exercise, check behavior of tools such as Process Explorer under XP, - // Vista, and above. Vista and above should exhibit slightly different behavior - // due to Restricted tokens. - if (!AddAccessAllowedAce(pDacl, ACL_REVISION, PROCESS_ALL_ACCESS, psidArray[2])) return GetLastError(); - - // Because of ACE ordering, Administrators will effectively have dwAllowed - // even though the ACE specifies PROCESS_ALL_ACCESS (unless the Administrator - // invokes 'discretionary security' by taking ownership and increasing access). - // As an exercise, check behavior of tools such as Process Explorer under XP, - // Vista, and above. Vista and above should exhibit slightly different behavior - // due to Restricted tokens. - if (!AddAccessAllowedAce(pDacl, ACL_REVISION, PROCESS_ALL_ACCESS, psidArray[3])) return GetLastError(); - - PSECURITY_DESCRIPTOR pSecDesc = allocator.allocate(); - if (!pSecDesc) return GetLastError(); - - // InitializeSecurityDescriptor initializes a security descriptor in - // absolute format, rather than self-relative format. See - // http://msdn.microsoft.com/en-us/library/aa378863(VS.85).aspx - if (!InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION)) return GetLastError(); - if (!SetSecurityDescriptorDacl(pSecDesc, TRUE, pDacl, FALSE)) return GetLastError(); - - return SetSecurityInfo( - GetCurrentProcess(), - SE_KERNEL_OBJECT, // process object - OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, - psidCurUser, // NULL, // Owner SID - NULL, // Group SID - pDacl, - NULL // SACL - ); - } - - AntiCheat::AntiCheat() - { - AntiCheat::Flags = NO_FLAG; - AntiCheat::Hash.clear(); - -#ifdef DEBUG - Command::Add("penis", [] (Command::Params*) - { - AntiCheat::CrashClient(); - }); -#else - - Utils::Hook(0x507BD5, AntiCheat::PatchWinAPI, HOOK_CALL).install()->quick(); - Utils::Hook(0x5082FD, AntiCheat::LostD3DStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x51C76C, AntiCheat::CinematicStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x418209, AntiCheat::SoundInitStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x60BE9D, AntiCheat::SoundInitStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x60BE8E, AntiCheat::SoundInitDriverStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x418204, AntiCheat::SoundInitDriverStub, HOOK_CALL).install()->quick(); - Renderer::OnFrame(AntiCheat::PerformScan); - - // Detect aimbots - Utils::Hook(0x426580, AntiCheat::DObjGetWorldTagPosStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x56AC60, AntiCheat::AimTargetGetTagPosStub, HOOK_JUMP).install()->quick(); - - // TODO: Probably move that :P - if (!Dedicated::IsEnabled()) - { - AntiCheat::InitLoadLibHook(); - } - - // Prevent external processes from accessing our memory - AntiCheat::ProtectProcess(); - Renderer::OnDeviceRecoveryEnd([] () - { - AntiCheat::ProtectProcess(); - }); - - // Set the integrity flag - AntiCheat::Flags |= AntiCheat::IntergrityFlag::INITIALIZATION; -#endif - } - - AntiCheat::~AntiCheat() - { - AntiCheat::Flags = NO_FLAG; - AntiCheat::Hash.clear(); - - for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i) - { - AntiCheat::LoadLibHook[i].uninstall(); - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Utils::Time::Interval AntiCheat::LastCheck; + std::string AntiCheat::Hash; + Utils::Hook AntiCheat::LoadLibHook[4]; + unsigned long AntiCheat::Flags = NO_FLAG; + + // This function does nothing, it only adds the two passed variables and returns the value + // The only important thing it does is to clean the first parameter, and then return + // By returning, the crash procedure will be called, as it hasn't been cleaned from the stack + __declspec(naked) void AntiCheat::NullSub() + { + __asm + { + push ebp + push ecx + mov ebp, esp + + xor eax, eax + mov eax, [ebp + 8h] + mov ecx, [ebp + 0Ch] + add eax, ecx + + pop ecx + pop ebp + retn 4 + } + } + + void AntiCheat::CrashClient() + { +#ifdef DEBUG_DETECTIONS + Logger::Flush(); + MessageBoxA(0, "Check the log for more information!", "AntiCheat triggered", MB_ICONERROR); + ExitProcess(0xFFFFFFFF); +#else + static std::thread triggerThread; + if (!triggerThread.joinable()) + { + triggerThread = std::thread([] () + { + std::this_thread::sleep_for(43s); + Utils::Hook::Set(0x41BA2C, 0xEB); + }); + } +#endif + } + + void AntiCheat::AssertCalleeModule(void* callee) + { + HMODULE hModuleSelf = nullptr, hModuleTarget = nullptr, hModuleProcess = GetModuleHandleA(NULL); + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(callee), &hModuleTarget); + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(AntiCheat::AssertCalleeModule), &hModuleSelf); + + if (!hModuleSelf || !hModuleTarget || !hModuleProcess || (hModuleTarget != hModuleSelf && hModuleTarget != hModuleProcess)) + { +#ifdef DEBUG_DETECTIONS + char buffer[MAX_PATH] = { 0 }; + GetModuleFileNameA(hModuleTarget, buffer, sizeof buffer); + + Logger::Print(Utils::String::VA("AntiCheat: Callee assertion failed: %X %s", reinterpret_cast(callee), buffer)); +#endif + + AntiCheat::CrashClient(); + } + } + + void AntiCheat::InitLoadLibHook() + { + static uint8_t loadLibStub[] = { 0x33, 0xC0, 0xC2, 0x04, 0x00 }; // xor eax, eax; retn 04h + static uint8_t loadLibExStub[] = { 0x33, 0xC0, 0xC2, 0x0C, 0x00 }; // xor eax, eax; retn 0Ch + + static uint8_t kernel32Str[] = { 0xB4, 0x9A, 0x8D, 0xB1, 0x9A, 0x93, 0xCC, 0xCD, 0xD1, 0x9B, 0x93, 0x93 }; // KerNel32.dll + static uint8_t loadLibAStr[] = { 0xB3, 0x90, 0x9E, 0x9B, 0xB3, 0x96, 0x9D, 0x8D, 0x9E, 0x8D, 0x86, 0xBE }; // LoadLibraryA + static uint8_t loadLibWStr[] = { 0xB3, 0x90, 0x9E, 0x9B, 0xB3, 0x96, 0x9D, 0x8D, 0x9E, 0x8D, 0x86, 0xA8 }; // LoadLibraryW + + HMODULE kernel32 = GetModuleHandleA(Utils::String::XOR(std::string(reinterpret_cast(kernel32Str), sizeof kernel32Str), -1).data()); + + if (kernel32) + { + FARPROC loadLibA = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast(loadLibAStr), sizeof loadLibAStr), -1).data()); + FARPROC loadLibW = GetProcAddress(kernel32, Utils::String::XOR(std::string(reinterpret_cast(loadLibWStr), sizeof loadLibWStr), -1).data()); + + if (loadLibA && loadLibW) + { +#ifdef DEBUG_LOAD_LIBRARY + AntiCheat::LoadLibHook[0].initialize(loadLibA, LoadLibaryAStub, HOOK_JUMP); + AntiCheat::LoadLibHook[1].initialize(loadLibW, LoadLibaryWStub, HOOK_JUMP); +#else + AntiCheat::LoadLibHook[0].initialize(loadLibA, loadLibStub, HOOK_JUMP); + AntiCheat::LoadLibHook[1].initialize(loadLibW, loadLibStub, HOOK_JUMP); +#endif + //AntiCheat::LoadLibHook[2].initialize(LoadLibraryExA, loadLibExStub, HOOK_JUMP); + //AntiCheat::LoadLibHook[3].initialize(LoadLibraryExW, loadLibExStub, HOOK_JUMP); + } + } + } + + void AntiCheat::ReadIntegrityCheck() + { + static Utils::Time::Interval check; + + if(check.elapsed(20s)) + { + check.update(); + + if (HANDLE h = OpenProcess(PROCESS_VM_READ, TRUE, GetCurrentProcessId())) + { +#ifdef DEBUG_DETECTIONS + Logger::Print("AntiCheat: Process integrity check failed"); +#endif + + CloseHandle(h); + AntiCheat::CrashClient(); + } + } + + // Set the integrity flag + AntiCheat::Flags |= AntiCheat::IntergrityFlag::READ_INTEGRITY_CHECK; + } + + void AntiCheat::FlagIntegrityCheck() + { + static Utils::Time::Interval check; + + if (check.elapsed(30s)) + { + check.update(); + + unsigned long flags = ((AntiCheat::IntergrityFlag::MAX_FLAG - 1) << 1) - 1; + + if (AntiCheat::Flags != flags) + { +#ifdef DEBUG_DETECTIONS + Logger::Print(Utils::String::VA("AntiCheat: Flag integrity check failed: %X", AntiCheat::Flags)); +#endif + + AntiCheat::CrashClient(); + } + } + } + + void AntiCheat::ScanIntegrityCheck() + { + // If there was no check within the last 40 seconds, crash! + if (AntiCheat::LastCheck.elapsed(40s)) + { +#ifdef DEBUG_DETECTIONS + Logger::Print("AntiCheat: Integrity check failed"); +#endif + + AntiCheat::CrashClient(); + } + + // Set the integrity flag + AntiCheat::Flags |= AntiCheat::IntergrityFlag::SCAN_INTEGRITY_CHECK; + } + + void AntiCheat::PerformScan() + { + // Perform check only every 10 seconds + if (!AntiCheat::LastCheck.elapsed(10s)) return; + AntiCheat::LastCheck.update(); + + // Hash .text segment + // Add 1 to each value, so searching in memory doesn't reveal anything + size_t textSize = 0x2D6001; + uint8_t* textBase = reinterpret_cast(0x401001); + std::string hash = Utils::Cryptography::SHA512::Compute(textBase - 1, textSize - 1, false); + + // Set the hash, if none is set + if (AntiCheat::Hash.empty()) + { + AntiCheat::Hash = hash; + } + // Crash if the hashes don't match + else if (AntiCheat::Hash != hash) + { +#ifdef DEBUG_DETECTIONS + Logger::Print("AntiCheat: Memory scan failed"); +#endif + + AntiCheat::CrashClient(); + } + + // Set the memory scan flag + AntiCheat::Flags |= AntiCheat::IntergrityFlag::MEMORY_SCAN; + } + +#ifdef DEBUG_LOAD_LIBRARY + HANDLE AntiCheat::LoadLibary(std::wstring library, void* callee) + { + HMODULE module; + char buffer[MAX_PATH] = { 0 }; + + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS, reinterpret_cast(callee), &module); + GetModuleFileNameA(module, buffer, sizeof buffer); + + MessageBoxA(0, Utils::String::VA("Loading library %s via %s %X", std::string(library.begin(), library.end()).data(), buffer, reinterpret_cast(callee)), 0, 0); + + return LoadLibraryExW(library.data(), NULL, 0); + } + + HANDLE WINAPI AntiCheat::LoadLibaryAStub(const char* library) + { + std::string lib(library); + return AntiCheat::LoadLibary(std::wstring(lib.begin(), lib.end()), _ReturnAddress()); + } + + HANDLE WINAPI AntiCheat::LoadLibaryWStub(const wchar_t* library) + { + return AntiCheat::LoadLibary(library, _ReturnAddress()); + } +#endif + + void AntiCheat::UninstallLibHook() + { + for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i) + { + AntiCheat::LoadLibHook[i].uninstall(); + } + } + + void AntiCheat::InstallLibHook() + { + AntiCheat::LoadLibHook[0].install(); + AntiCheat::LoadLibHook[1].install(); + //AntiCheat::LoadLibHook[2].install(); + //AntiCheat::LoadLibHook[3].install(); + } + + void AntiCheat::PatchWinAPI() + { + AntiCheat::UninstallLibHook(); + + // Initialize directx + Utils::Hook::Call(0x5078C0)(); + + AntiCheat::InstallLibHook(); + } + + void AntiCheat::SoundInitStub(int a1, int a2, int a3) + { + AntiCheat::UninstallLibHook(); + + Game::SND_Init(a1, a2, a3); + + AntiCheat::InstallLibHook(); + } + + void AntiCheat::SoundInitDriverStub() + { + AntiCheat::UninstallLibHook(); + + Game::SND_InitDriver(); + + AntiCheat::InstallLibHook(); + } + + void AntiCheat::LostD3DStub() + { + AntiCheat::UninstallLibHook(); + + // Reset directx + Utils::Hook::Call(0x508070)(); + + AntiCheat::InstallLibHook(); + } + + __declspec(naked) void AntiCheat::CinematicStub() + { + __asm + { + pushad + call AntiCheat::UninstallLibHook + popad + + call Game::R_Cinematic_StartPlayback_Now + + pushad + call AntiCheat::InstallLibHook + popad + + retn + } + } + + __declspec(naked) void AntiCheat::DObjGetWorldTagPosStub() + { + __asm + { + pushad + push [esp + 20h] + + call AntiCheat::AssertCalleeModule + + pop esi + popad + + push ecx + mov ecx, [esp + 10h] + + push 426585h + retn + } + } + + __declspec(naked) void AntiCheat::AimTargetGetTagPosStub() + { + __asm + { + pushad + push [esp + 20h] + + call AntiCheat::AssertCalleeModule + + pop esi + popad + + sub esp, 14h + cmp dword ptr[esi + 0E0h], 1 + push 56AC6Ah + ret + } + } + + unsigned long AntiCheat::ProtectProcess() + { + Utils::Memory::Allocator allocator; + + HANDLE hToken = nullptr; + if (!OpenProcessToken(GetCurrentProcess(), TOKEN_READ, &hToken)) + { + if (!OpenThreadToken(GetCurrentThread(), TOKEN_READ, TRUE, &hToken)) + { + return GetLastError(); + } + } + + auto freeSid = [] (void* sid) + { + if (sid) + { + FreeSid(reinterpret_cast(sid)); + } + }; + + allocator.reference(hToken, [] (void* hToken) + { + if (hToken) + { + CloseHandle(hToken); + } + }); + + DWORD dwSize = 0; + PVOID pTokenInfo = nullptr; + if (GetTokenInformation(hToken, TokenUser, NULL, 0, &dwSize) || GetLastError() != ERROR_INSUFFICIENT_BUFFER) return GetLastError(); + + if (dwSize) + { + pTokenInfo = allocator.allocate(dwSize); + if (!pTokenInfo) return GetLastError(); + } + + if (!GetTokenInformation(hToken, TokenUser, pTokenInfo, dwSize, &dwSize) || !pTokenInfo) return GetLastError(); + + PSID psidCurUser = reinterpret_cast(pTokenInfo)->User.Sid; + + PSID psidEveryone = nullptr; + SID_IDENTIFIER_AUTHORITY sidEveryone = SECURITY_WORLD_SID_AUTHORITY; + if (!AllocateAndInitializeSid(&sidEveryone, 1, SECURITY_WORLD_RID, 0, 0, 0, 0, 0, 0, 0, &psidEveryone) || !psidEveryone) return GetLastError(); + allocator.reference(psidEveryone, freeSid); + + PSID psidSystem = nullptr; + SID_IDENTIFIER_AUTHORITY sidSystem = SECURITY_NT_AUTHORITY; + if (!AllocateAndInitializeSid(&sidSystem, 1, SECURITY_LOCAL_SYSTEM_RID, 0, 0, 0, 0, 0, 0, 0, &psidSystem) || !psidSystem) return GetLastError(); + allocator.reference(psidSystem, freeSid); + + PSID psidAdmins = nullptr; + SID_IDENTIFIER_AUTHORITY sidAdministrators = SECURITY_NT_AUTHORITY; + if (!AllocateAndInitializeSid(&sidAdministrators, 2, SECURITY_BUILTIN_DOMAIN_RID, DOMAIN_ALIAS_RID_ADMINS, 0, 0, 0, 0, 0, 0, &psidAdmins) || !psidAdmins) return GetLastError(); + allocator.reference(psidAdmins, freeSid); + + const PSID psidArray[] = + { + psidEveryone, /* Deny most rights to everyone */ + psidCurUser, /* Allow what was not denied */ + psidSystem, /* Full control */ + psidAdmins, /* Full control */ + }; + + // Determine required size of the ACL + dwSize = sizeof(ACL); + + // First the DENY, then the ALLOW + dwSize += GetLengthSid(psidArray[0]); + dwSize += sizeof(ACCESS_DENIED_ACE) - sizeof(DWORD); + + for (UINT i = 1; i < _countof(psidArray); ++i) + { + // DWORD is the SidStart field, which is not used for absolute format + dwSize += GetLengthSid(psidArray[i]); + dwSize += sizeof(ACCESS_ALLOWED_ACE) - sizeof(DWORD); + } + + PACL pDacl = reinterpret_cast(allocator.allocate(dwSize)); + if (!pDacl || !InitializeAcl(pDacl, dwSize, ACL_REVISION)) return GetLastError(); + + // Mimic Protected Process + // http://www.microsoft.com/whdc/system/vista/process_vista.mspx + // Protected processes allow PROCESS_TERMINATE, which is + // probably not appropriate for high integrity software. + static const DWORD dwPoison = + /*READ_CONTROL |*/ WRITE_DAC | WRITE_OWNER | + PROCESS_CREATE_PROCESS | PROCESS_CREATE_THREAD | + PROCESS_DUP_HANDLE | PROCESS_QUERY_INFORMATION | + PROCESS_SET_QUOTA | PROCESS_SET_INFORMATION | + PROCESS_VM_OPERATION | PROCESS_VM_READ | PROCESS_VM_WRITE | + // In addition to protected process + PROCESS_SUSPEND_RESUME | PROCESS_TERMINATE; + + if (!AddAccessDeniedAce(pDacl, ACL_REVISION, dwPoison, psidArray[0])) return GetLastError(); + + // Standard and specific rights not explicitly denied + static const DWORD dwAllowed = ~dwPoison & 0x1FFF; + if (!AddAccessAllowedAce(pDacl, ACL_REVISION, dwAllowed, psidArray[1])) return GetLastError(); + + // Because of ACE ordering, System will effectively have dwAllowed even + // though the ACE specifies PROCESS_ALL_ACCESS (unless software uses + // SeDebugPrivilege or SeTcbName and increases access). + // As an exercise, check behavior of tools such as Process Explorer under XP, + // Vista, and above. Vista and above should exhibit slightly different behavior + // due to Restricted tokens. + if (!AddAccessAllowedAce(pDacl, ACL_REVISION, PROCESS_ALL_ACCESS, psidArray[2])) return GetLastError(); + + // Because of ACE ordering, Administrators will effectively have dwAllowed + // even though the ACE specifies PROCESS_ALL_ACCESS (unless the Administrator + // invokes 'discretionary security' by taking ownership and increasing access). + // As an exercise, check behavior of tools such as Process Explorer under XP, + // Vista, and above. Vista and above should exhibit slightly different behavior + // due to Restricted tokens. + if (!AddAccessAllowedAce(pDacl, ACL_REVISION, PROCESS_ALL_ACCESS, psidArray[3])) return GetLastError(); + + PSECURITY_DESCRIPTOR pSecDesc = allocator.allocate(); + if (!pSecDesc) return GetLastError(); + + // InitializeSecurityDescriptor initializes a security descriptor in + // absolute format, rather than self-relative format. See + // http://msdn.microsoft.com/en-us/library/aa378863(VS.85).aspx + if (!InitializeSecurityDescriptor(pSecDesc, SECURITY_DESCRIPTOR_REVISION)) return GetLastError(); + if (!SetSecurityDescriptorDacl(pSecDesc, TRUE, pDacl, FALSE)) return GetLastError(); + + return SetSecurityInfo( + GetCurrentProcess(), + SE_KERNEL_OBJECT, // process object + OWNER_SECURITY_INFORMATION | DACL_SECURITY_INFORMATION, + psidCurUser, // NULL, // Owner SID + NULL, // Group SID + pDacl, + NULL // SACL + ); + } + + AntiCheat::AntiCheat() + { + AntiCheat::Flags = NO_FLAG; + AntiCheat::Hash.clear(); + +#ifdef DEBUG + Command::Add("penis", [] (Command::Params*) + { + AntiCheat::CrashClient(); + }); +#else + + Utils::Hook(0x507BD5, AntiCheat::PatchWinAPI, HOOK_CALL).install()->quick(); + Utils::Hook(0x5082FD, AntiCheat::LostD3DStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x51C76C, AntiCheat::CinematicStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x418209, AntiCheat::SoundInitStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x60BE9D, AntiCheat::SoundInitStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x60BE8E, AntiCheat::SoundInitDriverStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x418204, AntiCheat::SoundInitDriverStub, HOOK_CALL).install()->quick(); + Renderer::OnFrame(AntiCheat::PerformScan); + + // Detect aimbots + Utils::Hook(0x426580, AntiCheat::DObjGetWorldTagPosStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x56AC60, AntiCheat::AimTargetGetTagPosStub, HOOK_JUMP).install()->quick(); + + // TODO: Probably move that :P + if (!Dedicated::IsEnabled()) + { + AntiCheat::InitLoadLibHook(); + } + + // Prevent external processes from accessing our memory + AntiCheat::ProtectProcess(); + Renderer::OnDeviceRecoveryEnd([] () + { + AntiCheat::ProtectProcess(); + }); + + // Set the integrity flag + AntiCheat::Flags |= AntiCheat::IntergrityFlag::INITIALIZATION; +#endif + } + + AntiCheat::~AntiCheat() + { + AntiCheat::Flags = NO_FLAG; + AntiCheat::Hash.clear(); + + for (int i = 0; i < ARRAYSIZE(AntiCheat::LoadLibHook); ++i) + { + AntiCheat::LoadLibHook[i].uninstall(); + } + } +} diff --git a/src/Components/Modules/AntiCheat.hpp b/src/Components/Modules/AntiCheat.hpp index 342731f4..864ff5a1 100644 --- a/src/Components/Modules/AntiCheat.hpp +++ b/src/Components/Modules/AntiCheat.hpp @@ -1,70 +1,70 @@ -#ifndef DEBUG -// Hide AntiCheat in embeded symbol names -#define AntiCheat SubComponent -#endif - -namespace Components -{ - class AntiCheat : public Component - { - public: - AntiCheat(); - ~AntiCheat(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "AntiCheat"; }; -#endif - - static void CrashClient(); - - static void InitLoadLibHook(); - - static void ReadIntegrityCheck(); - static void ScanIntegrityCheck(); - static void FlagIntegrityCheck(); - - private: - enum IntergrityFlag - { - NO_FLAG = (0), - INITIALIZATION = (1 << 0), - MEMORY_SCAN = (1 << 1), - SCAN_INTEGRITY_CHECK = (1 << 2), - READ_INTEGRITY_CHECK = (1 << 3), - - MAX_FLAG, - }; - - static Utils::Time::Interval LastCheck; - static std::string Hash; - static unsigned long Flags; - - static void PerformScan(); - static void PatchWinAPI(); - - static unsigned long ProtectProcess(); - - static void NullSub(); - - static void AssertCalleeModule(void* callee); - - static void UninstallLibHook(); - static void InstallLibHook(); - -#ifdef DEBUG_LOAD_LIBRARY - static HANDLE LoadLibary(std::wstring library, void* callee); - static HANDLE WINAPI LoadLibaryAStub(const char* library); - static HANDLE WINAPI LoadLibaryWStub(const wchar_t* library); -#endif - - static void LostD3DStub(); - static void CinematicStub(); - static void SoundInitStub(int a1, int a2, int a3); - static void SoundInitDriverStub(); - - static void DObjGetWorldTagPosStub(); - static void AimTargetGetTagPosStub(); - - static Utils::Hook LoadLibHook[4]; - }; -} +#ifndef DEBUG +// Hide AntiCheat in embeded symbol names +#define AntiCheat SubComponent +#endif + +namespace Components +{ + class AntiCheat : public Component + { + public: + AntiCheat(); + ~AntiCheat(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "AntiCheat"; }; +#endif + + static void CrashClient(); + + static void InitLoadLibHook(); + + static void ReadIntegrityCheck(); + static void ScanIntegrityCheck(); + static void FlagIntegrityCheck(); + + private: + enum IntergrityFlag + { + NO_FLAG = (0), + INITIALIZATION = (1 << 0), + MEMORY_SCAN = (1 << 1), + SCAN_INTEGRITY_CHECK = (1 << 2), + READ_INTEGRITY_CHECK = (1 << 3), + + MAX_FLAG, + }; + + static Utils::Time::Interval LastCheck; + static std::string Hash; + static unsigned long Flags; + + static void PerformScan(); + static void PatchWinAPI(); + + static unsigned long ProtectProcess(); + + static void NullSub(); + + static void AssertCalleeModule(void* callee); + + static void UninstallLibHook(); + static void InstallLibHook(); + +#ifdef DEBUG_LOAD_LIBRARY + static HANDLE LoadLibary(std::wstring library, void* callee); + static HANDLE WINAPI LoadLibaryAStub(const char* library); + static HANDLE WINAPI LoadLibaryWStub(const wchar_t* library); +#endif + + static void LostD3DStub(); + static void CinematicStub(); + static void SoundInitStub(int a1, int a2, int a3); + static void SoundInitDriverStub(); + + static void DObjGetWorldTagPosStub(); + static void AimTargetGetTagPosStub(); + + static Utils::Hook LoadLibHook[4]; + }; +} diff --git a/src/Components/Modules/ArenaLength.cpp b/src/Components/Modules/ArenaLength.cpp index 636fbaa4..d3c0893a 100644 --- a/src/Components/Modules/ArenaLength.cpp +++ b/src/Components/Modules/ArenaLength.cpp @@ -1,116 +1,116 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Game::newMapArena_t ArenaLength::NewArenas[128]; - - __declspec(naked) void ArenaLength::ArenaMapOffsetHook1() - { - __asm - { - lea eax, [esi + Game::newMapArena_t::mapName] - push eax - push ebx - - push 420725h - retn - } - } - - __declspec(naked) void ArenaLength::ArenaMapOffsetHook2() - { - __asm - { - lea eax, [edi + Game::newMapArena_t::mapName] - push eax - push edx - - push 49BD3Eh - retn - } - } - - __declspec(naked) void ArenaLength::ArenaMapOffsetHook3() - { - __asm - { - lea eax, [esi + Game::newMapArena_t::mapName] - push eax - push edx - - push 63279Eh - retn - } - } - - __declspec(naked) void ArenaLength::ArenaMapOffsetHook4() - { - __asm - { - lea edi, [esi - Game::newMapArena_t::mapName] - lea edx, [eax + 1] - - push 4064B8h - retn - } - } - - ArenaLength::ArenaLength() - { - // Reallocate array - Utils::Hook::Set(0x417807, &ArenaLength::NewArenas[0]); - Utils::Hook::Set(0x420717, &ArenaLength::NewArenas[0]); - Utils::Hook::Set(0x49BD22, &ArenaLength::NewArenas[0]); - Utils::Hook::Set(0x4A9649, &ArenaLength::NewArenas[0]); - Utils::Hook::Set(0x4A97C2, &ArenaLength::NewArenas[0]); - Utils::Hook::Set(0x4D077E, &ArenaLength::NewArenas[0]); - Utils::Hook::Set(0x630B00, &ArenaLength::NewArenas[0]); - Utils::Hook::Set(0x630B2E, &ArenaLength::NewArenas[0]); - Utils::Hook::Set(0x632782, &ArenaLength::NewArenas[0]); - - Utils::Hook::Set(0x4A967A, ArenaLength::NewArenas[0].description); - Utils::Hook::Set(0x4A96AD, ArenaLength::NewArenas[0].mapimage); - - Utils::Hook::Set(0x4A9616, ArenaLength::NewArenas[0].mapName); - Utils::Hook::Set(0x4A9703, ArenaLength::NewArenas[0].mapName); - Utils::Hook::Set(0x4064A8, ArenaLength::NewArenas[0].mapName); - - Utils::Hook::Set(0x42F214, &ArenaLength::NewArenas[0].other[0]); - - Utils::Hook::Set(0x4A96ED, &ArenaLength::NewArenas[0].other[0x8]); - Utils::Hook::Set(0x4A9769, &ArenaLength::NewArenas[0].other[0x8]); - Utils::Hook::Set(0x4A97A5, &ArenaLength::NewArenas[0].other[0x8]); - - Utils::Hook::Set(0x631E92, &ArenaLength::NewArenas[0].other[0x8C]); - - // Resize the array - Utils::Hook::Set(0x4064DE, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x417802, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x420736, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x42F271, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x49BD4F, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4A960C, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4A963F, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4A9675, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4A96A3, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4A96E7, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4A96FD, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4A975A, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4A979F, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4A97BC, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x4D0779, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x630B29, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x630B81, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x631EBC, sizeof(Game::newMapArena_t)); - Utils::Hook::Set(0x6327AF, sizeof(Game::newMapArena_t)); - - Utils::Hook(0x420720, ArenaLength::ArenaMapOffsetHook1, HOOK_JUMP).install()->quick(); - Utils::Hook(0x49BD39, ArenaLength::ArenaMapOffsetHook2, HOOK_JUMP).install()->quick(); - Utils::Hook(0x632799, ArenaLength::ArenaMapOffsetHook3, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4064B2, ArenaLength::ArenaMapOffsetHook4, HOOK_JUMP).install()->quick(); - - Utils::Hook::Set(0x4A95F8, 32); - - Utils::Hook::Set(0x42F22B, offsetof(Game::newMapArena_t, mapName) - offsetof(Game::newMapArena_t, other)); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Game::newMapArena_t ArenaLength::NewArenas[128]; + + __declspec(naked) void ArenaLength::ArenaMapOffsetHook1() + { + __asm + { + lea eax, [esi + Game::newMapArena_t::mapName] + push eax + push ebx + + push 420725h + retn + } + } + + __declspec(naked) void ArenaLength::ArenaMapOffsetHook2() + { + __asm + { + lea eax, [edi + Game::newMapArena_t::mapName] + push eax + push edx + + push 49BD3Eh + retn + } + } + + __declspec(naked) void ArenaLength::ArenaMapOffsetHook3() + { + __asm + { + lea eax, [esi + Game::newMapArena_t::mapName] + push eax + push edx + + push 63279Eh + retn + } + } + + __declspec(naked) void ArenaLength::ArenaMapOffsetHook4() + { + __asm + { + lea edi, [esi - Game::newMapArena_t::mapName] + lea edx, [eax + 1] + + push 4064B8h + retn + } + } + + ArenaLength::ArenaLength() + { + // Reallocate array + Utils::Hook::Set(0x417807, &ArenaLength::NewArenas[0]); + Utils::Hook::Set(0x420717, &ArenaLength::NewArenas[0]); + Utils::Hook::Set(0x49BD22, &ArenaLength::NewArenas[0]); + Utils::Hook::Set(0x4A9649, &ArenaLength::NewArenas[0]); + Utils::Hook::Set(0x4A97C2, &ArenaLength::NewArenas[0]); + Utils::Hook::Set(0x4D077E, &ArenaLength::NewArenas[0]); + Utils::Hook::Set(0x630B00, &ArenaLength::NewArenas[0]); + Utils::Hook::Set(0x630B2E, &ArenaLength::NewArenas[0]); + Utils::Hook::Set(0x632782, &ArenaLength::NewArenas[0]); + + Utils::Hook::Set(0x4A967A, ArenaLength::NewArenas[0].description); + Utils::Hook::Set(0x4A96AD, ArenaLength::NewArenas[0].mapimage); + + Utils::Hook::Set(0x4A9616, ArenaLength::NewArenas[0].mapName); + Utils::Hook::Set(0x4A9703, ArenaLength::NewArenas[0].mapName); + Utils::Hook::Set(0x4064A8, ArenaLength::NewArenas[0].mapName); + + Utils::Hook::Set(0x42F214, &ArenaLength::NewArenas[0].other[0]); + + Utils::Hook::Set(0x4A96ED, &ArenaLength::NewArenas[0].other[0x8]); + Utils::Hook::Set(0x4A9769, &ArenaLength::NewArenas[0].other[0x8]); + Utils::Hook::Set(0x4A97A5, &ArenaLength::NewArenas[0].other[0x8]); + + Utils::Hook::Set(0x631E92, &ArenaLength::NewArenas[0].other[0x8C]); + + // Resize the array + Utils::Hook::Set(0x4064DE, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x417802, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x420736, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x42F271, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x49BD4F, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4A960C, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4A963F, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4A9675, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4A96A3, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4A96E7, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4A96FD, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4A975A, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4A979F, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4A97BC, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x4D0779, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x630B29, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x630B81, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x631EBC, sizeof(Game::newMapArena_t)); + Utils::Hook::Set(0x6327AF, sizeof(Game::newMapArena_t)); + + Utils::Hook(0x420720, ArenaLength::ArenaMapOffsetHook1, HOOK_JUMP).install()->quick(); + Utils::Hook(0x49BD39, ArenaLength::ArenaMapOffsetHook2, HOOK_JUMP).install()->quick(); + Utils::Hook(0x632799, ArenaLength::ArenaMapOffsetHook3, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4064B2, ArenaLength::ArenaMapOffsetHook4, HOOK_JUMP).install()->quick(); + + Utils::Hook::Set(0x4A95F8, 32); + + Utils::Hook::Set(0x42F22B, offsetof(Game::newMapArena_t, mapName) - offsetof(Game::newMapArena_t, other)); + } +} diff --git a/src/Components/Modules/ArenaLength.hpp b/src/Components/Modules/ArenaLength.hpp index b8e8bf45..9c93faab 100644 --- a/src/Components/Modules/ArenaLength.hpp +++ b/src/Components/Modules/ArenaLength.hpp @@ -1,20 +1,20 @@ -namespace Components -{ - class ArenaLength : public Component - { - public: - ArenaLength(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "ArenaLength"; }; -#endif - - static Game::newMapArena_t NewArenas[128]; - - private: - static void ArenaMapOffsetHook1(); - static void ArenaMapOffsetHook2(); - static void ArenaMapOffsetHook3(); - static void ArenaMapOffsetHook4(); - }; -} +namespace Components +{ + class ArenaLength : public Component + { + public: + ArenaLength(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "ArenaLength"; }; +#endif + + static Game::newMapArena_t NewArenas[128]; + + private: + static void ArenaMapOffsetHook1(); + static void ArenaMapOffsetHook2(); + static void ArenaMapOffsetHook3(); + static void ArenaMapOffsetHook4(); + }; +} diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index 041c548c..081e1bda 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -1,439 +1,439 @@ -#include "STDInclude.hpp" - -namespace Components -{ - thread_local bool AssetHandler::BypassState; - std::map AssetHandler::AssetInterfaces; - std::map> AssetHandler::TypeCallbacks; - Utils::Signal AssetHandler::RestrictSignal; - - std::map AssetHandler::Relocations; - - std::vector> AssetHandler::EmptyAssets; - - std::map AssetHandler::TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT]; - - void AssetHandler::RegisterInterface(IAsset* iAsset) - { - if (!iAsset) return; - if (iAsset->getType() == Game::XAssetType::ASSET_TYPE_INVALID) - { - delete iAsset; - return; - } - - if (AssetHandler::AssetInterfaces.find(iAsset->getType()) != AssetHandler::AssetInterfaces.end()) - { - Logger::Print("Duplicate asset interface: %s\n", Game::DB_GetXAssetTypeName(iAsset->getType())); - delete AssetHandler::AssetInterfaces[iAsset->getType()]; - } - else - { - Logger::Print("Asset interface registered: %s\n", Game::DB_GetXAssetTypeName(iAsset->getType())); - } - - AssetHandler::AssetInterfaces[iAsset->getType()] = iAsset; - } - - void AssetHandler::ClearTemporaryAssets() - { - for (int i = 0; i < Game::XAssetType::ASSET_TYPE_COUNT; ++i) - { - AssetHandler::TemporaryAssets[i].clear(); - } - } - - void AssetHandler::StoreTemporaryAsset(Game::XAssetType type, Game::XAssetHeader asset) - { - AssetHandler::TemporaryAssets[type][Game::DB_GetXAssetNameHandlers[type](&asset)] = asset; - } - - Game::XAssetHeader AssetHandler::FindAsset(Game::XAssetType type, const char* filename) - { - Game::XAssetHeader header = { 0 }; - - if (filename) - { - // Allow call DB_FindXAssetHeader within the hook - AssetHandler::BypassState = true; - - if (AssetHandler::TypeCallbacks.find(type) != AssetHandler::TypeCallbacks.end()) - { - header = AssetHandler::TypeCallbacks[type](type, filename); - } - - // Disallow calling DB_FindXAssetHeader ;) - AssetHandler::BypassState = false; - } - - return header; - } - - int AssetHandler::HasThreadBypass() - { - return AssetHandler::BypassState & 1; - } - - __declspec(naked) void AssetHandler::FindAssetStub() - { - __asm - { - push ecx - push ebx - push ebp - push esi - push edi - - // Check if custom handler should be bypassed - call AssetHandler::HasThreadBypass - - test al, al - jnz finishOriginal - - mov ecx, [esp + 18h] // Asset type - mov ebx, [esp + 1Ch] // Filename - - push ebx - push ecx - - call AssetHandler::FindAsset - - add esp, 8h - - test eax, eax - jnz finishFound - - finishOriginal: - // Asset not found using custom handlers, redirect to DB_FindXAssetHeader - mov ebx, ds:6D7190h // InterlockedDecrement - mov eax, 40793Bh - jmp eax - - finishFound: - pop edi - pop esi - pop ebp - pop ebx - pop ecx - retn - } - } - - void AssetHandler::ModifyAsset(Game::XAssetType type, Game::XAssetHeader asset, std::string name) - { - if (type == Game::XAssetType::ASSET_TYPE_MATERIAL && (name == "wc/codo_ui_viewer_black_decal3" || name == "wc/codo_ui_viewer_black_decal2" || name == "wc/hint_arrows01" || name == "wc/hint_arrows02")) - { - asset.material->sortKey = 0xE; - } - - if (type == Game::XAssetType::ASSET_TYPE_VEHICLE && Zones::Version() >= VERSION_ALPHA2) - { - asset.vehicle->weaponDef = nullptr; - } - - // Fix shader const stuff - if (type == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 359) - { - for (int i = 0; i < 48; ++i) - { - if (asset.techniqueSet->techniques[i]) - { - for (int j = 0; j < asset.techniqueSet->techniques[i]->numPasses; ++j) - { - Game::MaterialPass* pass = &asset.techniqueSet->techniques[i]->passes[j]; - - for (int k = 0; k < (pass->argCount1 + pass->argCount2 + pass->argCount3); ++k) - { - if (pass->argumentDef[k].type == D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_CONSTINT) - { - if (pass->argumentDef[k].paramID == -28132) - { - pass->argumentDef[k].paramID = 2644; - } - } - } - } - } - } - } - } - - bool AssetHandler::IsAssetEligible(Game::XAssetType type, Game::XAssetHeader *asset) - { - const char* name = Game::DB_GetXAssetNameHandlers[type](asset); - if (!name) return false; - - for (auto i = AssetHandler::EmptyAssets.begin(); i != AssetHandler::EmptyAssets.end();) - { - if (i->first == type && i->second == name) - { - i = AssetHandler::EmptyAssets.erase(i); - } - else - { - ++i; - } - } - - if (Flags::HasFlag("entries")) - { - OutputDebugStringA(Utils::String::VA("%s: %d: %s\n", FastFiles::Current().data(), type, name)); - } - - bool restrict = false; - AssetHandler::RestrictSignal(type, *asset, name, &restrict); - - if (!restrict) - { - AssetHandler::ModifyAsset(type, *asset, name); - } - - // If no slot restricts the loading, we can load the asset - return (!restrict); - } - - __declspec(naked) void AssetHandler::AddAssetStub() - { - __asm - { - push [esp + 8] - push [esp + 8] - call AssetHandler::IsAssetEligible - add esp, 08h - - test al, al - jz doNotLoad - - mov eax, [esp + 8] - sub esp, 14h - mov ecx, 5BB657h - jmp ecx - - doNotLoad: - mov eax, [esp + 8] - retn - } - } - - void AssetHandler::OnFind(Game::XAssetType type, Utils::Slot callback) - { - AssetHandler::TypeCallbacks[type] = callback; - } - - void AssetHandler::OnLoad(Utils::Slot callback) - { - AssetHandler::RestrictSignal.connect(callback); - } - - void AssetHandler::ClearRelocations() - { - AssetHandler::Relocations.clear(); - } - - void AssetHandler::Relocate(void* start, void* to, DWORD size) - { - for (DWORD i = 0; i < size; i += 4) - { - AssetHandler::Relocations[reinterpret_cast(start) + i] = reinterpret_cast(to) + i; - } - } - - void AssetHandler::OffsetToAlias(Utils::Stream::Offset* offset) - { - void* pointer = (*Game::g_streamBlocks)[offset->getUnpackedBlock()].data + offset->getUnpackedOffset(); - - if (AssetHandler::Relocations.find(pointer) != AssetHandler::Relocations.end()) - { - pointer = AssetHandler::Relocations[pointer]; - } - - offset->pointer = *reinterpret_cast(pointer); - } - - void AssetHandler::ZoneSave(Game::XAsset asset, ZoneBuilder::Zone* builder) - { - if (AssetHandler::AssetInterfaces.find(asset.type) != AssetHandler::AssetInterfaces.end()) - { - AssetHandler::AssetInterfaces[asset.type]->save(asset.header, builder); - } - else - { - Logger::Error("No interface for type '%s'!", Game::DB_GetXAssetTypeName(asset.type)); - } - } - - void AssetHandler::ZoneMark(Game::XAsset asset, ZoneBuilder::Zone* builder) - { - if (AssetHandler::AssetInterfaces.find(asset.type) != AssetHandler::AssetInterfaces.end()) - { - AssetHandler::AssetInterfaces[asset.type]->mark(asset.header, builder); - } - else - { - Logger::Error("No interface for type '%s'!", Game::DB_GetXAssetTypeName(asset.type)); - } - } - - Game::XAssetHeader AssetHandler::FindAssetForZone(Game::XAssetType type, std::string filename, ZoneBuilder::Zone* builder, bool isSubAsset) - { - Game::XAssetHeader header = { 0 }; - if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header; - - auto tempPool = &AssetHandler::TemporaryAssets[type]; - auto entry = tempPool->find(filename); - if (entry != tempPool->end()) - { - return { entry->second }; - } - - if (AssetHandler::AssetInterfaces.find(type) != AssetHandler::AssetInterfaces.end()) - { - AssetHandler::AssetInterfaces[type]->load(&header, filename, builder); - - if (header.data) - { - Components::AssetHandler::StoreTemporaryAsset(type, header); - } - } - - if (!header.data && isSubAsset) - { - header = ZoneBuilder::GetEmptyAssetIfCommon(type, filename, builder); - } - - if (!header.data) - { - header = Game::DB_FindXAssetHeader(type, filename.data()); - if(header.data) Components::AssetHandler::StoreTemporaryAsset(type, header); // Might increase efficiency... - } - - return header; - } - - Game::XAssetHeader AssetHandler::FindOriginalAsset(Game::XAssetType type, const char* filename) - { - int originalState = AssetHandler::HasThreadBypass(); - - AssetHandler::BypassState = true; - Game::XAssetHeader header = Game::DB_FindXAssetHeader(type, filename); - if (!originalState) AssetHandler::BypassState = false; - - return header; - } - - void AssetHandler::StoreEmptyAsset(Game::XAssetType type, const char* name) - { - AssetHandler::EmptyAssets.push_back({ type, name }); - } - - __declspec(naked) void AssetHandler::StoreEmptyAssetStub() - { - __asm - { - pushad - push ebx - push eax - - call AssetHandler::StoreEmptyAsset - - pop eax - pop ebx - popad - - push 5BB290h - retn - } - } - - AssetHandler::AssetHandler() - { - Dvar::Register("r_noVoid", false, Game::DVAR_FLAG_SAVED, "Disable void model (red fx)"); - - AssetHandler::ClearTemporaryAssets(); - - // DB_FindXAssetHeader - Utils::Hook(Game::DB_FindXAssetHeader, AssetHandler::FindAssetStub).install()->quick(); - - // DB_ConvertOffsetToAlias - Utils::Hook(0x4FDFA0, AssetHandler::OffsetToAlias, HOOK_JUMP).install()->quick(); - - // DB_AddXAsset - Utils::Hook(0x5BB650, AssetHandler::AddAssetStub, HOOK_JUMP).install()->quick(); - - // Store empty assets - Utils::Hook(0x5BB6EC, AssetHandler::StoreEmptyAssetStub, HOOK_CALL).install()->quick(); - - // Log missing empty assets - QuickPatch::OnFrame([] () - { - if (FastFiles::Ready() && !AssetHandler::EmptyAssets.empty()) - { - for (auto& asset : AssetHandler::EmptyAssets) - { - Game::Sys_Error(25, reinterpret_cast(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data()); - } - - AssetHandler::EmptyAssets.clear(); - } - }); - - AssetHandler::OnLoad([] (Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool*) - { - if (Dvar::Var("r_noVoid").get() && type == Game::XAssetType::ASSET_TYPE_XMODEL && name == "void") - { - asset.model->numLods = 0; - } - }); - - // Register asset interfaces - if (ZoneBuilder::IsEnabled()) - { - AssetHandler::RegisterInterface(new Assets::IXModel()); - AssetHandler::RegisterInterface(new Assets::IFxWorld()); - AssetHandler::RegisterInterface(new Assets::IMapEnts()); - AssetHandler::RegisterInterface(new Assets::IRawFile()); - AssetHandler::RegisterInterface(new Assets::IComWorld()); - AssetHandler::RegisterInterface(new Assets::IGfxImage()); -#ifdef ENABLE_EXPERIMENTAL_MAP_CODE - AssetHandler::RegisterInterface(new Assets::IGfxWorld()); -#endif - AssetHandler::RegisterInterface(new Assets::ISndCurve()); - AssetHandler::RegisterInterface(new Assets::IMaterial()); -#ifdef ENABLE_EXPERIMENTAL_MAP_CODE - AssetHandler::RegisterInterface(new Assets::IclipMap_t()); -#endif - AssetHandler::RegisterInterface(new Assets::IPhysPreset()); - AssetHandler::RegisterInterface(new Assets::IXAnimParts()); - AssetHandler::RegisterInterface(new Assets::IFxEffectDef()); - AssetHandler::RegisterInterface(new Assets::IGameWorldMp()); - AssetHandler::RegisterInterface(new Assets::IGameWorldSp()); - AssetHandler::RegisterInterface(new Assets::IGfxLightDef()); - AssetHandler::RegisterInterface(new Assets::ILoadedSound()); - AssetHandler::RegisterInterface(new Assets::IPhysCollmap()); - AssetHandler::RegisterInterface(new Assets::IStringTable()); - AssetHandler::RegisterInterface(new Assets::IXModelSurfs()); - AssetHandler::RegisterInterface(new Assets::ILocalizeEntry()); - AssetHandler::RegisterInterface(new Assets::Isnd_alias_list_t()); - AssetHandler::RegisterInterface(new Assets::IMaterialPixelShader()); - AssetHandler::RegisterInterface(new Assets::IMaterialTechniqueSet()); - AssetHandler::RegisterInterface(new Assets::IMaterialVertexShader()); - AssetHandler::RegisterInterface(new Assets::IStructuredDataDefSet()); - AssetHandler::RegisterInterface(new Assets::IMaterialVertexDeclaration()); - } - } - - AssetHandler::~AssetHandler() - { - AssetHandler::ClearTemporaryAssets(); - - for (auto i = AssetHandler::AssetInterfaces.begin(); i != AssetHandler::AssetInterfaces.end(); ++i) - { - delete i->second; - } - - AssetHandler::Relocations.clear(); - AssetHandler::AssetInterfaces.clear(); - AssetHandler::RestrictSignal.clear(); - AssetHandler::TypeCallbacks.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + thread_local bool AssetHandler::BypassState; + std::map AssetHandler::AssetInterfaces; + std::map> AssetHandler::TypeCallbacks; + Utils::Signal AssetHandler::RestrictSignal; + + std::map AssetHandler::Relocations; + + std::vector> AssetHandler::EmptyAssets; + + std::map AssetHandler::TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT]; + + void AssetHandler::RegisterInterface(IAsset* iAsset) + { + if (!iAsset) return; + if (iAsset->getType() == Game::XAssetType::ASSET_TYPE_INVALID) + { + delete iAsset; + return; + } + + if (AssetHandler::AssetInterfaces.find(iAsset->getType()) != AssetHandler::AssetInterfaces.end()) + { + Logger::Print("Duplicate asset interface: %s\n", Game::DB_GetXAssetTypeName(iAsset->getType())); + delete AssetHandler::AssetInterfaces[iAsset->getType()]; + } + else + { + Logger::Print("Asset interface registered: %s\n", Game::DB_GetXAssetTypeName(iAsset->getType())); + } + + AssetHandler::AssetInterfaces[iAsset->getType()] = iAsset; + } + + void AssetHandler::ClearTemporaryAssets() + { + for (int i = 0; i < Game::XAssetType::ASSET_TYPE_COUNT; ++i) + { + AssetHandler::TemporaryAssets[i].clear(); + } + } + + void AssetHandler::StoreTemporaryAsset(Game::XAssetType type, Game::XAssetHeader asset) + { + AssetHandler::TemporaryAssets[type][Game::DB_GetXAssetNameHandlers[type](&asset)] = asset; + } + + Game::XAssetHeader AssetHandler::FindAsset(Game::XAssetType type, const char* filename) + { + Game::XAssetHeader header = { 0 }; + + if (filename) + { + // Allow call DB_FindXAssetHeader within the hook + AssetHandler::BypassState = true; + + if (AssetHandler::TypeCallbacks.find(type) != AssetHandler::TypeCallbacks.end()) + { + header = AssetHandler::TypeCallbacks[type](type, filename); + } + + // Disallow calling DB_FindXAssetHeader ;) + AssetHandler::BypassState = false; + } + + return header; + } + + int AssetHandler::HasThreadBypass() + { + return AssetHandler::BypassState & 1; + } + + __declspec(naked) void AssetHandler::FindAssetStub() + { + __asm + { + push ecx + push ebx + push ebp + push esi + push edi + + // Check if custom handler should be bypassed + call AssetHandler::HasThreadBypass + + test al, al + jnz finishOriginal + + mov ecx, [esp + 18h] // Asset type + mov ebx, [esp + 1Ch] // Filename + + push ebx + push ecx + + call AssetHandler::FindAsset + + add esp, 8h + + test eax, eax + jnz finishFound + + finishOriginal: + // Asset not found using custom handlers, redirect to DB_FindXAssetHeader + mov ebx, ds:6D7190h // InterlockedDecrement + mov eax, 40793Bh + jmp eax + + finishFound: + pop edi + pop esi + pop ebp + pop ebx + pop ecx + retn + } + } + + void AssetHandler::ModifyAsset(Game::XAssetType type, Game::XAssetHeader asset, std::string name) + { + if (type == Game::XAssetType::ASSET_TYPE_MATERIAL && (name == "wc/codo_ui_viewer_black_decal3" || name == "wc/codo_ui_viewer_black_decal2" || name == "wc/hint_arrows01" || name == "wc/hint_arrows02")) + { + asset.material->sortKey = 0xE; + } + + if (type == Game::XAssetType::ASSET_TYPE_VEHICLE && Zones::Version() >= VERSION_ALPHA2) + { + asset.vehicle->weaponDef = nullptr; + } + + // Fix shader const stuff + if (type == Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET && Zones::Version() >= 359) + { + for (int i = 0; i < 48; ++i) + { + if (asset.techniqueSet->techniques[i]) + { + for (int j = 0; j < asset.techniqueSet->techniques[i]->numPasses; ++j) + { + Game::MaterialPass* pass = &asset.techniqueSet->techniques[i]->passes[j]; + + for (int k = 0; k < (pass->argCount1 + pass->argCount2 + pass->argCount3); ++k) + { + if (pass->argumentDef[k].type == D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_CONSTINT) + { + if (pass->argumentDef[k].paramID == -28132) + { + pass->argumentDef[k].paramID = 2644; + } + } + } + } + } + } + } + } + + bool AssetHandler::IsAssetEligible(Game::XAssetType type, Game::XAssetHeader *asset) + { + const char* name = Game::DB_GetXAssetNameHandlers[type](asset); + if (!name) return false; + + for (auto i = AssetHandler::EmptyAssets.begin(); i != AssetHandler::EmptyAssets.end();) + { + if (i->first == type && i->second == name) + { + i = AssetHandler::EmptyAssets.erase(i); + } + else + { + ++i; + } + } + + if (Flags::HasFlag("entries")) + { + OutputDebugStringA(Utils::String::VA("%s: %d: %s\n", FastFiles::Current().data(), type, name)); + } + + bool restrict = false; + AssetHandler::RestrictSignal(type, *asset, name, &restrict); + + if (!restrict) + { + AssetHandler::ModifyAsset(type, *asset, name); + } + + // If no slot restricts the loading, we can load the asset + return (!restrict); + } + + __declspec(naked) void AssetHandler::AddAssetStub() + { + __asm + { + push [esp + 8] + push [esp + 8] + call AssetHandler::IsAssetEligible + add esp, 08h + + test al, al + jz doNotLoad + + mov eax, [esp + 8] + sub esp, 14h + mov ecx, 5BB657h + jmp ecx + + doNotLoad: + mov eax, [esp + 8] + retn + } + } + + void AssetHandler::OnFind(Game::XAssetType type, Utils::Slot callback) + { + AssetHandler::TypeCallbacks[type] = callback; + } + + void AssetHandler::OnLoad(Utils::Slot callback) + { + AssetHandler::RestrictSignal.connect(callback); + } + + void AssetHandler::ClearRelocations() + { + AssetHandler::Relocations.clear(); + } + + void AssetHandler::Relocate(void* start, void* to, DWORD size) + { + for (DWORD i = 0; i < size; i += 4) + { + AssetHandler::Relocations[reinterpret_cast(start) + i] = reinterpret_cast(to) + i; + } + } + + void AssetHandler::OffsetToAlias(Utils::Stream::Offset* offset) + { + void* pointer = (*Game::g_streamBlocks)[offset->getUnpackedBlock()].data + offset->getUnpackedOffset(); + + if (AssetHandler::Relocations.find(pointer) != AssetHandler::Relocations.end()) + { + pointer = AssetHandler::Relocations[pointer]; + } + + offset->pointer = *reinterpret_cast(pointer); + } + + void AssetHandler::ZoneSave(Game::XAsset asset, ZoneBuilder::Zone* builder) + { + if (AssetHandler::AssetInterfaces.find(asset.type) != AssetHandler::AssetInterfaces.end()) + { + AssetHandler::AssetInterfaces[asset.type]->save(asset.header, builder); + } + else + { + Logger::Error("No interface for type '%s'!", Game::DB_GetXAssetTypeName(asset.type)); + } + } + + void AssetHandler::ZoneMark(Game::XAsset asset, ZoneBuilder::Zone* builder) + { + if (AssetHandler::AssetInterfaces.find(asset.type) != AssetHandler::AssetInterfaces.end()) + { + AssetHandler::AssetInterfaces[asset.type]->mark(asset.header, builder); + } + else + { + Logger::Error("No interface for type '%s'!", Game::DB_GetXAssetTypeName(asset.type)); + } + } + + Game::XAssetHeader AssetHandler::FindAssetForZone(Game::XAssetType type, std::string filename, ZoneBuilder::Zone* builder, bool isSubAsset) + { + Game::XAssetHeader header = { 0 }; + if (type >= Game::XAssetType::ASSET_TYPE_COUNT) return header; + + auto tempPool = &AssetHandler::TemporaryAssets[type]; + auto entry = tempPool->find(filename); + if (entry != tempPool->end()) + { + return { entry->second }; + } + + if (AssetHandler::AssetInterfaces.find(type) != AssetHandler::AssetInterfaces.end()) + { + AssetHandler::AssetInterfaces[type]->load(&header, filename, builder); + + if (header.data) + { + Components::AssetHandler::StoreTemporaryAsset(type, header); + } + } + + if (!header.data && isSubAsset) + { + header = ZoneBuilder::GetEmptyAssetIfCommon(type, filename, builder); + } + + if (!header.data) + { + header = Game::DB_FindXAssetHeader(type, filename.data()); + if(header.data) Components::AssetHandler::StoreTemporaryAsset(type, header); // Might increase efficiency... + } + + return header; + } + + Game::XAssetHeader AssetHandler::FindOriginalAsset(Game::XAssetType type, const char* filename) + { + int originalState = AssetHandler::HasThreadBypass(); + + AssetHandler::BypassState = true; + Game::XAssetHeader header = Game::DB_FindXAssetHeader(type, filename); + if (!originalState) AssetHandler::BypassState = false; + + return header; + } + + void AssetHandler::StoreEmptyAsset(Game::XAssetType type, const char* name) + { + AssetHandler::EmptyAssets.push_back({ type, name }); + } + + __declspec(naked) void AssetHandler::StoreEmptyAssetStub() + { + __asm + { + pushad + push ebx + push eax + + call AssetHandler::StoreEmptyAsset + + pop eax + pop ebx + popad + + push 5BB290h + retn + } + } + + AssetHandler::AssetHandler() + { + Dvar::Register("r_noVoid", false, Game::DVAR_FLAG_SAVED, "Disable void model (red fx)"); + + AssetHandler::ClearTemporaryAssets(); + + // DB_FindXAssetHeader + Utils::Hook(Game::DB_FindXAssetHeader, AssetHandler::FindAssetStub).install()->quick(); + + // DB_ConvertOffsetToAlias + Utils::Hook(0x4FDFA0, AssetHandler::OffsetToAlias, HOOK_JUMP).install()->quick(); + + // DB_AddXAsset + Utils::Hook(0x5BB650, AssetHandler::AddAssetStub, HOOK_JUMP).install()->quick(); + + // Store empty assets + Utils::Hook(0x5BB6EC, AssetHandler::StoreEmptyAssetStub, HOOK_CALL).install()->quick(); + + // Log missing empty assets + QuickPatch::OnFrame([] () + { + if (FastFiles::Ready() && !AssetHandler::EmptyAssets.empty()) + { + for (auto& asset : AssetHandler::EmptyAssets) + { + Game::Sys_Error(25, reinterpret_cast(0x724428), Game::DB_GetXAssetTypeName(asset.first), asset.second.data()); + } + + AssetHandler::EmptyAssets.clear(); + } + }); + + AssetHandler::OnLoad([] (Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool*) + { + if (Dvar::Var("r_noVoid").get() && type == Game::XAssetType::ASSET_TYPE_XMODEL && name == "void") + { + asset.model->numLods = 0; + } + }); + + // Register asset interfaces + if (ZoneBuilder::IsEnabled()) + { + AssetHandler::RegisterInterface(new Assets::IXModel()); + AssetHandler::RegisterInterface(new Assets::IFxWorld()); + AssetHandler::RegisterInterface(new Assets::IMapEnts()); + AssetHandler::RegisterInterface(new Assets::IRawFile()); + AssetHandler::RegisterInterface(new Assets::IComWorld()); + AssetHandler::RegisterInterface(new Assets::IGfxImage()); +#ifdef ENABLE_EXPERIMENTAL_MAP_CODE + AssetHandler::RegisterInterface(new Assets::IGfxWorld()); +#endif + AssetHandler::RegisterInterface(new Assets::ISndCurve()); + AssetHandler::RegisterInterface(new Assets::IMaterial()); +#ifdef ENABLE_EXPERIMENTAL_MAP_CODE + AssetHandler::RegisterInterface(new Assets::IclipMap_t()); +#endif + AssetHandler::RegisterInterface(new Assets::IPhysPreset()); + AssetHandler::RegisterInterface(new Assets::IXAnimParts()); + AssetHandler::RegisterInterface(new Assets::IFxEffectDef()); + AssetHandler::RegisterInterface(new Assets::IGameWorldMp()); + AssetHandler::RegisterInterface(new Assets::IGameWorldSp()); + AssetHandler::RegisterInterface(new Assets::IGfxLightDef()); + AssetHandler::RegisterInterface(new Assets::ILoadedSound()); + AssetHandler::RegisterInterface(new Assets::IPhysCollmap()); + AssetHandler::RegisterInterface(new Assets::IStringTable()); + AssetHandler::RegisterInterface(new Assets::IXModelSurfs()); + AssetHandler::RegisterInterface(new Assets::ILocalizeEntry()); + AssetHandler::RegisterInterface(new Assets::Isnd_alias_list_t()); + AssetHandler::RegisterInterface(new Assets::IMaterialPixelShader()); + AssetHandler::RegisterInterface(new Assets::IMaterialTechniqueSet()); + AssetHandler::RegisterInterface(new Assets::IMaterialVertexShader()); + AssetHandler::RegisterInterface(new Assets::IStructuredDataDefSet()); + AssetHandler::RegisterInterface(new Assets::IMaterialVertexDeclaration()); + } + } + + AssetHandler::~AssetHandler() + { + AssetHandler::ClearTemporaryAssets(); + + for (auto i = AssetHandler::AssetInterfaces.begin(); i != AssetHandler::AssetInterfaces.end(); ++i) + { + delete i->second; + } + + AssetHandler::Relocations.clear(); + AssetHandler::AssetInterfaces.clear(); + AssetHandler::RestrictSignal.clear(); + AssetHandler::TypeCallbacks.clear(); + } +} diff --git a/src/Components/Modules/AssetHandler.hpp b/src/Components/Modules/AssetHandler.hpp index 20705d77..4d2b26a9 100644 --- a/src/Components/Modules/AssetHandler.hpp +++ b/src/Components/Modules/AssetHandler.hpp @@ -1,99 +1,99 @@ -namespace Components -{ - class AssetHandler : public Component - { - public: - class IAsset - { - public: - virtual ~IAsset() {}; - virtual Game::XAssetType getType() { return Game::XAssetType::ASSET_TYPE_INVALID; }; - virtual void mark(Game::XAssetHeader /*header*/, ZoneBuilder::Zone* /*builder*/) { /*ErrorTypeNotSupported(this);*/ }; - virtual void save(Game::XAssetHeader /*header*/, ZoneBuilder::Zone* /*builder*/) { /*ErrorTypeNotSupported(this);*/ }; - virtual void dump(Game::XAssetHeader /*header*/) { /*ErrorTypeNotSupported(this);*/ }; - virtual void load(Game::XAssetHeader* /*header*/, std::string name, ZoneBuilder::Zone* /*builder*/) { /*ErrorTypeNotSupported(this);*/ }; - }; - - typedef Game::XAssetHeader(Callback)(Game::XAssetType type, std::string name); - typedef void(RestrictCallback)(Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool* restrict); - - AssetHandler(); - ~AssetHandler(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "AssetHandler"; }; -#endif - - static void OnFind(Game::XAssetType type, Utils::Slot callback); - static void OnLoad(Utils::Slot callback); - - static void ClearRelocations(); - static void Relocate(void* start, void* to, DWORD size = 4); - - static void ZoneSave(Game::XAsset asset, ZoneBuilder::Zone* builder); - static void ZoneMark(Game::XAsset asset, ZoneBuilder::Zone* builder); - - static Game::XAssetHeader FindOriginalAsset(Game::XAssetType type, const char* filename); - static Game::XAssetHeader FindAssetForZone(Game::XAssetType type, std::string filename, ZoneBuilder::Zone* builder, bool isSubAsset = true); - - static void ClearTemporaryAssets(); - static void StoreTemporaryAsset(Game::XAssetType type, Game::XAssetHeader asset); - - private: - static thread_local bool BypassState; - - static std::map TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT]; - - static std::map AssetInterfaces; - static std::map> TypeCallbacks; - static Utils::Signal RestrictSignal; - - static std::map Relocations; - - static std::vector> EmptyAssets; - - static void RegisterInterface(IAsset* iAsset); - - static Game::XAssetHeader FindAsset(Game::XAssetType type, const char* filename); - static bool IsAssetEligible(Game::XAssetType type, Game::XAssetHeader* asset); - static void FindAssetStub(); - static void AddAssetStub(); - - static void OffsetToAlias(Utils::Stream::Offset* offset); - - static void StoreEmptyAsset(Game::XAssetType type, const char* name); - static void StoreEmptyAssetStub(); - - static void ModifyAsset(Game::XAssetType type, Game::XAssetHeader asset, std::string name); - - static int HasThreadBypass(); - }; -} - -#include "AssetInterfaces\IXModel.hpp" -#include "AssetInterfaces\IFxWorld.hpp" -#include "AssetInterfaces\IMapEnts.hpp" -#include "AssetInterfaces\IRawFile.hpp" -#include "AssetInterfaces\IComWorld.hpp" -#include "AssetInterfaces\IGfxImage.hpp" -#include "AssetInterfaces\IGfxWorld.hpp" -#include "AssetInterfaces\IMaterial.hpp" -#include "AssetInterfaces\ISndCurve.hpp" -#include "AssetInterfaces\IclipMap_t.hpp" -#include "AssetInterfaces\IPhysPreset.hpp" -#include "AssetInterfaces\IXAnimParts.hpp" -#include "AssetInterfaces\IFxEffectDef.hpp" -#include "AssetInterfaces\IGameWorldMp.hpp" -#include "AssetInterfaces\IGameWorldSp.hpp" -#include "AssetInterfaces\IGfxLightDef.hpp" -#include "AssetInterfaces\ILoadedSound.hpp" -#include "AssetInterfaces\IPhysCollmap.hpp" -#include "AssetInterfaces\IStringTable.hpp" -#include "AssetInterfaces\IXModelSurfs.hpp" -#include "AssetInterfaces\ILocalizeEntry.hpp" -#include "AssetInterfaces\Isnd_alias_list_t.hpp" -#include "AssetInterfaces\IMaterialPixelShader.hpp" -#include "AssetInterfaces\IMaterialTechniqueSet.hpp" -#include "AssetInterfaces\IMaterialVertexShader.hpp" -#include "AssetInterfaces\IStructuredDataDefSet.hpp" -#include "AssetInterfaces\IMaterialVertexDeclaration.hpp" +namespace Components +{ + class AssetHandler : public Component + { + public: + class IAsset + { + public: + virtual ~IAsset() {}; + virtual Game::XAssetType getType() { return Game::XAssetType::ASSET_TYPE_INVALID; }; + virtual void mark(Game::XAssetHeader /*header*/, ZoneBuilder::Zone* /*builder*/) { /*ErrorTypeNotSupported(this);*/ }; + virtual void save(Game::XAssetHeader /*header*/, ZoneBuilder::Zone* /*builder*/) { /*ErrorTypeNotSupported(this);*/ }; + virtual void dump(Game::XAssetHeader /*header*/) { /*ErrorTypeNotSupported(this);*/ }; + virtual void load(Game::XAssetHeader* /*header*/, std::string name, ZoneBuilder::Zone* /*builder*/) { /*ErrorTypeNotSupported(this);*/ }; + }; + + typedef Game::XAssetHeader(Callback)(Game::XAssetType type, std::string name); + typedef void(RestrictCallback)(Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool* restrict); + + AssetHandler(); + ~AssetHandler(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "AssetHandler"; }; +#endif + + static void OnFind(Game::XAssetType type, Utils::Slot callback); + static void OnLoad(Utils::Slot callback); + + static void ClearRelocations(); + static void Relocate(void* start, void* to, DWORD size = 4); + + static void ZoneSave(Game::XAsset asset, ZoneBuilder::Zone* builder); + static void ZoneMark(Game::XAsset asset, ZoneBuilder::Zone* builder); + + static Game::XAssetHeader FindOriginalAsset(Game::XAssetType type, const char* filename); + static Game::XAssetHeader FindAssetForZone(Game::XAssetType type, std::string filename, ZoneBuilder::Zone* builder, bool isSubAsset = true); + + static void ClearTemporaryAssets(); + static void StoreTemporaryAsset(Game::XAssetType type, Game::XAssetHeader asset); + + private: + static thread_local bool BypassState; + + static std::map TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT]; + + static std::map AssetInterfaces; + static std::map> TypeCallbacks; + static Utils::Signal RestrictSignal; + + static std::map Relocations; + + static std::vector> EmptyAssets; + + static void RegisterInterface(IAsset* iAsset); + + static Game::XAssetHeader FindAsset(Game::XAssetType type, const char* filename); + static bool IsAssetEligible(Game::XAssetType type, Game::XAssetHeader* asset); + static void FindAssetStub(); + static void AddAssetStub(); + + static void OffsetToAlias(Utils::Stream::Offset* offset); + + static void StoreEmptyAsset(Game::XAssetType type, const char* name); + static void StoreEmptyAssetStub(); + + static void ModifyAsset(Game::XAssetType type, Game::XAssetHeader asset, std::string name); + + static int HasThreadBypass(); + }; +} + +#include "AssetInterfaces\IXModel.hpp" +#include "AssetInterfaces\IFxWorld.hpp" +#include "AssetInterfaces\IMapEnts.hpp" +#include "AssetInterfaces\IRawFile.hpp" +#include "AssetInterfaces\IComWorld.hpp" +#include "AssetInterfaces\IGfxImage.hpp" +#include "AssetInterfaces\IGfxWorld.hpp" +#include "AssetInterfaces\IMaterial.hpp" +#include "AssetInterfaces\ISndCurve.hpp" +#include "AssetInterfaces\IclipMap_t.hpp" +#include "AssetInterfaces\IPhysPreset.hpp" +#include "AssetInterfaces\IXAnimParts.hpp" +#include "AssetInterfaces\IFxEffectDef.hpp" +#include "AssetInterfaces\IGameWorldMp.hpp" +#include "AssetInterfaces\IGameWorldSp.hpp" +#include "AssetInterfaces\IGfxLightDef.hpp" +#include "AssetInterfaces\ILoadedSound.hpp" +#include "AssetInterfaces\IPhysCollmap.hpp" +#include "AssetInterfaces\IStringTable.hpp" +#include "AssetInterfaces\IXModelSurfs.hpp" +#include "AssetInterfaces\ILocalizeEntry.hpp" +#include "AssetInterfaces\Isnd_alias_list_t.hpp" +#include "AssetInterfaces\IMaterialPixelShader.hpp" +#include "AssetInterfaces\IMaterialTechniqueSet.hpp" +#include "AssetInterfaces\IMaterialVertexShader.hpp" +#include "AssetInterfaces\IStructuredDataDefSet.hpp" +#include "AssetInterfaces\IMaterialVertexDeclaration.hpp" diff --git a/src/Components/Modules/AssetInterfaces/IComWorld.cpp b/src/Components/Modules/AssetInterfaces/IComWorld.cpp index 0c708372..2d687ed6 100644 --- a/src/Components/Modules/AssetInterfaces/IComWorld.cpp +++ b/src/Components/Modules/AssetInterfaces/IComWorld.cpp @@ -1,62 +1,62 @@ -#include - -namespace Assets -{ - void IComWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::ComWorld* asset = header.comWorld; - if (asset->lights) - { - for (int i = 0; i < asset->lightCount; ++i) - { - if (asset->lights[i].name) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_LIGHT_DEF, std::string(asset->lights[i].name), false); - } - } - } - } - - void IComWorld::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::ComWorld, 16); - - Utils::Stream* buffer = builder->getBuffer(); - Game::ComWorld* asset = header.comWorld; - Game::ComWorld* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->lights) - { - AssertSize(Game::ComPrimaryLight, 68); - buffer->align(Utils::Stream::ALIGN_4); - - Game::ComPrimaryLight* destLights = buffer->dest(); - buffer->saveArray(asset->lights, asset->lightCount); - - for (int i = 0; i < asset->lightCount; ++i) - { - Game::ComPrimaryLight* destLight = &destLights[i]; - Game::ComPrimaryLight* light = &asset->lights[i]; - - if (light->name) - { - buffer->saveString(light->name); - Utils::Stream::ClearPointer(&destLight->name); - } - } - - Utils::Stream::ClearPointer(&dest->lights); - } - - buffer->popBlock(); - } -} +#include + +namespace Assets +{ + void IComWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::ComWorld* asset = header.comWorld; + if (asset->lights) + { + for (int i = 0; i < asset->lightCount; ++i) + { + if (asset->lights[i].name) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_LIGHT_DEF, std::string(asset->lights[i].name), false); + } + } + } + } + + void IComWorld::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::ComWorld, 16); + + Utils::Stream* buffer = builder->getBuffer(); + Game::ComWorld* asset = header.comWorld; + Game::ComWorld* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->lights) + { + AssertSize(Game::ComPrimaryLight, 68); + buffer->align(Utils::Stream::ALIGN_4); + + Game::ComPrimaryLight* destLights = buffer->dest(); + buffer->saveArray(asset->lights, asset->lightCount); + + for (int i = 0; i < asset->lightCount; ++i) + { + Game::ComPrimaryLight* destLight = &destLights[i]; + Game::ComPrimaryLight* light = &asset->lights[i]; + + if (light->name) + { + buffer->saveString(light->name); + Utils::Stream::ClearPointer(&destLight->name); + } + } + + Utils::Stream::ClearPointer(&dest->lights); + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp index 8470546b..b738b03d 100644 --- a/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp +++ b/src/Components/Modules/AssetInterfaces/IFxEffectDef.cpp @@ -21,10 +21,10 @@ namespace Assets } int version = atoi(Game::Com_Parse(&session)); - if (version > 2) - { - Game::Com_EndParseSession(); - Components::Logger::Error("Version %i is too high. I can only handle up to %i.\n", version, 2); + if (version > 2) + { + Game::Com_EndParseSession(); + Components::Logger::Error("Version %i is too high. I can only handle up to %i.\n", version, 2); } Game::FxEditorEffectDef efx; diff --git a/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp b/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp index f6ff1344..e7a881d9 100644 --- a/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp +++ b/src/Components/Modules/AssetInterfaces/IGameWorldMp.cpp @@ -1,77 +1,77 @@ -#include - -namespace Assets -{ - void IGameWorldMp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GameWorldMp, 8); - - Utils::Stream* buffer = builder->getBuffer(); - Game::GameWorldMp* asset = header.gameWorldMp; - Game::GameWorldMp* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->data) - { - buffer->align(Utils::Stream::ALIGN_4); - - // Save_G_GlassData - { - AssertSize(Game::G_GlassData, 128); - +#include + +namespace Assets +{ + void IGameWorldMp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GameWorldMp, 8); + + Utils::Stream* buffer = builder->getBuffer(); + Game::GameWorldMp* asset = header.gameWorldMp; + Game::GameWorldMp* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->data) + { + buffer->align(Utils::Stream::ALIGN_4); + + // Save_G_GlassData + { + AssertSize(Game::G_GlassData, 128); + Game::G_GlassData* destGlass = buffer->dest(); - buffer->save(asset->data); - - if (asset->data->glassPieces) - { - AssertSize(Game::G_GlassPiece, 12); - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->data->glassPieces, asset->data->pieceCount); - Utils::Stream::ClearPointer(&destGlass->glassPieces); - } - - if (asset->data->glassNames) - { - AssertSize(Game::G_GlassName, 12); - buffer->align(Utils::Stream::ALIGN_4); - - Game::G_GlassName* destGlassNames = buffer->dest(); - buffer->saveArray(asset->data->glassNames, asset->data->glassNameCount); - - for (unsigned int i = 0; i < asset->data->glassNameCount; ++i) - { - Game::G_GlassName* destGlassName = &destGlassNames[i]; - Game::G_GlassName* glassName = &asset->data->glassNames[i]; - - if (glassName->nameStr) - { - buffer->saveString(glassName->nameStr); - Utils::Stream::ClearPointer(&destGlassName->nameStr); - } - - if (glassName->pieceIndices) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(glassName->pieceIndices, glassName->pieceCount); - Utils::Stream::ClearPointer(&destGlassName->pieceIndices); - } - } - - Utils::Stream::ClearPointer(&destGlass->glassNames); - } - } - - Utils::Stream::ClearPointer(&dest->data); - } - - buffer->popBlock(); - } -} + buffer->save(asset->data); + + if (asset->data->glassPieces) + { + AssertSize(Game::G_GlassPiece, 12); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->data->glassPieces, asset->data->pieceCount); + Utils::Stream::ClearPointer(&destGlass->glassPieces); + } + + if (asset->data->glassNames) + { + AssertSize(Game::G_GlassName, 12); + buffer->align(Utils::Stream::ALIGN_4); + + Game::G_GlassName* destGlassNames = buffer->dest(); + buffer->saveArray(asset->data->glassNames, asset->data->glassNameCount); + + for (unsigned int i = 0; i < asset->data->glassNameCount; ++i) + { + Game::G_GlassName* destGlassName = &destGlassNames[i]; + Game::G_GlassName* glassName = &asset->data->glassNames[i]; + + if (glassName->nameStr) + { + buffer->saveString(glassName->nameStr); + Utils::Stream::ClearPointer(&destGlassName->nameStr); + } + + if (glassName->pieceIndices) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(glassName->pieceIndices, glassName->pieceCount); + Utils::Stream::ClearPointer(&destGlassName->pieceIndices); + } + } + + Utils::Stream::ClearPointer(&destGlass->glassNames); + } + } + + Utils::Stream::ClearPointer(&dest->data); + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IGameWorldSp.cpp b/src/Components/Modules/AssetInterfaces/IGameWorldSp.cpp index 1afb09cd..3d769e2a 100644 --- a/src/Components/Modules/AssetInterfaces/IGameWorldSp.cpp +++ b/src/Components/Modules/AssetInterfaces/IGameWorldSp.cpp @@ -1,135 +1,135 @@ -#include - -namespace Assets -{ - void IGameWorldSp::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::GameWorldSp* asset = header.gameWorldSp; - - if (asset->pathData.nodes) - { - for (unsigned int i = 0; i < asset->pathData.nodeCount; ++i) - { - Game::pathnode_t* node = &asset->pathData.nodes[i]; - - for (char j = 0; j < 5; ++j) - { - builder->addScriptString((&node->constant.targetname)[j]); - } - } - } - } - - void IGameWorldSp::savepathnode_tree_info_t(Game::pathnode_tree_t* nodeTree, Game::pathnode_tree_t* destNodeTree, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::pathnode_tree_info_t, 8); - Utils::Stream* buffer = builder->getBuffer(); - - if (nodeTree->axis < 0) - { - AssertSize(Game::pathnode_tree_nodes_t, 8); - - if (nodeTree->u.s.nodes) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(nodeTree->u.s.nodes, nodeTree->u.s.nodeCount); - Utils::Stream::ClearPointer(&destNodeTree->u.s.nodes); - } - } - else - { - for (int i = 0; i < 2; ++i) - { - Game::pathnode_tree_t** destChildNodeTreePtr = &destNodeTree->u.child[i]; - Game::pathnode_tree_t** childNodeTreePtr = &nodeTree->u.child[i]; - - if (*childNodeTreePtr) - { - if (builder->hasPointer(*childNodeTreePtr)) - { - *destChildNodeTreePtr = builder->getPointer(*childNodeTreePtr); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(*childNodeTreePtr); - +#include + +namespace Assets +{ + void IGameWorldSp::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::GameWorldSp* asset = header.gameWorldSp; + + if (asset->pathData.nodes) + { + for (unsigned int i = 0; i < asset->pathData.nodeCount; ++i) + { + Game::pathnode_t* node = &asset->pathData.nodes[i]; + + for (char j = 0; j < 5; ++j) + { + builder->addScriptString((&node->constant.targetname)[j]); + } + } + } + } + + void IGameWorldSp::savepathnode_tree_info_t(Game::pathnode_tree_t* nodeTree, Game::pathnode_tree_t* destNodeTree, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::pathnode_tree_info_t, 8); + Utils::Stream* buffer = builder->getBuffer(); + + if (nodeTree->axis < 0) + { + AssertSize(Game::pathnode_tree_nodes_t, 8); + + if (nodeTree->u.s.nodes) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(nodeTree->u.s.nodes, nodeTree->u.s.nodeCount); + Utils::Stream::ClearPointer(&destNodeTree->u.s.nodes); + } + } + else + { + for (int i = 0; i < 2; ++i) + { + Game::pathnode_tree_t** destChildNodeTreePtr = &destNodeTree->u.child[i]; + Game::pathnode_tree_t** childNodeTreePtr = &nodeTree->u.child[i]; + + if (*childNodeTreePtr) + { + if (builder->hasPointer(*childNodeTreePtr)) + { + *destChildNodeTreePtr = builder->getPointer(*childNodeTreePtr); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(*childNodeTreePtr); + Game::pathnode_tree_t* destChildNodeTree = buffer->dest(); buffer->save(*childNodeTreePtr); this->savepathnode_tree_info_t(*childNodeTreePtr, destChildNodeTree, builder); - Utils::Stream::ClearPointer(destChildNodeTreePtr); - } - } - } - } - } - - void IGameWorldSp::saveVehicleTrackSegment_ptrArray(Game::VehicleTrackSegment** trackSegmentPtrs, int count, Components::ZoneBuilder::Zone* builder) - { - Utils::Stream* buffer = builder->getBuffer(); - if (!trackSegmentPtrs) return; - - Game::VehicleTrackSegment** destTrackSegmentPtrs = buffer->dest(); - buffer->saveArray(trackSegmentPtrs, count); - - for (int i = 0; i < count; ++i) - { - Game::VehicleTrackSegment** destTrackSegmentPtr = &destTrackSegmentPtrs[i]; - Game::VehicleTrackSegment** trackSegmentPtr = &trackSegmentPtrs[i]; - - if (*trackSegmentPtr) - { - if (builder->hasPointer(*trackSegmentPtr)) - { - *destTrackSegmentPtr = builder->getPointer(*trackSegmentPtr); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(*trackSegmentPtr); + Utils::Stream::ClearPointer(destChildNodeTreePtr); + } + } + } + } + } - Game::VehicleTrackSegment* destTrackSegment = buffer->dest(); + void IGameWorldSp::saveVehicleTrackSegment_ptrArray(Game::VehicleTrackSegment** trackSegmentPtrs, int count, Components::ZoneBuilder::Zone* builder) + { + Utils::Stream* buffer = builder->getBuffer(); + if (!trackSegmentPtrs) return; + + Game::VehicleTrackSegment** destTrackSegmentPtrs = buffer->dest(); + buffer->saveArray(trackSegmentPtrs, count); + + for (int i = 0; i < count; ++i) + { + Game::VehicleTrackSegment** destTrackSegmentPtr = &destTrackSegmentPtrs[i]; + Game::VehicleTrackSegment** trackSegmentPtr = &trackSegmentPtrs[i]; + + if (*trackSegmentPtr) + { + if (builder->hasPointer(*trackSegmentPtr)) + { + *destTrackSegmentPtr = builder->getPointer(*trackSegmentPtr); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(*trackSegmentPtr); + + Game::VehicleTrackSegment* destTrackSegment = buffer->dest(); buffer->save(*trackSegmentPtr); this->saveVehicleTrackSegment(*trackSegmentPtr, destTrackSegment, builder); - Utils::Stream::ClearPointer(destTrackSegmentPtr); - } - } - } - } - - void IGameWorldSp::saveVehicleTrackSegment(Game::VehicleTrackSegment* trackSegment, Game::VehicleTrackSegment* destTrackSegment, Components::ZoneBuilder::Zone* builder) - { - Utils::Stream* buffer = builder->getBuffer(); - - if (trackSegment->name) - { - buffer->saveString(trackSegment->name); - Utils::Stream::ClearPointer(&destTrackSegment->name); + Utils::Stream::ClearPointer(destTrackSegmentPtr); + } + } + } + } + + void IGameWorldSp::saveVehicleTrackSegment(Game::VehicleTrackSegment* trackSegment, Game::VehicleTrackSegment* destTrackSegment, Components::ZoneBuilder::Zone* builder) + { + Utils::Stream* buffer = builder->getBuffer(); + + if (trackSegment->name) + { + buffer->saveString(trackSegment->name); + Utils::Stream::ClearPointer(&destTrackSegment->name); } if (trackSegment->trackSectors) { - AssertSize(Game::VehicleTrackSector, 60); - buffer->align(Utils::Stream::ALIGN_4); - - Game::VehicleTrackSector* destTrackSectors = buffer->dest(); + AssertSize(Game::VehicleTrackSector, 60); + buffer->align(Utils::Stream::ALIGN_4); + + Game::VehicleTrackSector* destTrackSectors = buffer->dest(); buffer->saveArray(trackSegment->trackSectors, trackSegment->trackSectorCount); - - for (int i = 0; i < trackSegment->trackSectorCount; ++i) - { - Game::VehicleTrackSector* destTrackSector = &destTrackSectors[i]; - Game::VehicleTrackSector* trackSector = &trackSegment->trackSectors[i]; - - if (trackSector->trackObstacles) - { - AssertSize(Game::VehicleTrackObstacle, 12); - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(trackSector->trackObstacles, trackSector->trackObstacleCount); - Utils::Stream::ClearPointer(&destTrackSector->trackObstacles); - } + + for (int i = 0; i < trackSegment->trackSectorCount; ++i) + { + Game::VehicleTrackSector* destTrackSector = &destTrackSectors[i]; + Game::VehicleTrackSector* trackSector = &trackSegment->trackSectors[i]; + + if (trackSector->trackObstacles) + { + AssertSize(Game::VehicleTrackObstacle, 12); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(trackSector->trackObstacles, trackSector->trackObstacleCount); + Utils::Stream::ClearPointer(&destTrackSector->trackObstacles); + } } } @@ -138,210 +138,210 @@ namespace Assets buffer->align(Utils::Stream::ALIGN_4); this->saveVehicleTrackSegment_ptrArray(trackSegment->trackSegments1, trackSegment->trackSegmentCount1, builder); Utils::Stream::ClearPointer(&destTrackSegment->trackSegments1); - } - + } + if (trackSegment->trackSegments2) { buffer->align(Utils::Stream::ALIGN_4); this->saveVehicleTrackSegment_ptrArray(trackSegment->trackSegments2, trackSegment->trackSegmentCount2, builder); Utils::Stream::ClearPointer(&destTrackSegment->trackSegments2); - } - } - - void IGameWorldSp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GameWorldMp, 8); - - Utils::Stream* buffer = builder->getBuffer(); - Game::GameWorldSp* asset = header.gameWorldSp; - Game::GameWorldSp* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - // Save_PathData - { - AssertSize(Game::PathData, 40); - - if (asset->pathData.nodes) - { - AssertSize(Game::pathnode_t, 136); - buffer->align(Utils::Stream::ALIGN_4); - - Game::pathnode_t* destNodes = buffer->dest(); - buffer->saveArray(asset->pathData.nodes, asset->pathData.nodeCount); - - for (unsigned int i = 0; i < asset->pathData.nodeCount; ++i) - { - Game::pathnode_t* destNode = &destNodes[i]; - Game::pathnode_t* node = &asset->pathData.nodes[i]; - - AssertSize(Game::pathnode_constant_t, 64); - - for (char j = 0; j < 5; ++j) - { - builder->mapScriptString(&(&node->constant.targetname)[j]); - } - - if (node->constant.Links) - { - AssertSize(Game::pathlink_s, 12); - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(node->constant.Links, node->constant.totalLinkCount); - Utils::Stream::ClearPointer(&destNode->constant.Links); - } - } - - Utils::Stream::ClearPointer(&dest->pathData.nodes); - } - - buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); - - if (asset->pathData.basenodes) - { - AssertSize(Game::pathbasenode_t, 16); - - buffer->align(Utils::Stream::ALIGN_16); - buffer->saveArray(asset->pathData.basenodes, asset->pathData.nodeCount); - Utils::Stream::ClearPointer(&dest->pathData.basenodes); - } - - buffer->popBlock(); - - if (asset->pathData.chainNodeForNode) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->pathData.chainNodeForNode, asset->pathData.nodeCount); - Utils::Stream::ClearPointer(&dest->pathData.chainNodeForNode); - } - - if (asset->pathData.nodeForChainNode) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->pathData.nodeForChainNode, asset->pathData.nodeCount); - Utils::Stream::ClearPointer(&dest->pathData.nodeForChainNode); - } - - if (asset->pathData.pathVis) - { - buffer->saveArray(asset->pathData.pathVis, asset->pathData.visBytes); - Utils::Stream::ClearPointer(&dest->pathData.pathVis); - } - - if (asset->pathData.nodeTree) - { - AssertSize(Game::pathnode_tree_t, 16); - buffer->align(Utils::Stream::ALIGN_4); - - Game::pathnode_tree_t* destNodeTrees = buffer->dest(); - buffer->saveArray(asset->pathData.nodeTree, asset->pathData.nodeTreeCount); - - for (int i = 0; i < asset->pathData.nodeTreeCount; ++i) - { - Game::pathnode_tree_t* destNodeTree = &destNodeTrees[i]; - Game::pathnode_tree_t* nodeTree = &asset->pathData.nodeTree[i]; - - this->savepathnode_tree_info_t(nodeTree, destNodeTree, builder); - } - - Utils::Stream::ClearPointer(&dest->pathData.nodeTree); - } - } - - // Save_VehicleTrack - { - AssertSize(Game::VehicleTrack, 8); - - if (asset->vehicleTrack.trackSegments) - { - if (builder->hasPointer(asset->vehicleTrack.trackSegments)) - { - dest->vehicleTrack.trackSegments = builder->getPointer(asset->vehicleTrack.trackSegments); - } - else - { - AssertSize(Game::VehicleTrackSegment, 44); - - buffer->align(Utils::Stream::ALIGN_4); - Game::VehicleTrackSegment* destTrackSegments = buffer->dest(); - - for (int i = 0; i < asset->vehicleTrack.trackSegmentCount; ++i) - { + } + } + + void IGameWorldSp::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GameWorldMp, 8); + + Utils::Stream* buffer = builder->getBuffer(); + Game::GameWorldSp* asset = header.gameWorldSp; + Game::GameWorldSp* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + // Save_PathData + { + AssertSize(Game::PathData, 40); + + if (asset->pathData.nodes) + { + AssertSize(Game::pathnode_t, 136); + buffer->align(Utils::Stream::ALIGN_4); + + Game::pathnode_t* destNodes = buffer->dest(); + buffer->saveArray(asset->pathData.nodes, asset->pathData.nodeCount); + + for (unsigned int i = 0; i < asset->pathData.nodeCount; ++i) + { + Game::pathnode_t* destNode = &destNodes[i]; + Game::pathnode_t* node = &asset->pathData.nodes[i]; + + AssertSize(Game::pathnode_constant_t, 64); + + for (char j = 0; j < 5; ++j) + { + builder->mapScriptString(&(&node->constant.targetname)[j]); + } + + if (node->constant.Links) + { + AssertSize(Game::pathlink_s, 12); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(node->constant.Links, node->constant.totalLinkCount); + Utils::Stream::ClearPointer(&destNode->constant.Links); + } + } + + Utils::Stream::ClearPointer(&dest->pathData.nodes); + } + + buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); + + if (asset->pathData.basenodes) + { + AssertSize(Game::pathbasenode_t, 16); + + buffer->align(Utils::Stream::ALIGN_16); + buffer->saveArray(asset->pathData.basenodes, asset->pathData.nodeCount); + Utils::Stream::ClearPointer(&dest->pathData.basenodes); + } + + buffer->popBlock(); + + if (asset->pathData.chainNodeForNode) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->pathData.chainNodeForNode, asset->pathData.nodeCount); + Utils::Stream::ClearPointer(&dest->pathData.chainNodeForNode); + } + + if (asset->pathData.nodeForChainNode) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->pathData.nodeForChainNode, asset->pathData.nodeCount); + Utils::Stream::ClearPointer(&dest->pathData.nodeForChainNode); + } + + if (asset->pathData.pathVis) + { + buffer->saveArray(asset->pathData.pathVis, asset->pathData.visBytes); + Utils::Stream::ClearPointer(&dest->pathData.pathVis); + } + + if (asset->pathData.nodeTree) + { + AssertSize(Game::pathnode_tree_t, 16); + buffer->align(Utils::Stream::ALIGN_4); + + Game::pathnode_tree_t* destNodeTrees = buffer->dest(); + buffer->saveArray(asset->pathData.nodeTree, asset->pathData.nodeTreeCount); + + for (int i = 0; i < asset->pathData.nodeTreeCount; ++i) + { + Game::pathnode_tree_t* destNodeTree = &destNodeTrees[i]; + Game::pathnode_tree_t* nodeTree = &asset->pathData.nodeTree[i]; + + this->savepathnode_tree_info_t(nodeTree, destNodeTree, builder); + } + + Utils::Stream::ClearPointer(&dest->pathData.nodeTree); + } + } + + // Save_VehicleTrack + { + AssertSize(Game::VehicleTrack, 8); + + if (asset->vehicleTrack.trackSegments) + { + if (builder->hasPointer(asset->vehicleTrack.trackSegments)) + { + dest->vehicleTrack.trackSegments = builder->getPointer(asset->vehicleTrack.trackSegments); + } + else + { + AssertSize(Game::VehicleTrackSegment, 44); + + buffer->align(Utils::Stream::ALIGN_4); + Game::VehicleTrackSegment* destTrackSegments = buffer->dest(); + + for (int i = 0; i < asset->vehicleTrack.trackSegmentCount; ++i) + { builder->storePointer(&asset->vehicleTrack.trackSegments[i]); buffer->save(&asset->vehicleTrack.trackSegments[i]); } - for (int i = 0; i < asset->vehicleTrack.trackSegmentCount; ++i) - { + for (int i = 0; i < asset->vehicleTrack.trackSegmentCount; ++i) + { Game::VehicleTrackSegment* destTrackSegment = &destTrackSegments[i]; Game::VehicleTrackSegment* trackSegment = &asset->vehicleTrack.trackSegments[i]; - + this->saveVehicleTrackSegment(trackSegment, destTrackSegment, builder); } - Utils::Stream::ClearPointer(&dest->vehicleTrack.trackSegments); - } - } - } - - if (asset->data) - { - // Save_G_GlassData - { - AssertSize(Game::G_GlassData, 128); - buffer->align(Utils::Stream::ALIGN_4); - + Utils::Stream::ClearPointer(&dest->vehicleTrack.trackSegments); + } + } + } + + if (asset->data) + { + // Save_G_GlassData + { + AssertSize(Game::G_GlassData, 128); + buffer->align(Utils::Stream::ALIGN_4); + Game::G_GlassData* destGlass = buffer->dest(); - buffer->save(asset->data); - - if (asset->data->glassPieces) - { - AssertSize(Game::G_GlassPiece, 12); - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->data->glassPieces, asset->data->pieceCount); - Utils::Stream::ClearPointer(&destGlass->glassPieces); - } - - if (asset->data->glassNames) - { - AssertSize(Game::G_GlassName, 12); - buffer->align(Utils::Stream::ALIGN_4); - - Game::G_GlassName* destGlassNames = buffer->dest(); - buffer->saveArray(asset->data->glassNames, asset->data->glassNameCount); - - for (unsigned int i = 0; i < asset->data->glassNameCount; ++i) - { - Game::G_GlassName* destGlassName = &destGlassNames[i]; - Game::G_GlassName* glassName = &asset->data->glassNames[i]; - - if (glassName->nameStr) - { - buffer->saveString(glassName->nameStr); - Utils::Stream::ClearPointer(&destGlassName->nameStr); - } - - if (glassName->pieceIndices) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(glassName->pieceIndices, glassName->pieceCount); - Utils::Stream::ClearPointer(&destGlassName->pieceIndices); - } - } - - Utils::Stream::ClearPointer(&destGlass->glassNames); - } - } - - Utils::Stream::ClearPointer(&dest->data); - } - - buffer->popBlock(); - } -} + buffer->save(asset->data); + + if (asset->data->glassPieces) + { + AssertSize(Game::G_GlassPiece, 12); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->data->glassPieces, asset->data->pieceCount); + Utils::Stream::ClearPointer(&destGlass->glassPieces); + } + + if (asset->data->glassNames) + { + AssertSize(Game::G_GlassName, 12); + buffer->align(Utils::Stream::ALIGN_4); + + Game::G_GlassName* destGlassNames = buffer->dest(); + buffer->saveArray(asset->data->glassNames, asset->data->glassNameCount); + + for (unsigned int i = 0; i < asset->data->glassNameCount; ++i) + { + Game::G_GlassName* destGlassName = &destGlassNames[i]; + Game::G_GlassName* glassName = &asset->data->glassNames[i]; + + if (glassName->nameStr) + { + buffer->saveString(glassName->nameStr); + Utils::Stream::ClearPointer(&destGlassName->nameStr); + } + + if (glassName->pieceIndices) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(glassName->pieceIndices, glassName->pieceCount); + Utils::Stream::ClearPointer(&destGlassName->pieceIndices); + } + } + + Utils::Stream::ClearPointer(&destGlass->glassNames); + } + } + + Utils::Stream::ClearPointer(&dest->data); + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IGfxImage.cpp b/src/Components/Modules/AssetInterfaces/IGfxImage.cpp index b2381a5a..db989dc6 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxImage.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxImage.cpp @@ -1,181 +1,181 @@ -#include - -#define IW4X_IMG_VERSION "0" - -namespace Assets -{ - void IGfxImage::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - Game::GfxImage* image = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_IMAGE, name.data()).image; - if (image && name[0] != '*') return; - - image = builder->getAllocator()->allocate(); - if (!image) - { - Components::Logger::Error("Failed to allocate GfxImage structure!"); - return; - } - - image->name = builder->getAllocator()->duplicateString(name); - image->semantic = 2; - image->category = 0; - image->cardMemory = 0; - - const char* tempName = image->name; - if (tempName[0] == '*') tempName++; - - Components::FileSystem::File imageFile(Utils::String::VA("images/%s.iw4xImage", tempName)); - if (imageFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), imageFile.getBuffer()); - - if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xImg" IW4X_IMG_VERSION)) - { - Components::Logger::Error(0, "Reading image '%s' failed, header is invalid!", name.data()); - } - - AssertSize(Game::MapType, 1); - image->mapType = reader.read(); - image->semantic = reader.read(); - image->category = reader.read(); - - image->dataLen1 = reader.read(); - image->dataLen2 = image->dataLen1; - - image->loadDef = reinterpret_cast(reader.readArray(image->dataLen1 + 16)); - - image->height = image->loadDef->dimensions[0]; - image->width = image->loadDef->dimensions[1]; - image->depth = image->loadDef->dimensions[2]; - - image->loaded = true; - image->loadDef->flags = 0; - - if (Utils::String::StartsWith(name, "*lightmap")) - { - image->loadDef->dimensions[0] = 0; - image->loadDef->dimensions[1] = 2; - image->loadDef->dimensions[2] = 0; - } - - header->image = image; - } - else if(name[0] != '*') - { - char nameBuffer[MAX_PATH] = { 0 }; - Components::Materials::FormatImagePath(nameBuffer, sizeof(nameBuffer), 0, 0, name.data()); - Components::FileSystem::File iwi(nameBuffer); - - if (!iwi.exists()) - { - Components::Logger::Error("Loading image '%s' failed!", iwi.getName().data()); - return; - } - - auto iwiBuffer = iwi.getBuffer(); - - const Game::GfxImageFileHeader* iwiHeader = reinterpret_cast(iwiBuffer.data()); - - if (std::memcmp(iwiHeader->tag, "IWi", 3) && iwiHeader->version == 8) - { - Components::Logger::Error("Image is not a valid IWi!"); - return; - } - - image->mapType = Game::MAPTYPE_2D; - image->dataLen1 = iwiHeader->fileSizeForPicmip[0] - 32; - image->dataLen2 = iwiHeader->fileSizeForPicmip[0] - 32; - - image->loadDef = builder->getAllocator()->allocate(); - if (!image->loadDef) - { - Components::Logger::Error("Failed to allocate GfxImageLoadDef structure!"); - return; - } - - std::memcpy(image->loadDef->dimensions, iwiHeader->dimensions, 6); - image->loadDef->flags = 0; - image->loadDef->levelCount = 0; - - image->height = image->loadDef->dimensions[0]; - image->width = image->loadDef->dimensions[1]; - image->depth = image->loadDef->dimensions[2]; - - switch (iwiHeader->format) - { - case Game::IWI_COMPRESSION::IWI_ARGB: - { - image->loadDef->format = 21; - break; - } - - case Game::IWI_COMPRESSION::IWI_RGB8: - { - image->loadDef->format = 20; - break; - } - - case Game::IWI_COMPRESSION::IWI_DXT1: - { - image->loadDef->format = 0x31545844; - break; - } - - case Game::IWI_COMPRESSION::IWI_DXT3: - { - image->loadDef->format = 0x33545844; - break; - } - - case Game::IWI_COMPRESSION::IWI_DXT5: - { - image->loadDef->format = 0x35545844; - break; - } - } - - header->image = image; - } - } - - void IGfxImage::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GfxImage, 32); - AssertSize(Game::MapType, 1); - - Utils::Stream* buffer = builder->getBuffer(); - Game::GfxImage* asset = header.image; - Game::GfxImage* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - buffer->pushBlock(Game::XFILE_BLOCK_TEMP); - - if (asset->loadDef) - { - buffer->align(Utils::Stream::ALIGN_4); - - Game::GfxImageLoadDef* destTexture = buffer->dest(); - buffer->save(asset->loadDef, 16, 1); - - builder->incrementExternalSize(asset->loadDef->resourceSize); - - if (destTexture->resourceSize > 0) - { - buffer->save(asset->loadDef->data, asset->loadDef->resourceSize); - } - - Utils::Stream::ClearPointer(&dest->loadDef); - } - - buffer->popBlock(); - buffer->popBlock(); - } -} +#include + +#define IW4X_IMG_VERSION "0" + +namespace Assets +{ + void IGfxImage::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Game::GfxImage* image = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_IMAGE, name.data()).image; + if (image && name[0] != '*') return; + + image = builder->getAllocator()->allocate(); + if (!image) + { + Components::Logger::Error("Failed to allocate GfxImage structure!"); + return; + } + + image->name = builder->getAllocator()->duplicateString(name); + image->semantic = 2; + image->category = 0; + image->cardMemory = 0; + + const char* tempName = image->name; + if (tempName[0] == '*') tempName++; + + Components::FileSystem::File imageFile(Utils::String::VA("images/%s.iw4xImage", tempName)); + if (imageFile.exists()) + { + Utils::Stream::Reader reader(builder->getAllocator(), imageFile.getBuffer()); + + if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xImg" IW4X_IMG_VERSION)) + { + Components::Logger::Error(0, "Reading image '%s' failed, header is invalid!", name.data()); + } + + AssertSize(Game::MapType, 1); + image->mapType = reader.read(); + image->semantic = reader.read(); + image->category = reader.read(); + + image->dataLen1 = reader.read(); + image->dataLen2 = image->dataLen1; + + image->loadDef = reinterpret_cast(reader.readArray(image->dataLen1 + 16)); + + image->height = image->loadDef->dimensions[0]; + image->width = image->loadDef->dimensions[1]; + image->depth = image->loadDef->dimensions[2]; + + image->loaded = true; + image->loadDef->flags = 0; + + if (Utils::String::StartsWith(name, "*lightmap")) + { + image->loadDef->dimensions[0] = 0; + image->loadDef->dimensions[1] = 2; + image->loadDef->dimensions[2] = 0; + } + + header->image = image; + } + else if(name[0] != '*') + { + char nameBuffer[MAX_PATH] = { 0 }; + Components::Materials::FormatImagePath(nameBuffer, sizeof(nameBuffer), 0, 0, name.data()); + Components::FileSystem::File iwi(nameBuffer); + + if (!iwi.exists()) + { + Components::Logger::Error("Loading image '%s' failed!", iwi.getName().data()); + return; + } + + auto iwiBuffer = iwi.getBuffer(); + + const Game::GfxImageFileHeader* iwiHeader = reinterpret_cast(iwiBuffer.data()); + + if (std::memcmp(iwiHeader->tag, "IWi", 3) && iwiHeader->version == 8) + { + Components::Logger::Error("Image is not a valid IWi!"); + return; + } + + image->mapType = Game::MAPTYPE_2D; + image->dataLen1 = iwiHeader->fileSizeForPicmip[0] - 32; + image->dataLen2 = iwiHeader->fileSizeForPicmip[0] - 32; + + image->loadDef = builder->getAllocator()->allocate(); + if (!image->loadDef) + { + Components::Logger::Error("Failed to allocate GfxImageLoadDef structure!"); + return; + } + + std::memcpy(image->loadDef->dimensions, iwiHeader->dimensions, 6); + image->loadDef->flags = 0; + image->loadDef->levelCount = 0; + + image->height = image->loadDef->dimensions[0]; + image->width = image->loadDef->dimensions[1]; + image->depth = image->loadDef->dimensions[2]; + + switch (iwiHeader->format) + { + case Game::IWI_COMPRESSION::IWI_ARGB: + { + image->loadDef->format = 21; + break; + } + + case Game::IWI_COMPRESSION::IWI_RGB8: + { + image->loadDef->format = 20; + break; + } + + case Game::IWI_COMPRESSION::IWI_DXT1: + { + image->loadDef->format = 0x31545844; + break; + } + + case Game::IWI_COMPRESSION::IWI_DXT3: + { + image->loadDef->format = 0x33545844; + break; + } + + case Game::IWI_COMPRESSION::IWI_DXT5: + { + image->loadDef->format = 0x35545844; + break; + } + } + + header->image = image; + } + } + + void IGfxImage::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GfxImage, 32); + AssertSize(Game::MapType, 1); + + Utils::Stream* buffer = builder->getBuffer(); + Game::GfxImage* asset = header.image; + Game::GfxImage* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + buffer->pushBlock(Game::XFILE_BLOCK_TEMP); + + if (asset->loadDef) + { + buffer->align(Utils::Stream::ALIGN_4); + + Game::GfxImageLoadDef* destTexture = buffer->dest(); + buffer->save(asset->loadDef, 16, 1); + + builder->incrementExternalSize(asset->loadDef->resourceSize); + + if (destTexture->resourceSize > 0) + { + buffer->save(asset->loadDef->data, asset->loadDef->resourceSize); + } + + Utils::Stream::ClearPointer(&dest->loadDef); + } + + buffer->popBlock(); + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp b/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp index f3cade2a..909d0bb3 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxLightDef.cpp @@ -1,40 +1,40 @@ -#include - -namespace Assets -{ - void IGfxLightDef::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::GfxLightDef* asset = header.lightDef; - - if (asset->attenuation.image) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->attenuation.image); - } - } - - void IGfxLightDef::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GfxLightDef, 16); - AssertSize(Game::GfxLightImage, 8); - - Utils::Stream* buffer = builder->getBuffer(); - Game::GfxLightDef* asset = header.lightDef; - Game::GfxLightDef* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->attenuation.image) - { - dest->attenuation.image = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->attenuation.image).image; - } - - buffer->popBlock(); - } -} +#include + +namespace Assets +{ + void IGfxLightDef::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::GfxLightDef* asset = header.lightDef; + + if (asset->attenuation.image) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->attenuation.image); + } + } + + void IGfxLightDef::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GfxLightDef, 16); + AssertSize(Game::GfxLightImage, 8); + + Utils::Stream* buffer = builder->getBuffer(); + Game::GfxLightDef* asset = header.lightDef; + Game::GfxLightDef* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->attenuation.image) + { + dest->attenuation.image = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->attenuation.image).image; + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp b/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp index 4157846f..ed279cb6 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxWorld.cpp @@ -1,1327 +1,1327 @@ -#include - -#define IW4X_GFXMAP_VERSION 1 - -#ifdef ENABLE_EXPERIMENTAL_MAP_CODE - -namespace Assets -{ - void IGfxWorld::loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) - { - if (asset->sortedSurfIndex) - { - asset->sortedSurfIndex = reader->readArray(asset->staticSurfaceCount + asset->staticSurfaceCountNoDecal); - } - - if (asset->smodelInsts) - { - asset->smodelInsts = reader->readArray(asset->smodelCount); - } - - if (asset->surfaces) - { - asset->surfaces = reader->readArray(world->surfaceCount); - - for (unsigned int i = 0; i < world->surfaceCount; ++i) - { - Game::GfxSurface* surface = &asset->surfaces[i]; - - if (surface->material) - { - world->dpvs.surfaces[i].material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader->readString().data(), builder).material; - } - } - } - - if (asset->surfacesBounds) - { - asset->surfacesBounds = reader->readArray(world->surfaceCount); - } - - if (asset->smodelDrawInsts) - { - asset->smodelDrawInsts = reader->readArray(asset->smodelCount); - - for (unsigned int i = 0; i < asset->smodelCount; ++i) - { - Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i]; - - if (model->model) - { - model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, reader->readString().data(), builder).model; - //reader->readString(); model->model = Components::AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_XMODEL, "void").model; // Use red-fx for now - } - } - } - } - - void IGfxWorld::loadGfxWorldDraw(Game::GfxWorldDraw* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) - { - if (asset->reflectionImages) - { - asset->reflectionImages = reader->readArray(asset->reflectionProbeCount); - - for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i) - { - asset->reflectionImages[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; - } - } - - if (asset->reflectionProbes) - { - asset->reflectionProbes = reader->readArray(asset->reflectionProbeCount); - } - - if (asset->lightmaps) - { - asset->lightmaps = reader->readArray(asset->lightmapCount); - - for (int i = 0; i < asset->lightmapCount; ++i) - { - Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i]; - - if (lightmapArray->primary) - { - lightmapArray->primary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; - } - - if (lightmapArray->secondary) - { - lightmapArray->secondary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; - } - } - } - - if (asset->skyImage) - { - asset->skyImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; - } - - if (asset->outdoorImage) - { - asset->outdoorImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; - } - - // saveGfxWorldVertexData - { - if (asset->vd.vertices) - { - asset->vd.vertices = reader->readArray(asset->vertexCount); - } - } - - // saveGfxWorldVertexLayerData - { - if (asset->vld.data) - { - // no align for char - asset->vld.data = reader->readArray(asset->vertexLayerDataSize); - } - } - - if (asset->indices) - { - asset->indices = reader->readArray(asset->indexCount); - } - } - - void IGfxWorld::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - Utils::String::Replace(name, "maps/mp/", ""); - Utils::String::Replace(name, ".d3dbsp", ""); - - Components::FileSystem::File mapFile(Utils::String::VA("gfxworld/%s.iw4xGfxWorld", name.data())); - - if (mapFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer()); - - if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xGfxW")) - { - Components::Logger::Error("Reading gfxworld '%s' failed, header is invalid!", name.data()); - } - - int version = reader.read(); - if (version != IW4X_GFXMAP_VERSION) - { - Components::Logger::Error("Reading gfxworld '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_GFXMAP_VERSION, version); - } - - Game::GfxWorld* asset = reader.readObject(); - header->gfxWorld = asset; - - if (asset->name) - { - asset->name = reader.readCString(); - } - - if (asset->baseName) - { - asset->baseName = reader.readCString(); - } - - if (asset->skies) - { - asset->skies = reader.readArray(asset->skyCount); - - for (int i = 0; i < asset->skyCount; ++i) - { - Game::GfxSky* sky = &asset->skies[i]; - - if (sky->skyStartSurfs) - { - sky->skyStartSurfs = reader.readArray(sky->skySurfCount); - } - - if (sky->skyImage) - { - sky->skyImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image; - } - } - } - - // GfxWorldDpvsPlanes - { - // TODO: Add pointer support - if (asset->dpvsPlanes.planes) - { - asset->dpvsPlanes.planes = reader.readArray(asset->planeCount); - } - - if (asset->dpvsPlanes.nodes) - { - asset->dpvsPlanes.nodes = reader.readArray(asset->nodeCount); - } - } - - - int cellCount = asset->dpvsPlanes.cellCount; - - if (asset->aabbTreeCounts) - { - asset->aabbTreeCounts = reader.readArray(cellCount); - } - - if (asset->aabbTrees) - { - asset->aabbTrees = reader.readArray(cellCount); - - for (int i = 0; i < cellCount; ++i) - { - Game::GfxCellTree* cellTree = &asset->aabbTrees[i]; - - if (cellTree->aabbTree) - { - cellTree->aabbTree = reader.readArray(asset->aabbTreeCounts[i].aabbTreeCount); - - for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j) - { - Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j]; - - if (aabbTree->smodelIndexes) - { - aabbTree->smodelIndexes = reader.readArray(aabbTree->smodelIndexCount); - } - } - } - } - } - - if (asset->cells) - { - asset->cells = reader.readArray(cellCount); - - for (int i = 0; i < cellCount; ++i) - { - Game::GfxCell* cell = &asset->cells[i]; - - if (cell->portals) - { - cell->portals = reader.readArray(cell->portalCount); - - for (int j = 0; j < cell->portalCount; ++j) - { - Game::GfxPortal* portal = &cell->portals[j]; - - if (portal->vertices) - { - portal->vertices = reader.readArray(portal->vertexCount); - } - } - } - - if (cell->reflectionProbes) - { - // no align for char - cell->reflectionProbes = reader.readArray(cell->reflectionProbeCount); - } - } - } - - this->loadGfxWorldDraw(&asset->draw, builder, &reader); - - // GfxLightGrid - { - if (asset->lightGrid.rowDataStart) - { - asset->lightGrid.rowDataStart = reader.readArray((asset->lightGrid.maxs[asset->lightGrid.rowAxis] - asset->lightGrid.mins[asset->lightGrid.rowAxis]) + 1); - } - - if (asset->lightGrid.rawRowData) - { - // no align for char - asset->lightGrid.rawRowData = reader.readArray(asset->lightGrid.rawRowDataSize); - } - - if (asset->lightGrid.entries) - { - asset->lightGrid.entries = reader.readArray(asset->lightGrid.entryCount); - } - - if (asset->lightGrid.colors) - { - asset->lightGrid.colors = reader.readArray(asset->lightGrid.colorCount); - } - } - - if (asset->models) - { - asset->models = reader.readArray(asset->modelCount); - } - - if (asset->materialMemory) - { - asset->materialMemory = reader.readArray(asset->materialMemoryCount); - - for (int i = 0; i < asset->materialMemoryCount; ++i) - { - Game::MaterialMemory* materialMemory = &asset->materialMemory[i]; - - if (materialMemory->material) - { - materialMemory->material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString().data(), builder).material; - } - } - } - - if (asset->sun.spriteMaterial) - { - asset->sun.spriteMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString().data(), builder).material; - } - - if (asset->sun.flareMaterial) - { - asset->sun.flareMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString().data(), builder).material; - } - - if (asset->outdoorImage) - { - asset->outdoorImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image; - } - - if (asset->primaryLightCount > 0) - { - Utils::Stream::ClearPointer(&asset->primaryLightEntityShadowVis); - } - - if (asset->dpvsDyn.dynEntClientCount[0] > 0) - { - Utils::Stream::ClearPointer(&asset->sceneDynModel); - Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[0]); - Utils::Stream::ClearPointer(&asset->nonSunPrimaryLightForModelDynEnt); - } - - if (asset->dpvsDyn.dynEntClientCount[1] > 0) - { - Utils::Stream::ClearPointer(&asset->sceneDynBrush); - Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[1]); - } - - if (asset->shadowGeom) - { - asset->shadowGeom = reader.readArray(asset->primaryLightCount); - - for (unsigned int i = 0; i < asset->primaryLightCount; ++i) - { - Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i]; - - if (shadowGeometry->sortedSurfIndex) - { - shadowGeometry->sortedSurfIndex = reader.readArray(shadowGeometry->surfaceCount); - } - - if (shadowGeometry->smodelIndex) - { - shadowGeometry->smodelIndex = reader.readArray(shadowGeometry->smodelCount); - } - } - } - - if (asset->lightRegion) - { - asset->lightRegion = reader.readArray(asset->primaryLightCount); - - for (unsigned int i = 0; i < asset->primaryLightCount; ++i) - { - Game::GfxLightRegion* lightRegion = &asset->lightRegion[i]; - - if (lightRegion->hulls) - { - lightRegion->hulls = reader.readArray(lightRegion->hullCount); - - for (unsigned int j = 0; j < lightRegion->hullCount; ++j) - { - Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j]; - - if (lightRegionHull->axis) - { - lightRegionHull->axis = reader.readArray(lightRegionHull->axisCount); - } - } - } - } - } - - this->loadGfxWorldDpvsStatic(asset, &asset->dpvs, builder, &reader); - - // Obsolete, IW3 has no support for that - if (asset->heroOnlyLights) - { - asset->heroOnlyLights = reader.readArray(asset->heroOnlyLightCount); - } - } - } - - void IGfxWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::GfxWorld* asset = header.gfxWorld; - - if (asset->draw.reflectionImages) - { - for (unsigned int i = 0; i < asset->draw.reflectionProbeCount; ++i) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.reflectionImages[i]); - } - } - - if (asset->draw.lightmaps) - { - for (int i = 0; i < asset->draw.lightmapCount; ++i) - { - if (asset->draw.lightmaps[i].primary) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.lightmaps[i].primary); - } - - if (asset->draw.lightmaps[i].secondary) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.lightmaps[i].secondary); - } - } - } - - if (asset->draw.skyImage) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.skyImage); - } - - if (asset->draw.outdoorImage) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.outdoorImage); - } - - if (asset->sun.spriteMaterial) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->sun.spriteMaterial); - } - - if (asset->sun.flareMaterial) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->sun.flareMaterial); - } - - if (asset->skies) - { - for (int i = 0; i < asset->skyCount; ++i) - { - if (asset->skies[i].skyImage) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->skies[i].skyImage); - } - } - } - - if (asset->materialMemory) - { - for (int i = 0; i < asset->materialMemoryCount; ++i) - { - if (asset->materialMemory[i].material) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materialMemory[i].material); - } - } - } - - if (asset->outdoorImage) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->outdoorImage); - } - - if (asset->dpvs.surfaces) - { - for (unsigned int i = 0; i < asset->surfaceCount; ++i) - { - if (asset->dpvs.surfaces[i].material) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->dpvs.surfaces[i].material); - } - } - } - - if (asset->dpvs.smodelDrawInsts) - { - for (unsigned int i = 0; i < asset->dpvs.smodelCount; ++i) - { - if (asset->dpvs.smodelDrawInsts[i].model) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->dpvs.smodelDrawInsts[i].model); - } - } - } - } - - void IGfxWorld::saveGfxWorldDpvsPlanes(Game::GfxWorld* world, Game::GfxWorldDpvsPlanes* asset, Game::GfxWorldDpvsPlanes* dest, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GfxWorldDpvsPlanes, 16); - - Utils::Stream* buffer = builder->getBuffer(); - SaveLogEnter("GfxWorldDpvsPlanes"); - - if (asset->planes) - { - if (builder->hasPointer(asset->planes)) - { - dest->planes = builder->getPointer(asset->planes); - } - else - { - AssertSize(Game::cplane_t, 20); - - buffer->align(Utils::Stream::ALIGN_4); - - for (int i = 0; i < world->planeCount; ++i) - { - builder->storePointer(&asset->planes[i]); - buffer->save(&asset->planes[i]); - } - - Utils::Stream::ClearPointer(&dest->planes); - } - } - - if (asset->nodes) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->nodes, world->nodeCount); - Utils::Stream::ClearPointer(&dest->nodes); - } - - buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); - - if (asset->sceneEntCellBits) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(asset->sceneEntCellBits, 1, asset->cellCount << 11); - Utils::Stream::ClearPointer(&dest->sceneEntCellBits); - } - - buffer->popBlock(); - SaveLogExit(); - } - - void IGfxWorld::saveGfxWorldDraw(Game::GfxWorldDraw* asset, Game::GfxWorldDraw* dest, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GfxWorldDraw, 72); - SaveLogEnter("GfxWorldDraw"); - - Utils::Stream* buffer = builder->getBuffer(); - - if (asset->reflectionImages) - { - buffer->align(Utils::Stream::ALIGN_4); - - Game::GfxImage** imageDest = buffer->dest(); - buffer->saveArray(asset->reflectionImages, asset->reflectionProbeCount); - - for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i) - { - imageDest[i] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->reflectionImages[i]).image; - } - - Utils::Stream::ClearPointer(&dest->reflectionImages); - } - - if (asset->reflectionProbes) - { - AssertSize(Game::GfxReflectionProbe, 12); - SaveLogEnter("GfxReflectionProbe"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->reflectionProbes, asset->reflectionProbeCount); - Utils::Stream::ClearPointer(&dest->reflectionProbes); - - SaveLogExit(); - } - - buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); - - if (asset->reflectionProbeTextures) - { - AssertSize(Game::GfxRawTexture, 4); - SaveLogEnter("GfxRawTexture"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->reflectionProbeTextures, asset->reflectionProbeCount); - Utils::Stream::ClearPointer(&dest->reflectionProbeTextures); - - SaveLogExit(); - } - - buffer->popBlock(); - - if (asset->lightmaps) - { - AssertSize(Game::GfxLightmapArray, 8); - SaveLogEnter("GfxLightmapArray"); - - buffer->align(Utils::Stream::ALIGN_4); - - Game::GfxLightmapArray* lightmapArrayDestTable = buffer->dest(); - buffer->saveArray(asset->lightmaps, asset->lightmapCount); - - for (int i = 0; i < asset->lightmapCount; ++i) - { - Game::GfxLightmapArray* lightmapArrayDest = &lightmapArrayDestTable[i]; - Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i]; - - if (lightmapArray->primary) - { - lightmapArrayDest->primary = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, lightmapArray->primary).image; - } - - if (lightmapArray->secondary) - { - lightmapArrayDest->secondary = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, lightmapArray->secondary).image; - } - } - - Utils::Stream::ClearPointer(&dest->lightmaps); - SaveLogExit(); - } - - buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); - - if (asset->lightmapPrimaryTextures) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->lightmapPrimaryTextures, asset->lightmapCount); - Utils::Stream::ClearPointer(&dest->lightmapPrimaryTextures); - } - - if (asset->lightmapSecondaryTextures) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->lightmapSecondaryTextures, asset->lightmapCount); - Utils::Stream::ClearPointer(&dest->lightmapSecondaryTextures); - } - - buffer->popBlock(); - - if (asset->skyImage) - { - dest->skyImage = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->skyImage).image; - } - - if (asset->outdoorImage) - { - dest->outdoorImage = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->outdoorImage).image; - } - - // saveGfxWorldVertexData - { - if (asset->vd.vertices) - { - AssertSize(Game::GfxWorldVertex, 44); - SaveLogEnter("GfxWorldVertex"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->vd.vertices, asset->vertexCount); - Utils::Stream::ClearPointer(&dest->vd.vertices); - - SaveLogExit(); - } - } - - // saveGfxWorldVertexLayerData - { - if (asset->vld.data) - { - // no align for char - buffer->saveArray(asset->vld.data, asset->vertexLayerDataSize); - Utils::Stream::ClearPointer(&dest->vld.data); - } - } - - if (asset->indices) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->indices, asset->indexCount); - Utils::Stream::ClearPointer(&dest->indices); - } - - SaveLogExit(); - } - - void IGfxWorld::saveGfxLightGrid(Game::GfxLightGrid* asset, Game::GfxLightGrid* dest, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GfxLightGrid, 56); - - Utils::Stream* buffer = builder->getBuffer(); - SaveLogEnter("GfxLightGrid"); - - if (asset->rowDataStart) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->rowDataStart, (asset->maxs[asset->rowAxis] - asset->mins[asset->rowAxis]) + 1); - Utils::Stream::ClearPointer(&dest->rowDataStart); - } - - if (asset->rawRowData) - { - // no align for char - buffer->saveArray(asset->rawRowData, asset->rawRowDataSize); - Utils::Stream::ClearPointer(&dest->rawRowData); - } - - if (asset->entries) - { - AssertSize(Game::GfxLightGridEntry, 4); - SaveLogEnter("GfxLightGridEntry"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->entries, asset->entryCount); - Utils::Stream::ClearPointer(&dest->entries); - - SaveLogExit(); - } - - if (asset->colors) - { - AssertSize(Game::GfxLightGridColors, 168); - SaveLogEnter("GfxLightGridColors"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->colors, asset->colorCount); - Utils::Stream::ClearPointer(&dest->colors); - - SaveLogExit(); - } - - SaveLogExit(); - } - - void IGfxWorld::savesunflare_t(Game::sunflare_t* asset, Game::sunflare_t* dest, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::sunflare_t, 96); - SaveLogEnter("sunflare_t"); - - if (asset->spriteMaterial) - { - dest->spriteMaterial = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->spriteMaterial).material; - } - - if (asset->flareMaterial) - { - dest->flareMaterial = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->flareMaterial).material; - } - - SaveLogExit(); - } - - void IGfxWorld::saveGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Game::GfxWorldDpvsStatic* dest, int /*planeCount*/, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GfxWorldDpvsStatic, 108); - - Utils::Stream* buffer = builder->getBuffer(); - SaveLogEnter("GfxWorldDpvsStatic"); - - buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); - - for (int i = 0; i < 3; ++i) - { - if (asset->smodelVisData[i]) - { - buffer->saveArray(asset->smodelVisData[i], asset->smodelCount); - Utils::Stream::ClearPointer(&dest->smodelVisData[i]); - } - } - - for (int i = 0; i < 3; ++i) - { - if (asset->surfaceVisData[i]) - { - buffer->saveArray(asset->surfaceVisData[i], asset->staticSurfaceCount); - Utils::Stream::ClearPointer(&dest->surfaceVisData[i]); - } - } - - buffer->popBlock(); - - if (asset->sortedSurfIndex) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->sortedSurfIndex, asset->staticSurfaceCount + asset->staticSurfaceCountNoDecal); - Utils::Stream::ClearPointer(&dest->sortedSurfIndex); - } - - if (asset->smodelInsts) - { - AssertSize(Game::GfxStaticModelInst, 36); - SaveLogEnter("GfxStaticModelInst"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->smodelInsts, asset->smodelCount); - Utils::Stream::ClearPointer(&dest->smodelInsts); - - SaveLogExit(); - } - - if (asset->surfaces) - { - AssertSize(Game::GfxSurface, 24); - SaveLogEnter("GfxSurface"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::GfxSurface* destSurfaceTable = buffer->dest(); - buffer->saveArray(asset->surfaces, world->surfaceCount); - - for (unsigned int i = 0; i < world->surfaceCount; ++i) - { - Game::GfxSurface* surface = &asset->surfaces[i]; - Game::GfxSurface* destSurface = &destSurfaceTable[i]; - - if (surface->material) - { - destSurface->material = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, surface->material).material; - } - } - - Utils::Stream::ClearPointer(&dest->surfaces); - SaveLogExit(); - } - - if (asset->surfacesBounds) - { - AssertSize(Game::GfxSurfaceBounds, 24); - SaveLogEnter("GfxSurfaceBounds"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->surfacesBounds, world->surfaceCount); - Utils::Stream::ClearPointer(&dest->surfacesBounds); - - SaveLogExit(); - } - - if (asset->smodelDrawInsts) - { - AssertSize(Game::GfxStaticModelDrawInst, 76); - SaveLogEnter("GfxStaticModelDrawInst"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::GfxStaticModelDrawInst* destModelTable = buffer->dest(); - buffer->saveArray(asset->smodelDrawInsts, asset->smodelCount); - - for (unsigned int i = 0; i < asset->smodelCount; ++i) - { - Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i]; - Game::GfxStaticModelDrawInst* destModel = &destModelTable[i]; - - if (model->model) - { - destModel->model = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, model->model).model; - } - } - - Utils::Stream::ClearPointer(&dest->smodelDrawInsts); - SaveLogExit(); - } - - buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); - - if (asset->surfaceMaterials) - { - AssertSize(Game::GfxDrawSurf, 8); - SaveLogEnter("GfxDrawSurf"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->surfaceMaterials, world->surfaceCount); - Utils::Stream::ClearPointer(&dest->surfaceMaterials); - - SaveLogExit(); - } - - if (asset->surfaceCastsSunShadow) - { - AssertSize(Game::GfxDrawSurf, 8); - SaveLogEnter("GfxDrawSurf"); - - buffer->align(Utils::Stream::ALIGN_128); - buffer->save(asset->surfaceCastsSunShadow, 4, asset->surfaceVisDataCount); - Utils::Stream::ClearPointer(&dest->surfaceCastsSunShadow); - - SaveLogExit(); - } - - buffer->popBlock(); - SaveLogExit(); - } - - void IGfxWorld::saveGfxWorldDpvsDynamic(Game::GfxWorldDpvsDynamic* asset, Game::GfxWorldDpvsDynamic* dest, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GfxWorldDpvsDynamic, 48); - - Utils::Stream* buffer = builder->getBuffer(); - SaveLogEnter("GfxWorldDpvsDynamic"); - - buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); - - if (asset->dynEntCellBits[0]) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->dynEntCellBits[0], asset->dynEntClientWordCount[0]); - Utils::Stream::ClearPointer(&dest->dynEntCellBits[0]); - } - - if (asset->dynEntCellBits[1]) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->dynEntCellBits[1], asset->dynEntClientWordCount[1]); - Utils::Stream::ClearPointer(&dest->dynEntCellBits[1]); - } - - // this covers [0][0], [1][0], [0][1], [1][1], [0][2], [1][2] - for (char i = 0; i < 3; ++i) - { - for (char j = 0; j < 2; ++j) - { - if (asset->dynEntVisData[j][i]) - { - buffer->align(Utils::Stream::ALIGN_16); - buffer->save(asset->dynEntVisData[j][i], 32, asset->dynEntClientWordCount[j]); - Utils::Stream::ClearPointer(&dest->dynEntVisData[j][i]); - } - } - } - - buffer->popBlock(); - SaveLogExit(); - } - - void IGfxWorld::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::GfxWorld, 628); - - Utils::Stream* buffer = builder->getBuffer(); - SaveLogEnter("GfxWorld"); - - Game::GfxWorld* asset = header.gfxWorld; - Game::GfxWorld* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->baseName) - { - buffer->saveString(asset->baseName); - Utils::Stream::ClearPointer(&dest->baseName); - } - - if (asset->skies) - { - AssertSize(Game::GfxSky, 16); - SaveLogEnter("GfxSky"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::GfxSky* destSkyTable = buffer->dest(); - buffer->saveArray(asset->skies, asset->skyCount); - - for (int i = 0; i < asset->skyCount; ++i) - { - Game::GfxSky* destSky = &destSkyTable[i]; - Game::GfxSky* sky = &asset->skies[i]; - - if (sky->skyStartSurfs) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(sky->skyStartSurfs, sky->skySurfCount); - Utils::Stream::ClearPointer(&destSky->skyStartSurfs); - } - - if (sky->skyImage) - { - destSky->skyImage = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, sky->skyImage).image; - } - } - - Utils::Stream::ClearPointer(&dest->skies); - SaveLogExit(); - } - - this->saveGfxWorldDpvsPlanes(asset, &asset->dpvsPlanes, &dest->dpvsPlanes, builder); - - int cellCount = asset->dpvsPlanes.cellCount; - - if (asset->aabbTreeCounts) - { - AssertSize(Game::GfxCellTreeCount, 4); - SaveLogEnter("GfxCellTreeCount"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->aabbTreeCounts, cellCount); - Utils::Stream::ClearPointer(&dest->aabbTreeCounts); - - SaveLogExit(); - } - - if (asset->aabbTrees) - { - AssertSize(Game::GfxCellTree, 4); - SaveLogEnter("GfxCellTree"); - - buffer->align(Utils::Stream::ALIGN_128); - Game::GfxCellTree* destCellTreeTable = buffer->dest(); - buffer->saveArray(asset->aabbTrees, cellCount); - - for (int i = 0; i < cellCount; ++i) - { - Game::GfxCellTree* destCellTree = &destCellTreeTable[i]; - Game::GfxCellTree* cellTree = &asset->aabbTrees[i]; - - if (cellTree->aabbTree) - { - AssertSize(Game::GfxAabbTree, 44); - SaveLogEnter("GfxAabbTree"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::GfxAabbTree* destAabbTreeTable = buffer->dest(); - buffer->saveArray(cellTree->aabbTree, asset->aabbTreeCounts[i].aabbTreeCount); - - // ok this one is based on some assumptions because the actual count is this - // *(int *)((char *)&varGfxWorld->aabbTreeCounts->aabbTreeCount + (((char *)varGfxCellTree - (char *)varGfxWorld->aabbTrees) & 0xFFFFFFFC)) - // which makes no sense - // what DOES make sense is using the count from the structure - - for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j) - { - Game::GfxAabbTree* destAabbTree = &destAabbTreeTable[j]; - Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j]; - - if (aabbTree->smodelIndexes) - { - if (builder->hasPointer(aabbTree->smodelIndexes)) - { - destAabbTree->smodelIndexes = builder->getPointer(aabbTree->smodelIndexes); - } - else - { - buffer->align(Utils::Stream::ALIGN_2); - - for (int k = 0; k < aabbTree->smodelIndexCount; ++k) - { - builder->storePointer(&aabbTree->smodelIndexes[k]); - buffer->save(&aabbTree->smodelIndexes[k]); - } - - Utils::Stream::ClearPointer(&destAabbTree->smodelIndexes); - } - } - } - - Utils::Stream::ClearPointer(&destCellTree->aabbTree); - SaveLogExit(); - } - } - - Utils::Stream::ClearPointer(&dest->aabbTrees); - SaveLogExit(); - } - - if (asset->cells) - { - AssertSize(Game::GfxCell, 40); - SaveLogEnter("GfxCell"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::GfxCell* destCellTable = buffer->dest(); - buffer->saveArray(asset->cells, cellCount); - - for (int i = 0; i < cellCount; ++i) - { - Game::GfxCell* destCell = &destCellTable[i]; - Game::GfxCell* cell = &asset->cells[i]; - - if (cell->portals) - { - AssertSize(Game::GfxPortal, 60); - SaveLogEnter("GfxPortal"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::GfxPortal* destPortalTable = buffer->dest(); - buffer->saveArray(cell->portals, cell->portalCount); - - for (int j = 0; j < cell->portalCount; ++j) - { - Game::GfxPortal* destPortal = &destPortalTable[j]; - Game::GfxPortal* portal = &cell->portals[j]; - - if (portal->vertices) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(portal->vertices, portal->vertexCount); - Utils::Stream::ClearPointer(&destPortal->vertices); - } - } - - Utils::Stream::ClearPointer(&destCell->portals); - SaveLogExit(); - } - - if (cell->reflectionProbes) - { - // no align for char - buffer->saveArray(cell->reflectionProbes, cell->reflectionProbeCount); - Utils::Stream::ClearPointer(&destCell->reflectionProbes); - } - } - - Utils::Stream::ClearPointer(&dest->cells); - SaveLogExit(); - } - - this->saveGfxWorldDraw(&asset->draw, &dest->draw, builder); - this->saveGfxLightGrid(&asset->lightGrid, &dest->lightGrid, builder); - - if (asset->models) - { - AssertSize(Game::GfxBrushModel, 60); - SaveLogEnter("GfxBrushModel"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->models, asset->modelCount); - Utils::Stream::ClearPointer(&dest->models); - - SaveLogExit(); - } - - if (asset->materialMemory) - { - AssertSize(Game::MaterialMemory, 8); - SaveLogEnter("MaterialMemory"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::MaterialMemory* destMaterialMemoryTable = buffer->dest(); - buffer->saveArray(asset->materialMemory, asset->materialMemoryCount); - - for (int i = 0; i < asset->materialMemoryCount; ++i) - { - Game::MaterialMemory* destMaterialMemory = &destMaterialMemoryTable[i]; - Game::MaterialMemory* materialMemory = &asset->materialMemory[i]; - - if (materialMemory->material) - { - destMaterialMemory->material = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, materialMemory->material).material; - } - } - - Utils::Stream::ClearPointer(&dest->materialMemory); - SaveLogExit(); - } - - this->savesunflare_t(&asset->sun, &dest->sun, builder); - - if (asset->outdoorImage) - { - dest->outdoorImage = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->outdoorImage).image; - } - - buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); - - if (asset->cellCasterBits) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->cellCasterBits, cellCount * ((cellCount + 31) >> 5)); - Utils::Stream::ClearPointer(&dest->cellCasterBits); - } - - if (asset->cellHasSunLitSurfsBits) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->cellHasSunLitSurfsBits, ((cellCount + 31) >> 5)); - Utils::Stream::ClearPointer(&dest->cellHasSunLitSurfsBits); - } - - if (asset->sceneDynModel) - { - AssertSize(Game::GfxSceneDynModel, 6); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->sceneDynModel, asset->dpvsDyn.dynEntClientCount[0]); - Utils::Stream::ClearPointer(&dest->sceneDynModel); - } - - if (asset->sceneDynBrush) - { - AssertSize(Game::GfxSceneDynBrush, 4); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->sceneDynBrush, asset->dpvsDyn.dynEntClientCount[1]); - Utils::Stream::ClearPointer(&dest->sceneDynBrush); - } - - if (asset->primaryLightEntityShadowVis) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(asset->primaryLightEntityShadowVis, 1, (asset->primaryLightCount + 0x1FFFF - asset->lastSunPrimaryLightIndex) << 15); - Utils::Stream::ClearPointer(&dest->primaryLightEntityShadowVis); - } - - if (asset->primaryLightDynEntShadowVis[0]) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->primaryLightDynEntShadowVis[0], asset->dpvsDyn.dynEntClientCount[0] * (asset->primaryLightCount - 1 - asset->lastSunPrimaryLightIndex)); - Utils::Stream::ClearPointer(&dest->primaryLightDynEntShadowVis[0]); - } - - if (asset->primaryLightDynEntShadowVis[1]) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->primaryLightDynEntShadowVis[1], asset->dpvsDyn.dynEntClientCount[1] * (asset->primaryLightCount - 1 - asset->lastSunPrimaryLightIndex)); - Utils::Stream::ClearPointer(&dest->primaryLightDynEntShadowVis[1]); - } - - if (asset->nonSunPrimaryLightForModelDynEnt) - { - // no align cause char - buffer->saveArray(asset->nonSunPrimaryLightForModelDynEnt, asset->dpvsDyn.dynEntClientCount[0]); - Utils::Stream::ClearPointer(&dest->nonSunPrimaryLightForModelDynEnt); - } - - buffer->popBlock(); - - if (asset->shadowGeom) - { - AssertSize(Game::GfxShadowGeometry, 12); - SaveLogEnter("GfxShadowGeometry"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::GfxShadowGeometry* destShadowGeometryTable = buffer->dest(); - buffer->saveArray(asset->shadowGeom, asset->primaryLightCount); - - for (unsigned int i = 0; i < asset->primaryLightCount; ++i) - { - Game::GfxShadowGeometry* destShadowGeometry = &destShadowGeometryTable[i]; - Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i]; - - if (shadowGeometry->sortedSurfIndex) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(shadowGeometry->sortedSurfIndex, shadowGeometry->surfaceCount); - Utils::Stream::ClearPointer(&destShadowGeometry->sortedSurfIndex); - } - - if (shadowGeometry->smodelIndex) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(shadowGeometry->smodelIndex, shadowGeometry->smodelCount); - Utils::Stream::ClearPointer(&destShadowGeometry->smodelIndex); - } - } - - Utils::Stream::ClearPointer(&dest->shadowGeom); - SaveLogExit(); - } - - if (asset->lightRegion) - { - AssertSize(Game::GfxLightRegion, 8); - SaveLogEnter("GfxLightRegion"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::GfxLightRegion* destLightRegionTable = buffer->dest(); - buffer->saveArray(asset->lightRegion, asset->primaryLightCount); - - for (unsigned int i = 0; i < asset->primaryLightCount; ++i) - { - Game::GfxLightRegion* destLightRegion = &destLightRegionTable[i]; - Game::GfxLightRegion* lightRegion = &asset->lightRegion[i]; - - if (lightRegion->hulls) - { - AssertSize(Game::GfxLightRegionHull, 80); - SaveLogEnter("GfxLightRegionHull"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::GfxLightRegionHull* destLightRegionHullTable = buffer->dest(); - buffer->saveArray(lightRegion->hulls, lightRegion->hullCount); - - for (unsigned int j = 0; j < lightRegion->hullCount; ++j) - { - Game::GfxLightRegionHull* destLightRegionHull = &destLightRegionHullTable[j]; - Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j]; - - if (lightRegionHull->axis) - { - AssertSize(Game::GfxLightRegionAxis, 20); - SaveLogEnter("GfxLightRegionAxis"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(lightRegionHull->axis, lightRegionHull->axisCount); - Utils::Stream::ClearPointer(&destLightRegionHull->axis); - - SaveLogExit(); - } - - } - - Utils::Stream::ClearPointer(&destLightRegion->hulls); - SaveLogExit(); - } - } - - Utils::Stream::ClearPointer(&dest->lightRegion); - SaveLogExit(); - } - - this->saveGfxWorldDpvsStatic(asset, &asset->dpvs, &dest->dpvs, asset->dpvsPlanes.cellCount, builder); - this->saveGfxWorldDpvsDynamic(&asset->dpvsDyn, &dest->dpvsDyn, builder); - - if (asset->heroOnlyLights) - { - AssertSize(Game::GfxHeroOnlyLight, 56); - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->heroOnlyLights, asset->heroOnlyLightCount); - Utils::Stream::ClearPointer(&dest->heroOnlyLights); - } - - buffer->popBlock(); - SaveLogExit(); - } -} - -#endif +#include + +#define IW4X_GFXMAP_VERSION 1 + +#ifdef ENABLE_EXPERIMENTAL_MAP_CODE + +namespace Assets +{ + void IGfxWorld::loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) + { + if (asset->sortedSurfIndex) + { + asset->sortedSurfIndex = reader->readArray(asset->staticSurfaceCount + asset->staticSurfaceCountNoDecal); + } + + if (asset->smodelInsts) + { + asset->smodelInsts = reader->readArray(asset->smodelCount); + } + + if (asset->surfaces) + { + asset->surfaces = reader->readArray(world->surfaceCount); + + for (unsigned int i = 0; i < world->surfaceCount; ++i) + { + Game::GfxSurface* surface = &asset->surfaces[i]; + + if (surface->material) + { + world->dpvs.surfaces[i].material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader->readString().data(), builder).material; + } + } + } + + if (asset->surfacesBounds) + { + asset->surfacesBounds = reader->readArray(world->surfaceCount); + } + + if (asset->smodelDrawInsts) + { + asset->smodelDrawInsts = reader->readArray(asset->smodelCount); + + for (unsigned int i = 0; i < asset->smodelCount; ++i) + { + Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i]; + + if (model->model) + { + model->model = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, reader->readString().data(), builder).model; + //reader->readString(); model->model = Components::AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_XMODEL, "void").model; // Use red-fx for now + } + } + } + } + + void IGfxWorld::loadGfxWorldDraw(Game::GfxWorldDraw* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader) + { + if (asset->reflectionImages) + { + asset->reflectionImages = reader->readArray(asset->reflectionProbeCount); + + for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i) + { + asset->reflectionImages[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; + } + } + + if (asset->reflectionProbes) + { + asset->reflectionProbes = reader->readArray(asset->reflectionProbeCount); + } + + if (asset->lightmaps) + { + asset->lightmaps = reader->readArray(asset->lightmapCount); + + for (int i = 0; i < asset->lightmapCount; ++i) + { + Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i]; + + if (lightmapArray->primary) + { + lightmapArray->primary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; + } + + if (lightmapArray->secondary) + { + lightmapArray->secondary = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; + } + } + } + + if (asset->skyImage) + { + asset->skyImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; + } + + if (asset->outdoorImage) + { + asset->outdoorImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader->readString().data(), builder).image; + } + + // saveGfxWorldVertexData + { + if (asset->vd.vertices) + { + asset->vd.vertices = reader->readArray(asset->vertexCount); + } + } + + // saveGfxWorldVertexLayerData + { + if (asset->vld.data) + { + // no align for char + asset->vld.data = reader->readArray(asset->vertexLayerDataSize); + } + } + + if (asset->indices) + { + asset->indices = reader->readArray(asset->indexCount); + } + } + + void IGfxWorld::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Utils::String::Replace(name, "maps/mp/", ""); + Utils::String::Replace(name, ".d3dbsp", ""); + + Components::FileSystem::File mapFile(Utils::String::VA("gfxworld/%s.iw4xGfxWorld", name.data())); + + if (mapFile.exists()) + { + Utils::Stream::Reader reader(builder->getAllocator(), mapFile.getBuffer()); + + if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xGfxW")) + { + Components::Logger::Error("Reading gfxworld '%s' failed, header is invalid!", name.data()); + } + + int version = reader.read(); + if (version != IW4X_GFXMAP_VERSION) + { + Components::Logger::Error("Reading gfxworld '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_GFXMAP_VERSION, version); + } + + Game::GfxWorld* asset = reader.readObject(); + header->gfxWorld = asset; + + if (asset->name) + { + asset->name = reader.readCString(); + } + + if (asset->baseName) + { + asset->baseName = reader.readCString(); + } + + if (asset->skies) + { + asset->skies = reader.readArray(asset->skyCount); + + for (int i = 0; i < asset->skyCount; ++i) + { + Game::GfxSky* sky = &asset->skies[i]; + + if (sky->skyStartSurfs) + { + sky->skyStartSurfs = reader.readArray(sky->skySurfCount); + } + + if (sky->skyImage) + { + sky->skyImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image; + } + } + } + + // GfxWorldDpvsPlanes + { + // TODO: Add pointer support + if (asset->dpvsPlanes.planes) + { + asset->dpvsPlanes.planes = reader.readArray(asset->planeCount); + } + + if (asset->dpvsPlanes.nodes) + { + asset->dpvsPlanes.nodes = reader.readArray(asset->nodeCount); + } + } + + + int cellCount = asset->dpvsPlanes.cellCount; + + if (asset->aabbTreeCounts) + { + asset->aabbTreeCounts = reader.readArray(cellCount); + } + + if (asset->aabbTrees) + { + asset->aabbTrees = reader.readArray(cellCount); + + for (int i = 0; i < cellCount; ++i) + { + Game::GfxCellTree* cellTree = &asset->aabbTrees[i]; + + if (cellTree->aabbTree) + { + cellTree->aabbTree = reader.readArray(asset->aabbTreeCounts[i].aabbTreeCount); + + for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j) + { + Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j]; + + if (aabbTree->smodelIndexes) + { + aabbTree->smodelIndexes = reader.readArray(aabbTree->smodelIndexCount); + } + } + } + } + } + + if (asset->cells) + { + asset->cells = reader.readArray(cellCount); + + for (int i = 0; i < cellCount; ++i) + { + Game::GfxCell* cell = &asset->cells[i]; + + if (cell->portals) + { + cell->portals = reader.readArray(cell->portalCount); + + for (int j = 0; j < cell->portalCount; ++j) + { + Game::GfxPortal* portal = &cell->portals[j]; + + if (portal->vertices) + { + portal->vertices = reader.readArray(portal->vertexCount); + } + } + } + + if (cell->reflectionProbes) + { + // no align for char + cell->reflectionProbes = reader.readArray(cell->reflectionProbeCount); + } + } + } + + this->loadGfxWorldDraw(&asset->draw, builder, &reader); + + // GfxLightGrid + { + if (asset->lightGrid.rowDataStart) + { + asset->lightGrid.rowDataStart = reader.readArray((asset->lightGrid.maxs[asset->lightGrid.rowAxis] - asset->lightGrid.mins[asset->lightGrid.rowAxis]) + 1); + } + + if (asset->lightGrid.rawRowData) + { + // no align for char + asset->lightGrid.rawRowData = reader.readArray(asset->lightGrid.rawRowDataSize); + } + + if (asset->lightGrid.entries) + { + asset->lightGrid.entries = reader.readArray(asset->lightGrid.entryCount); + } + + if (asset->lightGrid.colors) + { + asset->lightGrid.colors = reader.readArray(asset->lightGrid.colorCount); + } + } + + if (asset->models) + { + asset->models = reader.readArray(asset->modelCount); + } + + if (asset->materialMemory) + { + asset->materialMemory = reader.readArray(asset->materialMemoryCount); + + for (int i = 0; i < asset->materialMemoryCount; ++i) + { + Game::MaterialMemory* materialMemory = &asset->materialMemory[i]; + + if (materialMemory->material) + { + materialMemory->material = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString().data(), builder).material; + } + } + } + + if (asset->sun.spriteMaterial) + { + asset->sun.spriteMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString().data(), builder).material; + } + + if (asset->sun.flareMaterial) + { + asset->sun.flareMaterial = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString().data(), builder).material; + } + + if (asset->outdoorImage) + { + asset->outdoorImage = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image; + } + + if (asset->primaryLightCount > 0) + { + Utils::Stream::ClearPointer(&asset->primaryLightEntityShadowVis); + } + + if (asset->dpvsDyn.dynEntClientCount[0] > 0) + { + Utils::Stream::ClearPointer(&asset->sceneDynModel); + Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[0]); + Utils::Stream::ClearPointer(&asset->nonSunPrimaryLightForModelDynEnt); + } + + if (asset->dpvsDyn.dynEntClientCount[1] > 0) + { + Utils::Stream::ClearPointer(&asset->sceneDynBrush); + Utils::Stream::ClearPointer(&asset->primaryLightDynEntShadowVis[1]); + } + + if (asset->shadowGeom) + { + asset->shadowGeom = reader.readArray(asset->primaryLightCount); + + for (unsigned int i = 0; i < asset->primaryLightCount; ++i) + { + Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i]; + + if (shadowGeometry->sortedSurfIndex) + { + shadowGeometry->sortedSurfIndex = reader.readArray(shadowGeometry->surfaceCount); + } + + if (shadowGeometry->smodelIndex) + { + shadowGeometry->smodelIndex = reader.readArray(shadowGeometry->smodelCount); + } + } + } + + if (asset->lightRegion) + { + asset->lightRegion = reader.readArray(asset->primaryLightCount); + + for (unsigned int i = 0; i < asset->primaryLightCount; ++i) + { + Game::GfxLightRegion* lightRegion = &asset->lightRegion[i]; + + if (lightRegion->hulls) + { + lightRegion->hulls = reader.readArray(lightRegion->hullCount); + + for (unsigned int j = 0; j < lightRegion->hullCount; ++j) + { + Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j]; + + if (lightRegionHull->axis) + { + lightRegionHull->axis = reader.readArray(lightRegionHull->axisCount); + } + } + } + } + } + + this->loadGfxWorldDpvsStatic(asset, &asset->dpvs, builder, &reader); + + // Obsolete, IW3 has no support for that + if (asset->heroOnlyLights) + { + asset->heroOnlyLights = reader.readArray(asset->heroOnlyLightCount); + } + } + } + + void IGfxWorld::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::GfxWorld* asset = header.gfxWorld; + + if (asset->draw.reflectionImages) + { + for (unsigned int i = 0; i < asset->draw.reflectionProbeCount; ++i) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.reflectionImages[i]); + } + } + + if (asset->draw.lightmaps) + { + for (int i = 0; i < asset->draw.lightmapCount; ++i) + { + if (asset->draw.lightmaps[i].primary) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.lightmaps[i].primary); + } + + if (asset->draw.lightmaps[i].secondary) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.lightmaps[i].secondary); + } + } + } + + if (asset->draw.skyImage) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.skyImage); + } + + if (asset->draw.outdoorImage) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->draw.outdoorImage); + } + + if (asset->sun.spriteMaterial) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->sun.spriteMaterial); + } + + if (asset->sun.flareMaterial) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->sun.flareMaterial); + } + + if (asset->skies) + { + for (int i = 0; i < asset->skyCount; ++i) + { + if (asset->skies[i].skyImage) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->skies[i].skyImage); + } + } + } + + if (asset->materialMemory) + { + for (int i = 0; i < asset->materialMemoryCount; ++i) + { + if (asset->materialMemory[i].material) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materialMemory[i].material); + } + } + } + + if (asset->outdoorImage) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->outdoorImage); + } + + if (asset->dpvs.surfaces) + { + for (unsigned int i = 0; i < asset->surfaceCount; ++i) + { + if (asset->dpvs.surfaces[i].material) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->dpvs.surfaces[i].material); + } + } + } + + if (asset->dpvs.smodelDrawInsts) + { + for (unsigned int i = 0; i < asset->dpvs.smodelCount; ++i) + { + if (asset->dpvs.smodelDrawInsts[i].model) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->dpvs.smodelDrawInsts[i].model); + } + } + } + } + + void IGfxWorld::saveGfxWorldDpvsPlanes(Game::GfxWorld* world, Game::GfxWorldDpvsPlanes* asset, Game::GfxWorldDpvsPlanes* dest, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GfxWorldDpvsPlanes, 16); + + Utils::Stream* buffer = builder->getBuffer(); + SaveLogEnter("GfxWorldDpvsPlanes"); + + if (asset->planes) + { + if (builder->hasPointer(asset->planes)) + { + dest->planes = builder->getPointer(asset->planes); + } + else + { + AssertSize(Game::cplane_t, 20); + + buffer->align(Utils::Stream::ALIGN_4); + + for (int i = 0; i < world->planeCount; ++i) + { + builder->storePointer(&asset->planes[i]); + buffer->save(&asset->planes[i]); + } + + Utils::Stream::ClearPointer(&dest->planes); + } + } + + if (asset->nodes) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->nodes, world->nodeCount); + Utils::Stream::ClearPointer(&dest->nodes); + } + + buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); + + if (asset->sceneEntCellBits) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(asset->sceneEntCellBits, 1, asset->cellCount << 11); + Utils::Stream::ClearPointer(&dest->sceneEntCellBits); + } + + buffer->popBlock(); + SaveLogExit(); + } + + void IGfxWorld::saveGfxWorldDraw(Game::GfxWorldDraw* asset, Game::GfxWorldDraw* dest, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GfxWorldDraw, 72); + SaveLogEnter("GfxWorldDraw"); + + Utils::Stream* buffer = builder->getBuffer(); + + if (asset->reflectionImages) + { + buffer->align(Utils::Stream::ALIGN_4); + + Game::GfxImage** imageDest = buffer->dest(); + buffer->saveArray(asset->reflectionImages, asset->reflectionProbeCount); + + for (unsigned int i = 0; i < asset->reflectionProbeCount; ++i) + { + imageDest[i] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->reflectionImages[i]).image; + } + + Utils::Stream::ClearPointer(&dest->reflectionImages); + } + + if (asset->reflectionProbes) + { + AssertSize(Game::GfxReflectionProbe, 12); + SaveLogEnter("GfxReflectionProbe"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->reflectionProbes, asset->reflectionProbeCount); + Utils::Stream::ClearPointer(&dest->reflectionProbes); + + SaveLogExit(); + } + + buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); + + if (asset->reflectionProbeTextures) + { + AssertSize(Game::GfxRawTexture, 4); + SaveLogEnter("GfxRawTexture"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->reflectionProbeTextures, asset->reflectionProbeCount); + Utils::Stream::ClearPointer(&dest->reflectionProbeTextures); + + SaveLogExit(); + } + + buffer->popBlock(); + + if (asset->lightmaps) + { + AssertSize(Game::GfxLightmapArray, 8); + SaveLogEnter("GfxLightmapArray"); + + buffer->align(Utils::Stream::ALIGN_4); + + Game::GfxLightmapArray* lightmapArrayDestTable = buffer->dest(); + buffer->saveArray(asset->lightmaps, asset->lightmapCount); + + for (int i = 0; i < asset->lightmapCount; ++i) + { + Game::GfxLightmapArray* lightmapArrayDest = &lightmapArrayDestTable[i]; + Game::GfxLightmapArray* lightmapArray = &asset->lightmaps[i]; + + if (lightmapArray->primary) + { + lightmapArrayDest->primary = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, lightmapArray->primary).image; + } + + if (lightmapArray->secondary) + { + lightmapArrayDest->secondary = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, lightmapArray->secondary).image; + } + } + + Utils::Stream::ClearPointer(&dest->lightmaps); + SaveLogExit(); + } + + buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); + + if (asset->lightmapPrimaryTextures) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->lightmapPrimaryTextures, asset->lightmapCount); + Utils::Stream::ClearPointer(&dest->lightmapPrimaryTextures); + } + + if (asset->lightmapSecondaryTextures) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->lightmapSecondaryTextures, asset->lightmapCount); + Utils::Stream::ClearPointer(&dest->lightmapSecondaryTextures); + } + + buffer->popBlock(); + + if (asset->skyImage) + { + dest->skyImage = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->skyImage).image; + } + + if (asset->outdoorImage) + { + dest->outdoorImage = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->outdoorImage).image; + } + + // saveGfxWorldVertexData + { + if (asset->vd.vertices) + { + AssertSize(Game::GfxWorldVertex, 44); + SaveLogEnter("GfxWorldVertex"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->vd.vertices, asset->vertexCount); + Utils::Stream::ClearPointer(&dest->vd.vertices); + + SaveLogExit(); + } + } + + // saveGfxWorldVertexLayerData + { + if (asset->vld.data) + { + // no align for char + buffer->saveArray(asset->vld.data, asset->vertexLayerDataSize); + Utils::Stream::ClearPointer(&dest->vld.data); + } + } + + if (asset->indices) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->indices, asset->indexCount); + Utils::Stream::ClearPointer(&dest->indices); + } + + SaveLogExit(); + } + + void IGfxWorld::saveGfxLightGrid(Game::GfxLightGrid* asset, Game::GfxLightGrid* dest, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GfxLightGrid, 56); + + Utils::Stream* buffer = builder->getBuffer(); + SaveLogEnter("GfxLightGrid"); + + if (asset->rowDataStart) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->rowDataStart, (asset->maxs[asset->rowAxis] - asset->mins[asset->rowAxis]) + 1); + Utils::Stream::ClearPointer(&dest->rowDataStart); + } + + if (asset->rawRowData) + { + // no align for char + buffer->saveArray(asset->rawRowData, asset->rawRowDataSize); + Utils::Stream::ClearPointer(&dest->rawRowData); + } + + if (asset->entries) + { + AssertSize(Game::GfxLightGridEntry, 4); + SaveLogEnter("GfxLightGridEntry"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->entries, asset->entryCount); + Utils::Stream::ClearPointer(&dest->entries); + + SaveLogExit(); + } + + if (asset->colors) + { + AssertSize(Game::GfxLightGridColors, 168); + SaveLogEnter("GfxLightGridColors"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->colors, asset->colorCount); + Utils::Stream::ClearPointer(&dest->colors); + + SaveLogExit(); + } + + SaveLogExit(); + } + + void IGfxWorld::savesunflare_t(Game::sunflare_t* asset, Game::sunflare_t* dest, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::sunflare_t, 96); + SaveLogEnter("sunflare_t"); + + if (asset->spriteMaterial) + { + dest->spriteMaterial = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->spriteMaterial).material; + } + + if (asset->flareMaterial) + { + dest->flareMaterial = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->flareMaterial).material; + } + + SaveLogExit(); + } + + void IGfxWorld::saveGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Game::GfxWorldDpvsStatic* dest, int /*planeCount*/, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GfxWorldDpvsStatic, 108); + + Utils::Stream* buffer = builder->getBuffer(); + SaveLogEnter("GfxWorldDpvsStatic"); + + buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); + + for (int i = 0; i < 3; ++i) + { + if (asset->smodelVisData[i]) + { + buffer->saveArray(asset->smodelVisData[i], asset->smodelCount); + Utils::Stream::ClearPointer(&dest->smodelVisData[i]); + } + } + + for (int i = 0; i < 3; ++i) + { + if (asset->surfaceVisData[i]) + { + buffer->saveArray(asset->surfaceVisData[i], asset->staticSurfaceCount); + Utils::Stream::ClearPointer(&dest->surfaceVisData[i]); + } + } + + buffer->popBlock(); + + if (asset->sortedSurfIndex) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->sortedSurfIndex, asset->staticSurfaceCount + asset->staticSurfaceCountNoDecal); + Utils::Stream::ClearPointer(&dest->sortedSurfIndex); + } + + if (asset->smodelInsts) + { + AssertSize(Game::GfxStaticModelInst, 36); + SaveLogEnter("GfxStaticModelInst"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->smodelInsts, asset->smodelCount); + Utils::Stream::ClearPointer(&dest->smodelInsts); + + SaveLogExit(); + } + + if (asset->surfaces) + { + AssertSize(Game::GfxSurface, 24); + SaveLogEnter("GfxSurface"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::GfxSurface* destSurfaceTable = buffer->dest(); + buffer->saveArray(asset->surfaces, world->surfaceCount); + + for (unsigned int i = 0; i < world->surfaceCount; ++i) + { + Game::GfxSurface* surface = &asset->surfaces[i]; + Game::GfxSurface* destSurface = &destSurfaceTable[i]; + + if (surface->material) + { + destSurface->material = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, surface->material).material; + } + } + + Utils::Stream::ClearPointer(&dest->surfaces); + SaveLogExit(); + } + + if (asset->surfacesBounds) + { + AssertSize(Game::GfxSurfaceBounds, 24); + SaveLogEnter("GfxSurfaceBounds"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->surfacesBounds, world->surfaceCount); + Utils::Stream::ClearPointer(&dest->surfacesBounds); + + SaveLogExit(); + } + + if (asset->smodelDrawInsts) + { + AssertSize(Game::GfxStaticModelDrawInst, 76); + SaveLogEnter("GfxStaticModelDrawInst"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::GfxStaticModelDrawInst* destModelTable = buffer->dest(); + buffer->saveArray(asset->smodelDrawInsts, asset->smodelCount); + + for (unsigned int i = 0; i < asset->smodelCount; ++i) + { + Game::GfxStaticModelDrawInst* model = &asset->smodelDrawInsts[i]; + Game::GfxStaticModelDrawInst* destModel = &destModelTable[i]; + + if (model->model) + { + destModel->model = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, model->model).model; + } + } + + Utils::Stream::ClearPointer(&dest->smodelDrawInsts); + SaveLogExit(); + } + + buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); + + if (asset->surfaceMaterials) + { + AssertSize(Game::GfxDrawSurf, 8); + SaveLogEnter("GfxDrawSurf"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->surfaceMaterials, world->surfaceCount); + Utils::Stream::ClearPointer(&dest->surfaceMaterials); + + SaveLogExit(); + } + + if (asset->surfaceCastsSunShadow) + { + AssertSize(Game::GfxDrawSurf, 8); + SaveLogEnter("GfxDrawSurf"); + + buffer->align(Utils::Stream::ALIGN_128); + buffer->save(asset->surfaceCastsSunShadow, 4, asset->surfaceVisDataCount); + Utils::Stream::ClearPointer(&dest->surfaceCastsSunShadow); + + SaveLogExit(); + } + + buffer->popBlock(); + SaveLogExit(); + } + + void IGfxWorld::saveGfxWorldDpvsDynamic(Game::GfxWorldDpvsDynamic* asset, Game::GfxWorldDpvsDynamic* dest, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GfxWorldDpvsDynamic, 48); + + Utils::Stream* buffer = builder->getBuffer(); + SaveLogEnter("GfxWorldDpvsDynamic"); + + buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); + + if (asset->dynEntCellBits[0]) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->dynEntCellBits[0], asset->dynEntClientWordCount[0]); + Utils::Stream::ClearPointer(&dest->dynEntCellBits[0]); + } + + if (asset->dynEntCellBits[1]) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->dynEntCellBits[1], asset->dynEntClientWordCount[1]); + Utils::Stream::ClearPointer(&dest->dynEntCellBits[1]); + } + + // this covers [0][0], [1][0], [0][1], [1][1], [0][2], [1][2] + for (char i = 0; i < 3; ++i) + { + for (char j = 0; j < 2; ++j) + { + if (asset->dynEntVisData[j][i]) + { + buffer->align(Utils::Stream::ALIGN_16); + buffer->save(asset->dynEntVisData[j][i], 32, asset->dynEntClientWordCount[j]); + Utils::Stream::ClearPointer(&dest->dynEntVisData[j][i]); + } + } + } + + buffer->popBlock(); + SaveLogExit(); + } + + void IGfxWorld::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::GfxWorld, 628); + + Utils::Stream* buffer = builder->getBuffer(); + SaveLogEnter("GfxWorld"); + + Game::GfxWorld* asset = header.gfxWorld; + Game::GfxWorld* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->baseName) + { + buffer->saveString(asset->baseName); + Utils::Stream::ClearPointer(&dest->baseName); + } + + if (asset->skies) + { + AssertSize(Game::GfxSky, 16); + SaveLogEnter("GfxSky"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::GfxSky* destSkyTable = buffer->dest(); + buffer->saveArray(asset->skies, asset->skyCount); + + for (int i = 0; i < asset->skyCount; ++i) + { + Game::GfxSky* destSky = &destSkyTable[i]; + Game::GfxSky* sky = &asset->skies[i]; + + if (sky->skyStartSurfs) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(sky->skyStartSurfs, sky->skySurfCount); + Utils::Stream::ClearPointer(&destSky->skyStartSurfs); + } + + if (sky->skyImage) + { + destSky->skyImage = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, sky->skyImage).image; + } + } + + Utils::Stream::ClearPointer(&dest->skies); + SaveLogExit(); + } + + this->saveGfxWorldDpvsPlanes(asset, &asset->dpvsPlanes, &dest->dpvsPlanes, builder); + + int cellCount = asset->dpvsPlanes.cellCount; + + if (asset->aabbTreeCounts) + { + AssertSize(Game::GfxCellTreeCount, 4); + SaveLogEnter("GfxCellTreeCount"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->aabbTreeCounts, cellCount); + Utils::Stream::ClearPointer(&dest->aabbTreeCounts); + + SaveLogExit(); + } + + if (asset->aabbTrees) + { + AssertSize(Game::GfxCellTree, 4); + SaveLogEnter("GfxCellTree"); + + buffer->align(Utils::Stream::ALIGN_128); + Game::GfxCellTree* destCellTreeTable = buffer->dest(); + buffer->saveArray(asset->aabbTrees, cellCount); + + for (int i = 0; i < cellCount; ++i) + { + Game::GfxCellTree* destCellTree = &destCellTreeTable[i]; + Game::GfxCellTree* cellTree = &asset->aabbTrees[i]; + + if (cellTree->aabbTree) + { + AssertSize(Game::GfxAabbTree, 44); + SaveLogEnter("GfxAabbTree"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::GfxAabbTree* destAabbTreeTable = buffer->dest(); + buffer->saveArray(cellTree->aabbTree, asset->aabbTreeCounts[i].aabbTreeCount); + + // ok this one is based on some assumptions because the actual count is this + // *(int *)((char *)&varGfxWorld->aabbTreeCounts->aabbTreeCount + (((char *)varGfxCellTree - (char *)varGfxWorld->aabbTrees) & 0xFFFFFFFC)) + // which makes no sense + // what DOES make sense is using the count from the structure + + for (int j = 0; j < asset->aabbTreeCounts[i].aabbTreeCount; ++j) + { + Game::GfxAabbTree* destAabbTree = &destAabbTreeTable[j]; + Game::GfxAabbTree* aabbTree = &cellTree->aabbTree[j]; + + if (aabbTree->smodelIndexes) + { + if (builder->hasPointer(aabbTree->smodelIndexes)) + { + destAabbTree->smodelIndexes = builder->getPointer(aabbTree->smodelIndexes); + } + else + { + buffer->align(Utils::Stream::ALIGN_2); + + for (int k = 0; k < aabbTree->smodelIndexCount; ++k) + { + builder->storePointer(&aabbTree->smodelIndexes[k]); + buffer->save(&aabbTree->smodelIndexes[k]); + } + + Utils::Stream::ClearPointer(&destAabbTree->smodelIndexes); + } + } + } + + Utils::Stream::ClearPointer(&destCellTree->aabbTree); + SaveLogExit(); + } + } + + Utils::Stream::ClearPointer(&dest->aabbTrees); + SaveLogExit(); + } + + if (asset->cells) + { + AssertSize(Game::GfxCell, 40); + SaveLogEnter("GfxCell"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::GfxCell* destCellTable = buffer->dest(); + buffer->saveArray(asset->cells, cellCount); + + for (int i = 0; i < cellCount; ++i) + { + Game::GfxCell* destCell = &destCellTable[i]; + Game::GfxCell* cell = &asset->cells[i]; + + if (cell->portals) + { + AssertSize(Game::GfxPortal, 60); + SaveLogEnter("GfxPortal"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::GfxPortal* destPortalTable = buffer->dest(); + buffer->saveArray(cell->portals, cell->portalCount); + + for (int j = 0; j < cell->portalCount; ++j) + { + Game::GfxPortal* destPortal = &destPortalTable[j]; + Game::GfxPortal* portal = &cell->portals[j]; + + if (portal->vertices) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(portal->vertices, portal->vertexCount); + Utils::Stream::ClearPointer(&destPortal->vertices); + } + } + + Utils::Stream::ClearPointer(&destCell->portals); + SaveLogExit(); + } + + if (cell->reflectionProbes) + { + // no align for char + buffer->saveArray(cell->reflectionProbes, cell->reflectionProbeCount); + Utils::Stream::ClearPointer(&destCell->reflectionProbes); + } + } + + Utils::Stream::ClearPointer(&dest->cells); + SaveLogExit(); + } + + this->saveGfxWorldDraw(&asset->draw, &dest->draw, builder); + this->saveGfxLightGrid(&asset->lightGrid, &dest->lightGrid, builder); + + if (asset->models) + { + AssertSize(Game::GfxBrushModel, 60); + SaveLogEnter("GfxBrushModel"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->models, asset->modelCount); + Utils::Stream::ClearPointer(&dest->models); + + SaveLogExit(); + } + + if (asset->materialMemory) + { + AssertSize(Game::MaterialMemory, 8); + SaveLogEnter("MaterialMemory"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::MaterialMemory* destMaterialMemoryTable = buffer->dest(); + buffer->saveArray(asset->materialMemory, asset->materialMemoryCount); + + for (int i = 0; i < asset->materialMemoryCount; ++i) + { + Game::MaterialMemory* destMaterialMemory = &destMaterialMemoryTable[i]; + Game::MaterialMemory* materialMemory = &asset->materialMemory[i]; + + if (materialMemory->material) + { + destMaterialMemory->material = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, materialMemory->material).material; + } + } + + Utils::Stream::ClearPointer(&dest->materialMemory); + SaveLogExit(); + } + + this->savesunflare_t(&asset->sun, &dest->sun, builder); + + if (asset->outdoorImage) + { + dest->outdoorImage = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->outdoorImage).image; + } + + buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); + + if (asset->cellCasterBits) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->cellCasterBits, cellCount * ((cellCount + 31) >> 5)); + Utils::Stream::ClearPointer(&dest->cellCasterBits); + } + + if (asset->cellHasSunLitSurfsBits) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->cellHasSunLitSurfsBits, ((cellCount + 31) >> 5)); + Utils::Stream::ClearPointer(&dest->cellHasSunLitSurfsBits); + } + + if (asset->sceneDynModel) + { + AssertSize(Game::GfxSceneDynModel, 6); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->sceneDynModel, asset->dpvsDyn.dynEntClientCount[0]); + Utils::Stream::ClearPointer(&dest->sceneDynModel); + } + + if (asset->sceneDynBrush) + { + AssertSize(Game::GfxSceneDynBrush, 4); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->sceneDynBrush, asset->dpvsDyn.dynEntClientCount[1]); + Utils::Stream::ClearPointer(&dest->sceneDynBrush); + } + + if (asset->primaryLightEntityShadowVis) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(asset->primaryLightEntityShadowVis, 1, (asset->primaryLightCount + 0x1FFFF - asset->lastSunPrimaryLightIndex) << 15); + Utils::Stream::ClearPointer(&dest->primaryLightEntityShadowVis); + } + + if (asset->primaryLightDynEntShadowVis[0]) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->primaryLightDynEntShadowVis[0], asset->dpvsDyn.dynEntClientCount[0] * (asset->primaryLightCount - 1 - asset->lastSunPrimaryLightIndex)); + Utils::Stream::ClearPointer(&dest->primaryLightDynEntShadowVis[0]); + } + + if (asset->primaryLightDynEntShadowVis[1]) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->primaryLightDynEntShadowVis[1], asset->dpvsDyn.dynEntClientCount[1] * (asset->primaryLightCount - 1 - asset->lastSunPrimaryLightIndex)); + Utils::Stream::ClearPointer(&dest->primaryLightDynEntShadowVis[1]); + } + + if (asset->nonSunPrimaryLightForModelDynEnt) + { + // no align cause char + buffer->saveArray(asset->nonSunPrimaryLightForModelDynEnt, asset->dpvsDyn.dynEntClientCount[0]); + Utils::Stream::ClearPointer(&dest->nonSunPrimaryLightForModelDynEnt); + } + + buffer->popBlock(); + + if (asset->shadowGeom) + { + AssertSize(Game::GfxShadowGeometry, 12); + SaveLogEnter("GfxShadowGeometry"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::GfxShadowGeometry* destShadowGeometryTable = buffer->dest(); + buffer->saveArray(asset->shadowGeom, asset->primaryLightCount); + + for (unsigned int i = 0; i < asset->primaryLightCount; ++i) + { + Game::GfxShadowGeometry* destShadowGeometry = &destShadowGeometryTable[i]; + Game::GfxShadowGeometry* shadowGeometry = &asset->shadowGeom[i]; + + if (shadowGeometry->sortedSurfIndex) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(shadowGeometry->sortedSurfIndex, shadowGeometry->surfaceCount); + Utils::Stream::ClearPointer(&destShadowGeometry->sortedSurfIndex); + } + + if (shadowGeometry->smodelIndex) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(shadowGeometry->smodelIndex, shadowGeometry->smodelCount); + Utils::Stream::ClearPointer(&destShadowGeometry->smodelIndex); + } + } + + Utils::Stream::ClearPointer(&dest->shadowGeom); + SaveLogExit(); + } + + if (asset->lightRegion) + { + AssertSize(Game::GfxLightRegion, 8); + SaveLogEnter("GfxLightRegion"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::GfxLightRegion* destLightRegionTable = buffer->dest(); + buffer->saveArray(asset->lightRegion, asset->primaryLightCount); + + for (unsigned int i = 0; i < asset->primaryLightCount; ++i) + { + Game::GfxLightRegion* destLightRegion = &destLightRegionTable[i]; + Game::GfxLightRegion* lightRegion = &asset->lightRegion[i]; + + if (lightRegion->hulls) + { + AssertSize(Game::GfxLightRegionHull, 80); + SaveLogEnter("GfxLightRegionHull"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::GfxLightRegionHull* destLightRegionHullTable = buffer->dest(); + buffer->saveArray(lightRegion->hulls, lightRegion->hullCount); + + for (unsigned int j = 0; j < lightRegion->hullCount; ++j) + { + Game::GfxLightRegionHull* destLightRegionHull = &destLightRegionHullTable[j]; + Game::GfxLightRegionHull* lightRegionHull = &lightRegion->hulls[j]; + + if (lightRegionHull->axis) + { + AssertSize(Game::GfxLightRegionAxis, 20); + SaveLogEnter("GfxLightRegionAxis"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(lightRegionHull->axis, lightRegionHull->axisCount); + Utils::Stream::ClearPointer(&destLightRegionHull->axis); + + SaveLogExit(); + } + + } + + Utils::Stream::ClearPointer(&destLightRegion->hulls); + SaveLogExit(); + } + } + + Utils::Stream::ClearPointer(&dest->lightRegion); + SaveLogExit(); + } + + this->saveGfxWorldDpvsStatic(asset, &asset->dpvs, &dest->dpvs, asset->dpvsPlanes.cellCount, builder); + this->saveGfxWorldDpvsDynamic(&asset->dpvsDyn, &dest->dpvsDyn, builder); + + if (asset->heroOnlyLights) + { + AssertSize(Game::GfxHeroOnlyLight, 56); + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->heroOnlyLights, asset->heroOnlyLightCount); + Utils::Stream::ClearPointer(&dest->heroOnlyLights); + } + + buffer->popBlock(); + SaveLogExit(); + } +} + +#endif diff --git a/src/Components/Modules/AssetInterfaces/IGfxWorld.hpp b/src/Components/Modules/AssetInterfaces/IGfxWorld.hpp index 651d0ba2..30a58d7b 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxWorld.hpp +++ b/src/Components/Modules/AssetInterfaces/IGfxWorld.hpp @@ -1,22 +1,22 @@ -namespace Assets -{ - class IGfxWorld : public Components::AssetHandler::IAsset - { - public: - virtual Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_GFXWORLD; }; - - virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; - virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; - virtual void load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) override; - private: - void saveGfxWorldDpvsPlanes(Game::GfxWorld* world, Game::GfxWorldDpvsPlanes* asset, Game::GfxWorldDpvsPlanes* dest, Components::ZoneBuilder::Zone* builder); - void saveGfxWorldDraw(Game::GfxWorldDraw* asset, Game::GfxWorldDraw* dest, Components::ZoneBuilder::Zone* builder); - void saveGfxLightGrid(Game::GfxLightGrid* asset, Game::GfxLightGrid* dest, Components::ZoneBuilder::Zone* builder); - void savesunflare_t(Game::sunflare_t* asset, Game::sunflare_t* dest, Components::ZoneBuilder::Zone* builder); - void saveGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Game::GfxWorldDpvsStatic* dest, int planeCount, Components::ZoneBuilder::Zone* builder); - void saveGfxWorldDpvsDynamic(Game::GfxWorldDpvsDynamic* asset, Game::GfxWorldDpvsDynamic* dest, Components::ZoneBuilder::Zone* builder); - - void loadGfxWorldDraw(Game::GfxWorldDraw* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader); - void loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader); - }; -} +namespace Assets +{ + class IGfxWorld : public Components::AssetHandler::IAsset + { + public: + virtual Game::XAssetType getType() override { return Game::XAssetType::ASSET_TYPE_GFXWORLD; }; + + virtual void save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + virtual void mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + virtual void load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) override; + private: + void saveGfxWorldDpvsPlanes(Game::GfxWorld* world, Game::GfxWorldDpvsPlanes* asset, Game::GfxWorldDpvsPlanes* dest, Components::ZoneBuilder::Zone* builder); + void saveGfxWorldDraw(Game::GfxWorldDraw* asset, Game::GfxWorldDraw* dest, Components::ZoneBuilder::Zone* builder); + void saveGfxLightGrid(Game::GfxLightGrid* asset, Game::GfxLightGrid* dest, Components::ZoneBuilder::Zone* builder); + void savesunflare_t(Game::sunflare_t* asset, Game::sunflare_t* dest, Components::ZoneBuilder::Zone* builder); + void saveGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Game::GfxWorldDpvsStatic* dest, int planeCount, Components::ZoneBuilder::Zone* builder); + void saveGfxWorldDpvsDynamic(Game::GfxWorldDpvsDynamic* asset, Game::GfxWorldDpvsDynamic* dest, Components::ZoneBuilder::Zone* builder); + + void loadGfxWorldDraw(Game::GfxWorldDraw* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader); + void loadGfxWorldDpvsStatic(Game::GfxWorld* world, Game::GfxWorldDpvsStatic* asset, Components::ZoneBuilder::Zone* builder, Utils::Stream::Reader* reader); + }; +} diff --git a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp index 5c9db108..627e4060 100644 --- a/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp +++ b/src/Components/Modules/AssetInterfaces/ILoadedSound.cpp @@ -1,45 +1,45 @@ -#include - -namespace Assets -{ - void ILoadedSound::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::LoadedSound, 44); - - Utils::Stream* buffer = builder->getBuffer(); - Game::LoadedSound* asset = header.loadSnd; - Game::LoadedSound* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - { - buffer->pushBlock(Game::XFILE_BLOCK_TEMP); - - if (asset->mssSound.data) - { - if (builder->hasPointer(asset->mssSound.data)) - { - dest->mssSound.data = builder->getPointer(asset->mssSound.data); - } - else - { - builder->storePointer(asset->mssSound.data); - - buffer->saveArray(asset->mssSound.data, asset->mssSound.size); - Utils::Stream::ClearPointer(&dest->mssSound.data); - } - } - - buffer->popBlock(); - } - - buffer->popBlock(); - } -} +#include + +namespace Assets +{ + void ILoadedSound::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::LoadedSound, 44); + + Utils::Stream* buffer = builder->getBuffer(); + Game::LoadedSound* asset = header.loadSnd; + Game::LoadedSound* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + { + buffer->pushBlock(Game::XFILE_BLOCK_TEMP); + + if (asset->mssSound.data) + { + if (builder->hasPointer(asset->mssSound.data)) + { + dest->mssSound.data = builder->getPointer(asset->mssSound.data); + } + else + { + builder->storePointer(asset->mssSound.data); + + buffer->saveArray(asset->mssSound.data, asset->mssSound.size); + Utils::Stream::ClearPointer(&dest->mssSound.data); + } + } + + buffer->popBlock(); + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IMapEnts.cpp b/src/Components/Modules/AssetInterfaces/IMapEnts.cpp index 2dc7737a..55fba97b 100644 --- a/src/Components/Modules/AssetInterfaces/IMapEnts.cpp +++ b/src/Components/Modules/AssetInterfaces/IMapEnts.cpp @@ -1,132 +1,132 @@ -#include - -namespace Assets -{ - void IMapEnts::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - Utils::String::Replace(name, "maps/", ""); - Utils::String::Replace(name, "mp/", ""); - Utils::String::Replace(name, ".d3dbsp", ""); - - Components::FileSystem::File ents(Utils::String::VA("mapents/%s.ents", name.c_str())); - if (ents.exists()) - { - Game::MapEnts* entites = builder->getAllocator()->allocate(); - Game::MapEnts* orgEnts = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).mapEnts; - - // TODO: Get rid of that - if (!orgEnts) - { - orgEnts = Components::AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, "maps/iw4_credits.d3dbsp").mapEnts; - - if (!orgEnts) - { - Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_MAP_ENTS, [](Game::XAssetHeader header, void* mapEnts) - { - if (!*reinterpret_cast(mapEnts)) - { - *reinterpret_cast(mapEnts) = header.mapEnts; - } - }, &orgEnts, false); - } - } - - if (orgEnts) - { - std::memcpy(entites, orgEnts, sizeof Game::MapEnts); - } - else - { - entites->name = builder->getAllocator()->duplicateString(Utils::String::VA("maps/mp/%s.d3dbsp", name)); - entites->stageCount = 1; - entites->stages = builder->getAllocator()->allocate(); - entites->stages[0].stageName = "stage 0"; - entites->stages[0].flags = 0x10400; - } - - entites->entityString = builder->getAllocator()->duplicateString(ents.getBuffer()); - entites->numEntityChars = ents.getBuffer().size() + 1; - - header->mapEnts = entites; - } - } - - void IMapEnts::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::MapEnts, 44); - - Utils::Stream* buffer = builder->getBuffer(); - Game::MapEnts* asset = header.mapEnts; - Game::MapEnts* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->entityString) - { - buffer->save(asset->entityString, asset->numEntityChars); - Utils::Stream::ClearPointer(&dest->entityString); - } - - AssertSize(Game::MapTriggers, 24); - - if (asset->trigger.models) - { - AssertSize(Game::TriggerModel, 8); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->trigger.models, asset->trigger.modelCount); - Utils::Stream::ClearPointer(&dest->trigger.models); - } - - if (asset->trigger.hulls) - { - AssertSize(Game::TriggerHull, 32); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->trigger.hulls, asset->trigger.hullCount); - Utils::Stream::ClearPointer(&dest->trigger.hulls); - } - - if (asset->trigger.slabs) - { - AssertSize(Game::TriggerSlab, 20); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->trigger.slabs, asset->trigger.slabCount); - Utils::Stream::ClearPointer(&dest->trigger.slabs); - } - - if (asset->stages) - { - AssertSize(Game::Stage, 20); - - buffer->align(Utils::Stream::ALIGN_4); - - Game::Stage* destStages = buffer->dest(); - buffer->saveArray(asset->stages, asset->stageCount); - - for (char i = 0; i < asset->stageCount; ++i) - { - Game::Stage* destStage = &destStages[i]; - Game::Stage* stage = &asset->stages[i]; - - if (stage->stageName) - { - buffer->saveString(stage->stageName); - Utils::Stream::ClearPointer(&destStage->stageName); - } - } - - Utils::Stream::ClearPointer(&dest->stages); - } - - buffer->popBlock(); - } -} +#include + +namespace Assets +{ + void IMapEnts::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Utils::String::Replace(name, "maps/", ""); + Utils::String::Replace(name, "mp/", ""); + Utils::String::Replace(name, ".d3dbsp", ""); + + Components::FileSystem::File ents(Utils::String::VA("mapents/%s.ents", name.c_str())); + if (ents.exists()) + { + Game::MapEnts* entites = builder->getAllocator()->allocate(); + Game::MapEnts* orgEnts = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).mapEnts; + + // TODO: Get rid of that + if (!orgEnts) + { + orgEnts = Components::AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, "maps/iw4_credits.d3dbsp").mapEnts; + + if (!orgEnts) + { + Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_MAP_ENTS, [](Game::XAssetHeader header, void* mapEnts) + { + if (!*reinterpret_cast(mapEnts)) + { + *reinterpret_cast(mapEnts) = header.mapEnts; + } + }, &orgEnts, false); + } + } + + if (orgEnts) + { + std::memcpy(entites, orgEnts, sizeof Game::MapEnts); + } + else + { + entites->name = builder->getAllocator()->duplicateString(Utils::String::VA("maps/mp/%s.d3dbsp", name)); + entites->stageCount = 1; + entites->stages = builder->getAllocator()->allocate(); + entites->stages[0].stageName = "stage 0"; + entites->stages[0].flags = 0x10400; + } + + entites->entityString = builder->getAllocator()->duplicateString(ents.getBuffer()); + entites->numEntityChars = ents.getBuffer().size() + 1; + + header->mapEnts = entites; + } + } + + void IMapEnts::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::MapEnts, 44); + + Utils::Stream* buffer = builder->getBuffer(); + Game::MapEnts* asset = header.mapEnts; + Game::MapEnts* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->entityString) + { + buffer->save(asset->entityString, asset->numEntityChars); + Utils::Stream::ClearPointer(&dest->entityString); + } + + AssertSize(Game::MapTriggers, 24); + + if (asset->trigger.models) + { + AssertSize(Game::TriggerModel, 8); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->trigger.models, asset->trigger.modelCount); + Utils::Stream::ClearPointer(&dest->trigger.models); + } + + if (asset->trigger.hulls) + { + AssertSize(Game::TriggerHull, 32); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->trigger.hulls, asset->trigger.hullCount); + Utils::Stream::ClearPointer(&dest->trigger.hulls); + } + + if (asset->trigger.slabs) + { + AssertSize(Game::TriggerSlab, 20); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->trigger.slabs, asset->trigger.slabCount); + Utils::Stream::ClearPointer(&dest->trigger.slabs); + } + + if (asset->stages) + { + AssertSize(Game::Stage, 20); + + buffer->align(Utils::Stream::ALIGN_4); + + Game::Stage* destStages = buffer->dest(); + buffer->saveArray(asset->stages, asset->stageCount); + + for (char i = 0; i < asset->stageCount; ++i) + { + Game::Stage* destStage = &destStages[i]; + Game::Stage* stage = &asset->stages[i]; + + if (stage->stageName) + { + buffer->saveString(stage->stageName); + Utils::Stream::ClearPointer(&destStage->stageName); + } + } + + Utils::Stream::ClearPointer(&dest->stages); + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index 85cd78da..06e4a191 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -1,465 +1,465 @@ -#include - -#define IW4X_MAT_VERSION "0" - -namespace Assets -{ - void IMaterial::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - if (!header->data) this->loadJson(header, name, builder); // Check if we want to override materials - if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one - if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game - } - - void IMaterial::loadBinary(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data())); - if (!materialFile.exists()) return; - - Game::Material* material = builder->getAllocator()->allocate(); - if (!material) - { - Components::Logger::Print("Error allocating memory for material structure!\n"); - return; - } - - Utils::Stream::Reader reader(builder->getAllocator(), materialFile.getBuffer()); - - if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xMat" IW4X_MAT_VERSION)) - { - Components::Logger::Error(0, "Reading material '%s' failed, header is invalid!", name.data()); - } - - material->name = reader.readCString(); - material->gameFlags = reader.readByte(); - material->sortKey = reader.readByte(); - material->textureAtlasRowCount = reader.readByte(); - material->textureAtlasColumnCount = reader.readByte(); - material->drawSurf.packed = reader.read<__int64>(); - material->surfaceTypeBits = reader.read(); - material->hashIndex = reader.read(); - char* stateBitsEntry = reader.readArray(48); - memcpy(material->stateBitsEntry, stateBitsEntry, 48); - material->textureCount = reader.readByte(); - material->constantCount = reader.readByte(); - material->stateBitsCount = reader.readByte(); - material->stateFlags = reader.readByte(); - material->cameraRegion = reader.readByte(); - - std::string techset = reader.readString(); - material->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techset.data(), builder).techniqueSet; - - if (!material->techniqueSet) - { - Components::Logger::Error("Techset '%s' not found!", techset.data()); - } - - material->textureTable = builder->getAllocator()->allocateArray(material->textureCount & 0xFF); - material->constantTable = builder->getAllocator()->allocateArray(material->constantCount & 0xFF); - material->stateBitTable = builder->getAllocator()->allocateArray(material->stateBitsCount & 0xFF); - - if (!material->textureTable || !material->constantTable || !material->stateBitTable) - { - Components::Logger::Print("Error allocating memory for material structure!\n"); - return; - } - - for (char i = 0; i < material->textureCount; ++i) - { - std::string mapName = reader.readString(); - material->textureTable[i].nameStart = mapName.front(); - material->textureTable[i].nameEnd = mapName.back(); - material->textureTable[i].nameHash = Game::R_HashString(mapName.data()); - material->textureTable[i].sampleState = reader.readByte(); - material->textureTable[i].semantic = reader.readByte(); - - if (material->textureTable[i].semantic == SEMANTIC_WATER_MAP) - { - material->textureTable[i].info.water = builder->getAllocator()->allocate(); - material->textureTable[i].info.water->writable.floatTime = reader.read(); - material->textureTable[i].info.water->M = reader.read(); - material->textureTable[i].info.water->N = reader.read(); - int count = material->textureTable[i].info.water->M * material->textureTable[i].info.water->N; - material->textureTable[i].info.water->H0 = reader.readArray(count); - material->textureTable[i].info.water->wTerm = reader.readArray(count); - material->textureTable[i].info.water->Lx = reader.read(); - material->textureTable[i].info.water->Lz = reader.read(); - material->textureTable[i].info.water->gravity = reader.read(); - material->textureTable[i].info.water->windvel = reader.read(); - material->textureTable[i].info.water->winddir[0] = reader.read(); - material->textureTable[i].info.water->winddir[1] = reader.read(); - material->textureTable[i].info.water->amplitude = reader.read(); - material->textureTable[i].info.water->codeConstant[0] = reader.read(); - material->textureTable[i].info.water->codeConstant[1] = reader.read(); - material->textureTable[i].info.water->codeConstant[2] = reader.read(); - material->textureTable[i].info.water->codeConstant[3] = reader.read(); - material->textureTable[i].info.water->image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image; - } - else - { - material->textureTable[i].info.image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image; - } - } - - for (char i = 0; i < material->constantCount; ++i) - { - for (int j = 0; j < 12; ++j) - { - material->constantTable[i].name[j] = reader.readByte(); - } - - std::string constName(material->constantTable[i].name, 12); - constName.push_back('0'); - - material->constantTable[i].nameHash = Game::R_HashString(constName.data()); - material->constantTable[i].literal[0] = reader.read(); - material->constantTable[i].literal[1] = reader.read(); - material->constantTable[i].literal[2] = reader.read(); - material->constantTable[i].literal[3] = reader.read(); - } - - material->stateBitTable = reader.readArray(material->stateBitsCount); - header->material = material; - - // Find correct sortkey by comparing techsets - Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_MATERIAL, [](Game::XAssetHeader header, void* data) - { - Game::Material* material = reinterpret_cast(data); - const char* name = material->techniqueSet->name; - if (name[0] == ',') ++name; - - if (std::string(name) == header.material->techniqueSet->name) - { - material->sortKey = header.material->sortKey; - - // This is temp, as nobody has time to fix materials - material->stateBitsCount = header.material->stateBitsCount; - material->stateBitTable = header.material->stateBitTable; - std::memcpy(material->stateBitsEntry, header.material->stateBitsEntry, 48); - material->constantCount = header.material->constantCount; - material->constantTable = header.material->constantTable; - } - }, material, false); - } - - void IMaterial::loadNative(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* /*builder*/) - { - header->material = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).material; - } - - void IMaterial::loadJson(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - Components::FileSystem::File materialInfo(Utils::String::VA("materials/%s.json", name.data())); - - if (!materialInfo.exists()) return; - - std::string errors; - json11::Json infoData = json11::Json::parse(materialInfo.getBuffer(), errors); - - if (!infoData.is_object()) - { - Components::Logger::Error("Failed to load material information for %s!", name.data()); - return; - } - - auto base = infoData["base"]; - - if (!base.is_string()) - { - Components::Logger::Error("No valid material base provided for %s!", name.data()); - return; - } - - Game::Material* baseMaterial = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, base.string_value().data()).material; - - if (!baseMaterial) // TODO: Maybe check if default asset? Maybe not? You could still want to use the default one as base!? - { - Components::Logger::Error("Basematerial '%s' not found for %s!", base.string_value().data(), name.data()); - return; - } - - Game::Material* material = builder->getAllocator()->allocate(); - - if (!material) - { - Components::Logger::Error("Failed to allocate material structure!"); - return; - } - - // Copy base material to our structure - std::memcpy(material, baseMaterial, sizeof(Game::Material)); - material->name = builder->getAllocator()->duplicateString(name); - - material->textureAtlasRowCount = 1; - material->textureAtlasColumnCount = 1; - - // Load animation frames - auto anims = infoData["anims"]; - if (anims.is_array()) - { - auto animCoords = anims.array_items(); - - if (animCoords.size() >= 2) - { - auto animCoordX = animCoords[0]; - auto animCoordY = animCoords[1]; - - if (animCoordX.is_number()) - { - material->textureAtlasColumnCount = static_cast(animCoordX.number_value()) & 0xFF; - } - - if (animCoordY.is_number()) - { - material->textureAtlasRowCount = static_cast(animCoordY.number_value()) & 0xFF; - } - } - } - - // Model surface textures are special, they need a special order and whatnot - bool replaceTexture = Utils::String::StartsWith(name, "mc/"); - if (replaceTexture) - { - Game::MaterialTextureDef* textureTable = builder->getAllocator()->allocateArray(baseMaterial->textureCount); - std::memcpy(textureTable, baseMaterial->textureTable, sizeof(Game::MaterialTextureDef) * baseMaterial->textureCount); - material->textureTable = textureTable; - material->textureCount = baseMaterial->textureCount; - } - - // Load referenced textures - auto textures = infoData["textures"]; - if (textures.is_array()) - { - std::vector textureList; - - for (auto texture : textures.array_items()) - { - if (!texture.is_array()) continue; - if (textureList.size() >= 0xFF) break; - - auto textureInfo = texture.array_items(); - if (textureInfo.size() < 2) continue; - - auto map = textureInfo[0]; - auto image = textureInfo[1]; - if (!map.is_string() || !image.is_string()) continue; - - Game::MaterialTextureDef textureDef; - - textureDef.semantic = 0; // No water image - textureDef.sampleState = -30; - textureDef.nameEnd = map.string_value().back(); - textureDef.nameStart = map.string_value().front(); - textureDef.nameHash = Game::R_HashString(map.string_value().data()); - - textureDef.info.image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, image.string_value(), builder).image; - - if (replaceTexture) - { - bool applied = false; - - for (char i = 0; i < baseMaterial->textureCount; ++i) - { - if (material->textureTable[i].nameHash == textureDef.nameHash) - { - applied = true; - material->textureTable[i].info.image = textureDef.info.image; - break; - } - } - - if (!applied) - { - Components::Logger::Error(0, "Unable to find texture for map '%s' in %s!", map.string_value().data(), baseMaterial->name); - } - } - else - { - textureList.push_back(textureDef); - } - } - - if(!replaceTexture) - { - if (!textureList.empty()) - { - Game::MaterialTextureDef* textureTable = builder->getAllocator()->allocateArray(textureList.size()); - - if (!textureTable) - { - Components::Logger::Error("Failed to allocate texture table!"); - return; - } - - std::memcpy(textureTable, textureList.data(), sizeof(Game::MaterialTextureDef) * textureList.size()); - - material->textureTable = textureTable; - } - else - { - material->textureTable = 0; - } - - material->textureCount = static_cast(textureList.size()) & 0xFF; - } - } - - header->material = material; - } - - void IMaterial::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::Material* asset = header.material; - - if (asset->techniqueSet) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, asset->techniqueSet); - } - - if (asset->textureTable) - { - for (char i = 0; i < asset->textureCount; ++i) - { - if (asset->textureTable[i].info.image) - { - if (asset->textureTable[i].semantic == SEMANTIC_WATER_MAP) - { - if (asset->textureTable[i].info.water->image) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->textureTable[i].info.water->image); - } - } - else - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->textureTable[i].info.image); - } - } - } - } - } - - void IMaterial::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::Material, 96); - - Utils::Stream* buffer = builder->getBuffer(); - Game::Material* asset = header.material; - Game::Material* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->techniqueSet) - { - dest->techniqueSet = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, asset->techniqueSet).techniqueSet; - } - - if (asset->textureTable) - { - AssertSize(Game::MaterialTextureDef, 12); - - // Pointer/Offset insertion is untested, but it worked in T6, so I think it's fine - if (builder->hasPointer(asset->textureTable)) - { - dest->textureTable = builder->getPointer(asset->textureTable); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(asset->textureTable); - - Game::MaterialTextureDef* destTextureTable = buffer->dest(); - buffer->saveArray(asset->textureTable, asset->textureCount); - - for (char i = 0; i < asset->textureCount; ++i) - { - Game::MaterialTextureDef* destTextureDef = &destTextureTable[i]; - Game::MaterialTextureDef* textureDef = &asset->textureTable[i]; - - if (textureDef->semantic == SEMANTIC_WATER_MAP) - { - AssertSize(Game::water_t, 68); - - Game::water_t* destWater = buffer->dest(); - Game::water_t* water = textureDef->info.water; - - if (water) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(water); - Utils::Stream::ClearPointer(&destTextureDef->info.water); - - // Save_water_t - if (water->H0) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(water->H0, 8, water->M * water->N); - Utils::Stream::ClearPointer(&destWater->H0); - } - - if (water->wTerm) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(water->wTerm, 4, water->M * water->N); - Utils::Stream::ClearPointer(&destWater->wTerm); - } - - if (water->image) - { - destWater->image = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, water->image).image; - } - } - } - else if (textureDef->info.image) - { - destTextureDef->info.image = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, textureDef->info.image).image; - } - } - - Utils::Stream::ClearPointer(&dest->textureTable); - } - } - - if (asset->constantTable) - { - AssertSize(Game::MaterialConstantDef, 32); - - if (builder->hasPointer(asset->constantTable)) - { - dest->constantTable = builder->getPointer(asset->constantTable); - } - else - { - buffer->align(Utils::Stream::ALIGN_16); - builder->storePointer(asset->constantTable); - - buffer->saveArray(asset->constantTable, asset->constantCount); - Utils::Stream::ClearPointer(&dest->constantTable); - } - } - - if (asset->stateBitTable) - { - if (builder->hasPointer(asset->stateBitTable)) - { - dest->stateBitTable = builder->getPointer(asset->stateBitTable); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(asset->stateBitTable); - - buffer->save(asset->stateBitTable, 8, asset->stateBitsCount); - Utils::Stream::ClearPointer(&dest->stateBitTable); - } - } - - buffer->popBlock(); - } -} +#include + +#define IW4X_MAT_VERSION "0" + +namespace Assets +{ + void IMaterial::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + if (!header->data) this->loadJson(header, name, builder); // Check if we want to override materials + if (!header->data) this->loadNative(header, name, builder); // Check if there is a native one + if (!header->data) this->loadBinary(header, name, builder); // Check if we need to import a new one into the game + } + + void IMaterial::loadBinary(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Components::FileSystem::File materialFile(Utils::String::VA("materials/%s.iw4xMaterial", name.data())); + if (!materialFile.exists()) return; + + Game::Material* material = builder->getAllocator()->allocate(); + if (!material) + { + Components::Logger::Print("Error allocating memory for material structure!\n"); + return; + } + + Utils::Stream::Reader reader(builder->getAllocator(), materialFile.getBuffer()); + + if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xMat" IW4X_MAT_VERSION)) + { + Components::Logger::Error(0, "Reading material '%s' failed, header is invalid!", name.data()); + } + + material->name = reader.readCString(); + material->gameFlags = reader.readByte(); + material->sortKey = reader.readByte(); + material->textureAtlasRowCount = reader.readByte(); + material->textureAtlasColumnCount = reader.readByte(); + material->drawSurf.packed = reader.read<__int64>(); + material->surfaceTypeBits = reader.read(); + material->hashIndex = reader.read(); + char* stateBitsEntry = reader.readArray(48); + memcpy(material->stateBitsEntry, stateBitsEntry, 48); + material->textureCount = reader.readByte(); + material->constantCount = reader.readByte(); + material->stateBitsCount = reader.readByte(); + material->stateFlags = reader.readByte(); + material->cameraRegion = reader.readByte(); + + std::string techset = reader.readString(); + material->techniqueSet = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, techset.data(), builder).techniqueSet; + + if (!material->techniqueSet) + { + Components::Logger::Error("Techset '%s' not found!", techset.data()); + } + + material->textureTable = builder->getAllocator()->allocateArray(material->textureCount & 0xFF); + material->constantTable = builder->getAllocator()->allocateArray(material->constantCount & 0xFF); + material->stateBitTable = builder->getAllocator()->allocateArray(material->stateBitsCount & 0xFF); + + if (!material->textureTable || !material->constantTable || !material->stateBitTable) + { + Components::Logger::Print("Error allocating memory for material structure!\n"); + return; + } + + for (char i = 0; i < material->textureCount; ++i) + { + std::string mapName = reader.readString(); + material->textureTable[i].nameStart = mapName.front(); + material->textureTable[i].nameEnd = mapName.back(); + material->textureTable[i].nameHash = Game::R_HashString(mapName.data()); + material->textureTable[i].sampleState = reader.readByte(); + material->textureTable[i].semantic = reader.readByte(); + + if (material->textureTable[i].semantic == SEMANTIC_WATER_MAP) + { + material->textureTable[i].info.water = builder->getAllocator()->allocate(); + material->textureTable[i].info.water->writable.floatTime = reader.read(); + material->textureTable[i].info.water->M = reader.read(); + material->textureTable[i].info.water->N = reader.read(); + int count = material->textureTable[i].info.water->M * material->textureTable[i].info.water->N; + material->textureTable[i].info.water->H0 = reader.readArray(count); + material->textureTable[i].info.water->wTerm = reader.readArray(count); + material->textureTable[i].info.water->Lx = reader.read(); + material->textureTable[i].info.water->Lz = reader.read(); + material->textureTable[i].info.water->gravity = reader.read(); + material->textureTable[i].info.water->windvel = reader.read(); + material->textureTable[i].info.water->winddir[0] = reader.read(); + material->textureTable[i].info.water->winddir[1] = reader.read(); + material->textureTable[i].info.water->amplitude = reader.read(); + material->textureTable[i].info.water->codeConstant[0] = reader.read(); + material->textureTable[i].info.water->codeConstant[1] = reader.read(); + material->textureTable[i].info.water->codeConstant[2] = reader.read(); + material->textureTable[i].info.water->codeConstant[3] = reader.read(); + material->textureTable[i].info.water->image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image; + } + else + { + material->textureTable[i].info.image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, reader.readString().data(), builder).image; + } + } + + for (char i = 0; i < material->constantCount; ++i) + { + for (int j = 0; j < 12; ++j) + { + material->constantTable[i].name[j] = reader.readByte(); + } + + std::string constName(material->constantTable[i].name, 12); + constName.push_back('0'); + + material->constantTable[i].nameHash = Game::R_HashString(constName.data()); + material->constantTable[i].literal[0] = reader.read(); + material->constantTable[i].literal[1] = reader.read(); + material->constantTable[i].literal[2] = reader.read(); + material->constantTable[i].literal[3] = reader.read(); + } + + material->stateBitTable = reader.readArray(material->stateBitsCount); + header->material = material; + + // Find correct sortkey by comparing techsets + Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_MATERIAL, [](Game::XAssetHeader header, void* data) + { + Game::Material* material = reinterpret_cast(data); + const char* name = material->techniqueSet->name; + if (name[0] == ',') ++name; + + if (std::string(name) == header.material->techniqueSet->name) + { + material->sortKey = header.material->sortKey; + + // This is temp, as nobody has time to fix materials + material->stateBitsCount = header.material->stateBitsCount; + material->stateBitTable = header.material->stateBitTable; + std::memcpy(material->stateBitsEntry, header.material->stateBitsEntry, 48); + material->constantCount = header.material->constantCount; + material->constantTable = header.material->constantTable; + } + }, material, false); + } + + void IMaterial::loadNative(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* /*builder*/) + { + header->material = Components::AssetHandler::FindOriginalAsset(this->getType(), name.data()).material; + } + + void IMaterial::loadJson(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Components::FileSystem::File materialInfo(Utils::String::VA("materials/%s.json", name.data())); + + if (!materialInfo.exists()) return; + + std::string errors; + json11::Json infoData = json11::Json::parse(materialInfo.getBuffer(), errors); + + if (!infoData.is_object()) + { + Components::Logger::Error("Failed to load material information for %s!", name.data()); + return; + } + + auto base = infoData["base"]; + + if (!base.is_string()) + { + Components::Logger::Error("No valid material base provided for %s!", name.data()); + return; + } + + Game::Material* baseMaterial = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, base.string_value().data()).material; + + if (!baseMaterial) // TODO: Maybe check if default asset? Maybe not? You could still want to use the default one as base!? + { + Components::Logger::Error("Basematerial '%s' not found for %s!", base.string_value().data(), name.data()); + return; + } + + Game::Material* material = builder->getAllocator()->allocate(); + + if (!material) + { + Components::Logger::Error("Failed to allocate material structure!"); + return; + } + + // Copy base material to our structure + std::memcpy(material, baseMaterial, sizeof(Game::Material)); + material->name = builder->getAllocator()->duplicateString(name); + + material->textureAtlasRowCount = 1; + material->textureAtlasColumnCount = 1; + + // Load animation frames + auto anims = infoData["anims"]; + if (anims.is_array()) + { + auto animCoords = anims.array_items(); + + if (animCoords.size() >= 2) + { + auto animCoordX = animCoords[0]; + auto animCoordY = animCoords[1]; + + if (animCoordX.is_number()) + { + material->textureAtlasColumnCount = static_cast(animCoordX.number_value()) & 0xFF; + } + + if (animCoordY.is_number()) + { + material->textureAtlasRowCount = static_cast(animCoordY.number_value()) & 0xFF; + } + } + } + + // Model surface textures are special, they need a special order and whatnot + bool replaceTexture = Utils::String::StartsWith(name, "mc/"); + if (replaceTexture) + { + Game::MaterialTextureDef* textureTable = builder->getAllocator()->allocateArray(baseMaterial->textureCount); + std::memcpy(textureTable, baseMaterial->textureTable, sizeof(Game::MaterialTextureDef) * baseMaterial->textureCount); + material->textureTable = textureTable; + material->textureCount = baseMaterial->textureCount; + } + + // Load referenced textures + auto textures = infoData["textures"]; + if (textures.is_array()) + { + std::vector textureList; + + for (auto texture : textures.array_items()) + { + if (!texture.is_array()) continue; + if (textureList.size() >= 0xFF) break; + + auto textureInfo = texture.array_items(); + if (textureInfo.size() < 2) continue; + + auto map = textureInfo[0]; + auto image = textureInfo[1]; + if (!map.is_string() || !image.is_string()) continue; + + Game::MaterialTextureDef textureDef; + + textureDef.semantic = 0; // No water image + textureDef.sampleState = -30; + textureDef.nameEnd = map.string_value().back(); + textureDef.nameStart = map.string_value().front(); + textureDef.nameHash = Game::R_HashString(map.string_value().data()); + + textureDef.info.image = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_IMAGE, image.string_value(), builder).image; + + if (replaceTexture) + { + bool applied = false; + + for (char i = 0; i < baseMaterial->textureCount; ++i) + { + if (material->textureTable[i].nameHash == textureDef.nameHash) + { + applied = true; + material->textureTable[i].info.image = textureDef.info.image; + break; + } + } + + if (!applied) + { + Components::Logger::Error(0, "Unable to find texture for map '%s' in %s!", map.string_value().data(), baseMaterial->name); + } + } + else + { + textureList.push_back(textureDef); + } + } + + if(!replaceTexture) + { + if (!textureList.empty()) + { + Game::MaterialTextureDef* textureTable = builder->getAllocator()->allocateArray(textureList.size()); + + if (!textureTable) + { + Components::Logger::Error("Failed to allocate texture table!"); + return; + } + + std::memcpy(textureTable, textureList.data(), sizeof(Game::MaterialTextureDef) * textureList.size()); + + material->textureTable = textureTable; + } + else + { + material->textureTable = 0; + } + + material->textureCount = static_cast(textureList.size()) & 0xFF; + } + } + + header->material = material; + } + + void IMaterial::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::Material* asset = header.material; + + if (asset->techniqueSet) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, asset->techniqueSet); + } + + if (asset->textureTable) + { + for (char i = 0; i < asset->textureCount; ++i) + { + if (asset->textureTable[i].info.image) + { + if (asset->textureTable[i].semantic == SEMANTIC_WATER_MAP) + { + if (asset->textureTable[i].info.water->image) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->textureTable[i].info.water->image); + } + } + else + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_IMAGE, asset->textureTable[i].info.image); + } + } + } + } + } + + void IMaterial::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::Material, 96); + + Utils::Stream* buffer = builder->getBuffer(); + Game::Material* asset = header.material; + Game::Material* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->techniqueSet) + { + dest->techniqueSet = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, asset->techniqueSet).techniqueSet; + } + + if (asset->textureTable) + { + AssertSize(Game::MaterialTextureDef, 12); + + // Pointer/Offset insertion is untested, but it worked in T6, so I think it's fine + if (builder->hasPointer(asset->textureTable)) + { + dest->textureTable = builder->getPointer(asset->textureTable); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(asset->textureTable); + + Game::MaterialTextureDef* destTextureTable = buffer->dest(); + buffer->saveArray(asset->textureTable, asset->textureCount); + + for (char i = 0; i < asset->textureCount; ++i) + { + Game::MaterialTextureDef* destTextureDef = &destTextureTable[i]; + Game::MaterialTextureDef* textureDef = &asset->textureTable[i]; + + if (textureDef->semantic == SEMANTIC_WATER_MAP) + { + AssertSize(Game::water_t, 68); + + Game::water_t* destWater = buffer->dest(); + Game::water_t* water = textureDef->info.water; + + if (water) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(water); + Utils::Stream::ClearPointer(&destTextureDef->info.water); + + // Save_water_t + if (water->H0) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(water->H0, 8, water->M * water->N); + Utils::Stream::ClearPointer(&destWater->H0); + } + + if (water->wTerm) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(water->wTerm, 4, water->M * water->N); + Utils::Stream::ClearPointer(&destWater->wTerm); + } + + if (water->image) + { + destWater->image = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, water->image).image; + } + } + } + else if (textureDef->info.image) + { + destTextureDef->info.image = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_IMAGE, textureDef->info.image).image; + } + } + + Utils::Stream::ClearPointer(&dest->textureTable); + } + } + + if (asset->constantTable) + { + AssertSize(Game::MaterialConstantDef, 32); + + if (builder->hasPointer(asset->constantTable)) + { + dest->constantTable = builder->getPointer(asset->constantTable); + } + else + { + buffer->align(Utils::Stream::ALIGN_16); + builder->storePointer(asset->constantTable); + + buffer->saveArray(asset->constantTable, asset->constantCount); + Utils::Stream::ClearPointer(&dest->constantTable); + } + } + + if (asset->stateBitTable) + { + if (builder->hasPointer(asset->stateBitTable)) + { + dest->stateBitTable = builder->getPointer(asset->stateBitTable); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(asset->stateBitTable); + + buffer->save(asset->stateBitTable, 8, asset->stateBitsCount); + Utils::Stream::ClearPointer(&dest->stateBitTable); + } + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IRawFile.cpp b/src/Components/Modules/AssetInterfaces/IRawFile.cpp index 19faa861..b187695f 100644 --- a/src/Components/Modules/AssetInterfaces/IRawFile.cpp +++ b/src/Components/Modules/AssetInterfaces/IRawFile.cpp @@ -1,60 +1,60 @@ -#include - -namespace Assets -{ - void IRawFile::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - Components::FileSystem::File rawFile(name); - - if (rawFile.exists()) - { - Game::RawFile* asset = builder->getAllocator()->allocate(); - - if (asset) - { - //std::string data = Utils::Compression::ZLib::Compress(rawFile.getBuffer()); - - asset->name = builder->getAllocator()->duplicateString(name); - asset->compressedData = builder->getAllocator()->duplicateString(rawFile.getBuffer()); - asset->sizeCompressed = 0;//data.size(); - asset->sizeUnCompressed = rawFile.getBuffer().size(); - - header->rawfile = asset; - } - } - } - - void IRawFile::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::RawFile, 16); - - Utils::Stream* buffer = builder->getBuffer(); - Game::RawFile* asset = header.rawfile; - Game::RawFile* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->compressedData) - { - if (asset->sizeCompressed) - { - buffer->save(asset->compressedData, asset->sizeCompressed); - } - else - { - buffer->save(asset->compressedData, asset->sizeUnCompressed + 1); - } - - Utils::Stream::ClearPointer(&dest->compressedData); - } - - buffer->popBlock(); - } -} +#include + +namespace Assets +{ + void IRawFile::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Components::FileSystem::File rawFile(name); + + if (rawFile.exists()) + { + Game::RawFile* asset = builder->getAllocator()->allocate(); + + if (asset) + { + //std::string data = Utils::Compression::ZLib::Compress(rawFile.getBuffer()); + + asset->name = builder->getAllocator()->duplicateString(name); + asset->compressedData = builder->getAllocator()->duplicateString(rawFile.getBuffer()); + asset->sizeCompressed = 0;//data.size(); + asset->sizeUnCompressed = rawFile.getBuffer().size(); + + header->rawfile = asset; + } + } + } + + void IRawFile::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::RawFile, 16); + + Utils::Stream* buffer = builder->getBuffer(); + Game::RawFile* asset = header.rawfile; + Game::RawFile* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->compressedData) + { + if (asset->sizeCompressed) + { + buffer->save(asset->compressedData, asset->sizeCompressed); + } + else + { + buffer->save(asset->compressedData, asset->sizeUnCompressed + 1); + } + + Utils::Stream::ClearPointer(&dest->compressedData); + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/ISndCurve.cpp b/src/Components/Modules/AssetInterfaces/ISndCurve.cpp index 30dcfd94..205fe210 100644 --- a/src/Components/Modules/AssetInterfaces/ISndCurve.cpp +++ b/src/Components/Modules/AssetInterfaces/ISndCurve.cpp @@ -1,24 +1,24 @@ -#include - -namespace Assets -{ - void ISndCurve::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::SndCurve, 136); - - Utils::Stream* buffer = builder->getBuffer(); - Game::SndCurve* asset = header.sndCurve; - Game::SndCurve* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->filename) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->filename)); - Utils::Stream::ClearPointer(&dest->filename); - } - - buffer->popBlock(); - } -} +#include + +namespace Assets +{ + void ISndCurve::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::SndCurve, 136); + + Utils::Stream* buffer = builder->getBuffer(); + Game::SndCurve* asset = header.sndCurve; + Game::SndCurve* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->filename) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->filename)); + Utils::Stream::ClearPointer(&dest->filename); + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp b/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp index dd891577..3cb5532b 100644 --- a/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp +++ b/src/Components/Modules/AssetInterfaces/IXAnimParts.cpp @@ -1,354 +1,354 @@ -#include - -#define IW4X_ANIM_VERSION 1 - -namespace Assets -{ - void IXAnimParts::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - Components::FileSystem::File animFile(Utils::String::VA("xanim/%s.iw4xAnim", name.data())); - - if (animFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), animFile.getBuffer()); - - if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xAnim")) - { - Components::Logger::Error(0, "Reading animation '%s' failed, header is invalid!", name.data()); - } - - int version = reader.read(); - if (version != IW4X_ANIM_VERSION) - { - Components::Logger::Error(0, "Reading animation '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_ANIM_VERSION, version); - } - - Game::XAnimParts* xanim = reader.readArray(); - - if (xanim) - { - if (xanim->name) - { - xanim->name = reader.readCString(); - } - - if (xanim->tagnames) - { - xanim->tagnames = builder->getAllocator()->allocateArray(xanim->boneCount[Game::XAnimPartType::PART_TYPE_ALL]); - for (int i = 0; i < xanim->boneCount[Game::XAnimPartType::PART_TYPE_ALL]; ++i) - { - xanim->tagnames[i] = Game::SL_GetString(reader.readCString(), 0); - } - } - - if (xanim->notetracks) - { - xanim->notetracks = reader.readArray(xanim->notetrackCount); - - for (int i = 0; i < xanim->notetrackCount; ++i) - { - xanim->notetracks[i].name = Game::SL_GetString(reader.readCString(), 0); - } - } - - if (xanim->dataByte) - { - xanim->dataByte = reader.readArray(xanim->dataByteCount); - } - - if (xanim->dataShort) - { - xanim->dataShort = reader.readArray(xanim->dataShortCount); - } - - if (xanim->dataInt) - { - xanim->dataInt = reader.readArray(xanim->dataIntCount); - } - - if (xanim->randomDataByte) - { - xanim->randomDataByte = reader.readArray(xanim->randomDataByteCount); - } - - if (xanim->randomDataShort) - { - xanim->randomDataShort = reader.readArray(xanim->randomDataShortCount); - } - - if (xanim->randomDataInt) - { - xanim->randomDataInt = reader.readArray(xanim->randomDataIntCount); - } - - if (xanim->indices.data) - { - if (xanim->framecount < 256) - { - xanim->indices._1 = reader.readArray(xanim->indexcount); - } - else - { - xanim->indices._2 = reader.readArray(xanim->indexcount); - } - } - - if (!reader.end()) - { - Components::Logger::Error(0, "Reading animation '%s' failed, remaining raw data found!", name.data()); - } - - header->parts = xanim; - } - } - } - - void IXAnimParts::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::XAnimParts* asset = header.parts; - - if (asset->tagnames) - { - for (char i = 0; i < asset->boneCount[Game::XAnimPartType::PART_TYPE_ALL]; ++i) - { - builder->addScriptString(asset->tagnames[i]); - } - } - - if (asset->notetracks) - { - for (char i = 0; i < asset->notetrackCount; ++i) - { - builder->addScriptString(asset->notetracks[i].name); - } - } - } - - void IXAnimParts::saveXAnimDeltaPart(Game::XAnimDeltaPart* delta, unsigned short framecount, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::XAnimDeltaPart, 12); - - Utils::Stream* buffer = builder->getBuffer(); - Game::XAnimDeltaPart* destDelta = buffer->dest(); - buffer->save(delta); - - if (delta->trans) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(delta->trans, 4); - - if (delta->trans->size) - { - buffer->save(&delta->trans->u.frames, 28); - - if (framecount > 0xFF) - { - buffer->saveArray(delta->trans->u.frames.indices._2, delta->trans->size + 1); - } - else - { - buffer->saveArray(delta->trans->u.frames.indices._1, delta->trans->size + 1); - } - - if (delta->trans->u.frames.frames._1) - { - if (delta->trans->smallTrans) - { - buffer->save(delta->trans->u.frames.frames._1, 3, delta->trans->size + 1); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(delta->trans->u.frames.frames._1, 6, delta->trans->size + 1); - } - } - } - else - { - buffer->save(delta->trans->u.frame0, 12); - } - - Utils::Stream::ClearPointer(&destDelta->trans); - } - - if (delta->quat2) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(delta->quat2, 4); - - if (delta->quat2->size) - { - buffer->save(&delta->quat2->u.frames, 4); - - if (framecount > 0xFF) - { - buffer->save(delta->quat2->u.frames.indices, 2, delta->quat2->size + 1); - } - else - { - buffer->save(delta->quat2->u.frames.indices, 1, delta->quat2->size + 1); - } - - if (delta->quat2->u.frames.frames) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(delta->quat2->u.frames.frames, 4, delta->quat2->size + 1); - } - } - else - { - buffer->save(delta->quat2->u.frame0, 4); - } - - Utils::Stream::ClearPointer(&destDelta->quat2); - } - - if (delta->quat) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(delta->quat, 4); - - if (delta->quat->size) - { - buffer->save(&delta->quat->u.frames, 4); - - if (framecount > 0xFF) - { - buffer->save(delta->quat->u.frames.indices, 2, delta->quat->size + 1); - } - else - { - buffer->save(delta->quat->u.frames.indices, 1, delta->quat->size + 1); - } - - if (delta->quat->u.frames.frames) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->save(delta->quat->u.frames.frames, 4, delta->quat->size + 1); - } - } - else - { - buffer->save(delta->quat->u.frame0, 4); - } - - Utils::Stream::ClearPointer(&destDelta->quat); - } - } - - void IXAnimParts::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::XAnimParts, 88); - - Utils::Stream* buffer = builder->getBuffer(); - Game::XAnimParts* asset = header.parts; - Game::XAnimParts* dest = buffer->dest(); - buffer->save(asset, sizeof(Game::XAnimParts)); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->tagnames) - { - buffer->align(Utils::Stream::ALIGN_2); - - unsigned short* destTagnames = buffer->dest(); - buffer->saveArray(asset->tagnames, asset->boneCount[Game::XAnimPartType::PART_TYPE_ALL]); - - for (char i = 0; i < asset->boneCount[Game::XAnimPartType::PART_TYPE_ALL]; ++i) - { - builder->mapScriptString(&destTagnames[i]); - } - - Utils::Stream::ClearPointer(&dest->tagnames); - } - - if (asset->notetracks) - { - AssertSize(Game::XAnimNotifyInfo, 8); - buffer->align(Utils::Stream::ALIGN_4); - - Game::XAnimNotifyInfo* destNotetracks = buffer->dest(); - buffer->saveArray(asset->notetracks, asset->notetrackCount); - - for (char i = 0; i < asset->notetrackCount; ++i) - { - builder->mapScriptString(&destNotetracks[i].name); - } - - Utils::Stream::ClearPointer(&dest->notetracks); - } - - if (asset->delta) - { - AssertSize(Game::XAnimDeltaPart, 12); - buffer->align(Utils::Stream::ALIGN_4); - - this->saveXAnimDeltaPart(asset->delta, asset->framecount, builder); - - Utils::Stream::ClearPointer(&dest->delta); - } - - if (asset->dataByte) - { - buffer->saveArray(asset->dataByte, asset->dataByteCount); - Utils::Stream::ClearPointer(&dest->dataByte); - } - - if (asset->dataShort) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->dataShort, asset->dataShortCount); - Utils::Stream::ClearPointer(&dest->dataShort); - } - - if (asset->dataInt) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->dataInt, asset->dataIntCount); - Utils::Stream::ClearPointer(&dest->dataInt); - } - - if (asset->randomDataShort) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->randomDataShort, asset->randomDataShortCount); - Utils::Stream::ClearPointer(&dest->randomDataShort); - } - - if (asset->randomDataByte) - { - buffer->saveArray(asset->randomDataByte, asset->randomDataByteCount); - Utils::Stream::ClearPointer(&dest->randomDataByte); - } - - if (asset->randomDataInt) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->randomDataInt, asset->randomDataIntCount); - Utils::Stream::ClearPointer(&dest->randomDataInt); - } - - if (asset->indices.data) - { - if (asset->framecount > 0xFF) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->indices._2, asset->indexcount); - } - else - { - buffer->saveArray(asset->indices._1, asset->indexcount); - } - - Utils::Stream::ClearPointer(&dest->indices.data); - } - - buffer->popBlock(); - } -} +#include + +#define IW4X_ANIM_VERSION 1 + +namespace Assets +{ + void IXAnimParts::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Components::FileSystem::File animFile(Utils::String::VA("xanim/%s.iw4xAnim", name.data())); + + if (animFile.exists()) + { + Utils::Stream::Reader reader(builder->getAllocator(), animFile.getBuffer()); + + if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xAnim")) + { + Components::Logger::Error(0, "Reading animation '%s' failed, header is invalid!", name.data()); + } + + int version = reader.read(); + if (version != IW4X_ANIM_VERSION) + { + Components::Logger::Error(0, "Reading animation '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_ANIM_VERSION, version); + } + + Game::XAnimParts* xanim = reader.readArray(); + + if (xanim) + { + if (xanim->name) + { + xanim->name = reader.readCString(); + } + + if (xanim->tagnames) + { + xanim->tagnames = builder->getAllocator()->allocateArray(xanim->boneCount[Game::XAnimPartType::PART_TYPE_ALL]); + for (int i = 0; i < xanim->boneCount[Game::XAnimPartType::PART_TYPE_ALL]; ++i) + { + xanim->tagnames[i] = Game::SL_GetString(reader.readCString(), 0); + } + } + + if (xanim->notetracks) + { + xanim->notetracks = reader.readArray(xanim->notetrackCount); + + for (int i = 0; i < xanim->notetrackCount; ++i) + { + xanim->notetracks[i].name = Game::SL_GetString(reader.readCString(), 0); + } + } + + if (xanim->dataByte) + { + xanim->dataByte = reader.readArray(xanim->dataByteCount); + } + + if (xanim->dataShort) + { + xanim->dataShort = reader.readArray(xanim->dataShortCount); + } + + if (xanim->dataInt) + { + xanim->dataInt = reader.readArray(xanim->dataIntCount); + } + + if (xanim->randomDataByte) + { + xanim->randomDataByte = reader.readArray(xanim->randomDataByteCount); + } + + if (xanim->randomDataShort) + { + xanim->randomDataShort = reader.readArray(xanim->randomDataShortCount); + } + + if (xanim->randomDataInt) + { + xanim->randomDataInt = reader.readArray(xanim->randomDataIntCount); + } + + if (xanim->indices.data) + { + if (xanim->framecount < 256) + { + xanim->indices._1 = reader.readArray(xanim->indexcount); + } + else + { + xanim->indices._2 = reader.readArray(xanim->indexcount); + } + } + + if (!reader.end()) + { + Components::Logger::Error(0, "Reading animation '%s' failed, remaining raw data found!", name.data()); + } + + header->parts = xanim; + } + } + } + + void IXAnimParts::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::XAnimParts* asset = header.parts; + + if (asset->tagnames) + { + for (char i = 0; i < asset->boneCount[Game::XAnimPartType::PART_TYPE_ALL]; ++i) + { + builder->addScriptString(asset->tagnames[i]); + } + } + + if (asset->notetracks) + { + for (char i = 0; i < asset->notetrackCount; ++i) + { + builder->addScriptString(asset->notetracks[i].name); + } + } + } + + void IXAnimParts::saveXAnimDeltaPart(Game::XAnimDeltaPart* delta, unsigned short framecount, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::XAnimDeltaPart, 12); + + Utils::Stream* buffer = builder->getBuffer(); + Game::XAnimDeltaPart* destDelta = buffer->dest(); + buffer->save(delta); + + if (delta->trans) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(delta->trans, 4); + + if (delta->trans->size) + { + buffer->save(&delta->trans->u.frames, 28); + + if (framecount > 0xFF) + { + buffer->saveArray(delta->trans->u.frames.indices._2, delta->trans->size + 1); + } + else + { + buffer->saveArray(delta->trans->u.frames.indices._1, delta->trans->size + 1); + } + + if (delta->trans->u.frames.frames._1) + { + if (delta->trans->smallTrans) + { + buffer->save(delta->trans->u.frames.frames._1, 3, delta->trans->size + 1); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(delta->trans->u.frames.frames._1, 6, delta->trans->size + 1); + } + } + } + else + { + buffer->save(delta->trans->u.frame0, 12); + } + + Utils::Stream::ClearPointer(&destDelta->trans); + } + + if (delta->quat2) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(delta->quat2, 4); + + if (delta->quat2->size) + { + buffer->save(&delta->quat2->u.frames, 4); + + if (framecount > 0xFF) + { + buffer->save(delta->quat2->u.frames.indices, 2, delta->quat2->size + 1); + } + else + { + buffer->save(delta->quat2->u.frames.indices, 1, delta->quat2->size + 1); + } + + if (delta->quat2->u.frames.frames) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(delta->quat2->u.frames.frames, 4, delta->quat2->size + 1); + } + } + else + { + buffer->save(delta->quat2->u.frame0, 4); + } + + Utils::Stream::ClearPointer(&destDelta->quat2); + } + + if (delta->quat) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(delta->quat, 4); + + if (delta->quat->size) + { + buffer->save(&delta->quat->u.frames, 4); + + if (framecount > 0xFF) + { + buffer->save(delta->quat->u.frames.indices, 2, delta->quat->size + 1); + } + else + { + buffer->save(delta->quat->u.frames.indices, 1, delta->quat->size + 1); + } + + if (delta->quat->u.frames.frames) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->save(delta->quat->u.frames.frames, 4, delta->quat->size + 1); + } + } + else + { + buffer->save(delta->quat->u.frame0, 4); + } + + Utils::Stream::ClearPointer(&destDelta->quat); + } + } + + void IXAnimParts::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::XAnimParts, 88); + + Utils::Stream* buffer = builder->getBuffer(); + Game::XAnimParts* asset = header.parts; + Game::XAnimParts* dest = buffer->dest(); + buffer->save(asset, sizeof(Game::XAnimParts)); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->tagnames) + { + buffer->align(Utils::Stream::ALIGN_2); + + unsigned short* destTagnames = buffer->dest(); + buffer->saveArray(asset->tagnames, asset->boneCount[Game::XAnimPartType::PART_TYPE_ALL]); + + for (char i = 0; i < asset->boneCount[Game::XAnimPartType::PART_TYPE_ALL]; ++i) + { + builder->mapScriptString(&destTagnames[i]); + } + + Utils::Stream::ClearPointer(&dest->tagnames); + } + + if (asset->notetracks) + { + AssertSize(Game::XAnimNotifyInfo, 8); + buffer->align(Utils::Stream::ALIGN_4); + + Game::XAnimNotifyInfo* destNotetracks = buffer->dest(); + buffer->saveArray(asset->notetracks, asset->notetrackCount); + + for (char i = 0; i < asset->notetrackCount; ++i) + { + builder->mapScriptString(&destNotetracks[i].name); + } + + Utils::Stream::ClearPointer(&dest->notetracks); + } + + if (asset->delta) + { + AssertSize(Game::XAnimDeltaPart, 12); + buffer->align(Utils::Stream::ALIGN_4); + + this->saveXAnimDeltaPart(asset->delta, asset->framecount, builder); + + Utils::Stream::ClearPointer(&dest->delta); + } + + if (asset->dataByte) + { + buffer->saveArray(asset->dataByte, asset->dataByteCount); + Utils::Stream::ClearPointer(&dest->dataByte); + } + + if (asset->dataShort) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->dataShort, asset->dataShortCount); + Utils::Stream::ClearPointer(&dest->dataShort); + } + + if (asset->dataInt) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->dataInt, asset->dataIntCount); + Utils::Stream::ClearPointer(&dest->dataInt); + } + + if (asset->randomDataShort) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->randomDataShort, asset->randomDataShortCount); + Utils::Stream::ClearPointer(&dest->randomDataShort); + } + + if (asset->randomDataByte) + { + buffer->saveArray(asset->randomDataByte, asset->randomDataByteCount); + Utils::Stream::ClearPointer(&dest->randomDataByte); + } + + if (asset->randomDataInt) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->randomDataInt, asset->randomDataIntCount); + Utils::Stream::ClearPointer(&dest->randomDataInt); + } + + if (asset->indices.data) + { + if (asset->framecount > 0xFF) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->indices._2, asset->indexcount); + } + else + { + buffer->saveArray(asset->indices._1, asset->indexcount); + } + + Utils::Stream::ClearPointer(&dest->indices.data); + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IXModel.cpp b/src/Components/Modules/AssetInterfaces/IXModel.cpp index c51bb949..99d7c629 100644 --- a/src/Components/Modules/AssetInterfaces/IXModel.cpp +++ b/src/Components/Modules/AssetInterfaces/IXModel.cpp @@ -1,9 +1,9 @@ -#include - -#define IW4X_MODEL_VERSION 4 - -namespace Assets -{ +#include + +#define IW4X_MODEL_VERSION 4 + +namespace Assets +{ void IXModel::loadXSurfaceCollisionTree(Game::XSurfaceCollisionTree* entry, Utils::Stream::Reader* reader) { if (entry->nodes) @@ -15,10 +15,10 @@ namespace Assets { entry->leafs = reader->readArray(entry->leafCount); } - } - - void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader) - { + } + + void IXModel::loadXSurface(Game::XSurface* surf, Utils::Stream::Reader* reader) + { if (surf->vertInfo.vertsBlend) { surf->vertInfo.vertsBlend = reader->readArray(surf->vertInfo.vertCount[0] + (surf->vertInfo.vertCount[1] * 3) + (surf->vertInfo.vertCount[2] * 5) + (surf->vertInfo.vertCount[3] * 7)); @@ -51,11 +51,11 @@ namespace Assets if (surf->triIndices) { surf->triIndices = reader->readArray(surf->triCount * 3); - } - } - - void IXModel::loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader) - { + } + } + + void IXModel::loadXModelSurfs(Game::XModelSurfs* asset, Utils::Stream::Reader* reader) + { if (asset->name) { asset->name = reader->readCString(); @@ -69,129 +69,129 @@ namespace Assets { this->loadXSurface(&asset->surfaces[i], reader); } - } - } - - void IXModel::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - Components::FileSystem::File modelFile(Utils::String::VA("xmodel/%s.iw4xModel", name.data())); - - if (modelFile.exists()) - { - Utils::Stream::Reader reader(builder->getAllocator(), modelFile.getBuffer()); - - if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xModl")) - { - Components::Logger::Error(0, "Reading model '%s' failed, header is invalid!", name.data()); - } - - int version = reader.read(); - if (version != IW4X_MODEL_VERSION) - { - Components::Logger::Error(0, "Reading model '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_MODEL_VERSION, version); - } - - Game::XModel* asset = reader.readObject(); - - if (asset->name) - { - asset->name = reader.readCString(); - } - - if (asset->boneNames) - { - asset->boneNames = builder->getAllocator()->allocateArray(asset->numBones); - - for (char i = 0; i < asset->numBones; ++i) - { - asset->boneNames[i] = Game::SL_GetString(reader.readCString(), 0); - } - } - - if (asset->parentList) - { - asset->parentList = reader.readArray(asset->numBones - asset->numRootBones); - } - - if (asset->quats) - { - asset->quats = reader.readArray((asset->numBones - asset->numRootBones) * 4); - } - - if (asset->trans) - { - asset->trans = reader.readArray((asset->numBones - asset->numRootBones) * 3); - } - - if (asset->partClassification) - { - asset->partClassification = reader.readArray(asset->numBones); - } - - if (asset->baseMat) - { - asset->baseMat = reader.readArray(asset->numBones); - } - - if (asset->materialHandles) - { - asset->materialHandles = reader.readArray(asset->numsurfs); - - for (char i = 0; i < asset->numsurfs; ++i) - { - if (asset->materialHandles[i]) - { - asset->materialHandles[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material; - } - } - } - - // Save_XModelLodInfoArray - { - for (int i = 0; i < 4; ++i) - { - if (asset->lodInfo[i].modelSurfs) - { - asset->lodInfo[i].modelSurfs = reader.readObject(); - this->loadXModelSurfs(asset->lodInfo[i].modelSurfs, &reader); - Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_XMODELSURFS, { asset->lodInfo[i].modelSurfs }); - - asset->lodInfo[i].surfs = asset->lodInfo[i].modelSurfs->surfaces; - - // Zero that for now, it breaks the models. - // TODO: Figure out how that can be converted - asset->lodInfo[i].smcBaseIndexPlusOne = 0; - asset->lodInfo[i].smcSubIndexMask = 0; - asset->lodInfo[i].smcBucket = 0; - } - } - } - - // Save_XModelCollSurfArray - if (asset->collSurfs) - { - asset->collSurfs = reader.readArray(asset->numCollSurfs); - - for (int i = 0; i < asset->numCollSurfs; ++i) - { - Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i]; - - if (collSurf->collTris) - { - collSurf->collTris = reader.readArray(collSurf->numCollTris); - } - } - } - - if (asset->boneInfo) - { - asset->boneInfo = reader.readArray(asset->numBones); - } - - if (asset->physPreset) - { - asset->physPreset = reader.readObject(); - + } + } + + void IXModel::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Components::FileSystem::File modelFile(Utils::String::VA("xmodel/%s.iw4xModel", name.data())); + + if (modelFile.exists()) + { + Utils::Stream::Reader reader(builder->getAllocator(), modelFile.getBuffer()); + + if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xModl")) + { + Components::Logger::Error(0, "Reading model '%s' failed, header is invalid!", name.data()); + } + + int version = reader.read(); + if (version != IW4X_MODEL_VERSION) + { + Components::Logger::Error(0, "Reading model '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_MODEL_VERSION, version); + } + + Game::XModel* asset = reader.readObject(); + + if (asset->name) + { + asset->name = reader.readCString(); + } + + if (asset->boneNames) + { + asset->boneNames = builder->getAllocator()->allocateArray(asset->numBones); + + for (char i = 0; i < asset->numBones; ++i) + { + asset->boneNames[i] = Game::SL_GetString(reader.readCString(), 0); + } + } + + if (asset->parentList) + { + asset->parentList = reader.readArray(asset->numBones - asset->numRootBones); + } + + if (asset->quats) + { + asset->quats = reader.readArray((asset->numBones - asset->numRootBones) * 4); + } + + if (asset->trans) + { + asset->trans = reader.readArray((asset->numBones - asset->numRootBones) * 3); + } + + if (asset->partClassification) + { + asset->partClassification = reader.readArray(asset->numBones); + } + + if (asset->baseMat) + { + asset->baseMat = reader.readArray(asset->numBones); + } + + if (asset->materialHandles) + { + asset->materialHandles = reader.readArray(asset->numsurfs); + + for (char i = 0; i < asset->numsurfs; ++i) + { + if (asset->materialHandles[i]) + { + asset->materialHandles[i] = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MATERIAL, reader.readString(), builder).material; + } + } + } + + // Save_XModelLodInfoArray + { + for (int i = 0; i < 4; ++i) + { + if (asset->lodInfo[i].modelSurfs) + { + asset->lodInfo[i].modelSurfs = reader.readObject(); + this->loadXModelSurfs(asset->lodInfo[i].modelSurfs, &reader); + Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_XMODELSURFS, { asset->lodInfo[i].modelSurfs }); + + asset->lodInfo[i].surfs = asset->lodInfo[i].modelSurfs->surfaces; + + // Zero that for now, it breaks the models. + // TODO: Figure out how that can be converted + asset->lodInfo[i].smcBaseIndexPlusOne = 0; + asset->lodInfo[i].smcSubIndexMask = 0; + asset->lodInfo[i].smcBucket = 0; + } + } + } + + // Save_XModelCollSurfArray + if (asset->collSurfs) + { + asset->collSurfs = reader.readArray(asset->numCollSurfs); + + for (int i = 0; i < asset->numCollSurfs; ++i) + { + Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i]; + + if (collSurf->collTris) + { + collSurf->collTris = reader.readArray(collSurf->numCollTris); + } + } + } + + if (asset->boneInfo) + { + asset->boneInfo = reader.readArray(asset->numBones); + } + + if (asset->physPreset) + { + asset->physPreset = reader.readObject(); + if (asset->physPreset->name) { asset->physPreset->name = reader.readCString(); @@ -200,213 +200,213 @@ namespace Assets if (asset->physPreset->sndAliasPrefix) { asset->physPreset->sndAliasPrefix = reader.readCString(); - } - - Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, { asset->physPreset }); - } - - if (asset->physCollmap) - { - // TODO - asset->physCollmap = nullptr; - } - - if (!reader.end()) - { - Components::Logger::Error(0, "Reading model '%s' failed, remaining raw data found!", name.data()); - } - - header->model = asset; - } - } - - void IXModel::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::XModel* asset = header.model; - - if (asset->boneNames) - { - for (char i = 0; i < asset->numBones; ++i) - { - builder->addScriptString(asset->boneNames[i]); - } - } - - if (asset->materialHandles) - { - for (char i = 0; i < asset->numsurfs; ++i) - { - if (asset->materialHandles[i]) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materialHandles[i]); - } - } - } - - for (int i = 0; i < 4; ++i) - { - if (asset->lodInfo[i].modelSurfs) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODELSURFS, asset->lodInfo[i].modelSurfs); - } - } - - if (asset->physPreset) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset); - } - - if (asset->physCollmap) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYS_COLLMAP, asset->physCollmap); - } - } - - void IXModel::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::XModel, 304); - - Utils::Stream* buffer = builder->getBuffer(); - Game::XModel* asset = header.model; - Game::XModel* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->boneNames) - { - buffer->align(Utils::Stream::ALIGN_2); - - unsigned short* destBoneNames = buffer->dest(); - buffer->saveArray(asset->boneNames, asset->numBones); - - for (char i = 0; i < asset->numBones; ++i) - { - builder->mapScriptString(&destBoneNames[i]); - } - - Utils::Stream::ClearPointer(&dest->boneNames); - } - - if (asset->parentList) - { - buffer->save(asset->parentList, asset->numBones - asset->numRootBones); - Utils::Stream::ClearPointer(&dest->parentList); - } - - if (asset->quats) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4); - Utils::Stream::ClearPointer(&dest->quats); - } - - if (asset->trans) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3); - Utils::Stream::ClearPointer(&dest->trans); - } - - if (asset->partClassification) - { - buffer->save(asset->partClassification, asset->numBones); - Utils::Stream::ClearPointer(&dest->partClassification); - } - - if (asset->baseMat) - { - AssertSize(Game::DObjAnimMat, 32); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->baseMat, asset->numBones); - Utils::Stream::ClearPointer(&dest->baseMat); - } - - if (asset->materialHandles) - { - buffer->align(Utils::Stream::ALIGN_4); - - Game::Material** destMaterials = buffer->dest(); - buffer->saveArray(asset->materialHandles, asset->numsurfs); - - for (char i = 0; i < asset->numsurfs; ++i) - { - if (asset->materialHandles[i]) - { - destMaterials[i] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materialHandles[i]).material; - } - } - - Utils::Stream::ClearPointer(&dest->materialHandles); - } - - // Save_XModelLodInfoArray - { - AssertSize(Game::XModelLodInfo, 44); - - for (int i = 0; i < 4; ++i) - { - if (asset->lodInfo[i].modelSurfs) - { - dest->lodInfo[i].modelSurfs = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODELSURFS, asset->lodInfo[i].modelSurfs).surfaces; - } - } - } - - // Save_XModelCollSurfArray - if (asset->collSurfs) - { - AssertSize(Game::XModelCollSurf_s, 44); - - buffer->align(Utils::Stream::ALIGN_4); - - Game::XModelCollSurf_s* destColSurfs = buffer->dest(); - buffer->saveArray(asset->collSurfs, asset->numCollSurfs); - - for (int i = 0; i < asset->numCollSurfs; ++i) - { - Game::XModelCollSurf_s* destCollSurf = &destColSurfs[i]; - Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i]; - - if (collSurf->collTris) - { - buffer->align(Utils::Stream::ALIGN_4); - - buffer->save(collSurf->collTris, 48, collSurf->numCollTris); - Utils::Stream::ClearPointer(&destCollSurf->collTris); - } - } - - Utils::Stream::ClearPointer(&dest->collSurfs); - } - - if (asset->boneInfo) - { - AssertSize(Game::XBoneInfo, 28); - - buffer->align(Utils::Stream::ALIGN_4); - - buffer->saveArray(asset->boneInfo, asset->numBones); - Utils::Stream::ClearPointer(&dest->boneInfo); - } - - if (asset->physPreset) - { - dest->physPreset = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset).physPreset; - } - - if (asset->physCollmap) - { - dest->physCollmap = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYS_COLLMAP, asset->physCollmap).physCollmap; - } - - buffer->popBlock(); - } -} + } + + Components::AssetHandler::StoreTemporaryAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, { asset->physPreset }); + } + + if (asset->physCollmap) + { + // TODO + asset->physCollmap = nullptr; + } + + if (!reader.end()) + { + Components::Logger::Error(0, "Reading model '%s' failed, remaining raw data found!", name.data()); + } + + header->model = asset; + } + } + + void IXModel::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::XModel* asset = header.model; + + if (asset->boneNames) + { + for (char i = 0; i < asset->numBones; ++i) + { + builder->addScriptString(asset->boneNames[i]); + } + } + + if (asset->materialHandles) + { + for (char i = 0; i < asset->numsurfs; ++i) + { + if (asset->materialHandles[i]) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materialHandles[i]); + } + } + } + + for (int i = 0; i < 4; ++i) + { + if (asset->lodInfo[i].modelSurfs) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODELSURFS, asset->lodInfo[i].modelSurfs); + } + } + + if (asset->physPreset) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset); + } + + if (asset->physCollmap) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYS_COLLMAP, asset->physCollmap); + } + } + + void IXModel::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::XModel, 304); + + Utils::Stream* buffer = builder->getBuffer(); + Game::XModel* asset = header.model; + Game::XModel* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->boneNames) + { + buffer->align(Utils::Stream::ALIGN_2); + + unsigned short* destBoneNames = buffer->dest(); + buffer->saveArray(asset->boneNames, asset->numBones); + + for (char i = 0; i < asset->numBones; ++i) + { + builder->mapScriptString(&destBoneNames[i]); + } + + Utils::Stream::ClearPointer(&dest->boneNames); + } + + if (asset->parentList) + { + buffer->save(asset->parentList, asset->numBones - asset->numRootBones); + Utils::Stream::ClearPointer(&dest->parentList); + } + + if (asset->quats) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->quats, (asset->numBones - asset->numRootBones) * 4); + Utils::Stream::ClearPointer(&dest->quats); + } + + if (asset->trans) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->trans, (asset->numBones - asset->numRootBones) * 3); + Utils::Stream::ClearPointer(&dest->trans); + } + + if (asset->partClassification) + { + buffer->save(asset->partClassification, asset->numBones); + Utils::Stream::ClearPointer(&dest->partClassification); + } + + if (asset->baseMat) + { + AssertSize(Game::DObjAnimMat, 32); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->baseMat, asset->numBones); + Utils::Stream::ClearPointer(&dest->baseMat); + } + + if (asset->materialHandles) + { + buffer->align(Utils::Stream::ALIGN_4); + + Game::Material** destMaterials = buffer->dest(); + buffer->saveArray(asset->materialHandles, asset->numsurfs); + + for (char i = 0; i < asset->numsurfs; ++i) + { + if (asset->materialHandles[i]) + { + destMaterials[i] = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MATERIAL, asset->materialHandles[i]).material; + } + } + + Utils::Stream::ClearPointer(&dest->materialHandles); + } + + // Save_XModelLodInfoArray + { + AssertSize(Game::XModelLodInfo, 44); + + for (int i = 0; i < 4; ++i) + { + if (asset->lodInfo[i].modelSurfs) + { + dest->lodInfo[i].modelSurfs = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODELSURFS, asset->lodInfo[i].modelSurfs).surfaces; + } + } + } + + // Save_XModelCollSurfArray + if (asset->collSurfs) + { + AssertSize(Game::XModelCollSurf_s, 44); + + buffer->align(Utils::Stream::ALIGN_4); + + Game::XModelCollSurf_s* destColSurfs = buffer->dest(); + buffer->saveArray(asset->collSurfs, asset->numCollSurfs); + + for (int i = 0; i < asset->numCollSurfs; ++i) + { + Game::XModelCollSurf_s* destCollSurf = &destColSurfs[i]; + Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i]; + + if (collSurf->collTris) + { + buffer->align(Utils::Stream::ALIGN_4); + + buffer->save(collSurf->collTris, 48, collSurf->numCollTris); + Utils::Stream::ClearPointer(&destCollSurf->collTris); + } + } + + Utils::Stream::ClearPointer(&dest->collSurfs); + } + + if (asset->boneInfo) + { + AssertSize(Game::XBoneInfo, 28); + + buffer->align(Utils::Stream::ALIGN_4); + + buffer->saveArray(asset->boneInfo, asset->numBones); + Utils::Stream::ClearPointer(&dest->boneInfo); + } + + if (asset->physPreset) + { + dest->physPreset = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, asset->physPreset).physPreset; + } + + if (asset->physCollmap) + { + dest->physCollmap = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYS_COLLMAP, asset->physCollmap).physCollmap; + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp index fed92f6f..f6265bf9 100644 --- a/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp +++ b/src/Components/Modules/AssetInterfaces/IclipMap_t.cpp @@ -1,606 +1,606 @@ -#include - -#define IW4X_CLIPMAP_VERSION 1 - -#ifdef ENABLE_EXPERIMENTAL_MAP_CODE - -namespace Assets -{ - void IclipMap_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::clipMap_t, 256); - SaveLogEnter("clipMap_t"); - - Utils::Stream* buffer = builder->getBuffer(); - Game::clipMap_t* asset = header.clipMap; - Game::clipMap_t* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->cPlanes) - { - AssertSize(Game::cplane_t, 20); - SaveLogEnter("cplane_t"); - - if (builder->hasPointer(asset->cPlanes)) - { - dest->cPlanes = builder->getPointer(asset->cPlanes); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - - // not sure if this is neede but both brushside and brushedge need it and it can't hurt - for (int i = 0; i < asset->numCPlanes; i++) - { - builder->storePointer(&asset->cPlanes[i]); - buffer->save(&asset->cPlanes[i]); - } - Utils::Stream::ClearPointer(&dest->cPlanes); - } - - SaveLogExit(); - } - - if (asset->staticModelList) - { - - AssertSize(Game::cStaticModel_t, 76); - SaveLogEnter("cStaticModel_t"); - - // xmodel is already stored - buffer->align(Utils::Stream::ALIGN_4); - Game::cStaticModel_t* destStaticModelList = buffer->dest(); - buffer->saveArray(asset->staticModelList, asset->numStaticModels); - - for (int i = 0; i < asset->numStaticModels; ++i) - { - if (asset->staticModelList[i].xmodel) - { - destStaticModelList[i].xmodel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->staticModelList[i].xmodel).model; - } - } - - Utils::Stream::ClearPointer(&dest->staticModelList); - SaveLogExit(); - } - - if (asset->materials) - { - AssertSize(Game::ClipMaterial, 12); - SaveLogEnter("ClipMaterial"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::ClipMaterial* mats = buffer->dest(); - buffer->saveArray(asset->materials, asset->numMaterials); - - for (int i = 0; i < asset->numMaterials; ++i) - { - buffer->saveString(asset->materials[i].name); - Utils::Stream::ClearPointer(&mats[i].name); - } - - Utils::Stream::ClearPointer(&dest->materials); - SaveLogExit(); - } - - if (asset->cBrushSides) - { - AssertSize(Game::cbrushside_t, 8); - SaveLogEnter("cbrushside_t"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::cbrushside_t* sides = buffer->dest(); - // we need the pointer to each of these to be stored so we can't write them all at once - for (int i = 0; i < asset->numCBrushSides; ++i) - { - builder->storePointer(&asset->cBrushSides[i]); // for reference in cBrush - buffer->save(&asset->cBrushSides[i]); - } - - for (int i = 0; i < asset->numCBrushSides; ++i) - { - if (sides[i].plane) - { - AssertSize(Game::cplane_t, 20); - - if (builder->hasPointer(sides[i].plane)) - { - sides[i].plane = builder->getPointer(sides[i].plane); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(sides[i].plane); - - buffer->save(sides[i].plane); - Utils::Stream::ClearPointer(&sides[i].plane); - } - } - } - - Utils::Stream::ClearPointer(&dest->cBrushSides); - SaveLogExit(); - } - - if (asset->cBrushEdges) - { - SaveLogEnter("cBrushEdge"); - - // no align for char - for (int i = 0; i < asset->numCBrushEdges; ++i) - { - builder->storePointer(&asset->cBrushEdges[i]); // for reference in cBrush - buffer->save(&asset->cBrushEdges[i]); - } - Utils::Stream::ClearPointer(&dest->cBrushEdges); - - SaveLogExit(); - } - - if (asset->cNodes) - { - AssertSize(Game::cNode_t, 8); - SaveLogEnter("cNode_t"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::cNode_t* nodes = buffer->dest(); - buffer->saveArray(asset->cNodes, asset->numCNodes); - - for (int i = 0; i < asset->numCNodes; ++i) - { - if (nodes[i].plane) - { - if (builder->hasPointer(nodes[i].plane)) - { - nodes[i].plane = builder->getPointer(nodes[i].plane); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(nodes[i].plane); - - buffer->save(nodes[i].plane); - Utils::Stream::ClearPointer(&nodes[i].plane); - } - } - } - - Utils::Stream::ClearPointer(&dest->cNodes); - SaveLogExit(); - } - - if (asset->cLeaf) - { - AssertSize(Game::cLeaf_t, 40); - SaveLogEnter("cLeaf_t"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->cLeaf, asset->numCLeaf); - Utils::Stream::ClearPointer(&dest->cLeaf); - SaveLogExit(); - } - - if (asset->leafBrushes) - { - SaveLogEnter("cLeafBrush_t"); - - buffer->align(Utils::Stream::ALIGN_2); - buffer->saveArray(asset->leafBrushes, asset->numLeafBrushes); - Utils::Stream::ClearPointer(&dest->leafBrushes); - - SaveLogExit(); - } - - if (asset->cLeafBrushNodes) - { - AssertSize(Game::cLeafBrushNode_t, 20); - SaveLogEnter("cLeafBrushNode_t"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::cLeafBrushNode_t* node = buffer->dest(); - buffer->saveArray(asset->cLeafBrushNodes, asset->numCLeafBrushNodes); - - for (int i = 0; i < asset->numCLeafBrushNodes; ++i) - { - if (node[i].leafBrushCount > 0) - { - if (node[i].data.brushes) - { - if (builder->hasPointer(node[i].data.brushes)) - { - node[i].data.brushes = builder->getPointer(node[i].data.brushes); - } - else - { - buffer->align(Utils::Stream::ALIGN_2); - - for (short j = 0; j < node[i].leafBrushCount; ++j) - { - builder->storePointer(&node[i].data.brushes[j]); - buffer->save(&node[i].data.brushes[j]); - } - - Utils::Stream::ClearPointer(&node[i].data.brushes); - } - } - } - } - - Utils::Stream::ClearPointer(&dest->cLeafBrushNodes); - SaveLogExit(); - } - - if (asset->leafSurfaces) - { - SaveLogEnter("cLeafSurface_t"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->leafSurfaces, asset->numLeafSurfaces); - Utils::Stream::ClearPointer(&dest->leafSurfaces); - - SaveLogExit(); - } - - if (asset->verts) - { - AssertSize(Game::vec3_t, 12); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->verts, asset->numVerts); - Utils::Stream::ClearPointer(&dest->verts); - } - - if (asset->triIndices) - { - buffer->align(Utils::Stream::ALIGN_2); - buffer->save(asset->triIndices, 6, asset->numTriIndices); - Utils::Stream::ClearPointer(&dest->triIndices); - } - - if (asset->triEdgeIsWalkable) - { - // no align for char - buffer->save(asset->triEdgeIsWalkable, 1, 4 * ((3 * asset->numTriIndices + 31) >> 5)); - Utils::Stream::ClearPointer(&dest->triEdgeIsWalkable); - } - - if (asset->collisionBorders) - { - AssertSize(Game::CollisionBorder, 28); - SaveLogEnter("CollisionBorder"); - - buffer->align(Utils::Stream::ALIGN_4); - - for (int i = 0; i < asset->numCollisionBorders; ++i) - { - builder->storePointer(&asset->collisionBorders[i]); - buffer->save(&asset->collisionBorders[i]); - } - - Utils::Stream::ClearPointer(&dest->collisionBorders); - SaveLogExit(); - } - - if (asset->collisionPartitions) - { - AssertSize(Game::CollisionPartition, 12); - SaveLogEnter("CollisionPartition"); - - buffer->align(Utils::Stream::ALIGN_4); - Game::CollisionPartition* destPartitions = buffer->dest(); - buffer->saveArray(asset->collisionPartitions, asset->numCollisionPartitions); - - for (int i = 0; i < asset->numCollisionPartitions; ++i) - { - Game::CollisionPartition* destPartition = &destPartitions[i]; - Game::CollisionPartition* partition = &asset->collisionPartitions[i]; - - if (partition->borders) - { - if (builder->hasPointer(partition->borders)) - { - destPartition->borders = builder->getPointer(partition->borders); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(partition->borders); - buffer->save(partition->borders); - Utils::Stream::ClearPointer(&destPartition->borders); - } - } - } - - Utils::Stream::ClearPointer(&dest->collisionPartitions); - SaveLogExit(); - } - - if (asset->collisionAABBTrees) - { - AssertSize(Game::CollisionAabbTree, 32); - SaveLogEnter("CollisionAabbTree"); - - buffer->align(Utils::Stream::ALIGN_16); - buffer->saveArray(asset->collisionAABBTrees, asset->numCollisionAABBTrees); - Utils::Stream::ClearPointer(&dest->collisionAABBTrees); - - SaveLogExit(); - } - - if (asset->cModels) - { - AssertSize(Game::cmodel_t, 68); - SaveLogEnter("cmodel_t"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->cModels, asset->numCModels); - Utils::Stream::ClearPointer(&dest->cModels); - - SaveLogExit(); - } - - if (asset->cBrushes) - { - AssertSize(Game::cbrush_t, 36); - SaveLogEnter("cbrush_t"); - - buffer->align(Utils::Stream::ALIGN_128); - Game::cbrush_t* destBrushes = buffer->dest(); - buffer->saveArray(asset->cBrushes, asset->numCBrushes); - - for (short i = 0; i < asset->numCBrushes; ++i) - { - Game::cbrush_t* destBrush = &destBrushes[i]; - Game::cbrush_t* brush = &asset->cBrushes[i]; - - if (brush->sides) - { - if (builder->hasPointer(brush->sides)) - { - destBrush->sides = builder->getPointer(brush->sides); - } - else - { - AssertSize(Game::cbrushside_t, 8); - - MessageBoxA(0, "BrushSide shouldn't be written in cBrush!", "WARNING", MB_ICONEXCLAMATION); - - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(brush->sides); - - Game::cbrushside_t* side = buffer->dest(); - buffer->save(brush->sides); - - if (brush->sides->plane) - { - if (builder->hasPointer(brush->sides->plane)) - { - side->plane = builder->getPointer(brush->sides->plane); - } - else - { - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(brush->sides->plane); - buffer->save(brush->sides->plane); - Utils::Stream::ClearPointer(&side->plane); - } - } - - Utils::Stream::ClearPointer(&destBrush->sides); - } - } - - if (brush->baseAdjacentSide) - { - if (builder->hasPointer(brush->baseAdjacentSide)) - { - destBrush->baseAdjacentSide = builder->getPointer(brush->baseAdjacentSide); - } - else - { - builder->storePointer(brush->baseAdjacentSide); - buffer->save(brush->baseAdjacentSide); - Utils::Stream::ClearPointer(&destBrush->baseAdjacentSide); - } - } - } - - Utils::Stream::ClearPointer(&dest->cBrushes); - SaveLogExit(); - } - - if (asset->cBrushBounds) - { - AssertSize(Game::Bounds, 24); - SaveLogEnter("Bounds"); - - buffer->align(Utils::Stream::ALIGN_128); - buffer->saveArray(asset->cBrushBounds, asset->numCBrushes); - Utils::Stream::ClearPointer(&dest->cBrushBounds); - - SaveLogExit(); - } - - if (asset->cBrushContents) - { - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->cBrushContents, asset->numCBrushes); - Utils::Stream::ClearPointer(&dest->cBrushContents); - } - - if (asset->smodelNodes) - { - AssertSize(Game::SModelAabbNode, 28); - SaveLogEnter("SModelAabbNode"); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->smodelNodes, asset->smodelNodeCount); - Utils::Stream::ClearPointer(&dest->smodelNodes); - - SaveLogExit(); - } - - if (asset->mapEnts) - { - dest->mapEnts = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, asset->mapEnts).mapEnts; - } - - for (int i = 0; i < 2; ++i) - { - if (asset->dynEntDefList[i]) - { - AssertSize(Game::DynEntityDef, 92); - - buffer->align(Utils::Stream::ALIGN_4); - Game::DynEntityDef* dynEntDest = buffer->dest(); - buffer->saveArray(asset->dynEntDefList[i], asset->dynEntCount[i]); - - Game::DynEntityDef* dynEnt = asset->dynEntDefList[i]; - for (int j = 0; j < asset->dynEntCount[i]; ++j) - { - if (dynEnt[j].xModel) - { - dynEntDest[j].xModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, dynEnt[j].xModel).model; - } - - if (dynEnt[j].destroyFx) - { - dynEntDest[j].destroyFx = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, dynEnt[j].destroyFx).fx; - } - - if (dynEnt[j].physPreset) - { - dynEntDest[j].physPreset = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, dynEnt[j].physPreset).physPreset; - } - } - - Utils::Stream::ClearPointer(&dest->dynEntDefList[i]); - } - } - - buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); - - for (int i = 0; i < 2; ++i) - { - if (asset->dynEntPoseList[i]) - { - AssertSize(Game::DynEntityPose, 32); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->dynEntPoseList[i], asset->dynEntCount[i]); - Utils::Stream::ClearPointer(&dest->dynEntPoseList[i]); - - } - } - - for (int i = 0; i < 2; ++i) - { - if (asset->dynEntClientList[i]) - { - AssertSize(Game::DynEntityClient, 12); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->dynEntClientList[i], asset->dynEntCount[i]); - Utils::Stream::ClearPointer(&dest->dynEntClientList[i]); - } - } - - for (int i = 0; i < 2; ++i) - { - if (asset->dynEntCollList[i]) - { - AssertSize(Game::DynEntityColl, 20); - - buffer->align(Utils::Stream::ALIGN_4); - buffer->saveArray(asset->dynEntCollList[i], asset->dynEntCount[i]); - Utils::Stream::ClearPointer(&dest->dynEntCollList[i]); - } - } - - buffer->popBlock(); - buffer->popBlock(); - - SaveLogExit(); - } - - void IclipMap_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - Game::clipMap_t* asset = header.clipMap; - for (int i = 0; i < asset->numStaticModels; ++i) - { - Game::XModel* m = asset->staticModelList[i].xmodel; - if (m) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, m); - } - } - - for (int j = 0; j < 2; ++j) - { - Game::DynEntityDef* def = asset->dynEntDefList[j]; - - for (int i = 0; i < asset->dynEntCount[j]; ++i) - { - if (def[i].xModel) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def[i].xModel); - } - - if (def[i].destroyFx) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, def[i].destroyFx); - } - - if (def[i].physPreset) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, def[i].physPreset); - } - } - } - builder->loadAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, asset); - } - - void IclipMap_t::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) - { - Utils::String::Replace(name, "maps/mp/", ""); - Utils::String::Replace(name, ".d3dbsp", ""); - - Components::FileSystem::File clipFile(Utils::String::VA("clipmap/%s.iw4xClipMap", name.data())); - if (!clipFile.exists()) - { - return; - } - - Game::clipMap_t* clipMap = builder->getAllocator()->allocate(); - if (!clipMap) - { - Components::Logger::Print("Error allocating memory for clipMap_t structure!\n"); - return; - } - - Utils::Stream::Reader reader(builder->getAllocator(), clipFile.getBuffer()); - - if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xClip")) - { - Components::Logger::Error(0, "Reading clipMap_t '%s' failed, header is invalid!", name.data()); - } - - int version = reader.read(); - if (version != IW4X_CLIPMAP_VERSION) - { - Components::Logger::Error(0, "Reading clipmap '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_CLIPMAP_VERSION, version); - } - - clipMap->name = reader.readCString(); - +#include + +#define IW4X_CLIPMAP_VERSION 1 + +#ifdef ENABLE_EXPERIMENTAL_MAP_CODE + +namespace Assets +{ + void IclipMap_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::clipMap_t, 256); + SaveLogEnter("clipMap_t"); + + Utils::Stream* buffer = builder->getBuffer(); + Game::clipMap_t* asset = header.clipMap; + Game::clipMap_t* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->cPlanes) + { + AssertSize(Game::cplane_t, 20); + SaveLogEnter("cplane_t"); + + if (builder->hasPointer(asset->cPlanes)) + { + dest->cPlanes = builder->getPointer(asset->cPlanes); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + + // not sure if this is neede but both brushside and brushedge need it and it can't hurt + for (int i = 0; i < asset->numCPlanes; i++) + { + builder->storePointer(&asset->cPlanes[i]); + buffer->save(&asset->cPlanes[i]); + } + Utils::Stream::ClearPointer(&dest->cPlanes); + } + + SaveLogExit(); + } + + if (asset->staticModelList) + { + + AssertSize(Game::cStaticModel_t, 76); + SaveLogEnter("cStaticModel_t"); + + // xmodel is already stored + buffer->align(Utils::Stream::ALIGN_4); + Game::cStaticModel_t* destStaticModelList = buffer->dest(); + buffer->saveArray(asset->staticModelList, asset->numStaticModels); + + for (int i = 0; i < asset->numStaticModels; ++i) + { + if (asset->staticModelList[i].xmodel) + { + destStaticModelList[i].xmodel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, asset->staticModelList[i].xmodel).model; + } + } + + Utils::Stream::ClearPointer(&dest->staticModelList); + SaveLogExit(); + } + + if (asset->materials) + { + AssertSize(Game::ClipMaterial, 12); + SaveLogEnter("ClipMaterial"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::ClipMaterial* mats = buffer->dest(); + buffer->saveArray(asset->materials, asset->numMaterials); + + for (int i = 0; i < asset->numMaterials; ++i) + { + buffer->saveString(asset->materials[i].name); + Utils::Stream::ClearPointer(&mats[i].name); + } + + Utils::Stream::ClearPointer(&dest->materials); + SaveLogExit(); + } + + if (asset->cBrushSides) + { + AssertSize(Game::cbrushside_t, 8); + SaveLogEnter("cbrushside_t"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::cbrushside_t* sides = buffer->dest(); + // we need the pointer to each of these to be stored so we can't write them all at once + for (int i = 0; i < asset->numCBrushSides; ++i) + { + builder->storePointer(&asset->cBrushSides[i]); // for reference in cBrush + buffer->save(&asset->cBrushSides[i]); + } + + for (int i = 0; i < asset->numCBrushSides; ++i) + { + if (sides[i].plane) + { + AssertSize(Game::cplane_t, 20); + + if (builder->hasPointer(sides[i].plane)) + { + sides[i].plane = builder->getPointer(sides[i].plane); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(sides[i].plane); + + buffer->save(sides[i].plane); + Utils::Stream::ClearPointer(&sides[i].plane); + } + } + } + + Utils::Stream::ClearPointer(&dest->cBrushSides); + SaveLogExit(); + } + + if (asset->cBrushEdges) + { + SaveLogEnter("cBrushEdge"); + + // no align for char + for (int i = 0; i < asset->numCBrushEdges; ++i) + { + builder->storePointer(&asset->cBrushEdges[i]); // for reference in cBrush + buffer->save(&asset->cBrushEdges[i]); + } + Utils::Stream::ClearPointer(&dest->cBrushEdges); + + SaveLogExit(); + } + + if (asset->cNodes) + { + AssertSize(Game::cNode_t, 8); + SaveLogEnter("cNode_t"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::cNode_t* nodes = buffer->dest(); + buffer->saveArray(asset->cNodes, asset->numCNodes); + + for (int i = 0; i < asset->numCNodes; ++i) + { + if (nodes[i].plane) + { + if (builder->hasPointer(nodes[i].plane)) + { + nodes[i].plane = builder->getPointer(nodes[i].plane); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(nodes[i].plane); + + buffer->save(nodes[i].plane); + Utils::Stream::ClearPointer(&nodes[i].plane); + } + } + } + + Utils::Stream::ClearPointer(&dest->cNodes); + SaveLogExit(); + } + + if (asset->cLeaf) + { + AssertSize(Game::cLeaf_t, 40); + SaveLogEnter("cLeaf_t"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->cLeaf, asset->numCLeaf); + Utils::Stream::ClearPointer(&dest->cLeaf); + SaveLogExit(); + } + + if (asset->leafBrushes) + { + SaveLogEnter("cLeafBrush_t"); + + buffer->align(Utils::Stream::ALIGN_2); + buffer->saveArray(asset->leafBrushes, asset->numLeafBrushes); + Utils::Stream::ClearPointer(&dest->leafBrushes); + + SaveLogExit(); + } + + if (asset->cLeafBrushNodes) + { + AssertSize(Game::cLeafBrushNode_t, 20); + SaveLogEnter("cLeafBrushNode_t"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::cLeafBrushNode_t* node = buffer->dest(); + buffer->saveArray(asset->cLeafBrushNodes, asset->numCLeafBrushNodes); + + for (int i = 0; i < asset->numCLeafBrushNodes; ++i) + { + if (node[i].leafBrushCount > 0) + { + if (node[i].data.brushes) + { + if (builder->hasPointer(node[i].data.brushes)) + { + node[i].data.brushes = builder->getPointer(node[i].data.brushes); + } + else + { + buffer->align(Utils::Stream::ALIGN_2); + + for (short j = 0; j < node[i].leafBrushCount; ++j) + { + builder->storePointer(&node[i].data.brushes[j]); + buffer->save(&node[i].data.brushes[j]); + } + + Utils::Stream::ClearPointer(&node[i].data.brushes); + } + } + } + } + + Utils::Stream::ClearPointer(&dest->cLeafBrushNodes); + SaveLogExit(); + } + + if (asset->leafSurfaces) + { + SaveLogEnter("cLeafSurface_t"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->leafSurfaces, asset->numLeafSurfaces); + Utils::Stream::ClearPointer(&dest->leafSurfaces); + + SaveLogExit(); + } + + if (asset->verts) + { + AssertSize(Game::vec3_t, 12); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->verts, asset->numVerts); + Utils::Stream::ClearPointer(&dest->verts); + } + + if (asset->triIndices) + { + buffer->align(Utils::Stream::ALIGN_2); + buffer->save(asset->triIndices, 6, asset->numTriIndices); + Utils::Stream::ClearPointer(&dest->triIndices); + } + + if (asset->triEdgeIsWalkable) + { + // no align for char + buffer->save(asset->triEdgeIsWalkable, 1, 4 * ((3 * asset->numTriIndices + 31) >> 5)); + Utils::Stream::ClearPointer(&dest->triEdgeIsWalkable); + } + + if (asset->collisionBorders) + { + AssertSize(Game::CollisionBorder, 28); + SaveLogEnter("CollisionBorder"); + + buffer->align(Utils::Stream::ALIGN_4); + + for (int i = 0; i < asset->numCollisionBorders; ++i) + { + builder->storePointer(&asset->collisionBorders[i]); + buffer->save(&asset->collisionBorders[i]); + } + + Utils::Stream::ClearPointer(&dest->collisionBorders); + SaveLogExit(); + } + + if (asset->collisionPartitions) + { + AssertSize(Game::CollisionPartition, 12); + SaveLogEnter("CollisionPartition"); + + buffer->align(Utils::Stream::ALIGN_4); + Game::CollisionPartition* destPartitions = buffer->dest(); + buffer->saveArray(asset->collisionPartitions, asset->numCollisionPartitions); + + for (int i = 0; i < asset->numCollisionPartitions; ++i) + { + Game::CollisionPartition* destPartition = &destPartitions[i]; + Game::CollisionPartition* partition = &asset->collisionPartitions[i]; + + if (partition->borders) + { + if (builder->hasPointer(partition->borders)) + { + destPartition->borders = builder->getPointer(partition->borders); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(partition->borders); + buffer->save(partition->borders); + Utils::Stream::ClearPointer(&destPartition->borders); + } + } + } + + Utils::Stream::ClearPointer(&dest->collisionPartitions); + SaveLogExit(); + } + + if (asset->collisionAABBTrees) + { + AssertSize(Game::CollisionAabbTree, 32); + SaveLogEnter("CollisionAabbTree"); + + buffer->align(Utils::Stream::ALIGN_16); + buffer->saveArray(asset->collisionAABBTrees, asset->numCollisionAABBTrees); + Utils::Stream::ClearPointer(&dest->collisionAABBTrees); + + SaveLogExit(); + } + + if (asset->cModels) + { + AssertSize(Game::cmodel_t, 68); + SaveLogEnter("cmodel_t"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->cModels, asset->numCModels); + Utils::Stream::ClearPointer(&dest->cModels); + + SaveLogExit(); + } + + if (asset->cBrushes) + { + AssertSize(Game::cbrush_t, 36); + SaveLogEnter("cbrush_t"); + + buffer->align(Utils::Stream::ALIGN_128); + Game::cbrush_t* destBrushes = buffer->dest(); + buffer->saveArray(asset->cBrushes, asset->numCBrushes); + + for (short i = 0; i < asset->numCBrushes; ++i) + { + Game::cbrush_t* destBrush = &destBrushes[i]; + Game::cbrush_t* brush = &asset->cBrushes[i]; + + if (brush->sides) + { + if (builder->hasPointer(brush->sides)) + { + destBrush->sides = builder->getPointer(brush->sides); + } + else + { + AssertSize(Game::cbrushside_t, 8); + + MessageBoxA(0, "BrushSide shouldn't be written in cBrush!", "WARNING", MB_ICONEXCLAMATION); + + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(brush->sides); + + Game::cbrushside_t* side = buffer->dest(); + buffer->save(brush->sides); + + if (brush->sides->plane) + { + if (builder->hasPointer(brush->sides->plane)) + { + side->plane = builder->getPointer(brush->sides->plane); + } + else + { + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(brush->sides->plane); + buffer->save(brush->sides->plane); + Utils::Stream::ClearPointer(&side->plane); + } + } + + Utils::Stream::ClearPointer(&destBrush->sides); + } + } + + if (brush->baseAdjacentSide) + { + if (builder->hasPointer(brush->baseAdjacentSide)) + { + destBrush->baseAdjacentSide = builder->getPointer(brush->baseAdjacentSide); + } + else + { + builder->storePointer(brush->baseAdjacentSide); + buffer->save(brush->baseAdjacentSide); + Utils::Stream::ClearPointer(&destBrush->baseAdjacentSide); + } + } + } + + Utils::Stream::ClearPointer(&dest->cBrushes); + SaveLogExit(); + } + + if (asset->cBrushBounds) + { + AssertSize(Game::Bounds, 24); + SaveLogEnter("Bounds"); + + buffer->align(Utils::Stream::ALIGN_128); + buffer->saveArray(asset->cBrushBounds, asset->numCBrushes); + Utils::Stream::ClearPointer(&dest->cBrushBounds); + + SaveLogExit(); + } + + if (asset->cBrushContents) + { + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->cBrushContents, asset->numCBrushes); + Utils::Stream::ClearPointer(&dest->cBrushContents); + } + + if (asset->smodelNodes) + { + AssertSize(Game::SModelAabbNode, 28); + SaveLogEnter("SModelAabbNode"); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->smodelNodes, asset->smodelNodeCount); + Utils::Stream::ClearPointer(&dest->smodelNodes); + + SaveLogExit(); + } + + if (asset->mapEnts) + { + dest->mapEnts = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, asset->mapEnts).mapEnts; + } + + for (int i = 0; i < 2; ++i) + { + if (asset->dynEntDefList[i]) + { + AssertSize(Game::DynEntityDef, 92); + + buffer->align(Utils::Stream::ALIGN_4); + Game::DynEntityDef* dynEntDest = buffer->dest(); + buffer->saveArray(asset->dynEntDefList[i], asset->dynEntCount[i]); + + Game::DynEntityDef* dynEnt = asset->dynEntDefList[i]; + for (int j = 0; j < asset->dynEntCount[i]; ++j) + { + if (dynEnt[j].xModel) + { + dynEntDest[j].xModel = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_XMODEL, dynEnt[j].xModel).model; + } + + if (dynEnt[j].destroyFx) + { + dynEntDest[j].destroyFx = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_FX, dynEnt[j].destroyFx).fx; + } + + if (dynEnt[j].physPreset) + { + dynEntDest[j].physPreset = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, dynEnt[j].physPreset).physPreset; + } + } + + Utils::Stream::ClearPointer(&dest->dynEntDefList[i]); + } + } + + buffer->pushBlock(Game::XFILE_BLOCK_RUNTIME); + + for (int i = 0; i < 2; ++i) + { + if (asset->dynEntPoseList[i]) + { + AssertSize(Game::DynEntityPose, 32); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->dynEntPoseList[i], asset->dynEntCount[i]); + Utils::Stream::ClearPointer(&dest->dynEntPoseList[i]); + + } + } + + for (int i = 0; i < 2; ++i) + { + if (asset->dynEntClientList[i]) + { + AssertSize(Game::DynEntityClient, 12); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->dynEntClientList[i], asset->dynEntCount[i]); + Utils::Stream::ClearPointer(&dest->dynEntClientList[i]); + } + } + + for (int i = 0; i < 2; ++i) + { + if (asset->dynEntCollList[i]) + { + AssertSize(Game::DynEntityColl, 20); + + buffer->align(Utils::Stream::ALIGN_4); + buffer->saveArray(asset->dynEntCollList[i], asset->dynEntCount[i]); + Utils::Stream::ClearPointer(&dest->dynEntCollList[i]); + } + } + + buffer->popBlock(); + buffer->popBlock(); + + SaveLogExit(); + } + + void IclipMap_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + Game::clipMap_t* asset = header.clipMap; + for (int i = 0; i < asset->numStaticModels; ++i) + { + Game::XModel* m = asset->staticModelList[i].xmodel; + if (m) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, m); + } + } + + for (int j = 0; j < 2; ++j) + { + Game::DynEntityDef* def = asset->dynEntDefList[j]; + + for (int i = 0; i < asset->dynEntCount[j]; ++i) + { + if (def[i].xModel) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_XMODEL, def[i].xModel); + } + + if (def[i].destroyFx) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_FX, def[i].destroyFx); + } + + if (def[i].physPreset) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_PHYSPRESET, def[i].physPreset); + } + } + } + builder->loadAsset(Game::XAssetType::ASSET_TYPE_MAP_ENTS, asset); + } + + void IclipMap_t::load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Utils::String::Replace(name, "maps/mp/", ""); + Utils::String::Replace(name, ".d3dbsp", ""); + + Components::FileSystem::File clipFile(Utils::String::VA("clipmap/%s.iw4xClipMap", name.data())); + if (!clipFile.exists()) + { + return; + } + + Game::clipMap_t* clipMap = builder->getAllocator()->allocate(); + if (!clipMap) + { + Components::Logger::Print("Error allocating memory for clipMap_t structure!\n"); + return; + } + + Utils::Stream::Reader reader(builder->getAllocator(), clipFile.getBuffer()); + + if (reader.read<__int64>() != *reinterpret_cast<__int64*>("IW4xClip")) + { + Components::Logger::Error(0, "Reading clipMap_t '%s' failed, header is invalid!", name.data()); + } + + int version = reader.read(); + if (version != IW4X_CLIPMAP_VERSION) + { + Components::Logger::Error(0, "Reading clipmap '%s' failed, expected version is %d, but it was %d!", name.data(), IW4X_CLIPMAP_VERSION, version); + } + + clipMap->name = reader.readCString(); + clipMap->numCPlanes = reader.read(); clipMap->numStaticModels = reader.read(); clipMap->numMaterials = reader.read(); @@ -619,268 +619,268 @@ namespace Assets clipMap->numCModels = reader.read(); clipMap->numCBrushes = reader.read(); clipMap->dynEntCount[0] = reader.read(); - clipMap->dynEntCount[1] = reader.read(); - - if (clipMap->numCPlanes) - { - clipMap->cPlanes = reader.readArray(clipMap->numCPlanes); - } - - if (clipMap->numStaticModels) - { - clipMap->staticModelList = builder->getAllocator()->allocateArray(clipMap->numStaticModels); - for (int i = 0; i < clipMap->numStaticModels; ++i) - { - std::string modelName = reader.readString(); - if (modelName != "NONE"s) - { - clipMap->staticModelList[i].xmodel = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, modelName, builder).model; - } - float* buf = reader.readArray(18); - memcpy(&clipMap->staticModelList[i].origin, buf, sizeof(float) * 18); - } - } - - if (clipMap->numMaterials) - { - clipMap->materials = builder->getAllocator()->allocateArray(clipMap->numMaterials); - for (int j = 0; j < clipMap->numMaterials; ++j) - { - clipMap->materials[j].name = reader.readArray(64); - clipMap->materials[j].unk = reader.read(); - clipMap->materials[j].unk2 = reader.read(); - } - } - - if (clipMap->numCBrushSides) - { - clipMap->cBrushSides = builder->getAllocator()->allocateArray(clipMap->numCBrushSides); - for (int i = 0; i < clipMap->numCBrushSides; ++i) - { - int planeIndex = reader.read(); - if (planeIndex < 0 || planeIndex > clipMap->numCBrushSides) - { - Components::Logger::Error("invalid plane index"); - return; - } - clipMap->cBrushSides[i].plane = &clipMap->cPlanes[planeIndex]; - clipMap->cBrushSides[i].materialNum = static_cast(reader.read()); // materialNum - clipMap->cBrushSides[i].firstAdjacentSideOffset = static_cast(reader.read()); // firstAdjacentSide - clipMap->cBrushSides[i].edgeCount = reader.read(); // edgeCount - } - } - - if (clipMap->numCBrushEdges) - { - clipMap->cBrushEdges = reader.readArray(clipMap->numCBrushEdges); - } - - if (clipMap->numCNodes) - { - clipMap->cNodes = builder->getAllocator()->allocateArray(clipMap->numCNodes); - for (int i = 0; i < clipMap->numCNodes; ++i) - { - int planeIndex = reader.read(); - if (planeIndex < 0 || planeIndex > clipMap->numCPlanes) - { - Components::Logger::Error("invalid plane index\n"); - return; - } - clipMap->cNodes[i].plane = &clipMap->cPlanes[planeIndex]; - clipMap->cNodes[i].children[0] = reader.read(); - clipMap->cNodes[i].children[1] = reader.read(); - } - } - - if (clipMap->numCLeaf) - { - clipMap->cLeaf = reader.readArray(clipMap->numCLeaf); - } - - if (clipMap->numCLeafBrushNodes) - { - clipMap->cLeafBrushNodes = builder->getAllocator()->allocateArray(clipMap->numCLeafBrushNodes); - for (int i = 0; i < clipMap->numCLeafBrushNodes; ++i) - { - clipMap->cLeafBrushNodes[i] = reader.read(); - - if (clipMap->cLeafBrushNodes[i].leafBrushCount > 0) - { - clipMap->cLeafBrushNodes[i].data.brushes = reader.readArray(clipMap->cLeafBrushNodes[i].leafBrushCount); - } - } - } - - if (clipMap->numLeafBrushes) - { - clipMap->leafBrushes = reader.readArray(clipMap->numLeafBrushes); - } - - if (clipMap->numLeafSurfaces) - { - clipMap->leafSurfaces = reader.readArray(clipMap->numLeafSurfaces); - } - - if (clipMap->numVerts) - { - clipMap->verts = reader.readArray(clipMap->numVerts); - } - - if (clipMap->numTriIndices) - { - clipMap->triIndices = reader.readArray(clipMap->numTriIndices * 3); - clipMap->triEdgeIsWalkable = reader.readArray(4 * ((3 * clipMap->numTriIndices + 31) >> 5)); - } - - if (clipMap->numCollisionBorders) - { - clipMap->collisionBorders = reader.readArray(clipMap->numCollisionBorders); - } - - if (clipMap->numCollisionPartitions) - { - clipMap->collisionPartitions = builder->getAllocator()->allocateArray(clipMap->numCollisionPartitions); - for (int i = 0; i < clipMap->numCollisionPartitions; ++i) - { - clipMap->collisionPartitions[i].triCount = reader.read(); - clipMap->collisionPartitions[i].borderCount = reader.read(); - clipMap->collisionPartitions[i].firstTri = reader.read(); - - if (clipMap->collisionPartitions[i].borderCount > 0) - { - int index = reader.read(); - if (index < 0 || index > clipMap->numCollisionBorders) - { - Components::Logger::Error("invalid border index\n"); - return; - } - clipMap->collisionPartitions[i].borders = &clipMap->collisionBorders[index]; - } - } - } - - if (clipMap->numCollisionAABBTrees) - { - clipMap->collisionAABBTrees = reader.readArray(clipMap->numCollisionAABBTrees); - } - - if (clipMap->numCModels) - { - clipMap->cModels = reader.readArray(clipMap->numCModels); - } - - if (clipMap->numCBrushes) - { - clipMap->cBrushes = builder->getAllocator()->allocateArray(clipMap->numCBrushes); - memset(clipMap->cBrushes, 0, sizeof(Game::cbrush_t) * clipMap->numCBrushes); - for (int i = 0; i < clipMap->numCBrushes; ++i) - { - clipMap->cBrushes[i].numsides = reader.read() & 0xFFFF; // todo: check for overflow here - if (clipMap->cBrushes[i].numsides > 0) - { - int index = reader.read(); - if (index < 0 || index > clipMap->numCBrushSides) - { - Components::Logger::Error("invalid side index\n"); - return; - } - clipMap->cBrushes[i].sides = &clipMap->cBrushSides[index]; - } - else - { - clipMap->cBrushes[i].sides = nullptr; - } - - int index = reader.read(); - if (index > clipMap->numCBrushEdges) - { - Components::Logger::Error("invalid edge index\n"); - return; - } - clipMap->cBrushes[i].baseAdjacentSide = &clipMap->cBrushEdges[index]; - - char* tmp = reader.readArray(12); - memcpy(&clipMap->cBrushes[i].axialMaterialNum, tmp, 12); - - //todo check for overflow - for (int r = 0; r < 2; ++r) - { - for (int c = 0; c < 3; ++c) - { - clipMap->cBrushes[i].firstAdjacentSideOffsets[r][c] = reader.read() & 0xFF; - } - } - - tmp = reader.readArray(6); - memcpy(&clipMap->cBrushes[i].edgeCount, tmp, 6); - } - - clipMap->cBrushBounds = reader.readArray(clipMap->numCBrushes); - clipMap->cBrushContents = reader.readArray(clipMap->numCBrushes); - } - - for (int x = 0; x < 2; ++x) - { - if (clipMap->dynEntCount[x]) - { - clipMap->dynEntDefList[x] = builder->getAllocator()->allocateArray(clipMap->dynEntCount[x]); - for (int i = 0; i < clipMap->dynEntCount[x]; ++i) - { - clipMap->dynEntDefList[x][i].type = reader.read(); - clipMap->dynEntDefList[x][i].pose = reader.read(); - std::string tempName = reader.readString(); - if (tempName != "NONE"s) - { - clipMap->dynEntDefList[x][i].xModel = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, tempName, builder).model; - } - - clipMap->dynEntDefList[x][i].brushModel = reader.read(); - clipMap->dynEntDefList[x][i].physicsBrushModel = reader.read(); - - tempName = reader.readString(); - if (tempName != "NONE"s) - { - clipMap->dynEntDefList[x][i].destroyFx = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, tempName, builder).fx; - } - - tempName = reader.readString(); - if (tempName != "NONE"s) - { - clipMap->dynEntDefList[x][i].physPreset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PHYSPRESET, tempName, builder).physPreset; - } - - clipMap->dynEntDefList[x][i].health = reader.read(); - clipMap->dynEntDefList[x][i].mass = reader.read(); - clipMap->dynEntDefList[x][i].contents = reader.read(); - } - } - } - - clipMap->checksum = reader.read(); - - clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.c_str()), builder).mapEnts; - - // This mustn't be null and has to have at least 1 'valid' entry. - if (!clipMap->smodelNodeCount || !clipMap->smodelNodes) - { - clipMap->smodelNodeCount = 1; - clipMap->smodelNodes = builder->getAllocator()->allocateArray(clipMap->smodelNodeCount); - - clipMap->smodelNodes[0].bounds.halfSize[0] = -131072.000f; - clipMap->smodelNodes[0].bounds.halfSize[1] = -131072.000f; - clipMap->smodelNodes[0].bounds.halfSize[2] = -131072.000f; - } - - // These mustn't be null, but they don't need to be valid. - for (int i = 0; i < 2 && clipMap->dynEntCount[i]; ++i) - { - Utils::Stream::ClearPointer(&clipMap->dynEntPoseList[i]); - Utils::Stream::ClearPointer(&clipMap->dynEntClientList[i]); - Utils::Stream::ClearPointer(&clipMap->dynEntCollList[i]); - } - - header->clipMap = clipMap; - } -} - + clipMap->dynEntCount[1] = reader.read(); + + if (clipMap->numCPlanes) + { + clipMap->cPlanes = reader.readArray(clipMap->numCPlanes); + } + + if (clipMap->numStaticModels) + { + clipMap->staticModelList = builder->getAllocator()->allocateArray(clipMap->numStaticModels); + for (int i = 0; i < clipMap->numStaticModels; ++i) + { + std::string modelName = reader.readString(); + if (modelName != "NONE"s) + { + clipMap->staticModelList[i].xmodel = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, modelName, builder).model; + } + float* buf = reader.readArray(18); + memcpy(&clipMap->staticModelList[i].origin, buf, sizeof(float) * 18); + } + } + + if (clipMap->numMaterials) + { + clipMap->materials = builder->getAllocator()->allocateArray(clipMap->numMaterials); + for (int j = 0; j < clipMap->numMaterials; ++j) + { + clipMap->materials[j].name = reader.readArray(64); + clipMap->materials[j].unk = reader.read(); + clipMap->materials[j].unk2 = reader.read(); + } + } + + if (clipMap->numCBrushSides) + { + clipMap->cBrushSides = builder->getAllocator()->allocateArray(clipMap->numCBrushSides); + for (int i = 0; i < clipMap->numCBrushSides; ++i) + { + int planeIndex = reader.read(); + if (planeIndex < 0 || planeIndex > clipMap->numCBrushSides) + { + Components::Logger::Error("invalid plane index"); + return; + } + clipMap->cBrushSides[i].plane = &clipMap->cPlanes[planeIndex]; + clipMap->cBrushSides[i].materialNum = static_cast(reader.read()); // materialNum + clipMap->cBrushSides[i].firstAdjacentSideOffset = static_cast(reader.read()); // firstAdjacentSide + clipMap->cBrushSides[i].edgeCount = reader.read(); // edgeCount + } + } + + if (clipMap->numCBrushEdges) + { + clipMap->cBrushEdges = reader.readArray(clipMap->numCBrushEdges); + } + + if (clipMap->numCNodes) + { + clipMap->cNodes = builder->getAllocator()->allocateArray(clipMap->numCNodes); + for (int i = 0; i < clipMap->numCNodes; ++i) + { + int planeIndex = reader.read(); + if (planeIndex < 0 || planeIndex > clipMap->numCPlanes) + { + Components::Logger::Error("invalid plane index\n"); + return; + } + clipMap->cNodes[i].plane = &clipMap->cPlanes[planeIndex]; + clipMap->cNodes[i].children[0] = reader.read(); + clipMap->cNodes[i].children[1] = reader.read(); + } + } + + if (clipMap->numCLeaf) + { + clipMap->cLeaf = reader.readArray(clipMap->numCLeaf); + } + + if (clipMap->numCLeafBrushNodes) + { + clipMap->cLeafBrushNodes = builder->getAllocator()->allocateArray(clipMap->numCLeafBrushNodes); + for (int i = 0; i < clipMap->numCLeafBrushNodes; ++i) + { + clipMap->cLeafBrushNodes[i] = reader.read(); + + if (clipMap->cLeafBrushNodes[i].leafBrushCount > 0) + { + clipMap->cLeafBrushNodes[i].data.brushes = reader.readArray(clipMap->cLeafBrushNodes[i].leafBrushCount); + } + } + } + + if (clipMap->numLeafBrushes) + { + clipMap->leafBrushes = reader.readArray(clipMap->numLeafBrushes); + } + + if (clipMap->numLeafSurfaces) + { + clipMap->leafSurfaces = reader.readArray(clipMap->numLeafSurfaces); + } + + if (clipMap->numVerts) + { + clipMap->verts = reader.readArray(clipMap->numVerts); + } + + if (clipMap->numTriIndices) + { + clipMap->triIndices = reader.readArray(clipMap->numTriIndices * 3); + clipMap->triEdgeIsWalkable = reader.readArray(4 * ((3 * clipMap->numTriIndices + 31) >> 5)); + } + + if (clipMap->numCollisionBorders) + { + clipMap->collisionBorders = reader.readArray(clipMap->numCollisionBorders); + } + + if (clipMap->numCollisionPartitions) + { + clipMap->collisionPartitions = builder->getAllocator()->allocateArray(clipMap->numCollisionPartitions); + for (int i = 0; i < clipMap->numCollisionPartitions; ++i) + { + clipMap->collisionPartitions[i].triCount = reader.read(); + clipMap->collisionPartitions[i].borderCount = reader.read(); + clipMap->collisionPartitions[i].firstTri = reader.read(); + + if (clipMap->collisionPartitions[i].borderCount > 0) + { + int index = reader.read(); + if (index < 0 || index > clipMap->numCollisionBorders) + { + Components::Logger::Error("invalid border index\n"); + return; + } + clipMap->collisionPartitions[i].borders = &clipMap->collisionBorders[index]; + } + } + } + + if (clipMap->numCollisionAABBTrees) + { + clipMap->collisionAABBTrees = reader.readArray(clipMap->numCollisionAABBTrees); + } + + if (clipMap->numCModels) + { + clipMap->cModels = reader.readArray(clipMap->numCModels); + } + + if (clipMap->numCBrushes) + { + clipMap->cBrushes = builder->getAllocator()->allocateArray(clipMap->numCBrushes); + memset(clipMap->cBrushes, 0, sizeof(Game::cbrush_t) * clipMap->numCBrushes); + for (int i = 0; i < clipMap->numCBrushes; ++i) + { + clipMap->cBrushes[i].numsides = reader.read() & 0xFFFF; // todo: check for overflow here + if (clipMap->cBrushes[i].numsides > 0) + { + int index = reader.read(); + if (index < 0 || index > clipMap->numCBrushSides) + { + Components::Logger::Error("invalid side index\n"); + return; + } + clipMap->cBrushes[i].sides = &clipMap->cBrushSides[index]; + } + else + { + clipMap->cBrushes[i].sides = nullptr; + } + + int index = reader.read(); + if (index > clipMap->numCBrushEdges) + { + Components::Logger::Error("invalid edge index\n"); + return; + } + clipMap->cBrushes[i].baseAdjacentSide = &clipMap->cBrushEdges[index]; + + char* tmp = reader.readArray(12); + memcpy(&clipMap->cBrushes[i].axialMaterialNum, tmp, 12); + + //todo check for overflow + for (int r = 0; r < 2; ++r) + { + for (int c = 0; c < 3; ++c) + { + clipMap->cBrushes[i].firstAdjacentSideOffsets[r][c] = reader.read() & 0xFF; + } + } + + tmp = reader.readArray(6); + memcpy(&clipMap->cBrushes[i].edgeCount, tmp, 6); + } + + clipMap->cBrushBounds = reader.readArray(clipMap->numCBrushes); + clipMap->cBrushContents = reader.readArray(clipMap->numCBrushes); + } + + for (int x = 0; x < 2; ++x) + { + if (clipMap->dynEntCount[x]) + { + clipMap->dynEntDefList[x] = builder->getAllocator()->allocateArray(clipMap->dynEntCount[x]); + for (int i = 0; i < clipMap->dynEntCount[x]; ++i) + { + clipMap->dynEntDefList[x][i].type = reader.read(); + clipMap->dynEntDefList[x][i].pose = reader.read(); + std::string tempName = reader.readString(); + if (tempName != "NONE"s) + { + clipMap->dynEntDefList[x][i].xModel = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_XMODEL, tempName, builder).model; + } + + clipMap->dynEntDefList[x][i].brushModel = reader.read(); + clipMap->dynEntDefList[x][i].physicsBrushModel = reader.read(); + + tempName = reader.readString(); + if (tempName != "NONE"s) + { + clipMap->dynEntDefList[x][i].destroyFx = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_FX, tempName, builder).fx; + } + + tempName = reader.readString(); + if (tempName != "NONE"s) + { + clipMap->dynEntDefList[x][i].physPreset = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_PHYSPRESET, tempName, builder).physPreset; + } + + clipMap->dynEntDefList[x][i].health = reader.read(); + clipMap->dynEntDefList[x][i].mass = reader.read(); + clipMap->dynEntDefList[x][i].contents = reader.read(); + } + } + } + + clipMap->checksum = reader.read(); + + clipMap->mapEnts = Components::AssetHandler::FindAssetForZone(Game::XAssetType::ASSET_TYPE_MAP_ENTS, Utils::String::VA("maps/mp/%s.d3dbsp", name.c_str()), builder).mapEnts; + + // This mustn't be null and has to have at least 1 'valid' entry. + if (!clipMap->smodelNodeCount || !clipMap->smodelNodes) + { + clipMap->smodelNodeCount = 1; + clipMap->smodelNodes = builder->getAllocator()->allocateArray(clipMap->smodelNodeCount); + + clipMap->smodelNodes[0].bounds.halfSize[0] = -131072.000f; + clipMap->smodelNodes[0].bounds.halfSize[1] = -131072.000f; + clipMap->smodelNodes[0].bounds.halfSize[2] = -131072.000f; + } + + // These mustn't be null, but they don't need to be valid. + for (int i = 0; i < 2 && clipMap->dynEntCount[i]; ++i) + { + Utils::Stream::ClearPointer(&clipMap->dynEntPoseList[i]); + Utils::Stream::ClearPointer(&clipMap->dynEntClientList[i]); + Utils::Stream::ClearPointer(&clipMap->dynEntCollList[i]); + } + + header->clipMap = clipMap; + } +} + #endif \ No newline at end of file diff --git a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp index 6c9db6f5..1626b785 100644 --- a/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp +++ b/src/Components/Modules/AssetInterfaces/Isnd_alias_list_t.cpp @@ -1,176 +1,176 @@ -#include - -namespace Assets -{ +#include + +namespace Assets +{ void Isnd_alias_list_t::mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { Game::snd_alias_list_t* asset = header.sound; - - for (int i = 0; i < asset->count; ++i) + + for (int i = 0; i < asset->count; ++i) { - Game::snd_alias_t* alias = &asset->head[i]; - - if (alias->soundFile && alias->soundFile->type == Game::snd_alias_type_t::SAT_LOADED) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, alias->soundFile->data.loaded); - } - - if (alias->volumeFalloffCurve) - { - builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve); - } - } - } - - void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) - { - AssertSize(Game::snd_alias_list_t, 12); - - Utils::Stream* buffer = builder->getBuffer(); - Game::snd_alias_list_t* asset = header.sound; - Game::snd_alias_list_t* dest = buffer->dest(); - buffer->save(asset); - - buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); - - if (asset->name) - { - buffer->saveString(builder->getAssetName(this->getType(), asset->name)); - Utils::Stream::ClearPointer(&dest->name); - } - - if (asset->head) - { - if (builder->hasPointer(asset->head)) - { - dest->head = builder->getPointer(asset->head); - } - else - { - AssertSize(Game::snd_alias_t, 100); - - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(asset->head); - + Game::snd_alias_t* alias = &asset->head[i]; + + if (alias->soundFile && alias->soundFile->type == Game::snd_alias_type_t::SAT_LOADED) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, alias->soundFile->data.loaded); + } + + if (alias->volumeFalloffCurve) + { + builder->loadAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve); + } + } + } + + void Isnd_alias_list_t::save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) + { + AssertSize(Game::snd_alias_list_t, 12); + + Utils::Stream* buffer = builder->getBuffer(); + Game::snd_alias_list_t* asset = header.sound; + Game::snd_alias_list_t* dest = buffer->dest(); + buffer->save(asset); + + buffer->pushBlock(Game::XFILE_BLOCK_VIRTUAL); + + if (asset->name) + { + buffer->saveString(builder->getAssetName(this->getType(), asset->name)); + Utils::Stream::ClearPointer(&dest->name); + } + + if (asset->head) + { + if (builder->hasPointer(asset->head)) + { + dest->head = builder->getPointer(asset->head); + } + else + { + AssertSize(Game::snd_alias_t, 100); + + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(asset->head); + Game::snd_alias_t* destHead = buffer->dest(); buffer->saveArray(asset->head, asset->count); - - for (int i = 0; i < asset->count; ++i) - { + + for (int i = 0; i < asset->count; ++i) + { Game::snd_alias_t* destAlias = &destHead[i]; - Game::snd_alias_t* alias = &asset->head[i]; - - if (alias->name) - { - buffer->saveString(alias->name); - Utils::Stream::ClearPointer(&destAlias->name); - } - - if (alias->subtitle) - { - buffer->saveString(alias->subtitle); - Utils::Stream::ClearPointer(&destAlias->subtitle); - } - - if (alias->secondaryAliasName) - { - buffer->saveString(alias->secondaryAliasName); - Utils::Stream::ClearPointer(&destAlias->secondaryAliasName); - } - - if (alias->chainAliasName) - { - buffer->saveString(alias->chainAliasName); - Utils::Stream::ClearPointer(&destAlias->chainAliasName); - } - - if (alias->string4) - { - buffer->saveString(alias->string4); - Utils::Stream::ClearPointer(&destAlias->string4); - } - - if (alias->soundFile) - { - if (builder->hasPointer(alias->soundFile)) - { - destAlias->soundFile = builder->getPointer(alias->soundFile); - } - else - { - AssertSize(Game::snd_alias_t, 100); - - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(alias->soundFile); - - Game::SoundFile* destSoundFile = buffer->dest(); - buffer->save(alias->soundFile); - - // Save_SoundFileRef - { - if (alias->soundFile->type == Game::snd_alias_type_t::SAT_LOADED) - { - destSoundFile->data.loaded = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, alias->soundFile->data.loaded).loadSnd; - } - else - { - // Save_StreamedSound - { - if (alias->soundFile->data.stream.dir) - { - buffer->saveString(alias->soundFile->data.stream.dir); - Utils::Stream::ClearPointer(&destSoundFile->data.stream.dir); - } - - if (alias->soundFile->data.stream.name) - { - buffer->saveString(alias->soundFile->data.stream.name); - Utils::Stream::ClearPointer(&destSoundFile->data.stream.name); - } - } - } - } - - Utils::Stream::ClearPointer(&destAlias->soundFile); - } - } - - if (alias->volumeFalloffCurve) - { - destAlias->volumeFalloffCurve = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve).sndCurve; - } - - if (alias->speakerMap) - { - if (builder->hasPointer(alias->speakerMap)) - { - destAlias->speakerMap = builder->getPointer(alias->speakerMap); - } - else - { - AssertSize(Game::SpeakerMap, 408); - - buffer->align(Utils::Stream::ALIGN_4); - builder->storePointer(alias->speakerMap); - - Game::SpeakerMap* destSoundFile = buffer->dest(); - buffer->save(alias->speakerMap); - - if (alias->speakerMap->name) - { - buffer->saveString(alias->speakerMap->name); - Utils::Stream::ClearPointer(&destSoundFile->name); - } - - Utils::Stream::ClearPointer(&destAlias->speakerMap); - } - } - } - - Utils::Stream::ClearPointer(&dest->head); - } - } - - buffer->popBlock(); - } -} + Game::snd_alias_t* alias = &asset->head[i]; + + if (alias->name) + { + buffer->saveString(alias->name); + Utils::Stream::ClearPointer(&destAlias->name); + } + + if (alias->subtitle) + { + buffer->saveString(alias->subtitle); + Utils::Stream::ClearPointer(&destAlias->subtitle); + } + + if (alias->secondaryAliasName) + { + buffer->saveString(alias->secondaryAliasName); + Utils::Stream::ClearPointer(&destAlias->secondaryAliasName); + } + + if (alias->chainAliasName) + { + buffer->saveString(alias->chainAliasName); + Utils::Stream::ClearPointer(&destAlias->chainAliasName); + } + + if (alias->string4) + { + buffer->saveString(alias->string4); + Utils::Stream::ClearPointer(&destAlias->string4); + } + + if (alias->soundFile) + { + if (builder->hasPointer(alias->soundFile)) + { + destAlias->soundFile = builder->getPointer(alias->soundFile); + } + else + { + AssertSize(Game::snd_alias_t, 100); + + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(alias->soundFile); + + Game::SoundFile* destSoundFile = buffer->dest(); + buffer->save(alias->soundFile); + + // Save_SoundFileRef + { + if (alias->soundFile->type == Game::snd_alias_type_t::SAT_LOADED) + { + destSoundFile->data.loaded = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, alias->soundFile->data.loaded).loadSnd; + } + else + { + // Save_StreamedSound + { + if (alias->soundFile->data.stream.dir) + { + buffer->saveString(alias->soundFile->data.stream.dir); + Utils::Stream::ClearPointer(&destSoundFile->data.stream.dir); + } + + if (alias->soundFile->data.stream.name) + { + buffer->saveString(alias->soundFile->data.stream.name); + Utils::Stream::ClearPointer(&destSoundFile->data.stream.name); + } + } + } + } + + Utils::Stream::ClearPointer(&destAlias->soundFile); + } + } + + if (alias->volumeFalloffCurve) + { + destAlias->volumeFalloffCurve = builder->saveSubAsset(Game::XAssetType::ASSET_TYPE_SOUND_CURVE, alias->volumeFalloffCurve).sndCurve; + } + + if (alias->speakerMap) + { + if (builder->hasPointer(alias->speakerMap)) + { + destAlias->speakerMap = builder->getPointer(alias->speakerMap); + } + else + { + AssertSize(Game::SpeakerMap, 408); + + buffer->align(Utils::Stream::ALIGN_4); + builder->storePointer(alias->speakerMap); + + Game::SpeakerMap* destSoundFile = buffer->dest(); + buffer->save(alias->speakerMap); + + if (alias->speakerMap->name) + { + buffer->saveString(alias->speakerMap->name); + Utils::Stream::ClearPointer(&destSoundFile->name); + } + + Utils::Stream::ClearPointer(&destAlias->speakerMap); + } + } + } + + Utils::Stream::ClearPointer(&dest->head); + } + } + + buffer->popBlock(); + } +} diff --git a/src/Components/Modules/Auth.cpp b/src/Components/Modules/Auth.cpp index f35b648b..8b58c619 100644 --- a/src/Components/Modules/Auth.cpp +++ b/src/Components/Modules/Auth.cpp @@ -1,514 +1,514 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Auth::TokenIncrementing Auth::TokenContainer; - - Utils::Cryptography::Token Auth::GuidToken; - Utils::Cryptography::Token Auth::ComputeToken; - Utils::Cryptography::ECC::Key Auth::GuidKey; - - void Auth::Frame() - { - if (Auth::TokenContainer.generating) - { - static double mseconds = 0; - static Utils::Time::Interval interval; - - if (interval.elapsed(500ms)) - { - interval.update(); - - int diff = Game::Sys_Milliseconds() - Auth::TokenContainer.startTime; - double hashPMS = (Auth::TokenContainer.hashes * 1.0) / diff; - double requiredHashes = std::pow(2, Auth::TokenContainer.targetLevel + 1) - Auth::TokenContainer.hashes; - mseconds = requiredHashes / hashPMS; - if (mseconds < 0) mseconds = 0; - } - - Localization::Set("MPUI_SECURITY_INCREASE_MESSAGE", Utils::String::VA("Increasing security level from %d to %d (est. %s)", Auth::GetSecurityLevel(), Auth::TokenContainer.targetLevel, Utils::String::FormatTimeSpan(static_cast(mseconds)).data())); - } - else if (Auth::TokenContainer.thread.joinable()) - { - Auth::TokenContainer.thread.join(); - Auth::TokenContainer.generating = false; - - Auth::StoreKey(); - Logger::Print("Security level is %d\n", Auth::GetSecurityLevel()); - Command::Execute("closemenu security_increase_popmenu", false); - - if (!Auth::TokenContainer.cancel) - { - if (Auth::TokenContainer.command.empty()) - { - Game::MessageBox(Utils::String::VA("Your new security level is %d", Auth::GetSecurityLevel()), "Success"); - } - else - { - Toast::Show("cardicon_locked", "Success", Utils::String::VA("Your new security level is %d", Auth::GetSecurityLevel()), 5000); - Command::Execute(Auth::TokenContainer.command, false); - } - } - - Auth::TokenContainer.cancel = false; - } - } - - void Auth::SendConnectDataStub(Game::netsrc_t sock, Game::netadr_t adr, const char *format, int len) - { - // Ensure our certificate is loaded - Steam::SteamUser()->GetSteamID(); - if (!Auth::GuidKey.isValid()) - { - Logger::SoftError("Connecting failed: Guid key is invalid!"); - return; - } - - std::string connectString(format, len); - Game::SV_Cmd_TokenizeString(connectString.data()); - - Command::ServerParams params; - - if (params.length() < 3) - { - Game::SV_Cmd_EndTokenizedString(); - Logger::SoftError("Connecting failed: Command parsing error!"); - return; - } - - Utils::InfoString infostr(params[2]); - std::string challenge = infostr.get("challenge"); - - if (challenge.empty()) - { - Game::SV_Cmd_EndTokenizedString(); - Logger::SoftError("Connecting failed: Challenge parsing error!"); - return; - } - - Game::SV_Cmd_EndTokenizedString(); - - Proto::Auth::Connect connectData; - connectData.set_token(Auth::GuidToken.toString()); - connectData.set_publickey(Auth::GuidKey.getPublicKey()); - connectData.set_signature(Utils::Cryptography::ECC::SignMessage(Auth::GuidKey, challenge)); - connectData.set_infostring(connectString); - - Network::SendCommand(sock, adr, "connect", connectData.SerializeAsString()); - } - - void Auth::ParseConnectData(Game::msg_t* msg, Game::netadr_t addr) - { - Network::Address address(addr); - - // Parse proto data - Proto::Auth::Connect connectData; - if (msg->cursize <= 12 || !connectData.ParseFromString(std::string(&msg->data[12], msg->cursize - 12))) - { - Network::Send(address, "error\nInvalid connect packet!"); - return; - } - - if (address.isLoopback() -// Simply connect, if we're in debug mode, we ignore all security checks -#ifdef DEBUG - || true -#endif - ) - { - if (!connectData.infostring().empty()) - { - Game::SV_Cmd_EndTokenizedString(); - Game::SV_Cmd_TokenizeString(connectData.infostring().data()); - Game::SV_DirectConnect(*address.get()); - } - else - { - Network::Send(address, "error\nInvalid infostring data!"); - } - } - else - { - // Validate proto data - if (connectData.signature().empty() || connectData.publickey().empty() || connectData.token().empty() || connectData.infostring().empty()) - { - Network::Send(address, "error\nInvalid connect data!"); - return; - } - - // Setup new cmd params - Game::SV_Cmd_EndTokenizedString(); - Game::SV_Cmd_TokenizeString(connectData.infostring().data()); - - // Access the params - Command::ServerParams params; - - // Ensure there are enough params - if (params.length() < 3) - { - Network::Send(address, "error\nInvalid connect string!"); - return; - } - - // Parse the infostring - Utils::InfoString infostr(params[2]); - - // Read the required data - std::string steamId = infostr.get("xuid"); - std::string challenge = infostr.get("challenge"); - - if (steamId.empty() || challenge.empty()) - { - Network::Send(address, "error\nInvalid connect data!"); - return; - } - - // Parse the id - unsigned __int64 xuid = strtoull(steamId.data(), nullptr, 16); - - SteamID guid; - guid.Bits = xuid; - - if (Bans::IsBanned({ guid, address.getIP() })) - { - Network::Send(address, "error\nEXE_ERR_BANNED_PERM"); - return; - } - - if (xuid != Auth::GetKeyHash(connectData.publickey())) - { - Network::Send(address, "error\nXUID doesn't match the certificate!"); - return; - } - - // Verify the signature - Utils::Cryptography::ECC::Key key; - key.set(connectData.publickey()); - - if (!key.isValid() || !Utils::Cryptography::ECC::VerifyMessage(key, challenge, connectData.signature())) - { - Network::Send(address, "error\nChallenge signature was invalid!"); - return; - } - - // Verify the security level - uint32_t ourLevel = static_cast(Dvar::Var("sv_securityLevel").get()); - uint32_t userLevel = Auth::GetZeroBits(connectData.token(), connectData.publickey()); - - if (userLevel < ourLevel) - { - Network::Send(address, Utils::String::VA("error\nYour security level (%d) is lower than the server's security level (%d)", userLevel, ourLevel)); - return; - } - - Logger::Print("Verified XUID %llX (%d) from %s\n", xuid, userLevel, address.getCString()); - Game::SV_DirectConnect(*address.get()); - } - } - - __declspec(naked) void Auth::DirectConnectStub() - { - __asm - { - push esi - call Auth::ParseConnectData - pop esi - - mov edi, 6265FEh - jmp edi - } - } - - unsigned __int64 Auth::GetKeyHash(std::string key) - { - std::string hash = Utils::Cryptography::SHA1::Compute(key); - - if (hash.size() >= 8) - { - return *reinterpret_cast(const_cast(hash.data())); - } - - return 0; - } - - unsigned __int64 Auth::GetKeyHash() - { - Auth::LoadKey(); - return Auth::GetKeyHash(Auth::GuidKey.getPublicKey()); - } - - void Auth::StoreKey() - { - if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled() && Auth::GuidKey.isValid()) - { - Proto::Auth::Certificate cert; - cert.set_token(Auth::GuidToken.toString()); - cert.set_ctoken(Auth::ComputeToken.toString()); - cert.set_privatekey(Auth::GuidKey.serialize(PK_PRIVATE)); - - Utils::IO::WriteFile("players/guid.dat", cert.SerializeAsString()); - } - } - - void Auth::LoadKey(bool force) - { - if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return; - if (!force && Auth::GuidKey.isValid()) return; - - Proto::Auth::Certificate cert; - if (cert.ParseFromString(::Utils::IO::ReadFile("players/guid.dat"))) - { - Auth::GuidKey.deserialize(cert.privatekey()); - Auth::GuidToken = cert.token(); - Auth::ComputeToken = cert.ctoken(); - } - else - { - Auth::GuidKey.free(); - } - - if (!Auth::GuidKey.isValid()) - { - Auth::GuidToken.clear(); - Auth::ComputeToken.clear(); - Auth::GuidKey = Utils::Cryptography::ECC::GenerateKey(512); - Auth::StoreKey(); - } - } - - uint32_t Auth::GetSecurityLevel() - { - return Auth::GetZeroBits(Auth::GuidToken, Auth::GuidKey.getPublicKey()); - } - - void Auth::IncreaseSecurityLevel(uint32_t level, std::string command) - { - if (Auth::GetSecurityLevel() >= level) return; - - if (!Auth::TokenContainer.generating) - { - Auth::TokenContainer.cancel = false; - Auth::TokenContainer.targetLevel = level; - Auth::TokenContainer.command = command; - - // Open menu - Command::Execute("openmenu security_increase_popmenu", true); - - // Start thread - Auth::TokenContainer.thread = std::thread([&level] () - { - Auth::TokenContainer.generating = true; - Auth::TokenContainer.hashes = 0; - Auth::TokenContainer.startTime = Game::Sys_Milliseconds(); - Auth::IncrementToken(Auth::GuidToken, Auth::ComputeToken, Auth::GuidKey.getPublicKey(), Auth::TokenContainer.targetLevel, &Auth::TokenContainer.cancel, &Auth::TokenContainer.hashes); - Auth::TokenContainer.generating = false; - - if (Auth::TokenContainer.cancel) - { - Logger::Print("Token incrementation thread terminated\n"); - } - }); - } - } - - uint32_t Auth::GetZeroBits(Utils::Cryptography::Token token, std::string publicKey) - { - std::string message = publicKey + token.toString(); - std::string hash = Utils::Cryptography::SHA512::Compute(message, false); - - uint32_t bits = 0; - - for (unsigned int i = 0; i < hash.size(); ++i) - { - if (hash[i] == '\0') - { - bits += 8; - continue; - } - - uint8_t value = static_cast(hash[i]); - for (int j = 7; j >= 0; --j) - { - if ((value >> j) & 1) - { - return bits; - } - - ++bits; - } - } - - return bits; - } - - void Auth::IncrementToken(Utils::Cryptography::Token& token, Utils::Cryptography::Token& computeToken, std::string publicKey, uint32_t zeroBits, bool* cancel, uint64_t* count) - { - if (zeroBits > 512) return; // Not possible, due to SHA512 - - if (computeToken < token) - { - computeToken = token; - } - - // Check if we already have the desired security level - uint32_t lastLevel = Auth::GetZeroBits(token, publicKey); - uint32_t level = lastLevel; - if (level >= zeroBits) return; - - do - { - ++computeToken; - if (count) ++(*count); - level = Auth::GetZeroBits(computeToken, publicKey); - - // Store level if higher than the last one - if (level >= lastLevel) - { - token = computeToken; - lastLevel = level; - } - - // Allow canceling that shit - if (cancel && *cancel) return; - } - while (level < zeroBits); - - token = computeToken; - } - - Auth::Auth() - { - Auth::TokenContainer.cancel = false; - Auth::TokenContainer.generating = false; - - Localization::Set("MPUI_SECURITY_INCREASE_MESSAGE", ""); - - // Load the key - Auth::LoadKey(true); - Steam::SteamUser()->GetSteamID(); - - QuickPatch::OnFrame(Auth::Frame); - - // Register dvar - Dvar::Register("sv_securityLevel", 23, 0, 512, Game::dvar_flag::DVAR_FLAG_SERVERINFO, "Security level for GUID certificates (POW)"); - - // Install registration hook - Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x41D3E3, Auth::SendConnectDataStub, HOOK_CALL).install()->quick(); - - // SteamIDs can only contain 31 bits of actual 'id' data. - // The other 33 bits are steam internal data like universe and so on. - // Using only 31 bits for fingerprints is pretty insecure. - // The function below verifies the integrity steam's part of the SteamID. - // Patching that check allows us to use 64 bit for fingerprints. - Utils::Hook::Set(0x4D0D60, 0xC301B0); - - // Guid command - Command::Add("guid", [] (Command::Params*) - { - Logger::Print("Your guid: %llX\n", Steam::SteamUser()->GetSteamID().Bits); - }); - - if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) - { - Command::Add("securityLevel", [] (Command::Params* params) - { - if (params->length() < 2) - { - uint32_t level = Auth::GetZeroBits(Auth::GuidToken, Auth::GuidKey.getPublicKey()); - Logger::Print("Your current security level is %d\n", level); - Logger::Print("Your security token is: %s\n", Utils::String::DumpHex(Auth::GuidToken.toString(), "").data()); - Logger::Print("Your computation token is: %s\n", Utils::String::DumpHex(Auth::ComputeToken.toString(), "").data()); - - Toast::Show("cardicon_locked", "^5Security Level", Utils::String::VA("Your security level is %d", level), 3000); - } - else - { - uint32_t level = static_cast(atoi(params->get(1))); - Auth::IncreaseSecurityLevel(level); - } - }); - } - - UIScript::Add("security_increase_cancel", [] (UIScript::Token) - { - Auth::TokenContainer.cancel = true; - Logger::Print("Token incrementation process canceled!\n"); - }); - } - - Auth::~Auth() - { - Auth::TokenContainer.cancel = true; - Auth::TokenContainer.generating = false; - - // Terminate thread - if (Auth::TokenContainer.thread.joinable()) - { - Auth::TokenContainer.thread.join(); - } - - Auth::StoreKey(); - } - - bool Auth::unitTest() - { - bool success = true; - - printf("Testing logical token operators:\n"); - - Utils::Cryptography::Token token1; - Utils::Cryptography::Token token2; - ++token1, token2++; // Test incrementation operator - - printf("Operator == : "); - if (token1 == token2 && !(++token1 == token2)) printf("Success\n"); - else - { - printf("Error\n"); - success = false; - } - - printf("Operator != : "); - if (token1 != token2 && !(++token2 != token1)) printf("Success\n"); - else - { - printf("Error\n"); - success = false; - } - - printf("Operator >= : "); - if (token1 >= token2 && ++token1 >= token2) printf("Success\n"); - else - { - printf("Error\n"); - success = false; - } - - printf("Operator > : "); - if (token1 > token2) printf("Success\n"); - else - { - printf("Error\n"); - success = false; - } - - printf("Operator <= : "); - if (token1 <= ++token2 && token1 <= ++token2) printf("Success\n"); - else - { - printf("Error\n"); - success = false; - } - - printf("Operator < : "); - if (token1 < token2) printf("Success\n"); - else - { - printf("Error\n"); - success = false; - } - - return success; - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Auth::TokenIncrementing Auth::TokenContainer; + + Utils::Cryptography::Token Auth::GuidToken; + Utils::Cryptography::Token Auth::ComputeToken; + Utils::Cryptography::ECC::Key Auth::GuidKey; + + void Auth::Frame() + { + if (Auth::TokenContainer.generating) + { + static double mseconds = 0; + static Utils::Time::Interval interval; + + if (interval.elapsed(500ms)) + { + interval.update(); + + int diff = Game::Sys_Milliseconds() - Auth::TokenContainer.startTime; + double hashPMS = (Auth::TokenContainer.hashes * 1.0) / diff; + double requiredHashes = std::pow(2, Auth::TokenContainer.targetLevel + 1) - Auth::TokenContainer.hashes; + mseconds = requiredHashes / hashPMS; + if (mseconds < 0) mseconds = 0; + } + + Localization::Set("MPUI_SECURITY_INCREASE_MESSAGE", Utils::String::VA("Increasing security level from %d to %d (est. %s)", Auth::GetSecurityLevel(), Auth::TokenContainer.targetLevel, Utils::String::FormatTimeSpan(static_cast(mseconds)).data())); + } + else if (Auth::TokenContainer.thread.joinable()) + { + Auth::TokenContainer.thread.join(); + Auth::TokenContainer.generating = false; + + Auth::StoreKey(); + Logger::Print("Security level is %d\n", Auth::GetSecurityLevel()); + Command::Execute("closemenu security_increase_popmenu", false); + + if (!Auth::TokenContainer.cancel) + { + if (Auth::TokenContainer.command.empty()) + { + Game::MessageBox(Utils::String::VA("Your new security level is %d", Auth::GetSecurityLevel()), "Success"); + } + else + { + Toast::Show("cardicon_locked", "Success", Utils::String::VA("Your new security level is %d", Auth::GetSecurityLevel()), 5000); + Command::Execute(Auth::TokenContainer.command, false); + } + } + + Auth::TokenContainer.cancel = false; + } + } + + void Auth::SendConnectDataStub(Game::netsrc_t sock, Game::netadr_t adr, const char *format, int len) + { + // Ensure our certificate is loaded + Steam::SteamUser()->GetSteamID(); + if (!Auth::GuidKey.isValid()) + { + Logger::SoftError("Connecting failed: Guid key is invalid!"); + return; + } + + std::string connectString(format, len); + Game::SV_Cmd_TokenizeString(connectString.data()); + + Command::ServerParams params; + + if (params.length() < 3) + { + Game::SV_Cmd_EndTokenizedString(); + Logger::SoftError("Connecting failed: Command parsing error!"); + return; + } + + Utils::InfoString infostr(params[2]); + std::string challenge = infostr.get("challenge"); + + if (challenge.empty()) + { + Game::SV_Cmd_EndTokenizedString(); + Logger::SoftError("Connecting failed: Challenge parsing error!"); + return; + } + + Game::SV_Cmd_EndTokenizedString(); + + Proto::Auth::Connect connectData; + connectData.set_token(Auth::GuidToken.toString()); + connectData.set_publickey(Auth::GuidKey.getPublicKey()); + connectData.set_signature(Utils::Cryptography::ECC::SignMessage(Auth::GuidKey, challenge)); + connectData.set_infostring(connectString); + + Network::SendCommand(sock, adr, "connect", connectData.SerializeAsString()); + } + + void Auth::ParseConnectData(Game::msg_t* msg, Game::netadr_t addr) + { + Network::Address address(addr); + + // Parse proto data + Proto::Auth::Connect connectData; + if (msg->cursize <= 12 || !connectData.ParseFromString(std::string(&msg->data[12], msg->cursize - 12))) + { + Network::Send(address, "error\nInvalid connect packet!"); + return; + } + + if (address.isLoopback() +// Simply connect, if we're in debug mode, we ignore all security checks +#ifdef DEBUG + || true +#endif + ) + { + if (!connectData.infostring().empty()) + { + Game::SV_Cmd_EndTokenizedString(); + Game::SV_Cmd_TokenizeString(connectData.infostring().data()); + Game::SV_DirectConnect(*address.get()); + } + else + { + Network::Send(address, "error\nInvalid infostring data!"); + } + } + else + { + // Validate proto data + if (connectData.signature().empty() || connectData.publickey().empty() || connectData.token().empty() || connectData.infostring().empty()) + { + Network::Send(address, "error\nInvalid connect data!"); + return; + } + + // Setup new cmd params + Game::SV_Cmd_EndTokenizedString(); + Game::SV_Cmd_TokenizeString(connectData.infostring().data()); + + // Access the params + Command::ServerParams params; + + // Ensure there are enough params + if (params.length() < 3) + { + Network::Send(address, "error\nInvalid connect string!"); + return; + } + + // Parse the infostring + Utils::InfoString infostr(params[2]); + + // Read the required data + std::string steamId = infostr.get("xuid"); + std::string challenge = infostr.get("challenge"); + + if (steamId.empty() || challenge.empty()) + { + Network::Send(address, "error\nInvalid connect data!"); + return; + } + + // Parse the id + unsigned __int64 xuid = strtoull(steamId.data(), nullptr, 16); + + SteamID guid; + guid.Bits = xuid; + + if (Bans::IsBanned({ guid, address.getIP() })) + { + Network::Send(address, "error\nEXE_ERR_BANNED_PERM"); + return; + } + + if (xuid != Auth::GetKeyHash(connectData.publickey())) + { + Network::Send(address, "error\nXUID doesn't match the certificate!"); + return; + } + + // Verify the signature + Utils::Cryptography::ECC::Key key; + key.set(connectData.publickey()); + + if (!key.isValid() || !Utils::Cryptography::ECC::VerifyMessage(key, challenge, connectData.signature())) + { + Network::Send(address, "error\nChallenge signature was invalid!"); + return; + } + + // Verify the security level + uint32_t ourLevel = static_cast(Dvar::Var("sv_securityLevel").get()); + uint32_t userLevel = Auth::GetZeroBits(connectData.token(), connectData.publickey()); + + if (userLevel < ourLevel) + { + Network::Send(address, Utils::String::VA("error\nYour security level (%d) is lower than the server's security level (%d)", userLevel, ourLevel)); + return; + } + + Logger::Print("Verified XUID %llX (%d) from %s\n", xuid, userLevel, address.getCString()); + Game::SV_DirectConnect(*address.get()); + } + } + + __declspec(naked) void Auth::DirectConnectStub() + { + __asm + { + push esi + call Auth::ParseConnectData + pop esi + + mov edi, 6265FEh + jmp edi + } + } + + unsigned __int64 Auth::GetKeyHash(std::string key) + { + std::string hash = Utils::Cryptography::SHA1::Compute(key); + + if (hash.size() >= 8) + { + return *reinterpret_cast(const_cast(hash.data())); + } + + return 0; + } + + unsigned __int64 Auth::GetKeyHash() + { + Auth::LoadKey(); + return Auth::GetKeyHash(Auth::GuidKey.getPublicKey()); + } + + void Auth::StoreKey() + { + if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled() && Auth::GuidKey.isValid()) + { + Proto::Auth::Certificate cert; + cert.set_token(Auth::GuidToken.toString()); + cert.set_ctoken(Auth::ComputeToken.toString()); + cert.set_privatekey(Auth::GuidKey.serialize(PK_PRIVATE)); + + Utils::IO::WriteFile("players/guid.dat", cert.SerializeAsString()); + } + } + + void Auth::LoadKey(bool force) + { + if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return; + if (!force && Auth::GuidKey.isValid()) return; + + Proto::Auth::Certificate cert; + if (cert.ParseFromString(::Utils::IO::ReadFile("players/guid.dat"))) + { + Auth::GuidKey.deserialize(cert.privatekey()); + Auth::GuidToken = cert.token(); + Auth::ComputeToken = cert.ctoken(); + } + else + { + Auth::GuidKey.free(); + } + + if (!Auth::GuidKey.isValid()) + { + Auth::GuidToken.clear(); + Auth::ComputeToken.clear(); + Auth::GuidKey = Utils::Cryptography::ECC::GenerateKey(512); + Auth::StoreKey(); + } + } + + uint32_t Auth::GetSecurityLevel() + { + return Auth::GetZeroBits(Auth::GuidToken, Auth::GuidKey.getPublicKey()); + } + + void Auth::IncreaseSecurityLevel(uint32_t level, std::string command) + { + if (Auth::GetSecurityLevel() >= level) return; + + if (!Auth::TokenContainer.generating) + { + Auth::TokenContainer.cancel = false; + Auth::TokenContainer.targetLevel = level; + Auth::TokenContainer.command = command; + + // Open menu + Command::Execute("openmenu security_increase_popmenu", true); + + // Start thread + Auth::TokenContainer.thread = std::thread([&level] () + { + Auth::TokenContainer.generating = true; + Auth::TokenContainer.hashes = 0; + Auth::TokenContainer.startTime = Game::Sys_Milliseconds(); + Auth::IncrementToken(Auth::GuidToken, Auth::ComputeToken, Auth::GuidKey.getPublicKey(), Auth::TokenContainer.targetLevel, &Auth::TokenContainer.cancel, &Auth::TokenContainer.hashes); + Auth::TokenContainer.generating = false; + + if (Auth::TokenContainer.cancel) + { + Logger::Print("Token incrementation thread terminated\n"); + } + }); + } + } + + uint32_t Auth::GetZeroBits(Utils::Cryptography::Token token, std::string publicKey) + { + std::string message = publicKey + token.toString(); + std::string hash = Utils::Cryptography::SHA512::Compute(message, false); + + uint32_t bits = 0; + + for (unsigned int i = 0; i < hash.size(); ++i) + { + if (hash[i] == '\0') + { + bits += 8; + continue; + } + + uint8_t value = static_cast(hash[i]); + for (int j = 7; j >= 0; --j) + { + if ((value >> j) & 1) + { + return bits; + } + + ++bits; + } + } + + return bits; + } + + void Auth::IncrementToken(Utils::Cryptography::Token& token, Utils::Cryptography::Token& computeToken, std::string publicKey, uint32_t zeroBits, bool* cancel, uint64_t* count) + { + if (zeroBits > 512) return; // Not possible, due to SHA512 + + if (computeToken < token) + { + computeToken = token; + } + + // Check if we already have the desired security level + uint32_t lastLevel = Auth::GetZeroBits(token, publicKey); + uint32_t level = lastLevel; + if (level >= zeroBits) return; + + do + { + ++computeToken; + if (count) ++(*count); + level = Auth::GetZeroBits(computeToken, publicKey); + + // Store level if higher than the last one + if (level >= lastLevel) + { + token = computeToken; + lastLevel = level; + } + + // Allow canceling that shit + if (cancel && *cancel) return; + } + while (level < zeroBits); + + token = computeToken; + } + + Auth::Auth() + { + Auth::TokenContainer.cancel = false; + Auth::TokenContainer.generating = false; + + Localization::Set("MPUI_SECURITY_INCREASE_MESSAGE", ""); + + // Load the key + Auth::LoadKey(true); + Steam::SteamUser()->GetSteamID(); + + QuickPatch::OnFrame(Auth::Frame); + + // Register dvar + Dvar::Register("sv_securityLevel", 23, 0, 512, Game::dvar_flag::DVAR_FLAG_SERVERINFO, "Security level for GUID certificates (POW)"); + + // Install registration hook + Utils::Hook(0x6265F9, Auth::DirectConnectStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x41D3E3, Auth::SendConnectDataStub, HOOK_CALL).install()->quick(); + + // SteamIDs can only contain 31 bits of actual 'id' data. + // The other 33 bits are steam internal data like universe and so on. + // Using only 31 bits for fingerprints is pretty insecure. + // The function below verifies the integrity steam's part of the SteamID. + // Patching that check allows us to use 64 bit for fingerprints. + Utils::Hook::Set(0x4D0D60, 0xC301B0); + + // Guid command + Command::Add("guid", [] (Command::Params*) + { + Logger::Print("Your guid: %llX\n", Steam::SteamUser()->GetSteamID().Bits); + }); + + if (!Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) + { + Command::Add("securityLevel", [] (Command::Params* params) + { + if (params->length() < 2) + { + uint32_t level = Auth::GetZeroBits(Auth::GuidToken, Auth::GuidKey.getPublicKey()); + Logger::Print("Your current security level is %d\n", level); + Logger::Print("Your security token is: %s\n", Utils::String::DumpHex(Auth::GuidToken.toString(), "").data()); + Logger::Print("Your computation token is: %s\n", Utils::String::DumpHex(Auth::ComputeToken.toString(), "").data()); + + Toast::Show("cardicon_locked", "^5Security Level", Utils::String::VA("Your security level is %d", level), 3000); + } + else + { + uint32_t level = static_cast(atoi(params->get(1))); + Auth::IncreaseSecurityLevel(level); + } + }); + } + + UIScript::Add("security_increase_cancel", [] (UIScript::Token) + { + Auth::TokenContainer.cancel = true; + Logger::Print("Token incrementation process canceled!\n"); + }); + } + + Auth::~Auth() + { + Auth::TokenContainer.cancel = true; + Auth::TokenContainer.generating = false; + + // Terminate thread + if (Auth::TokenContainer.thread.joinable()) + { + Auth::TokenContainer.thread.join(); + } + + Auth::StoreKey(); + } + + bool Auth::unitTest() + { + bool success = true; + + printf("Testing logical token operators:\n"); + + Utils::Cryptography::Token token1; + Utils::Cryptography::Token token2; + ++token1, token2++; // Test incrementation operator + + printf("Operator == : "); + if (token1 == token2 && !(++token1 == token2)) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator != : "); + if (token1 != token2 && !(++token2 != token1)) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator >= : "); + if (token1 >= token2 && ++token1 >= token2) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator > : "); + if (token1 > token2) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator <= : "); + if (token1 <= ++token2 && token1 <= ++token2) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + printf("Operator < : "); + if (token1 < token2) printf("Success\n"); + else + { + printf("Error\n"); + success = false; + } + + return success; + } +} diff --git a/src/Components/Modules/Auth.hpp b/src/Components/Modules/Auth.hpp index 8cd4bef2..66b994a4 100644 --- a/src/Components/Modules/Auth.hpp +++ b/src/Components/Modules/Auth.hpp @@ -1,52 +1,52 @@ -namespace Components -{ - class Auth : public Component - { - public: - Auth(); - ~Auth(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Auth"; }; -#endif - - bool unitTest(); - - static void StoreKey(); - static void LoadKey(bool force = false); - static unsigned __int64 GetKeyHash(); - static unsigned __int64 GetKeyHash(std::string key); - - static uint32_t GetSecurityLevel(); - static void IncreaseSecurityLevel(uint32_t level, std::string command = ""); - - static uint32_t GetZeroBits(Utils::Cryptography::Token token, std::string publicKey); - static void IncrementToken(Utils::Cryptography::Token& token, Utils::Cryptography::Token& computeToken, std::string publicKey, uint32_t zeroBits, bool* cancel = nullptr, uint64_t* count = nullptr); - - private: - - class TokenIncrementing - { - public: - bool cancel; - bool generating; - std::thread thread; - uint32_t targetLevel; - int startTime; - std::string command; - uint64_t hashes; - }; - - static TokenIncrementing TokenContainer; - - static Utils::Cryptography::Token GuidToken; - static Utils::Cryptography::Token ComputeToken; - static Utils::Cryptography::ECC::Key GuidKey; - - static void SendConnectDataStub(Game::netsrc_t sock, Game::netadr_t adr, const char *format, int len); - static void ParseConnectData(Game::msg_t* msg, Game::netadr_t addr); - static void DirectConnectStub(); - - static void Frame(); - }; -} +namespace Components +{ + class Auth : public Component + { + public: + Auth(); + ~Auth(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Auth"; }; +#endif + + bool unitTest(); + + static void StoreKey(); + static void LoadKey(bool force = false); + static unsigned __int64 GetKeyHash(); + static unsigned __int64 GetKeyHash(std::string key); + + static uint32_t GetSecurityLevel(); + static void IncreaseSecurityLevel(uint32_t level, std::string command = ""); + + static uint32_t GetZeroBits(Utils::Cryptography::Token token, std::string publicKey); + static void IncrementToken(Utils::Cryptography::Token& token, Utils::Cryptography::Token& computeToken, std::string publicKey, uint32_t zeroBits, bool* cancel = nullptr, uint64_t* count = nullptr); + + private: + + class TokenIncrementing + { + public: + bool cancel; + bool generating; + std::thread thread; + uint32_t targetLevel; + int startTime; + std::string command; + uint64_t hashes; + }; + + static TokenIncrementing TokenContainer; + + static Utils::Cryptography::Token GuidToken; + static Utils::Cryptography::Token ComputeToken; + static Utils::Cryptography::ECC::Key GuidKey; + + static void SendConnectDataStub(Game::netsrc_t sock, Game::netadr_t adr, const char *format, int len); + static void ParseConnectData(Game::msg_t* msg, Game::netadr_t addr); + static void DirectConnectStub(); + + static void Frame(); + }; +} diff --git a/src/Components/Modules/Bans.cpp b/src/Components/Modules/Bans.cpp index 7401adeb..a76ab317 100644 --- a/src/Components/Modules/Bans.cpp +++ b/src/Components/Modules/Bans.cpp @@ -1,212 +1,212 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::mutex Bans::AccessMutex; - - bool Bans::IsBanned(Bans::Entry entry) - { - Bans::BanList list; - Bans::LoadBans(&list); - - if (entry.first.Bits) - { - for (auto& idEntry : list.idList) - { - if (idEntry.Bits == entry.first.Bits) - { - return true; - } - } - } - - if (entry.second.full) - { - for (auto& ipEntry : list.ipList) - { - if (ipEntry.full == entry.second.full) - { - return true; - } - } - } - - return false; - } - - void Bans::InsertBan(Bans::Entry entry) - { - Bans::BanList list; - Bans::LoadBans(&list); - - std::lock_guard _(Bans::AccessMutex); - - if (entry.first.Bits) - { - bool found = false; - for (auto& idEntry : list.idList) - { - if (idEntry.Bits == entry.first.Bits) - { - found = true; - break; - } - } - - if (!found) - { - list.idList.push_back(entry.first); - } - } - - if (entry.second.full) - { - bool found = false; - for (auto& ipEntry : list.ipList) - { - if (ipEntry.full == entry.second.full) - { - found = true; - break; - } - } - - if (!found) - { - list.ipList.push_back(entry.second); - } - } - - std::vector idVector; - std::vector ipVector; - - for (auto& idEntry : list.idList) - { - idVector.push_back(Utils::String::VA("%llX", idEntry.Bits)); - } - - for (auto& ipEntry : list.ipList) - { - ipVector.push_back(Utils::String::VA("%u.%u.%u.%u", - ipEntry.bytes[0] & 0xFF, - ipEntry.bytes[1] & 0xFF, - ipEntry.bytes[2] & 0xFF, - ipEntry.bytes[3] & 0xFF)); - } - - json11::Json bans = json11::Json::object - { - { "ip", ipVector }, - { "id", idVector }, - }; - - FileSystem::FileWriter ban("bans.json"); - ban.write(bans.dump()); - } - - void Bans::LoadBans(Bans::BanList* list) - { - std::lock_guard _(Bans::AccessMutex); - - FileSystem::File bans("bans.json"); - - if (bans.exists()) - { - std::string error; - json11::Json banData = json11::Json::parse(bans.getBuffer(), error); - - if (!error.empty()) - { - Logger::Error("Failed to parse bans (bans.json): %s", error.data()); - } - - if (!list) - { - Bans::AccessMutex.unlock(); - return; - } - - if (banData.is_object()) - { - auto idList = banData["id"]; - auto ipList = banData["ip"]; - - if (idList.is_array()) - { - for (auto &idEntry : idList.array_items()) - { - if (idEntry.is_string()) - { - SteamID id; - id.Bits = strtoull(idEntry.string_value().data(), nullptr, 16); - - list->idList.push_back(id); - } - } - } - - if (ipList.is_array()) - { - for (auto &ipEntry : ipList.array_items()) - { - if (ipEntry.is_string()) - { - Network::Address addr(ipEntry.string_value()); - - list->ipList.push_back(addr.getIP()); - } - } - } - } - } - } - - void Bans::BanClientNum(int num, std::string reason) - { - if (!Dvar::Var("sv_running").get()) - { - Logger::Print("Server is not running.\n"); - return; - } - - if (*Game::svs_numclients <= num) - { - Logger::Print("Player %d is not on the server\n", num); - return; - } - - Game::client_t* client = &Game::svs_clients[num]; - - SteamID guid; - guid.Bits = client->steamid; - - Bans::InsertBan({ guid, client->addr.ip }); - - Game::SV_KickClientError(client, reason); - } - - Bans::Bans() - { - Command::Add("banclient", [] (Command::Params* params) - { - if (params->length() < 2) return; - - std::string reason = "EXE_ERR_BANNED_PERM"; - if (params->length() >= 3) reason = params->get(2); - - Bans::BanClientNum(atoi(params->get(1)), reason); - }); - - // Verify the list on startup - QuickPatch::Once([] () - { - Bans::BanList list; - Bans::LoadBans(&list); - }); - } - - Bans::~Bans() - { - - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::mutex Bans::AccessMutex; + + bool Bans::IsBanned(Bans::Entry entry) + { + Bans::BanList list; + Bans::LoadBans(&list); + + if (entry.first.Bits) + { + for (auto& idEntry : list.idList) + { + if (idEntry.Bits == entry.first.Bits) + { + return true; + } + } + } + + if (entry.second.full) + { + for (auto& ipEntry : list.ipList) + { + if (ipEntry.full == entry.second.full) + { + return true; + } + } + } + + return false; + } + + void Bans::InsertBan(Bans::Entry entry) + { + Bans::BanList list; + Bans::LoadBans(&list); + + std::lock_guard _(Bans::AccessMutex); + + if (entry.first.Bits) + { + bool found = false; + for (auto& idEntry : list.idList) + { + if (idEntry.Bits == entry.first.Bits) + { + found = true; + break; + } + } + + if (!found) + { + list.idList.push_back(entry.first); + } + } + + if (entry.second.full) + { + bool found = false; + for (auto& ipEntry : list.ipList) + { + if (ipEntry.full == entry.second.full) + { + found = true; + break; + } + } + + if (!found) + { + list.ipList.push_back(entry.second); + } + } + + std::vector idVector; + std::vector ipVector; + + for (auto& idEntry : list.idList) + { + idVector.push_back(Utils::String::VA("%llX", idEntry.Bits)); + } + + for (auto& ipEntry : list.ipList) + { + ipVector.push_back(Utils::String::VA("%u.%u.%u.%u", + ipEntry.bytes[0] & 0xFF, + ipEntry.bytes[1] & 0xFF, + ipEntry.bytes[2] & 0xFF, + ipEntry.bytes[3] & 0xFF)); + } + + json11::Json bans = json11::Json::object + { + { "ip", ipVector }, + { "id", idVector }, + }; + + FileSystem::FileWriter ban("bans.json"); + ban.write(bans.dump()); + } + + void Bans::LoadBans(Bans::BanList* list) + { + std::lock_guard _(Bans::AccessMutex); + + FileSystem::File bans("bans.json"); + + if (bans.exists()) + { + std::string error; + json11::Json banData = json11::Json::parse(bans.getBuffer(), error); + + if (!error.empty()) + { + Logger::Error("Failed to parse bans (bans.json): %s", error.data()); + } + + if (!list) + { + Bans::AccessMutex.unlock(); + return; + } + + if (banData.is_object()) + { + auto idList = banData["id"]; + auto ipList = banData["ip"]; + + if (idList.is_array()) + { + for (auto &idEntry : idList.array_items()) + { + if (idEntry.is_string()) + { + SteamID id; + id.Bits = strtoull(idEntry.string_value().data(), nullptr, 16); + + list->idList.push_back(id); + } + } + } + + if (ipList.is_array()) + { + for (auto &ipEntry : ipList.array_items()) + { + if (ipEntry.is_string()) + { + Network::Address addr(ipEntry.string_value()); + + list->ipList.push_back(addr.getIP()); + } + } + } + } + } + } + + void Bans::BanClientNum(int num, std::string reason) + { + if (!Dvar::Var("sv_running").get()) + { + Logger::Print("Server is not running.\n"); + return; + } + + if (*Game::svs_numclients <= num) + { + Logger::Print("Player %d is not on the server\n", num); + return; + } + + Game::client_t* client = &Game::svs_clients[num]; + + SteamID guid; + guid.Bits = client->steamid; + + Bans::InsertBan({ guid, client->addr.ip }); + + Game::SV_KickClientError(client, reason); + } + + Bans::Bans() + { + Command::Add("banclient", [] (Command::Params* params) + { + if (params->length() < 2) return; + + std::string reason = "EXE_ERR_BANNED_PERM"; + if (params->length() >= 3) reason = params->get(2); + + Bans::BanClientNum(atoi(params->get(1)), reason); + }); + + // Verify the list on startup + QuickPatch::Once([] () + { + Bans::BanList list; + Bans::LoadBans(&list); + }); + } + + Bans::~Bans() + { + + } +} diff --git a/src/Components/Modules/Bans.hpp b/src/Components/Modules/Bans.hpp index b8e79ab0..477810e8 100644 --- a/src/Components/Modules/Bans.hpp +++ b/src/Components/Modules/Bans.hpp @@ -1,31 +1,31 @@ -namespace Components -{ - class Bans : public Component - { - public: - typedef std::pair Entry; - - Bans(); - ~Bans(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Bans"; }; -#endif - - static void BanClientNum(int num, std::string reason); - - static bool IsBanned(Entry entry); - static void InsertBan(Entry entry); - - private: - class BanList - { - public: - std::vector idList; - std::vector ipList; - }; - - static std::mutex AccessMutex; - static void LoadBans(BanList* list); - }; -} +namespace Components +{ + class Bans : public Component + { + public: + typedef std::pair Entry; + + Bans(); + ~Bans(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Bans"; }; +#endif + + static void BanClientNum(int num, std::string reason); + + static bool IsBanned(Entry entry); + static void InsertBan(Entry entry); + + private: + class BanList + { + public: + std::vector idList; + std::vector ipList; + }; + + static std::mutex AccessMutex; + static void LoadBans(BanList* list); + }; +} diff --git a/src/Components/Modules/Bots.cpp b/src/Components/Modules/Bots.cpp index 310a0ed8..d8bdbf92 100644 --- a/src/Components/Modules/Bots.cpp +++ b/src/Components/Modules/Bots.cpp @@ -1,54 +1,54 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::vector Bots::BotNames; - - void Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port) - { - static int botId = 0; - - if (Bots::BotNames.empty()) - { - FileSystem::File bots("bots.txt"); - - if (bots.exists()) - { - std::vector names = Utils::String::Explode(bots.getBuffer(), '\n'); - - for (auto name : names) - { - Utils::String::Replace(name, "\r", ""); - name = Utils::String::Trim(name); - - if (!name.empty()) - { - Bots::BotNames.push_back(name); - } - } - } - - if (Bots::BotNames.empty()) - { - Bots::BotNames.push_back("bot"); - } - } - - botId %= Bots::BotNames.size(); - strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, Bots::BotNames[botId++].data(), protocol, checksum, statVer, statStuff, port), 0x400); - } - - Bots::Bots() - { - // Replace connect string - Utils::Hook::Set(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\""); - - // Intercept sprintf for the connect string - Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick(); - } - - Bots::~Bots() - { - Bots::BotNames.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::vector Bots::BotNames; + + void Bots::BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port) + { + static int botId = 0; + + if (Bots::BotNames.empty()) + { + FileSystem::File bots("bots.txt"); + + if (bots.exists()) + { + std::vector names = Utils::String::Explode(bots.getBuffer(), '\n'); + + for (auto name : names) + { + Utils::String::Replace(name, "\r", ""); + name = Utils::String::Trim(name); + + if (!name.empty()) + { + Bots::BotNames.push_back(name); + } + } + } + + if (Bots::BotNames.empty()) + { + Bots::BotNames.push_back("bot"); + } + } + + botId %= Bots::BotNames.size(); + strncpy_s(buffer, 0x400, Utils::String::VA(connectString, num, Bots::BotNames[botId++].data(), protocol, checksum, statVer, statStuff, port), 0x400); + } + + Bots::Bots() + { + // Replace connect string + Utils::Hook::Set(0x48ADA6, "connect bot%d \"\\cg_predictItems\\1\\cl_anonymous\\0\\color\\4\\head\\default\\model\\multi\\snaps\\20\\rate\\5000\\name\\%s\\protocol\\%d\\checksum\\%d\\statver\\%d %u\\qport\\%d\""); + + // Intercept sprintf for the connect string + Utils::Hook(0x48ADAB, Bots::BuildConnectString, HOOK_CALL).install()->quick(); + } + + Bots::~Bots() + { + Bots::BotNames.clear(); + } +} diff --git a/src/Components/Modules/Bots.hpp b/src/Components/Modules/Bots.hpp index a17cc4e1..57bea9b8 100644 --- a/src/Components/Modules/Bots.hpp +++ b/src/Components/Modules/Bots.hpp @@ -1,18 +1,18 @@ -namespace Components -{ - class Bots : public Component - { - public: - Bots(); - ~Bots(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Bots"; }; -#endif - - private: - static std::vector BotNames; - - static void BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port); - }; -} +namespace Components +{ + class Bots : public Component + { + public: + Bots(); + ~Bots(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Bots"; }; +#endif + + private: + static std::vector BotNames; + + static void BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port); + }; +} diff --git a/src/Components/Modules/Colors.cpp b/src/Components/Modules/Colors.cpp index e1f1af61..2b5f0b3e 100644 --- a/src/Components/Modules/Colors.cpp +++ b/src/Components/Modules/Colors.cpp @@ -1,254 +1,254 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Dvar::Var Colors::NewColors; - std::vector Colors::ColorTable; - - DWORD Colors::HsvToRgb(Colors::HsvColor hsv) - { - DWORD rgb; - unsigned char region, p, q, t; - unsigned int h, s, v, remainder; - - if (hsv.s == 0) - { - rgb = RGB(hsv.v, hsv.v, hsv.v); - return rgb; - } - - // converting to 16 bit to prevent overflow - h = hsv.h; - s = hsv.s; - v = hsv.v; - - region = static_cast(h / 43); - remainder = (h - (region * 43)) * 6; - - p = static_cast((v * (255 - s)) >> 8); - q = static_cast((v * (255 - ((s * remainder) >> 8))) >> 8); - t = static_cast((v * (255 - ((s * (255 - remainder)) >> 8))) >> 8); - - switch (region) - { - case 0: - rgb = RGB(v, t, p); - break; - case 1: - rgb = RGB(q, v, p); - break; - case 2: - rgb = RGB(p, v, t); - break; - case 3: - rgb = RGB(p, q, v); - break; - case 4: - rgb = RGB(t, p, v); - break; - default: - rgb = RGB(v, p, q); - break; - } - - return rgb; - } - - void Colors::Strip(const char* in, char* out, int max) - { - if (!in || !out) return; - - max--; - int current = 0; - while (*in != 0 && current < max) - { - char index = *(in + 1); - if (*in == '^' && (Colors::ColorIndex(index) != 7 || index == '7')) - { - ++in; - } - else - { - *out = *in; - ++out; - ++current; - } - - ++in; - } - *out = '\0'; - } - - std::string Colors::Strip(std::string in) - { - char buffer[1000] = { 0 }; // Should be more than enough - Colors::Strip(in.data(), buffer, sizeof(buffer)); - return std::string(buffer); - } - - __declspec(naked) void Colors::ClientUserinfoChanged() - { - __asm - { - mov eax, [esp + 4h] // length - sub eax, 1 - push eax - - push ecx // name - push edx // buffer - - call strncpy - - add esp, 0Ch - retn - } - } - - char* Colors::GetClientName(int localClientNum, int index, char *buf, size_t size) - { - Game::CL_GetClientName(localClientNum, index, buf, size); - - // Remove the colors - strncpy_s(buf, size, Colors::Strip(buf).data(), size); - - return buf; - } - - void Colors::PatchColorLimit(char limit) - { - Utils::Hook::Set(0x535629, limit); // DrawText2d - Utils::Hook::Set(0x4C1BE4, limit); // No idea :P - Utils::Hook::Set(0x4863DD, limit); // No idea :P - Utils::Hook::Set(0x486429, limit); // No idea :P - Utils::Hook::Set(0x49A5A8, limit); // No idea :P - Utils::Hook::Set(0x505721, limit); // R_TextWidth - Utils::Hook::Set(0x505801, limit); // No idea :P - Utils::Hook::Set(0x50597F, limit); // No idea :P - Utils::Hook::Set(0x5815DB, limit); // No idea :P - Utils::Hook::Set(0x592ED0, limit); // No idea :P - Utils::Hook::Set(0x5A2E2E, limit); // No idea :P - - Utils::Hook::Set(0x5A2733, limit - '0'); // No idea :P - } - - char Colors::Add(uint8_t r, uint8_t g, uint8_t b) - { - char index = '0' + static_cast(Colors::ColorTable.size()); - Colors::ColorTable.push_back(RGB(r, g, b)); - Colors::PatchColorLimit(index); - return index; - } - - unsigned int Colors::ColorIndex(unsigned char index) - { - unsigned int result = index - '0'; - if (result >= Colors::ColorTable.size() || result < 0) result = 7; - return result; - } - - void Colors::LookupColor(DWORD* color, char index) - { - *color = RGB(255, 255, 255); - - if (index == '8') // Color 8 - { - *color = *reinterpret_cast(0x66E5F70); - } - else if (index == '9') // Color 9 - { - *color = *reinterpret_cast(0x66E5F74); - } - else if (index == ':') - { - *color = Colors::HsvToRgb({ static_cast((Game::Sys_Milliseconds() / 200) % 256), 255,255 }); - } - else if (index == ';') - { - float fltColor[4]; - Game::Dvar_GetUnpackedColorByName("sv_customTextColor", fltColor); - *color = RGB(fltColor[0] * 255, fltColor[1] * 255, fltColor[2] * 255); - } - else - { - int clrIndex = Colors::ColorIndex(index); - - // Use native colors - if (clrIndex <= 7 && !Colors::NewColors.get()) - { - *color = reinterpret_cast(0x78DC70)[index - 48]; - } - else - { - *color = Colors::ColorTable[clrIndex]; - } - } - } - - char* Colors::CleanStrStub(char* string) - { - Colors::Strip(string, string, strlen(string) + 1); - return string; - } - - __declspec(naked) void Colors::LookupColorStub() - { - __asm - { - push ebx - push [esp + 8h] // Index - push esi // Color ref - call Colors::LookupColor - add esp, 8h - pop ebx - retn - } - } - - Colors::Colors() - { - // Disable SV_UpdateUserinfo_f, to block changing the name ingame - Utils::Hook::Set(0x6258D0, 0xC3); - - // Allow colored names ingame - Utils::Hook(0x5D8B40, Colors::ClientUserinfoChanged, HOOK_JUMP).install()->quick(); - - // Though, don't apply that to overhead names. - Utils::Hook(0x581932, Colors::GetClientName, HOOK_CALL).install()->quick(); - - // Patch RB_LookupColor - Utils::Hook(0x534CD0, Colors::LookupColorStub, HOOK_JUMP).install()->quick(); - - // Patch ColorIndex - Utils::Hook(0x417770, Colors::ColorIndex, HOOK_JUMP).install()->quick(); - - // Patch I_CleanStr - Utils::Hook(0x4AD470, Colors::CleanStrStub, HOOK_JUMP).install()->quick(); - - // Register dvar - Colors::NewColors = Dvar::Register("cg_newColors", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Use Warfare² color code style."); - Game::Dvar_RegisterColor("sv_customTextColor", 1, 0.7f, 0, 1, Game::dvar_flag::DVAR_FLAG_REPLICATED, "Color for the extended color code."); - - // Add our colors - Colors::Add(0, 0, 0); // 0 - Black - Colors::Add(255, 49, 49); // 1 - Red - Colors::Add(134, 192, 0); // 2 - Green - Colors::Add(255, 173, 34); // 3 - Yellow - Colors::Add(0, 135, 193); // 4 - Blue - Colors::Add(32, 197, 255); // 5 - Light Blue - Colors::Add(151, 80, 221); // 6 - Pink - - Colors::Add(255, 255, 255); // 7 - White - - Colors::Add(0, 0, 0); // 8 - Team color (axis?) - Colors::Add(0, 0, 0); // 9 - Team color (allies?) - - // Custom colors - Colors::Add(0, 0, 0); // 10 - Rainbow (:) - Colors::Add(0, 0, 0); // 11 - Server color (;) - using that color in infostrings (e.g. your name) fails, ';' is an illegal character! - } - - Colors::~Colors() - { - Colors::ColorTable.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Dvar::Var Colors::NewColors; + std::vector Colors::ColorTable; + + DWORD Colors::HsvToRgb(Colors::HsvColor hsv) + { + DWORD rgb; + unsigned char region, p, q, t; + unsigned int h, s, v, remainder; + + if (hsv.s == 0) + { + rgb = RGB(hsv.v, hsv.v, hsv.v); + return rgb; + } + + // converting to 16 bit to prevent overflow + h = hsv.h; + s = hsv.s; + v = hsv.v; + + region = static_cast(h / 43); + remainder = (h - (region * 43)) * 6; + + p = static_cast((v * (255 - s)) >> 8); + q = static_cast((v * (255 - ((s * remainder) >> 8))) >> 8); + t = static_cast((v * (255 - ((s * (255 - remainder)) >> 8))) >> 8); + + switch (region) + { + case 0: + rgb = RGB(v, t, p); + break; + case 1: + rgb = RGB(q, v, p); + break; + case 2: + rgb = RGB(p, v, t); + break; + case 3: + rgb = RGB(p, q, v); + break; + case 4: + rgb = RGB(t, p, v); + break; + default: + rgb = RGB(v, p, q); + break; + } + + return rgb; + } + + void Colors::Strip(const char* in, char* out, int max) + { + if (!in || !out) return; + + max--; + int current = 0; + while (*in != 0 && current < max) + { + char index = *(in + 1); + if (*in == '^' && (Colors::ColorIndex(index) != 7 || index == '7')) + { + ++in; + } + else + { + *out = *in; + ++out; + ++current; + } + + ++in; + } + *out = '\0'; + } + + std::string Colors::Strip(std::string in) + { + char buffer[1000] = { 0 }; // Should be more than enough + Colors::Strip(in.data(), buffer, sizeof(buffer)); + return std::string(buffer); + } + + __declspec(naked) void Colors::ClientUserinfoChanged() + { + __asm + { + mov eax, [esp + 4h] // length + sub eax, 1 + push eax + + push ecx // name + push edx // buffer + + call strncpy + + add esp, 0Ch + retn + } + } + + char* Colors::GetClientName(int localClientNum, int index, char *buf, size_t size) + { + Game::CL_GetClientName(localClientNum, index, buf, size); + + // Remove the colors + strncpy_s(buf, size, Colors::Strip(buf).data(), size); + + return buf; + } + + void Colors::PatchColorLimit(char limit) + { + Utils::Hook::Set(0x535629, limit); // DrawText2d + Utils::Hook::Set(0x4C1BE4, limit); // No idea :P + Utils::Hook::Set(0x4863DD, limit); // No idea :P + Utils::Hook::Set(0x486429, limit); // No idea :P + Utils::Hook::Set(0x49A5A8, limit); // No idea :P + Utils::Hook::Set(0x505721, limit); // R_TextWidth + Utils::Hook::Set(0x505801, limit); // No idea :P + Utils::Hook::Set(0x50597F, limit); // No idea :P + Utils::Hook::Set(0x5815DB, limit); // No idea :P + Utils::Hook::Set(0x592ED0, limit); // No idea :P + Utils::Hook::Set(0x5A2E2E, limit); // No idea :P + + Utils::Hook::Set(0x5A2733, limit - '0'); // No idea :P + } + + char Colors::Add(uint8_t r, uint8_t g, uint8_t b) + { + char index = '0' + static_cast(Colors::ColorTable.size()); + Colors::ColorTable.push_back(RGB(r, g, b)); + Colors::PatchColorLimit(index); + return index; + } + + unsigned int Colors::ColorIndex(unsigned char index) + { + unsigned int result = index - '0'; + if (result >= Colors::ColorTable.size() || result < 0) result = 7; + return result; + } + + void Colors::LookupColor(DWORD* color, char index) + { + *color = RGB(255, 255, 255); + + if (index == '8') // Color 8 + { + *color = *reinterpret_cast(0x66E5F70); + } + else if (index == '9') // Color 9 + { + *color = *reinterpret_cast(0x66E5F74); + } + else if (index == ':') + { + *color = Colors::HsvToRgb({ static_cast((Game::Sys_Milliseconds() / 200) % 256), 255,255 }); + } + else if (index == ';') + { + float fltColor[4]; + Game::Dvar_GetUnpackedColorByName("sv_customTextColor", fltColor); + *color = RGB(fltColor[0] * 255, fltColor[1] * 255, fltColor[2] * 255); + } + else + { + int clrIndex = Colors::ColorIndex(index); + + // Use native colors + if (clrIndex <= 7 && !Colors::NewColors.get()) + { + *color = reinterpret_cast(0x78DC70)[index - 48]; + } + else + { + *color = Colors::ColorTable[clrIndex]; + } + } + } + + char* Colors::CleanStrStub(char* string) + { + Colors::Strip(string, string, strlen(string) + 1); + return string; + } + + __declspec(naked) void Colors::LookupColorStub() + { + __asm + { + push ebx + push [esp + 8h] // Index + push esi // Color ref + call Colors::LookupColor + add esp, 8h + pop ebx + retn + } + } + + Colors::Colors() + { + // Disable SV_UpdateUserinfo_f, to block changing the name ingame + Utils::Hook::Set(0x6258D0, 0xC3); + + // Allow colored names ingame + Utils::Hook(0x5D8B40, Colors::ClientUserinfoChanged, HOOK_JUMP).install()->quick(); + + // Though, don't apply that to overhead names. + Utils::Hook(0x581932, Colors::GetClientName, HOOK_CALL).install()->quick(); + + // Patch RB_LookupColor + Utils::Hook(0x534CD0, Colors::LookupColorStub, HOOK_JUMP).install()->quick(); + + // Patch ColorIndex + Utils::Hook(0x417770, Colors::ColorIndex, HOOK_JUMP).install()->quick(); + + // Patch I_CleanStr + Utils::Hook(0x4AD470, Colors::CleanStrStub, HOOK_JUMP).install()->quick(); + + // Register dvar + Colors::NewColors = Dvar::Register("cg_newColors", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Use Warfare² color code style."); + Game::Dvar_RegisterColor("sv_customTextColor", 1, 0.7f, 0, 1, Game::dvar_flag::DVAR_FLAG_REPLICATED, "Color for the extended color code."); + + // Add our colors + Colors::Add(0, 0, 0); // 0 - Black + Colors::Add(255, 49, 49); // 1 - Red + Colors::Add(134, 192, 0); // 2 - Green + Colors::Add(255, 173, 34); // 3 - Yellow + Colors::Add(0, 135, 193); // 4 - Blue + Colors::Add(32, 197, 255); // 5 - Light Blue + Colors::Add(151, 80, 221); // 6 - Pink + + Colors::Add(255, 255, 255); // 7 - White + + Colors::Add(0, 0, 0); // 8 - Team color (axis?) + Colors::Add(0, 0, 0); // 9 - Team color (allies?) + + // Custom colors + Colors::Add(0, 0, 0); // 10 - Rainbow (:) + Colors::Add(0, 0, 0); // 11 - Server color (;) - using that color in infostrings (e.g. your name) fails, ';' is an illegal character! + } + + Colors::~Colors() + { + Colors::ColorTable.clear(); + } +} diff --git a/src/Components/Modules/Colors.hpp b/src/Components/Modules/Colors.hpp index 8d5354c9..d6d826b1 100644 --- a/src/Components/Modules/Colors.hpp +++ b/src/Components/Modules/Colors.hpp @@ -1,40 +1,40 @@ -namespace Components -{ - class Colors : public Component - { - public: - Colors(); - ~Colors(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Colors"; }; -#endif - - static void Strip(const char* in, char* out, int max); - static std::string Strip(std::string in); - - static char Add(uint8_t r, uint8_t g, uint8_t b); - - private: - struct HsvColor - { - unsigned char h; - unsigned char s; - unsigned char v; - }; - - static Dvar::Var NewColors; - - static DWORD HsvToRgb(HsvColor hsv); - - static void ClientUserinfoChanged(); - static char* GetClientName(int localClientNum, int index, char *buf, size_t size); - static void PatchColorLimit(char limit); - - static unsigned int ColorIndex(unsigned char); - static void LookupColor(DWORD* color, char index); - static void LookupColorStub(); - static char* CleanStrStub(char* string); - static std::vector ColorTable; - }; -} +namespace Components +{ + class Colors : public Component + { + public: + Colors(); + ~Colors(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Colors"; }; +#endif + + static void Strip(const char* in, char* out, int max); + static std::string Strip(std::string in); + + static char Add(uint8_t r, uint8_t g, uint8_t b); + + private: + struct HsvColor + { + unsigned char h; + unsigned char s; + unsigned char v; + }; + + static Dvar::Var NewColors; + + static DWORD HsvToRgb(HsvColor hsv); + + static void ClientUserinfoChanged(); + static char* GetClientName(int localClientNum, int index, char *buf, size_t size); + static void PatchColorLimit(char limit); + + static unsigned int ColorIndex(unsigned char); + static void LookupColor(DWORD* color, char index); + static void LookupColorStub(); + static char* CleanStrStub(char* string); + static std::vector ColorTable; + }; +} diff --git a/src/Components/Modules/Command.hpp b/src/Components/Modules/Command.hpp index 4681ca25..95c91ca4 100644 --- a/src/Components/Modules/Command.hpp +++ b/src/Components/Modules/Command.hpp @@ -1,73 +1,73 @@ -namespace Components -{ - class Command : public Component - { - public: - class Params - { - public: - Params() {}; - virtual ~Params() {}; - virtual char* get(size_t index) = 0; - virtual size_t length() = 0; - - virtual std::string join(size_t startIndex); - virtual char* operator[](size_t index); - }; - - class ClientParams : public Params - { - public: - ClientParams(unsigned int id) : commandId(id) {}; - ClientParams(const ClientParams &obj) : commandId(obj.commandId) {}; - ClientParams() : ClientParams(*Game::cmd_id) {}; - - char* get(size_t index) override; - size_t length() override; - - private: - unsigned int commandId; - }; - - class ServerParams : public Params - { - public: - ServerParams(unsigned int id) : commandId(id) {}; - ServerParams(const ServerParams &obj) : commandId(obj.commandId) {}; - ServerParams() : ServerParams(*Game::cmd_id_sv) {}; - - char* get(size_t index) override; - size_t length() override; - - private: - unsigned int commandId; - }; - - typedef void(Callback)(Command::Params* params); - - Command(); - ~Command(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Command"; }; -#endif - - static Game::cmd_function_t* Allocate(); - - static void Add(const char* name, Utils::Slot callback); - static void AddSV(const char* name, Utils::Slot callback); - static void AddRaw(const char* name, void(*callback)(), bool key = false); - static void AddRawSV(const char* name, void(*callback)()); - static void Execute(std::string command, bool sync = true); - - static Game::cmd_function_t* Find(std::string command); - - private: - static Utils::Memory::Allocator MemAllocator; - static std::unordered_map> FunctionMap; - static std::unordered_map> FunctionMapSV; - - static void MainCallback(); - static void MainCallbackSV(); - }; -} +namespace Components +{ + class Command : public Component + { + public: + class Params + { + public: + Params() {}; + virtual ~Params() {}; + virtual char* get(size_t index) = 0; + virtual size_t length() = 0; + + virtual std::string join(size_t startIndex); + virtual char* operator[](size_t index); + }; + + class ClientParams : public Params + { + public: + ClientParams(unsigned int id) : commandId(id) {}; + ClientParams(const ClientParams &obj) : commandId(obj.commandId) {}; + ClientParams() : ClientParams(*Game::cmd_id) {}; + + char* get(size_t index) override; + size_t length() override; + + private: + unsigned int commandId; + }; + + class ServerParams : public Params + { + public: + ServerParams(unsigned int id) : commandId(id) {}; + ServerParams(const ServerParams &obj) : commandId(obj.commandId) {}; + ServerParams() : ServerParams(*Game::cmd_id_sv) {}; + + char* get(size_t index) override; + size_t length() override; + + private: + unsigned int commandId; + }; + + typedef void(Callback)(Command::Params* params); + + Command(); + ~Command(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Command"; }; +#endif + + static Game::cmd_function_t* Allocate(); + + static void Add(const char* name, Utils::Slot callback); + static void AddSV(const char* name, Utils::Slot callback); + static void AddRaw(const char* name, void(*callback)(), bool key = false); + static void AddRawSV(const char* name, void(*callback)()); + static void Execute(std::string command, bool sync = true); + + static Game::cmd_function_t* Find(std::string command); + + private: + static Utils::Memory::Allocator MemAllocator; + static std::unordered_map> FunctionMap; + static std::unordered_map> FunctionMapSV; + + static void MainCallback(); + static void MainCallbackSV(); + }; +} diff --git a/src/Components/Modules/ConnectProtocol.cpp b/src/Components/Modules/ConnectProtocol.cpp index 64ed0d9d..cbea2ee9 100644 --- a/src/Components/Modules/ConnectProtocol.cpp +++ b/src/Components/Modules/ConnectProtocol.cpp @@ -1,242 +1,242 @@ -#include "STDInclude.hpp" - -namespace Components -{ - bool ConnectProtocol::Evaluated = false; - std::string ConnectProtocol::ConnectString; - - bool ConnectProtocol::IsEvaluated() - { - return ConnectProtocol::Evaluated; - } - - bool ConnectProtocol::Used() - { - if (!ConnectProtocol::IsEvaluated()) - { - ConnectProtocol::EvaluateProtocol(); - } - - return (!ConnectProtocol::ConnectString.empty()); - } - - bool ConnectProtocol::InstallProtocol() - { - HKEY hKey = NULL; - std::string data; - - char ownPth[MAX_PATH] = { 0 }; - char workdir[MAX_PATH] = { 0 }; - - DWORD dwsize = MAX_PATH; - HMODULE hModule = GetModuleHandle(NULL); - - if (hModule != NULL) - { - if (GetModuleFileNameA(hModule, ownPth, MAX_PATH) == ERROR) - { - return false; - } - - if (GetModuleFileNameA(hModule, workdir, MAX_PATH) == ERROR) - { - return false; - } - else - { - char* endPtr = strstr(workdir, "iw4x.exe"); - if (endPtr != NULL) - { - *endPtr = 0; - } - else - { - return false; - } - } - } - else - { - return false; - } - - SetCurrentDirectoryA(workdir); - - LONG openRes = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x\\shell\\open\\command", 0, KEY_ALL_ACCESS, &hKey); - if (openRes == ERROR_SUCCESS) - { - char regred[MAX_PATH] = { 0 }; - - // Check if the game has been moved. - openRes = RegQueryValueExA(hKey, 0, 0, 0, reinterpret_cast(regred), &dwsize); - if (openRes == ERROR_SUCCESS) - { - char* endPtr = strstr(regred, "\" \"%1\""); - if (endPtr != NULL) - { - *endPtr = 0; - } - else - { - return false; - } - - RegCloseKey(hKey); - if (strcmp(regred + 1, ownPth)) - { - openRes = RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x"); - } - else - { - return true; - } - } - else - { - openRes = RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x"); - } - } - else - { - openRes = RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x"); - } - - // Open SOFTWARE\\Classes - openRes = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Classes", 0, KEY_ALL_ACCESS, &hKey); - - if (openRes != ERROR_SUCCESS) - { - return false; - } - - // Create SOFTWARE\\Classes\\iw4x - openRes = RegCreateKeyExA(hKey, "iw4x", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, 0); - - if (openRes != ERROR_SUCCESS) - { - return false; - } - - // Write URL:IW4x Protocol - data = "URL:IW4x Protocol"; - openRes = RegSetValueExA(hKey, "URL Protocol", 0, REG_SZ, reinterpret_cast(data.data()), data.size() + 1); - - if (openRes != ERROR_SUCCESS) - { - RegCloseKey(hKey); - return false; - } - - // Create SOFTWARE\\Classes\\iw4x\\DefaultIcon - openRes = RegCreateKeyExA(hKey, "DefaultIcon", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, 0); - - if (openRes != ERROR_SUCCESS) - { - return false; - } - - data = Utils::String::VA("%s,1", ownPth); - openRes = RegSetValueExA(hKey, 0, 0, REG_SZ, reinterpret_cast(data.data()), data.size() + 1); - RegCloseKey(hKey); - - if (openRes != ERROR_SUCCESS) - { - RegCloseKey(hKey); - return false; - } - - openRes = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x", 0, KEY_ALL_ACCESS, &hKey); - - if (openRes != ERROR_SUCCESS) - { - return false; - } - - openRes = RegCreateKeyExA(hKey, "shell\\open\\command", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, 0); - - if (openRes != ERROR_SUCCESS) - { - return false; - } - - data = Utils::String::VA("\"%s\" \"%s\"", ownPth, "%1"); - openRes = RegSetValueExA(hKey, 0, 0, REG_SZ, reinterpret_cast(data.data()), data.size() + 1); - RegCloseKey(hKey); - - if (openRes != ERROR_SUCCESS) - { - return false; - } - - return true; - } - - void ConnectProtocol::EvaluateProtocol() - { - if (ConnectProtocol::Evaluated) return; - ConnectProtocol::Evaluated = true; - - std::string cmdLine = GetCommandLineA(); - - auto pos = cmdLine.find("iw4x://"); - - if (pos != std::string::npos) - { - cmdLine = cmdLine.substr(pos + 7); - pos = cmdLine.find_first_of("/"); - - if (pos != std::string::npos) - { - cmdLine = cmdLine.substr(0, pos); - } - - ConnectProtocol::ConnectString = cmdLine; - } - } - - void ConnectProtocol::Invocation() - { - if (ConnectProtocol::Used()) - { - if (!FastFiles::Ready()) - { - QuickPatch::Once(ConnectProtocol::Invocation); - } - else - { - Command::Execute(Utils::String::VA("connect %s", ConnectProtocol::ConnectString.data()), false); - } - } - } - - ConnectProtocol::ConnectProtocol() - { - // IPC handler - IPCPipe::On("connect", [] (std::string data) - { - Command::Execute(Utils::String::VA("connect %s", data.data()), false); - }); - - // Invocation handler - QuickPatch::Once(ConnectProtocol::Invocation); - - ConnectProtocol::InstallProtocol(); - ConnectProtocol::EvaluateProtocol(); - - // Fire protocol handlers - // Make sure this happens after the pipe-initialization! - if (ConnectProtocol::Used()) - { - if (!Singleton::IsFirstInstance()) - { - IPCPipe::Write("connect", ConnectProtocol::ConnectString); - ExitProcess(0); - } - else - { - // Only skip intro here, invocation will be done later. - //Utils::Hook::Set(0x60BECF, 0xEB); - } - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + bool ConnectProtocol::Evaluated = false; + std::string ConnectProtocol::ConnectString; + + bool ConnectProtocol::IsEvaluated() + { + return ConnectProtocol::Evaluated; + } + + bool ConnectProtocol::Used() + { + if (!ConnectProtocol::IsEvaluated()) + { + ConnectProtocol::EvaluateProtocol(); + } + + return (!ConnectProtocol::ConnectString.empty()); + } + + bool ConnectProtocol::InstallProtocol() + { + HKEY hKey = NULL; + std::string data; + + char ownPth[MAX_PATH] = { 0 }; + char workdir[MAX_PATH] = { 0 }; + + DWORD dwsize = MAX_PATH; + HMODULE hModule = GetModuleHandle(NULL); + + if (hModule != NULL) + { + if (GetModuleFileNameA(hModule, ownPth, MAX_PATH) == ERROR) + { + return false; + } + + if (GetModuleFileNameA(hModule, workdir, MAX_PATH) == ERROR) + { + return false; + } + else + { + char* endPtr = strstr(workdir, "iw4x.exe"); + if (endPtr != NULL) + { + *endPtr = 0; + } + else + { + return false; + } + } + } + else + { + return false; + } + + SetCurrentDirectoryA(workdir); + + LONG openRes = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x\\shell\\open\\command", 0, KEY_ALL_ACCESS, &hKey); + if (openRes == ERROR_SUCCESS) + { + char regred[MAX_PATH] = { 0 }; + + // Check if the game has been moved. + openRes = RegQueryValueExA(hKey, 0, 0, 0, reinterpret_cast(regred), &dwsize); + if (openRes == ERROR_SUCCESS) + { + char* endPtr = strstr(regred, "\" \"%1\""); + if (endPtr != NULL) + { + *endPtr = 0; + } + else + { + return false; + } + + RegCloseKey(hKey); + if (strcmp(regred + 1, ownPth)) + { + openRes = RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x"); + } + else + { + return true; + } + } + else + { + openRes = RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x"); + } + } + else + { + openRes = RegDeleteKeyA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x"); + } + + // Open SOFTWARE\\Classes + openRes = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Classes", 0, KEY_ALL_ACCESS, &hKey); + + if (openRes != ERROR_SUCCESS) + { + return false; + } + + // Create SOFTWARE\\Classes\\iw4x + openRes = RegCreateKeyExA(hKey, "iw4x", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, 0); + + if (openRes != ERROR_SUCCESS) + { + return false; + } + + // Write URL:IW4x Protocol + data = "URL:IW4x Protocol"; + openRes = RegSetValueExA(hKey, "URL Protocol", 0, REG_SZ, reinterpret_cast(data.data()), data.size() + 1); + + if (openRes != ERROR_SUCCESS) + { + RegCloseKey(hKey); + return false; + } + + // Create SOFTWARE\\Classes\\iw4x\\DefaultIcon + openRes = RegCreateKeyExA(hKey, "DefaultIcon", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, 0); + + if (openRes != ERROR_SUCCESS) + { + return false; + } + + data = Utils::String::VA("%s,1", ownPth); + openRes = RegSetValueExA(hKey, 0, 0, REG_SZ, reinterpret_cast(data.data()), data.size() + 1); + RegCloseKey(hKey); + + if (openRes != ERROR_SUCCESS) + { + RegCloseKey(hKey); + return false; + } + + openRes = RegOpenKeyExA(HKEY_CURRENT_USER, "SOFTWARE\\Classes\\iw4x", 0, KEY_ALL_ACCESS, &hKey); + + if (openRes != ERROR_SUCCESS) + { + return false; + } + + openRes = RegCreateKeyExA(hKey, "shell\\open\\command", 0, 0, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, 0, &hKey, 0); + + if (openRes != ERROR_SUCCESS) + { + return false; + } + + data = Utils::String::VA("\"%s\" \"%s\"", ownPth, "%1"); + openRes = RegSetValueExA(hKey, 0, 0, REG_SZ, reinterpret_cast(data.data()), data.size() + 1); + RegCloseKey(hKey); + + if (openRes != ERROR_SUCCESS) + { + return false; + } + + return true; + } + + void ConnectProtocol::EvaluateProtocol() + { + if (ConnectProtocol::Evaluated) return; + ConnectProtocol::Evaluated = true; + + std::string cmdLine = GetCommandLineA(); + + auto pos = cmdLine.find("iw4x://"); + + if (pos != std::string::npos) + { + cmdLine = cmdLine.substr(pos + 7); + pos = cmdLine.find_first_of("/"); + + if (pos != std::string::npos) + { + cmdLine = cmdLine.substr(0, pos); + } + + ConnectProtocol::ConnectString = cmdLine; + } + } + + void ConnectProtocol::Invocation() + { + if (ConnectProtocol::Used()) + { + if (!FastFiles::Ready()) + { + QuickPatch::Once(ConnectProtocol::Invocation); + } + else + { + Command::Execute(Utils::String::VA("connect %s", ConnectProtocol::ConnectString.data()), false); + } + } + } + + ConnectProtocol::ConnectProtocol() + { + // IPC handler + IPCPipe::On("connect", [] (std::string data) + { + Command::Execute(Utils::String::VA("connect %s", data.data()), false); + }); + + // Invocation handler + QuickPatch::Once(ConnectProtocol::Invocation); + + ConnectProtocol::InstallProtocol(); + ConnectProtocol::EvaluateProtocol(); + + // Fire protocol handlers + // Make sure this happens after the pipe-initialization! + if (ConnectProtocol::Used()) + { + if (!Singleton::IsFirstInstance()) + { + IPCPipe::Write("connect", ConnectProtocol::ConnectString); + ExitProcess(0); + } + else + { + // Only skip intro here, invocation will be done later. + //Utils::Hook::Set(0x60BECF, 0xEB); + } + } + } +} diff --git a/src/Components/Modules/ConnectProtocol.hpp b/src/Components/Modules/ConnectProtocol.hpp index a73bd59b..27788868 100644 --- a/src/Components/Modules/ConnectProtocol.hpp +++ b/src/Components/Modules/ConnectProtocol.hpp @@ -1,24 +1,24 @@ -namespace Components -{ - class ConnectProtocol : public Component - { - public: - ConnectProtocol(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "ConnectProtocol"; }; -#endif - - static bool IsEvaluated(); - static bool Used(); - - private: - static bool Evaluated; - static std::string ConnectString; - - static void EvaluateProtocol(); - static bool InstallProtocol(); - - static void Invocation(); - }; -} +namespace Components +{ + class ConnectProtocol : public Component + { + public: + ConnectProtocol(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "ConnectProtocol"; }; +#endif + + static bool IsEvaluated(); + static bool Used(); + + private: + static bool Evaluated; + static std::string ConnectString; + + static void EvaluateProtocol(); + static bool InstallProtocol(); + + static void Invocation(); + }; +} diff --git a/src/Components/Modules/Console.cpp b/src/Components/Modules/Console.cpp index c45f89fb..db769fc3 100644 --- a/src/Components/Modules/Console.cpp +++ b/src/Components/Modules/Console.cpp @@ -1,618 +1,618 @@ -#include "STDInclude.hpp" - -namespace Components -{ - WINDOW* Console::OutputWindow; - WINDOW* Console::InputWindow; - WINDOW* Console::InfoWindow; - - int Console::OutputTop = 0; - int Console::OutBuffer = 0; - int Console::LastRefresh = 0; - - int Console::Height = 25; - int Console::Width = 80; - - char Console::LineBuffer[1024] = { 0 }; - char Console::LineBuffer2[1024] = { 0 }; - int Console::LineBufferIndex = 0; - - bool Console::HasConsole = false; - bool Console::SkipShutdown = false; - - std::thread Console::ConsoleThread; - - Game::SafeArea Console::OriginalSafeArea; - - char** Console::GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType) - { - if (path == reinterpret_cast(0xBAADF00D) || path == reinterpret_cast(0xCDCDCDCD) || IsBadReadPtr(path, 1)) return nullptr; - return Game::FS_GetFileList(path, extension, behavior, numfiles, allocTrackType); - } - - void Console::ToggleConsole() - { - // possibly cls.keyCatchers? - Utils::Hook::Xor(0xB2C538, 1); - - // g_consoleField - Game::Field_Clear((void*)0xA1B6B0); - - // show console output? - Utils::Hook::Set(0xA15F38, 0); - } - - void Console::RefreshStatus() - { - std::string mapname = Dvar::Var("mapname").get(); - std::string hostname = Colors::Strip(Dvar::Var("sv_hostname").get()); - - if (Console::HasConsole) - { - SetConsoleTitleA(hostname.data()); - - int clientCount = 0; - int maxclientCount = *Game::svs_numclients; - - if (maxclientCount) - { - for (int i = 0; i < maxclientCount; ++i) - { - if (Game::svs_clients[i].state >= 3) - { - ++clientCount; - } - } - } - else - { - //maxclientCount = Dvar::Var("sv_maxclients").get(); - maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); - clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); - } - - wclear(Console::InfoWindow); - wprintw(Console::InfoWindow, "%s : %d/%d players : map %s", hostname.data(), clientCount, maxclientCount, (mapname.size() ? mapname.data() : "none")); - wnoutrefresh(Console::InfoWindow); - } - else if(IsWindow(*reinterpret_cast(0x64A3288)) != FALSE) - { - SetWindowTextA(*reinterpret_cast(0x64A3288), Utils::String::VA("IW4x(" VERSION ") : %s", hostname.data())); - } - } - - void Console::ShowPrompt() - { - wattron(Console::InputWindow, COLOR_PAIR(10) | A_BOLD); - wprintw(Console::InputWindow, "%s> ", VERSION); - } - - void Console::RefreshOutput() - { - prefresh(Console::OutputWindow, ((Console::OutputTop > 0) ? (Console::OutputTop - 1) : 0), 0, 1, 0, Console::Height - 2, Console::Width); - } - - void Console::ScrollOutput(int amount) - { - Console::OutputTop += amount; - - if (Console::OutputTop > OUTPUT_MAX_TOP) - { - Console::OutputTop = OUTPUT_MAX_TOP; - } - else if (Console::OutputTop < 0) - { - Console::OutputTop = 0; - } - - // make it only scroll the top if there's more than HEIGHT lines - if (Console::OutBuffer >= 0) - { - Console::OutBuffer += amount; - - if (Console::OutBuffer >= Console::Height) - { - Console::OutBuffer = -1; - } - - if (Console::OutputTop < Console::Height) - { - Console::OutputTop = 0; - } - } - } - - const char* Console::Input() - { - if (!Console::HasConsole) - { - Console::ShowPrompt(); - wrefresh(Console::InputWindow); - Console::HasConsole = true; - } - - int currentTime = static_cast(GetTickCount64()); // Make our compiler happy - if ((currentTime - Console::LastRefresh) > 250) - { - Console::RefreshOutput(); - Console::LastRefresh = currentTime; - } - - int c = wgetch(Console::InputWindow); - - if (c == ERR) - { - return NULL; - } - - switch (c) - { - case '\r': - case 459: // keypad enter - { - wattron(Console::OutputWindow, COLOR_PAIR(10) | A_BOLD); - wprintw(Console::OutputWindow, "%s", "]"); - - if (Console::LineBufferIndex) - { - wprintw(Console::OutputWindow, "%s", Console::LineBuffer); - } - - wprintw(Console::OutputWindow, "%s", "\n"); - wattroff(Console::OutputWindow, A_BOLD); - wclear(Console::InputWindow); - - Console::ShowPrompt(); - - wrefresh(Console::InputWindow); - - Console::ScrollOutput(1); - Console::RefreshOutput(); - - if (Console::LineBufferIndex) - { - strcpy_s(Console::LineBuffer2, Console::LineBuffer); - strcat_s(Console::LineBuffer, "\n"); - Console::LineBufferIndex = 0; - return Console::LineBuffer; - } - - break; - } - case 'c' - 'a' + 1: // ctrl-c - case 27: - { - Console::LineBuffer[0] = '\0'; - Console::LineBufferIndex = 0; - - wclear(Console::InputWindow); - - Console::ShowPrompt(); - - wrefresh(Console::InputWindow); - break; - } - case 8: // backspace - { - if (Console::LineBufferIndex > 0) - { - Console::LineBufferIndex--; - Console::LineBuffer[Console::LineBufferIndex] = '\0'; - - wprintw(Console::InputWindow, "%c %c", static_cast(c), static_cast(c)); - wrefresh(Console::InputWindow); - } - break; - } - case KEY_PPAGE: - { - Console::ScrollOutput(-1); - Console::RefreshOutput(); - break; - } - case KEY_NPAGE: - { - Console::ScrollOutput(1); - Console::RefreshOutput(); - break; - } - case KEY_UP: - { - wclear(Console::InputWindow); - Console::ShowPrompt(); - wprintw(Console::InputWindow, "%s", Console::LineBuffer2); - wrefresh(Console::InputWindow); - - strcpy_s(Console::LineBuffer, Console::LineBuffer2); - Console::LineBufferIndex = strlen(Console::LineBuffer); - break; - } - default: - if (c <= 127 && Console::LineBufferIndex < 1022) - { - // temporary workaround , find out what overwrites our index later on - //consoleLineBufferIndex = strlen(consoleLineBuffer); - - Console::LineBuffer[Console::LineBufferIndex++] = static_cast(c); - Console::LineBuffer[Console::LineBufferIndex] = '\0'; - wprintw(Console::InputWindow, "%c", static_cast(c)); - wrefresh(Console::InputWindow); - } - break; - } - - return NULL; - } - - void Console::Destroy() - { - delwin(Console::OutputWindow); - delwin(Console::InputWindow); - delwin(Console::InfoWindow); - endwin(); - - Console::OutputWindow = nullptr; - Console::InputWindow = nullptr; - Console::InfoWindow = nullptr; - } - - void Console::Create() - { - Console::OutputTop = 0; - Console::OutBuffer = 0; - Console::LastRefresh = 0; - Console::LineBufferIndex = 0; - Console::HasConsole = false; - - CONSOLE_SCREEN_BUFFER_INFO info; - if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info)) - { - Console::Width = info.dwSize.X; - Console::Height = info.srWindow.Bottom - info.srWindow.Top + 1; - } - else - { - Console::Height = 25; - Console::Width = 80; - } - - initscr(); - raw(); - noecho(); - - Console::OutputWindow = newpad(Console::Height - 1, Console::Width); - Console::InputWindow = newwin(1, Console::Width, Console::Height - 1, 0); - Console::InfoWindow = newwin(1, Console::Width, 0, 0); - - scrollok(Console::OutputWindow, true); - idlok(Console::OutputWindow, true); - scrollok(Console::InputWindow, true); - nodelay(Console::InputWindow, true); - keypad(Console::InputWindow, true); - - if (has_colors()) - { - start_color(); - init_pair(1, COLOR_BLACK, COLOR_WHITE); - init_pair(2, COLOR_WHITE, COLOR_BLACK); - init_pair(3, COLOR_RED, COLOR_BLACK); - init_pair(4, COLOR_GREEN, COLOR_BLACK); - init_pair(5, COLOR_YELLOW, COLOR_BLACK); - init_pair(6, COLOR_BLUE, COLOR_BLACK); - init_pair(7, COLOR_CYAN, COLOR_BLACK); - init_pair(8, COLOR_RED, COLOR_BLACK); - init_pair(9, COLOR_WHITE, COLOR_BLACK); - init_pair(10, COLOR_WHITE, COLOR_BLACK); - } - - wbkgd(Console::InfoWindow, COLOR_PAIR(1)); - - wrefresh(Console::InfoWindow); - wrefresh(Console::InputWindow); - - Console::RefreshOutput(); - } - - void Console::Error(const char* format, ...) - { - static char buffer[32768]; - - va_list va; - va_start(va, format); - _vsnprintf_s(buffer, sizeof(buffer), format, va); - va_end(va); - - Game::Com_Printf(0, "ERROR:\n"); - Game::Com_Printf(0, buffer); - - Console::RefreshOutput(); - - if (IsDebuggerPresent()) - { - while (true) - { - std::this_thread::sleep_for(5s); - } - } - - TerminateProcess(GetCurrentProcess(), 0xDEADDEAD); - } - - void Console::Print(const char* message) - { - if (!Console::OutputWindow) return; - - const char* p = message; - while (*p != '\0') - { - if (*p == '^') - { - char color; - ++p; - - color = (*p - '0'); - - if (color < 9 && color > 0) - { - wattron(Console::OutputWindow, COLOR_PAIR(color + 2)); - ++p; - continue; - } - } - - waddch(Console::OutputWindow, *p); - - ++p; - } - - wattron(Console::OutputWindow, COLOR_PAIR(9)); - -// int currentTime = static_cast(GetTickCount64()); // Make our compiler happy -// -// if (!Console::HasConsole) -// { -// Console::RefreshOutput(); -// } -// else if ((currentTime - Console::LastRefresh) > 100) -// { -// Console::RefreshOutput(); -// Console::LastRefresh = currentTime; -// } - - Console::RefreshOutput(); - } - - void Console::ConsoleRunner() - { - Console::SkipShutdown = false; - Game::Sys_ShowConsole(); - - MSG message; - while (IsWindow(*reinterpret_cast(0x64A3288)) != FALSE && GetMessageA(&message, 0, 0, 0)) - { - TranslateMessage(&message); - DispatchMessageA(&message); - } - - if (Console::SkipShutdown) return; - - if (Game::Sys_Milliseconds() - Console::LastRefresh > 100 && - MessageBoxA(0, "The application is not responding anymore, do you want to force its termination?", "Application is not responding", MB_ICONEXCLAMATION | MB_YESNO) == IDYES) - { - // Force process termination - // if the main thread is not responding - OutputDebugStringA("Process termination forced, as the main thread is not responding!"); - - // We can not force the termination in this thread - // The destructor would be called in this thread - // and would try to join this thread, which is impossible - std::thread([] () - { - std::this_thread::sleep_for(200ms); - ExitProcess(static_cast(-1)); - }).detach(); - } - else - { - // Send quit command to safely terminate the application - Command::Execute("wait 200;quit\n", false); - } - } - - void Console::StdOutPrint(const char* message) - { - printf("%s", message); - fflush(stdout); - } - - void Console::StdOutError(const char* format, ...) - { - char buffer[0x1000] = { 0 }; - - va_list ap; - va_start(ap, format); - _vsnprintf_s(buffer, sizeof(buffer), format, ap); - va_end(ap); - - perror(buffer); - fflush(stderr); - - ExitProcess(1); - } - - __declspec(naked) void Console::DrawSolidConsoleStub() - { - __asm - { - pushad - call Console::StoreSafeArea - popad - - // We need esi preserved here, so we have to backup 'all' registers when storing the safearea - call Game::Con_DrawSolidConsole - - call Console::RestoreSafeArea - retn - } - } - - void Console::StoreSafeArea() - { - // Backup the original safe area - Console::OriginalSafeArea = *Game::safeArea; - - // Apply new safe area and border - float border = 6.0f; - Game::safeArea->top = border; - Game::safeArea->left = border; - Game::safeArea->bottom = static_cast(Renderer::Height()) - border; - Game::safeArea->right = static_cast(Renderer::Width()) - border; - - Game::safeArea->textHeight = static_cast((Game::safeArea->bottom - Game::safeArea->top - (2 * Game::safeArea->fontHeight) - 24.0) / Game::safeArea->fontHeight); - Game::safeArea->textWidth = static_cast(Game::safeArea->right - Game::safeArea->left - 10.0f - 18.0); - } - - void Console::RestoreSafeArea() - { - // Restore the initial safe area - *Game::safeArea = Console::OriginalSafeArea; - } - - void Console::SetSkipShutdown() - { - Console::SkipShutdown = true; - } - - void Console::FreeNativeConsole() - { - if (Flags::HasFlag("console") || ZoneBuilder::IsEnabled()) - { - FreeConsole(); - } - } - - Game::dvar_t* Console::RegisterConColor(const char* name, float r, float g, float b, float a, float min, float max, int flags, const char* description) - { - static struct - { - const char* name; - float color[4]; - } patchedColors[] = - { - { "con_inputBoxColor", { 0.20f, 0.20f, 0.20f, 1.00f } }, - { "con_inputHintBoxColor", { 0.30f, 0.30f, 0.30f, 1.00f } }, - { "con_outputBarColor", { 0.50f, 0.50f, 0.50f, 0.60f } }, - { "con_outputSliderColor", { 0.70f, 1.00f, 0.00f, 1.00f } }, - { "con_outputWindowColor", { 0.25f, 0.25f, 0.25f, 0.85f } }, - }; - - for (int i = 0; i < ARRAY_SIZE(patchedColors); ++i) - { - if (std::string(name) == patchedColors[i].name) - { - r = patchedColors[i].color[0]; - g = patchedColors[i].color[1]; - b = patchedColors[i].color[2]; - a = patchedColors[i].color[3]; - break; - } - } - - return reinterpret_cast(0x471500)(name, r, g, b, a, min, max, flags, description); - } - - Console::Console() - { - // Console '%s: %s> ' string - Utils::Hook::Set(0x5A44B4, "IW4x: " VERSION "> "); - - // Patch console color - static float consoleColor[] = { 0.70f, 1.00f, 0.00f, 1.00f }; - Utils::Hook::Set(0x5A451A, consoleColor); - Utils::Hook::Set(0x5A4400, consoleColor); - - // Internal console - Utils::Hook(0x4F690C, Console::ToggleConsole, HOOK_CALL).install()->quick(); - Utils::Hook(0x4F65A5, Console::ToggleConsole, HOOK_JUMP).install()->quick(); - - // Patch safearea for ingame-console - Utils::Hook(0x5A50EF, Console::DrawSolidConsoleStub, HOOK_CALL).install()->quick(); - - // Check for bad food ;) - Utils::Hook(0x4CB9F4, Console::GetAutoCompleteFileList, HOOK_CALL).install()->quick(); - - // Patch console dvars - Utils::Hook(0x4829AB, Console::RegisterConColor, HOOK_CALL).install()->quick(); - Utils::Hook(0x4829EE, Console::RegisterConColor, HOOK_CALL).install()->quick(); - Utils::Hook(0x482A31, Console::RegisterConColor, HOOK_CALL).install()->quick(); - Utils::Hook(0x482A7A, Console::RegisterConColor, HOOK_CALL).install()->quick(); - Utils::Hook(0x482AC3, Console::RegisterConColor, HOOK_CALL).install()->quick(); - - // Modify console style - Utils::Hook::Set(0x428A8E, 0); // Adjust logo Y pos - Utils::Hook::Set(0x428A90, 0); // Adjust logo X pos - Utils::Hook::Set(0x428AF2, 67); // Adjust output Y pos - Utils::Hook::Set(0x428AC5, 397); // Adjust input Y pos - Utils::Hook::Set(0x428951, 609); // Reduce window width - Utils::Hook::Set(0x42895D, 423); // Reduce window height - Utils::Hook::Set(0x428AC0, 597); // Reduce input width - Utils::Hook::Set(0x428AED, 596); // Reduce output width - - // Don't resize the console - Utils::Hook(0x64DC6B, 0x64DCC2, HOOK_JUMP).install()->quick(); - - if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) - { - Dedicated::OnFrame(Console::RefreshStatus); - } - - // Code below is not necessary when performing unit tests! - if (Loader::PerformingUnitTests()) return; - - // External console - if (Flags::HasFlag("stdout")) - { - Utils::Hook(0x4B2080, Console::StdOutPrint, HOOK_JUMP).install()->quick(); - Utils::Hook(0x43D570, Console::StdOutError, HOOK_JUMP).install()->quick(); - } - else if (Flags::HasFlag("console") || ZoneBuilder::IsEnabled()) // ZoneBuilder uses the game's console, until the native one is adapted. - { - Utils::Hook::Nop(0x60BB58, 11); - - // Redirect input (]command) - Utils::Hook(0x47025A, 0x4F5770, HOOK_CALL).install()->quick(); - - Utils::Hook(0x60BB68, [] () - { - Console::ConsoleThread = std::thread(Console::ConsoleRunner); - }, HOOK_CALL).install()->quick(); - - QuickPatch::OnFrame([] () - { - Console::LastRefresh = Game::Sys_Milliseconds(); - }); - } - else if (Dedicated::IsEnabled()/* || ZoneBuilder::IsEnabled()*/) - { - Utils::Hook::Nop(0x60BB58, 11); - - Utils::Hook(0x4305E0, Console::Create, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4528A0, Console::Destroy, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4B2080, Console::Print, HOOK_JUMP).install()->quick(); - Utils::Hook(0x43D570, Console::Error, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4859A5, Console::Input, HOOK_CALL).install()->quick(); - } - else - { - FreeConsole(); - } - } - - Console::~Console() - { - if (Console::ConsoleThread.joinable()) - { - Console::ConsoleThread.join(); - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + WINDOW* Console::OutputWindow; + WINDOW* Console::InputWindow; + WINDOW* Console::InfoWindow; + + int Console::OutputTop = 0; + int Console::OutBuffer = 0; + int Console::LastRefresh = 0; + + int Console::Height = 25; + int Console::Width = 80; + + char Console::LineBuffer[1024] = { 0 }; + char Console::LineBuffer2[1024] = { 0 }; + int Console::LineBufferIndex = 0; + + bool Console::HasConsole = false; + bool Console::SkipShutdown = false; + + std::thread Console::ConsoleThread; + + Game::SafeArea Console::OriginalSafeArea; + + char** Console::GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType) + { + if (path == reinterpret_cast(0xBAADF00D) || path == reinterpret_cast(0xCDCDCDCD) || IsBadReadPtr(path, 1)) return nullptr; + return Game::FS_GetFileList(path, extension, behavior, numfiles, allocTrackType); + } + + void Console::ToggleConsole() + { + // possibly cls.keyCatchers? + Utils::Hook::Xor(0xB2C538, 1); + + // g_consoleField + Game::Field_Clear((void*)0xA1B6B0); + + // show console output? + Utils::Hook::Set(0xA15F38, 0); + } + + void Console::RefreshStatus() + { + std::string mapname = Dvar::Var("mapname").get(); + std::string hostname = Colors::Strip(Dvar::Var("sv_hostname").get()); + + if (Console::HasConsole) + { + SetConsoleTitleA(hostname.data()); + + int clientCount = 0; + int maxclientCount = *Game::svs_numclients; + + if (maxclientCount) + { + for (int i = 0; i < maxclientCount; ++i) + { + if (Game::svs_clients[i].state >= 3) + { + ++clientCount; + } + } + } + else + { + //maxclientCount = Dvar::Var("sv_maxclients").get(); + maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); + clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); + } + + wclear(Console::InfoWindow); + wprintw(Console::InfoWindow, "%s : %d/%d players : map %s", hostname.data(), clientCount, maxclientCount, (mapname.size() ? mapname.data() : "none")); + wnoutrefresh(Console::InfoWindow); + } + else if(IsWindow(*reinterpret_cast(0x64A3288)) != FALSE) + { + SetWindowTextA(*reinterpret_cast(0x64A3288), Utils::String::VA("IW4x(" VERSION ") : %s", hostname.data())); + } + } + + void Console::ShowPrompt() + { + wattron(Console::InputWindow, COLOR_PAIR(10) | A_BOLD); + wprintw(Console::InputWindow, "%s> ", VERSION); + } + + void Console::RefreshOutput() + { + prefresh(Console::OutputWindow, ((Console::OutputTop > 0) ? (Console::OutputTop - 1) : 0), 0, 1, 0, Console::Height - 2, Console::Width); + } + + void Console::ScrollOutput(int amount) + { + Console::OutputTop += amount; + + if (Console::OutputTop > OUTPUT_MAX_TOP) + { + Console::OutputTop = OUTPUT_MAX_TOP; + } + else if (Console::OutputTop < 0) + { + Console::OutputTop = 0; + } + + // make it only scroll the top if there's more than HEIGHT lines + if (Console::OutBuffer >= 0) + { + Console::OutBuffer += amount; + + if (Console::OutBuffer >= Console::Height) + { + Console::OutBuffer = -1; + } + + if (Console::OutputTop < Console::Height) + { + Console::OutputTop = 0; + } + } + } + + const char* Console::Input() + { + if (!Console::HasConsole) + { + Console::ShowPrompt(); + wrefresh(Console::InputWindow); + Console::HasConsole = true; + } + + int currentTime = static_cast(GetTickCount64()); // Make our compiler happy + if ((currentTime - Console::LastRefresh) > 250) + { + Console::RefreshOutput(); + Console::LastRefresh = currentTime; + } + + int c = wgetch(Console::InputWindow); + + if (c == ERR) + { + return NULL; + } + + switch (c) + { + case '\r': + case 459: // keypad enter + { + wattron(Console::OutputWindow, COLOR_PAIR(10) | A_BOLD); + wprintw(Console::OutputWindow, "%s", "]"); + + if (Console::LineBufferIndex) + { + wprintw(Console::OutputWindow, "%s", Console::LineBuffer); + } + + wprintw(Console::OutputWindow, "%s", "\n"); + wattroff(Console::OutputWindow, A_BOLD); + wclear(Console::InputWindow); + + Console::ShowPrompt(); + + wrefresh(Console::InputWindow); + + Console::ScrollOutput(1); + Console::RefreshOutput(); + + if (Console::LineBufferIndex) + { + strcpy_s(Console::LineBuffer2, Console::LineBuffer); + strcat_s(Console::LineBuffer, "\n"); + Console::LineBufferIndex = 0; + return Console::LineBuffer; + } + + break; + } + case 'c' - 'a' + 1: // ctrl-c + case 27: + { + Console::LineBuffer[0] = '\0'; + Console::LineBufferIndex = 0; + + wclear(Console::InputWindow); + + Console::ShowPrompt(); + + wrefresh(Console::InputWindow); + break; + } + case 8: // backspace + { + if (Console::LineBufferIndex > 0) + { + Console::LineBufferIndex--; + Console::LineBuffer[Console::LineBufferIndex] = '\0'; + + wprintw(Console::InputWindow, "%c %c", static_cast(c), static_cast(c)); + wrefresh(Console::InputWindow); + } + break; + } + case KEY_PPAGE: + { + Console::ScrollOutput(-1); + Console::RefreshOutput(); + break; + } + case KEY_NPAGE: + { + Console::ScrollOutput(1); + Console::RefreshOutput(); + break; + } + case KEY_UP: + { + wclear(Console::InputWindow); + Console::ShowPrompt(); + wprintw(Console::InputWindow, "%s", Console::LineBuffer2); + wrefresh(Console::InputWindow); + + strcpy_s(Console::LineBuffer, Console::LineBuffer2); + Console::LineBufferIndex = strlen(Console::LineBuffer); + break; + } + default: + if (c <= 127 && Console::LineBufferIndex < 1022) + { + // temporary workaround , find out what overwrites our index later on + //consoleLineBufferIndex = strlen(consoleLineBuffer); + + Console::LineBuffer[Console::LineBufferIndex++] = static_cast(c); + Console::LineBuffer[Console::LineBufferIndex] = '\0'; + wprintw(Console::InputWindow, "%c", static_cast(c)); + wrefresh(Console::InputWindow); + } + break; + } + + return NULL; + } + + void Console::Destroy() + { + delwin(Console::OutputWindow); + delwin(Console::InputWindow); + delwin(Console::InfoWindow); + endwin(); + + Console::OutputWindow = nullptr; + Console::InputWindow = nullptr; + Console::InfoWindow = nullptr; + } + + void Console::Create() + { + Console::OutputTop = 0; + Console::OutBuffer = 0; + Console::LastRefresh = 0; + Console::LineBufferIndex = 0; + Console::HasConsole = false; + + CONSOLE_SCREEN_BUFFER_INFO info; + if (GetConsoleScreenBufferInfo(GetStdHandle(STD_OUTPUT_HANDLE), &info)) + { + Console::Width = info.dwSize.X; + Console::Height = info.srWindow.Bottom - info.srWindow.Top + 1; + } + else + { + Console::Height = 25; + Console::Width = 80; + } + + initscr(); + raw(); + noecho(); + + Console::OutputWindow = newpad(Console::Height - 1, Console::Width); + Console::InputWindow = newwin(1, Console::Width, Console::Height - 1, 0); + Console::InfoWindow = newwin(1, Console::Width, 0, 0); + + scrollok(Console::OutputWindow, true); + idlok(Console::OutputWindow, true); + scrollok(Console::InputWindow, true); + nodelay(Console::InputWindow, true); + keypad(Console::InputWindow, true); + + if (has_colors()) + { + start_color(); + init_pair(1, COLOR_BLACK, COLOR_WHITE); + init_pair(2, COLOR_WHITE, COLOR_BLACK); + init_pair(3, COLOR_RED, COLOR_BLACK); + init_pair(4, COLOR_GREEN, COLOR_BLACK); + init_pair(5, COLOR_YELLOW, COLOR_BLACK); + init_pair(6, COLOR_BLUE, COLOR_BLACK); + init_pair(7, COLOR_CYAN, COLOR_BLACK); + init_pair(8, COLOR_RED, COLOR_BLACK); + init_pair(9, COLOR_WHITE, COLOR_BLACK); + init_pair(10, COLOR_WHITE, COLOR_BLACK); + } + + wbkgd(Console::InfoWindow, COLOR_PAIR(1)); + + wrefresh(Console::InfoWindow); + wrefresh(Console::InputWindow); + + Console::RefreshOutput(); + } + + void Console::Error(const char* format, ...) + { + static char buffer[32768]; + + va_list va; + va_start(va, format); + _vsnprintf_s(buffer, sizeof(buffer), format, va); + va_end(va); + + Game::Com_Printf(0, "ERROR:\n"); + Game::Com_Printf(0, buffer); + + Console::RefreshOutput(); + + if (IsDebuggerPresent()) + { + while (true) + { + std::this_thread::sleep_for(5s); + } + } + + TerminateProcess(GetCurrentProcess(), 0xDEADDEAD); + } + + void Console::Print(const char* message) + { + if (!Console::OutputWindow) return; + + const char* p = message; + while (*p != '\0') + { + if (*p == '^') + { + char color; + ++p; + + color = (*p - '0'); + + if (color < 9 && color > 0) + { + wattron(Console::OutputWindow, COLOR_PAIR(color + 2)); + ++p; + continue; + } + } + + waddch(Console::OutputWindow, *p); + + ++p; + } + + wattron(Console::OutputWindow, COLOR_PAIR(9)); + +// int currentTime = static_cast(GetTickCount64()); // Make our compiler happy +// +// if (!Console::HasConsole) +// { +// Console::RefreshOutput(); +// } +// else if ((currentTime - Console::LastRefresh) > 100) +// { +// Console::RefreshOutput(); +// Console::LastRefresh = currentTime; +// } + + Console::RefreshOutput(); + } + + void Console::ConsoleRunner() + { + Console::SkipShutdown = false; + Game::Sys_ShowConsole(); + + MSG message; + while (IsWindow(*reinterpret_cast(0x64A3288)) != FALSE && GetMessageA(&message, 0, 0, 0)) + { + TranslateMessage(&message); + DispatchMessageA(&message); + } + + if (Console::SkipShutdown) return; + + if (Game::Sys_Milliseconds() - Console::LastRefresh > 100 && + MessageBoxA(0, "The application is not responding anymore, do you want to force its termination?", "Application is not responding", MB_ICONEXCLAMATION | MB_YESNO) == IDYES) + { + // Force process termination + // if the main thread is not responding + OutputDebugStringA("Process termination forced, as the main thread is not responding!"); + + // We can not force the termination in this thread + // The destructor would be called in this thread + // and would try to join this thread, which is impossible + std::thread([] () + { + std::this_thread::sleep_for(200ms); + ExitProcess(static_cast(-1)); + }).detach(); + } + else + { + // Send quit command to safely terminate the application + Command::Execute("wait 200;quit\n", false); + } + } + + void Console::StdOutPrint(const char* message) + { + printf("%s", message); + fflush(stdout); + } + + void Console::StdOutError(const char* format, ...) + { + char buffer[0x1000] = { 0 }; + + va_list ap; + va_start(ap, format); + _vsnprintf_s(buffer, sizeof(buffer), format, ap); + va_end(ap); + + perror(buffer); + fflush(stderr); + + ExitProcess(1); + } + + __declspec(naked) void Console::DrawSolidConsoleStub() + { + __asm + { + pushad + call Console::StoreSafeArea + popad + + // We need esi preserved here, so we have to backup 'all' registers when storing the safearea + call Game::Con_DrawSolidConsole + + call Console::RestoreSafeArea + retn + } + } + + void Console::StoreSafeArea() + { + // Backup the original safe area + Console::OriginalSafeArea = *Game::safeArea; + + // Apply new safe area and border + float border = 6.0f; + Game::safeArea->top = border; + Game::safeArea->left = border; + Game::safeArea->bottom = static_cast(Renderer::Height()) - border; + Game::safeArea->right = static_cast(Renderer::Width()) - border; + + Game::safeArea->textHeight = static_cast((Game::safeArea->bottom - Game::safeArea->top - (2 * Game::safeArea->fontHeight) - 24.0) / Game::safeArea->fontHeight); + Game::safeArea->textWidth = static_cast(Game::safeArea->right - Game::safeArea->left - 10.0f - 18.0); + } + + void Console::RestoreSafeArea() + { + // Restore the initial safe area + *Game::safeArea = Console::OriginalSafeArea; + } + + void Console::SetSkipShutdown() + { + Console::SkipShutdown = true; + } + + void Console::FreeNativeConsole() + { + if (Flags::HasFlag("console") || ZoneBuilder::IsEnabled()) + { + FreeConsole(); + } + } + + Game::dvar_t* Console::RegisterConColor(const char* name, float r, float g, float b, float a, float min, float max, int flags, const char* description) + { + static struct + { + const char* name; + float color[4]; + } patchedColors[] = + { + { "con_inputBoxColor", { 0.20f, 0.20f, 0.20f, 1.00f } }, + { "con_inputHintBoxColor", { 0.30f, 0.30f, 0.30f, 1.00f } }, + { "con_outputBarColor", { 0.50f, 0.50f, 0.50f, 0.60f } }, + { "con_outputSliderColor", { 0.70f, 1.00f, 0.00f, 1.00f } }, + { "con_outputWindowColor", { 0.25f, 0.25f, 0.25f, 0.85f } }, + }; + + for (int i = 0; i < ARRAY_SIZE(patchedColors); ++i) + { + if (std::string(name) == patchedColors[i].name) + { + r = patchedColors[i].color[0]; + g = patchedColors[i].color[1]; + b = patchedColors[i].color[2]; + a = patchedColors[i].color[3]; + break; + } + } + + return reinterpret_cast(0x471500)(name, r, g, b, a, min, max, flags, description); + } + + Console::Console() + { + // Console '%s: %s> ' string + Utils::Hook::Set(0x5A44B4, "IW4x: " VERSION "> "); + + // Patch console color + static float consoleColor[] = { 0.70f, 1.00f, 0.00f, 1.00f }; + Utils::Hook::Set(0x5A451A, consoleColor); + Utils::Hook::Set(0x5A4400, consoleColor); + + // Internal console + Utils::Hook(0x4F690C, Console::ToggleConsole, HOOK_CALL).install()->quick(); + Utils::Hook(0x4F65A5, Console::ToggleConsole, HOOK_JUMP).install()->quick(); + + // Patch safearea for ingame-console + Utils::Hook(0x5A50EF, Console::DrawSolidConsoleStub, HOOK_CALL).install()->quick(); + + // Check for bad food ;) + Utils::Hook(0x4CB9F4, Console::GetAutoCompleteFileList, HOOK_CALL).install()->quick(); + + // Patch console dvars + Utils::Hook(0x4829AB, Console::RegisterConColor, HOOK_CALL).install()->quick(); + Utils::Hook(0x4829EE, Console::RegisterConColor, HOOK_CALL).install()->quick(); + Utils::Hook(0x482A31, Console::RegisterConColor, HOOK_CALL).install()->quick(); + Utils::Hook(0x482A7A, Console::RegisterConColor, HOOK_CALL).install()->quick(); + Utils::Hook(0x482AC3, Console::RegisterConColor, HOOK_CALL).install()->quick(); + + // Modify console style + Utils::Hook::Set(0x428A8E, 0); // Adjust logo Y pos + Utils::Hook::Set(0x428A90, 0); // Adjust logo X pos + Utils::Hook::Set(0x428AF2, 67); // Adjust output Y pos + Utils::Hook::Set(0x428AC5, 397); // Adjust input Y pos + Utils::Hook::Set(0x428951, 609); // Reduce window width + Utils::Hook::Set(0x42895D, 423); // Reduce window height + Utils::Hook::Set(0x428AC0, 597); // Reduce input width + Utils::Hook::Set(0x428AED, 596); // Reduce output width + + // Don't resize the console + Utils::Hook(0x64DC6B, 0x64DCC2, HOOK_JUMP).install()->quick(); + + if (Dedicated::IsEnabled() && !ZoneBuilder::IsEnabled()) + { + Dedicated::OnFrame(Console::RefreshStatus); + } + + // Code below is not necessary when performing unit tests! + if (Loader::PerformingUnitTests()) return; + + // External console + if (Flags::HasFlag("stdout")) + { + Utils::Hook(0x4B2080, Console::StdOutPrint, HOOK_JUMP).install()->quick(); + Utils::Hook(0x43D570, Console::StdOutError, HOOK_JUMP).install()->quick(); + } + else if (Flags::HasFlag("console") || ZoneBuilder::IsEnabled()) // ZoneBuilder uses the game's console, until the native one is adapted. + { + Utils::Hook::Nop(0x60BB58, 11); + + // Redirect input (]command) + Utils::Hook(0x47025A, 0x4F5770, HOOK_CALL).install()->quick(); + + Utils::Hook(0x60BB68, [] () + { + Console::ConsoleThread = std::thread(Console::ConsoleRunner); + }, HOOK_CALL).install()->quick(); + + QuickPatch::OnFrame([] () + { + Console::LastRefresh = Game::Sys_Milliseconds(); + }); + } + else if (Dedicated::IsEnabled()/* || ZoneBuilder::IsEnabled()*/) + { + Utils::Hook::Nop(0x60BB58, 11); + + Utils::Hook(0x4305E0, Console::Create, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4528A0, Console::Destroy, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4B2080, Console::Print, HOOK_JUMP).install()->quick(); + Utils::Hook(0x43D570, Console::Error, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4859A5, Console::Input, HOOK_CALL).install()->quick(); + } + else + { + FreeConsole(); + } + } + + Console::~Console() + { + if (Console::ConsoleThread.joinable()) + { + Console::ConsoleThread.join(); + } + } +} diff --git a/src/Components/Modules/Console.hpp b/src/Components/Modules/Console.hpp index 75560511..95e067cd 100644 --- a/src/Components/Modules/Console.hpp +++ b/src/Components/Modules/Console.hpp @@ -1,69 +1,69 @@ -#define OUTPUT_HEIGHT 250 -#define OUTPUT_MAX_TOP (OUTPUT_HEIGHT - (Console::Height - 2)) - -namespace Components -{ - class Console : public Component - { - public: - Console(); - ~Console(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Console"; }; -#endif - - static void SetSkipShutdown(); - - static void FreeNativeConsole(); - - private: - // Text-based console stuff - static WINDOW* OutputWindow; - static WINDOW* InputWindow; - static WINDOW* InfoWindow; - - static int Width; - static int Height; - - static int OutputTop; - static int OutBuffer; - static int LastRefresh; - - static char LineBuffer[1024]; - static char LineBuffer2[1024]; - static int LineBufferIndex; - - static bool HasConsole; - static bool SkipShutdown; - - static std::thread ConsoleThread; - - static Game::SafeArea OriginalSafeArea; - - static void ShowPrompt(); - static void RefreshStatus(); - static void RefreshOutput(); - static void ScrollOutput(int amount); - - static const char* Input(); - static void Print(const char* message); - static void Error(const char* format, ...); - static void Create(); - static void Destroy(); - - static void StdOutPrint(const char* message); - static void StdOutError(const char* format, ...); - - static void ConsoleRunner(); - - static void DrawSolidConsoleStub(); - static void StoreSafeArea(); - static void RestoreSafeArea(); - - static void ToggleConsole(); - static char** GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType); - - static Game::dvar_t* RegisterConColor(const char* name, float r, float g, float b, float a, float min, float max, int flags, const char* description); - }; -} +#define OUTPUT_HEIGHT 250 +#define OUTPUT_MAX_TOP (OUTPUT_HEIGHT - (Console::Height - 2)) + +namespace Components +{ + class Console : public Component + { + public: + Console(); + ~Console(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Console"; }; +#endif + + static void SetSkipShutdown(); + + static void FreeNativeConsole(); + + private: + // Text-based console stuff + static WINDOW* OutputWindow; + static WINDOW* InputWindow; + static WINDOW* InfoWindow; + + static int Width; + static int Height; + + static int OutputTop; + static int OutBuffer; + static int LastRefresh; + + static char LineBuffer[1024]; + static char LineBuffer2[1024]; + static int LineBufferIndex; + + static bool HasConsole; + static bool SkipShutdown; + + static std::thread ConsoleThread; + + static Game::SafeArea OriginalSafeArea; + + static void ShowPrompt(); + static void RefreshStatus(); + static void RefreshOutput(); + static void ScrollOutput(int amount); + + static const char* Input(); + static void Print(const char* message); + static void Error(const char* format, ...); + static void Create(); + static void Destroy(); + + static void StdOutPrint(const char* message); + static void StdOutError(const char* format, ...); + + static void ConsoleRunner(); + + static void DrawSolidConsoleStub(); + static void StoreSafeArea(); + static void RestoreSafeArea(); + + static void ToggleConsole(); + static char** GetAutoCompleteFileList(const char *path, const char *extension, Game::FsListBehavior_e behavior, int *numfiles, int allocTrackType); + + static Game::dvar_t* RegisterConColor(const char* name, float r, float g, float b, float a, float min, float max, int flags, const char* description); + }; +} diff --git a/src/Components/Modules/D3D9Ex.hpp b/src/Components/Modules/D3D9Ex.hpp index bb97cf52..c70a0020 100644 --- a/src/Components/Modules/D3D9Ex.hpp +++ b/src/Components/Modules/D3D9Ex.hpp @@ -1,174 +1,174 @@ -namespace Components -{ - class D3D9Ex : public Component - { - public: - D3D9Ex(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "D3D9Ex"; }; -#endif - - private: - - class D3D9Device : public IDirect3DDevice9 - { - public: - D3D9Device(IDirect3DDevice9* pOriginal) : m_pIDirect3DDevice9(pOriginal) {}; - virtual ~D3D9Device(void) {}; - - HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj); - ULONG __stdcall AddRef(void); - ULONG __stdcall Release(void); - HRESULT __stdcall TestCooperativeLevel(void); - UINT __stdcall GetAvailableTextureMem(void); - HRESULT __stdcall EvictManagedResources(void); - HRESULT __stdcall GetDirect3D(IDirect3D9** ppD3D9); - HRESULT __stdcall GetDeviceCaps(D3DCAPS9* pCaps); - HRESULT __stdcall GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode); - HRESULT __stdcall GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS *pParameters); - HRESULT __stdcall SetCursorProperties(UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap); - void __stdcall SetCursorPosition(int X, int Y, DWORD Flags); - BOOL __stdcall ShowCursor(BOOL bShow); - HRESULT __stdcall CreateAdditionalSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** pSwapChain); - HRESULT __stdcall GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain); - UINT __stdcall GetNumberOfSwapChains(void); - HRESULT __stdcall Reset(D3DPRESENT_PARAMETERS* pPresentationParameters); - HRESULT __stdcall Present(CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion); - HRESULT __stdcall GetBackBuffer(UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer); - HRESULT __stdcall GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus); - HRESULT __stdcall SetDialogBoxMode(BOOL bEnableDialogs); - void __stdcall SetGammaRamp(UINT iSwapChain, DWORD Flags, CONST D3DGAMMARAMP* pRamp); - void __stdcall GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp); - HRESULT __stdcall CreateTexture(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle); - HRESULT __stdcall CreateVolumeTexture(UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle); - HRESULT __stdcall CreateCubeTexture(UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle); - HRESULT __stdcall CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle); - HRESULT __stdcall CreateIndexBuffer(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle); - HRESULT __stdcall CreateRenderTarget(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle); - HRESULT __stdcall CreateDepthStencilSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle); - HRESULT __stdcall UpdateSurface(IDirect3DSurface9* pSourceSurface, CONST RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface, CONST POINT* pDestPoint); - HRESULT __stdcall UpdateTexture(IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture); - HRESULT __stdcall GetRenderTargetData(IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface); - HRESULT __stdcall GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface); - HRESULT __stdcall StretchRect(IDirect3DSurface9* pSourceSurface, CONST RECT* pSourceRect, IDirect3DSurface9* pDestSurface, CONST RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter); - HRESULT __stdcall ColorFill(IDirect3DSurface9* pSurface, CONST RECT* pRect, D3DCOLOR color); - HRESULT __stdcall CreateOffscreenPlainSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle); - HRESULT __stdcall SetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget); - HRESULT __stdcall GetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget); - HRESULT __stdcall SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil); - HRESULT __stdcall GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface); - HRESULT __stdcall BeginScene(void); - HRESULT __stdcall EndScene(void); - HRESULT __stdcall Clear(DWORD Count, CONST D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil); - HRESULT __stdcall SetTransform(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX* pMatrix); - HRESULT __stdcall GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix); - HRESULT __stdcall MultiplyTransform(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX* pMatrix); - HRESULT __stdcall SetViewport(CONST D3DVIEWPORT9* pViewport); - HRESULT __stdcall GetViewport(D3DVIEWPORT9* pViewport); - HRESULT __stdcall SetMaterial(CONST D3DMATERIAL9* pMaterial); - HRESULT __stdcall GetMaterial(D3DMATERIAL9* pMaterial); - HRESULT __stdcall SetLight(DWORD Index, CONST D3DLIGHT9* pLight); - HRESULT __stdcall GetLight(DWORD Index, D3DLIGHT9* pLight); - HRESULT __stdcall LightEnable(DWORD Index, BOOL Enable); - HRESULT __stdcall GetLightEnable(DWORD Index, BOOL* pEnable); - HRESULT __stdcall SetClipPlane(DWORD Index, CONST float* pPlane); - HRESULT __stdcall GetClipPlane(DWORD Index, float* pPlane); - HRESULT __stdcall SetRenderState(D3DRENDERSTATETYPE State, DWORD Value); - HRESULT __stdcall GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue); - HRESULT __stdcall CreateStateBlock(D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB); - HRESULT __stdcall BeginStateBlock(void); - HRESULT __stdcall EndStateBlock(IDirect3DStateBlock9** ppSB); - HRESULT __stdcall SetClipStatus(CONST D3DCLIPSTATUS9* pClipStatus); - HRESULT __stdcall GetClipStatus(D3DCLIPSTATUS9* pClipStatus); - HRESULT __stdcall GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture); - HRESULT __stdcall SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture); - HRESULT __stdcall GetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue); - HRESULT __stdcall SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value); - HRESULT __stdcall GetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue); - HRESULT __stdcall SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value); - HRESULT __stdcall ValidateDevice(DWORD* pNumPasses); - HRESULT __stdcall SetPaletteEntries(UINT PaletteNumber, CONST PALETTEENTRY* pEntries); - HRESULT __stdcall GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries); - HRESULT __stdcall SetCurrentTexturePalette(UINT PaletteNumber); - HRESULT __stdcall GetCurrentTexturePalette(UINT *PaletteNumber); - HRESULT __stdcall SetScissorRect(CONST RECT* pRect); - HRESULT __stdcall GetScissorRect(RECT* pRect); - HRESULT __stdcall SetSoftwareVertexProcessing(BOOL bSoftware); - BOOL __stdcall GetSoftwareVertexProcessing(void); - HRESULT __stdcall SetNPatchMode(float nSegments); - float __stdcall GetNPatchMode(void); - HRESULT __stdcall DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount); - HRESULT __stdcall DrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount); - HRESULT __stdcall DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride); - HRESULT __stdcall DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, CONST void* pIndexData, D3DFORMAT IndexDataFormat, CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride); - HRESULT __stdcall ProcessVertices(UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags); - HRESULT __stdcall CreateVertexDeclaration(CONST D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl); - HRESULT __stdcall SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl); - HRESULT __stdcall GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl); - HRESULT __stdcall SetFVF(DWORD FVF); - HRESULT __stdcall GetFVF(DWORD* pFVF); - HRESULT __stdcall CreateVertexShader(CONST DWORD* pFunction, IDirect3DVertexShader9** ppShader); - HRESULT __stdcall SetVertexShader(IDirect3DVertexShader9* pShader); - HRESULT __stdcall GetVertexShader(IDirect3DVertexShader9** ppShader); - HRESULT __stdcall SetVertexShaderConstantF(UINT StartRegister, CONST float* pConstantData, UINT Vector4fCount); - HRESULT __stdcall GetVertexShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount); - HRESULT __stdcall SetVertexShaderConstantI(UINT StartRegister, CONST int* pConstantData, UINT Vector4iCount); - HRESULT __stdcall GetVertexShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount); - HRESULT __stdcall SetVertexShaderConstantB(UINT StartRegister, CONST BOOL* pConstantData, UINT BoolCount); - HRESULT __stdcall GetVertexShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount); - HRESULT __stdcall SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride); - HRESULT __stdcall GetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* OffsetInBytes, UINT* pStride); - HRESULT __stdcall SetStreamSourceFreq(UINT StreamNumber, UINT Divider); - HRESULT __stdcall GetStreamSourceFreq(UINT StreamNumber, UINT* Divider); - HRESULT __stdcall SetIndices(IDirect3DIndexBuffer9* pIndexData); - HRESULT __stdcall GetIndices(IDirect3DIndexBuffer9** ppIndexData); - HRESULT __stdcall CreatePixelShader(CONST DWORD* pFunction, IDirect3DPixelShader9** ppShader); - HRESULT __stdcall SetPixelShader(IDirect3DPixelShader9* pShader); - HRESULT __stdcall GetPixelShader(IDirect3DPixelShader9** ppShader); - HRESULT __stdcall SetPixelShaderConstantF(UINT StartRegister, CONST float* pConstantData, UINT Vector4fCount); - HRESULT __stdcall GetPixelShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount); - HRESULT __stdcall SetPixelShaderConstantI(UINT StartRegister, CONST int* pConstantData, UINT Vector4iCount); - HRESULT __stdcall GetPixelShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount); - HRESULT __stdcall SetPixelShaderConstantB(UINT StartRegister, CONST BOOL* pConstantData, UINT BoolCount); - HRESULT __stdcall GetPixelShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount); - HRESULT __stdcall DrawRectPatch(UINT Handle, CONST float* pNumSegs, CONST D3DRECTPATCH_INFO* pRectPatchInfo); - HRESULT __stdcall DrawTriPatch(UINT Handle, CONST float* pNumSegs, CONST D3DTRIPATCH_INFO* pTriPatchInfo); - HRESULT __stdcall DeletePatch(UINT Handle); - HRESULT __stdcall CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery); - - private: - IDirect3DDevice9 *m_pIDirect3DDevice9; - }; - - class D3D9 : public IDirect3D9 - { - public: - D3D9(IDirect3D9Ex *pOriginal) : m_pIDirect3D9(pOriginal) {}; - virtual ~D3D9(void) {}; - - HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj); - ULONG __stdcall AddRef(void); - ULONG __stdcall Release(void); - HRESULT __stdcall RegisterSoftwareDevice(void* pInitializeFunction); - UINT __stdcall GetAdapterCount(void); - HRESULT __stdcall GetAdapterIdentifier(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier); - UINT __stdcall GetAdapterModeCount(UINT Adapter, D3DFORMAT Format); - HRESULT __stdcall EnumAdapterModes(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode); - HRESULT __stdcall GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode); - HRESULT __stdcall CheckDeviceType(UINT iAdapter, D3DDEVTYPE DevType, D3DFORMAT DisplayFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed); - HRESULT __stdcall CheckDeviceFormat(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat); - HRESULT __stdcall CheckDeviceMultiSampleType(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels); - HRESULT __stdcall CheckDepthStencilMatch(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat); - HRESULT __stdcall CheckDeviceFormatConversion(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat); - HRESULT __stdcall GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps); - HMONITOR __stdcall GetAdapterMonitor(UINT Adapter); - HRESULT __stdcall CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface); - - private: - IDirect3D9 *m_pIDirect3D9; - }; - - static IDirect3D9* __stdcall Direct3DCreate9Stub(UINT sdk); - }; -} +namespace Components +{ + class D3D9Ex : public Component + { + public: + D3D9Ex(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "D3D9Ex"; }; +#endif + + private: + + class D3D9Device : public IDirect3DDevice9 + { + public: + D3D9Device(IDirect3DDevice9* pOriginal) : m_pIDirect3DDevice9(pOriginal) {}; + virtual ~D3D9Device(void) {}; + + HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj); + ULONG __stdcall AddRef(void); + ULONG __stdcall Release(void); + HRESULT __stdcall TestCooperativeLevel(void); + UINT __stdcall GetAvailableTextureMem(void); + HRESULT __stdcall EvictManagedResources(void); + HRESULT __stdcall GetDirect3D(IDirect3D9** ppD3D9); + HRESULT __stdcall GetDeviceCaps(D3DCAPS9* pCaps); + HRESULT __stdcall GetDisplayMode(UINT iSwapChain, D3DDISPLAYMODE* pMode); + HRESULT __stdcall GetCreationParameters(D3DDEVICE_CREATION_PARAMETERS *pParameters); + HRESULT __stdcall SetCursorProperties(UINT XHotSpot, UINT YHotSpot, IDirect3DSurface9* pCursorBitmap); + void __stdcall SetCursorPosition(int X, int Y, DWORD Flags); + BOOL __stdcall ShowCursor(BOOL bShow); + HRESULT __stdcall CreateAdditionalSwapChain(D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DSwapChain9** pSwapChain); + HRESULT __stdcall GetSwapChain(UINT iSwapChain, IDirect3DSwapChain9** pSwapChain); + UINT __stdcall GetNumberOfSwapChains(void); + HRESULT __stdcall Reset(D3DPRESENT_PARAMETERS* pPresentationParameters); + HRESULT __stdcall Present(CONST RECT* pSourceRect, CONST RECT* pDestRect, HWND hDestWindowOverride, CONST RGNDATA* pDirtyRegion); + HRESULT __stdcall GetBackBuffer(UINT iSwapChain, UINT iBackBuffer, D3DBACKBUFFER_TYPE Type, IDirect3DSurface9** ppBackBuffer); + HRESULT __stdcall GetRasterStatus(UINT iSwapChain, D3DRASTER_STATUS* pRasterStatus); + HRESULT __stdcall SetDialogBoxMode(BOOL bEnableDialogs); + void __stdcall SetGammaRamp(UINT iSwapChain, DWORD Flags, CONST D3DGAMMARAMP* pRamp); + void __stdcall GetGammaRamp(UINT iSwapChain, D3DGAMMARAMP* pRamp); + HRESULT __stdcall CreateTexture(UINT Width, UINT Height, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DTexture9** ppTexture, HANDLE* pSharedHandle); + HRESULT __stdcall CreateVolumeTexture(UINT Width, UINT Height, UINT Depth, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DVolumeTexture9** ppVolumeTexture, HANDLE* pSharedHandle); + HRESULT __stdcall CreateCubeTexture(UINT EdgeLength, UINT Levels, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DCubeTexture9** ppCubeTexture, HANDLE* pSharedHandle); + HRESULT __stdcall CreateVertexBuffer(UINT Length, DWORD Usage, DWORD FVF, D3DPOOL Pool, IDirect3DVertexBuffer9** ppVertexBuffer, HANDLE* pSharedHandle); + HRESULT __stdcall CreateIndexBuffer(UINT Length, DWORD Usage, D3DFORMAT Format, D3DPOOL Pool, IDirect3DIndexBuffer9** ppIndexBuffer, HANDLE* pSharedHandle); + HRESULT __stdcall CreateRenderTarget(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Lockable, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle); + HRESULT __stdcall CreateDepthStencilSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DMULTISAMPLE_TYPE MultiSample, DWORD MultisampleQuality, BOOL Discard, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle); + HRESULT __stdcall UpdateSurface(IDirect3DSurface9* pSourceSurface, CONST RECT* pSourceRect, IDirect3DSurface9* pDestinationSurface, CONST POINT* pDestPoint); + HRESULT __stdcall UpdateTexture(IDirect3DBaseTexture9* pSourceTexture, IDirect3DBaseTexture9* pDestinationTexture); + HRESULT __stdcall GetRenderTargetData(IDirect3DSurface9* pRenderTarget, IDirect3DSurface9* pDestSurface); + HRESULT __stdcall GetFrontBufferData(UINT iSwapChain, IDirect3DSurface9* pDestSurface); + HRESULT __stdcall StretchRect(IDirect3DSurface9* pSourceSurface, CONST RECT* pSourceRect, IDirect3DSurface9* pDestSurface, CONST RECT* pDestRect, D3DTEXTUREFILTERTYPE Filter); + HRESULT __stdcall ColorFill(IDirect3DSurface9* pSurface, CONST RECT* pRect, D3DCOLOR color); + HRESULT __stdcall CreateOffscreenPlainSurface(UINT Width, UINT Height, D3DFORMAT Format, D3DPOOL Pool, IDirect3DSurface9** ppSurface, HANDLE* pSharedHandle); + HRESULT __stdcall SetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9* pRenderTarget); + HRESULT __stdcall GetRenderTarget(DWORD RenderTargetIndex, IDirect3DSurface9** ppRenderTarget); + HRESULT __stdcall SetDepthStencilSurface(IDirect3DSurface9* pNewZStencil); + HRESULT __stdcall GetDepthStencilSurface(IDirect3DSurface9** ppZStencilSurface); + HRESULT __stdcall BeginScene(void); + HRESULT __stdcall EndScene(void); + HRESULT __stdcall Clear(DWORD Count, CONST D3DRECT* pRects, DWORD Flags, D3DCOLOR Color, float Z, DWORD Stencil); + HRESULT __stdcall SetTransform(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX* pMatrix); + HRESULT __stdcall GetTransform(D3DTRANSFORMSTATETYPE State, D3DMATRIX* pMatrix); + HRESULT __stdcall MultiplyTransform(D3DTRANSFORMSTATETYPE State, CONST D3DMATRIX* pMatrix); + HRESULT __stdcall SetViewport(CONST D3DVIEWPORT9* pViewport); + HRESULT __stdcall GetViewport(D3DVIEWPORT9* pViewport); + HRESULT __stdcall SetMaterial(CONST D3DMATERIAL9* pMaterial); + HRESULT __stdcall GetMaterial(D3DMATERIAL9* pMaterial); + HRESULT __stdcall SetLight(DWORD Index, CONST D3DLIGHT9* pLight); + HRESULT __stdcall GetLight(DWORD Index, D3DLIGHT9* pLight); + HRESULT __stdcall LightEnable(DWORD Index, BOOL Enable); + HRESULT __stdcall GetLightEnable(DWORD Index, BOOL* pEnable); + HRESULT __stdcall SetClipPlane(DWORD Index, CONST float* pPlane); + HRESULT __stdcall GetClipPlane(DWORD Index, float* pPlane); + HRESULT __stdcall SetRenderState(D3DRENDERSTATETYPE State, DWORD Value); + HRESULT __stdcall GetRenderState(D3DRENDERSTATETYPE State, DWORD* pValue); + HRESULT __stdcall CreateStateBlock(D3DSTATEBLOCKTYPE Type, IDirect3DStateBlock9** ppSB); + HRESULT __stdcall BeginStateBlock(void); + HRESULT __stdcall EndStateBlock(IDirect3DStateBlock9** ppSB); + HRESULT __stdcall SetClipStatus(CONST D3DCLIPSTATUS9* pClipStatus); + HRESULT __stdcall GetClipStatus(D3DCLIPSTATUS9* pClipStatus); + HRESULT __stdcall GetTexture(DWORD Stage, IDirect3DBaseTexture9** ppTexture); + HRESULT __stdcall SetTexture(DWORD Stage, IDirect3DBaseTexture9* pTexture); + HRESULT __stdcall GetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD* pValue); + HRESULT __stdcall SetTextureStageState(DWORD Stage, D3DTEXTURESTAGESTATETYPE Type, DWORD Value); + HRESULT __stdcall GetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD* pValue); + HRESULT __stdcall SetSamplerState(DWORD Sampler, D3DSAMPLERSTATETYPE Type, DWORD Value); + HRESULT __stdcall ValidateDevice(DWORD* pNumPasses); + HRESULT __stdcall SetPaletteEntries(UINT PaletteNumber, CONST PALETTEENTRY* pEntries); + HRESULT __stdcall GetPaletteEntries(UINT PaletteNumber, PALETTEENTRY* pEntries); + HRESULT __stdcall SetCurrentTexturePalette(UINT PaletteNumber); + HRESULT __stdcall GetCurrentTexturePalette(UINT *PaletteNumber); + HRESULT __stdcall SetScissorRect(CONST RECT* pRect); + HRESULT __stdcall GetScissorRect(RECT* pRect); + HRESULT __stdcall SetSoftwareVertexProcessing(BOOL bSoftware); + BOOL __stdcall GetSoftwareVertexProcessing(void); + HRESULT __stdcall SetNPatchMode(float nSegments); + float __stdcall GetNPatchMode(void); + HRESULT __stdcall DrawPrimitive(D3DPRIMITIVETYPE PrimitiveType, UINT StartVertex, UINT PrimitiveCount); + HRESULT __stdcall DrawIndexedPrimitive(D3DPRIMITIVETYPE PrimitiveType, INT BaseVertexIndex, UINT MinVertexIndex, UINT NumVertices, UINT startIndex, UINT primCount); + HRESULT __stdcall DrawPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT PrimitiveCount, CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride); + HRESULT __stdcall DrawIndexedPrimitiveUP(D3DPRIMITIVETYPE PrimitiveType, UINT MinVertexIndex, UINT NumVertices, UINT PrimitiveCount, CONST void* pIndexData, D3DFORMAT IndexDataFormat, CONST void* pVertexStreamZeroData, UINT VertexStreamZeroStride); + HRESULT __stdcall ProcessVertices(UINT SrcStartIndex, UINT DestIndex, UINT VertexCount, IDirect3DVertexBuffer9* pDestBuffer, IDirect3DVertexDeclaration9* pVertexDecl, DWORD Flags); + HRESULT __stdcall CreateVertexDeclaration(CONST D3DVERTEXELEMENT9* pVertexElements, IDirect3DVertexDeclaration9** ppDecl); + HRESULT __stdcall SetVertexDeclaration(IDirect3DVertexDeclaration9* pDecl); + HRESULT __stdcall GetVertexDeclaration(IDirect3DVertexDeclaration9** ppDecl); + HRESULT __stdcall SetFVF(DWORD FVF); + HRESULT __stdcall GetFVF(DWORD* pFVF); + HRESULT __stdcall CreateVertexShader(CONST DWORD* pFunction, IDirect3DVertexShader9** ppShader); + HRESULT __stdcall SetVertexShader(IDirect3DVertexShader9* pShader); + HRESULT __stdcall GetVertexShader(IDirect3DVertexShader9** ppShader); + HRESULT __stdcall SetVertexShaderConstantF(UINT StartRegister, CONST float* pConstantData, UINT Vector4fCount); + HRESULT __stdcall GetVertexShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount); + HRESULT __stdcall SetVertexShaderConstantI(UINT StartRegister, CONST int* pConstantData, UINT Vector4iCount); + HRESULT __stdcall GetVertexShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount); + HRESULT __stdcall SetVertexShaderConstantB(UINT StartRegister, CONST BOOL* pConstantData, UINT BoolCount); + HRESULT __stdcall GetVertexShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount); + HRESULT __stdcall SetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9* pStreamData, UINT OffsetInBytes, UINT Stride); + HRESULT __stdcall GetStreamSource(UINT StreamNumber, IDirect3DVertexBuffer9** ppStreamData, UINT* OffsetInBytes, UINT* pStride); + HRESULT __stdcall SetStreamSourceFreq(UINT StreamNumber, UINT Divider); + HRESULT __stdcall GetStreamSourceFreq(UINT StreamNumber, UINT* Divider); + HRESULT __stdcall SetIndices(IDirect3DIndexBuffer9* pIndexData); + HRESULT __stdcall GetIndices(IDirect3DIndexBuffer9** ppIndexData); + HRESULT __stdcall CreatePixelShader(CONST DWORD* pFunction, IDirect3DPixelShader9** ppShader); + HRESULT __stdcall SetPixelShader(IDirect3DPixelShader9* pShader); + HRESULT __stdcall GetPixelShader(IDirect3DPixelShader9** ppShader); + HRESULT __stdcall SetPixelShaderConstantF(UINT StartRegister, CONST float* pConstantData, UINT Vector4fCount); + HRESULT __stdcall GetPixelShaderConstantF(UINT StartRegister, float* pConstantData, UINT Vector4fCount); + HRESULT __stdcall SetPixelShaderConstantI(UINT StartRegister, CONST int* pConstantData, UINT Vector4iCount); + HRESULT __stdcall GetPixelShaderConstantI(UINT StartRegister, int* pConstantData, UINT Vector4iCount); + HRESULT __stdcall SetPixelShaderConstantB(UINT StartRegister, CONST BOOL* pConstantData, UINT BoolCount); + HRESULT __stdcall GetPixelShaderConstantB(UINT StartRegister, BOOL* pConstantData, UINT BoolCount); + HRESULT __stdcall DrawRectPatch(UINT Handle, CONST float* pNumSegs, CONST D3DRECTPATCH_INFO* pRectPatchInfo); + HRESULT __stdcall DrawTriPatch(UINT Handle, CONST float* pNumSegs, CONST D3DTRIPATCH_INFO* pTriPatchInfo); + HRESULT __stdcall DeletePatch(UINT Handle); + HRESULT __stdcall CreateQuery(D3DQUERYTYPE Type, IDirect3DQuery9** ppQuery); + + private: + IDirect3DDevice9 *m_pIDirect3DDevice9; + }; + + class D3D9 : public IDirect3D9 + { + public: + D3D9(IDirect3D9Ex *pOriginal) : m_pIDirect3D9(pOriginal) {}; + virtual ~D3D9(void) {}; + + HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObj); + ULONG __stdcall AddRef(void); + ULONG __stdcall Release(void); + HRESULT __stdcall RegisterSoftwareDevice(void* pInitializeFunction); + UINT __stdcall GetAdapterCount(void); + HRESULT __stdcall GetAdapterIdentifier(UINT Adapter, DWORD Flags, D3DADAPTER_IDENTIFIER9* pIdentifier); + UINT __stdcall GetAdapterModeCount(UINT Adapter, D3DFORMAT Format); + HRESULT __stdcall EnumAdapterModes(UINT Adapter, D3DFORMAT Format, UINT Mode, D3DDISPLAYMODE* pMode); + HRESULT __stdcall GetAdapterDisplayMode(UINT Adapter, D3DDISPLAYMODE* pMode); + HRESULT __stdcall CheckDeviceType(UINT iAdapter, D3DDEVTYPE DevType, D3DFORMAT DisplayFormat, D3DFORMAT BackBufferFormat, BOOL bWindowed); + HRESULT __stdcall CheckDeviceFormat(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, DWORD Usage, D3DRESOURCETYPE RType, D3DFORMAT CheckFormat); + HRESULT __stdcall CheckDeviceMultiSampleType(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SurfaceFormat, BOOL Windowed, D3DMULTISAMPLE_TYPE MultiSampleType, DWORD* pQualityLevels); + HRESULT __stdcall CheckDepthStencilMatch(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT AdapterFormat, D3DFORMAT RenderTargetFormat, D3DFORMAT DepthStencilFormat); + HRESULT __stdcall CheckDeviceFormatConversion(UINT Adapter, D3DDEVTYPE DeviceType, D3DFORMAT SourceFormat, D3DFORMAT TargetFormat); + HRESULT __stdcall GetDeviceCaps(UINT Adapter, D3DDEVTYPE DeviceType, D3DCAPS9* pCaps); + HMONITOR __stdcall GetAdapterMonitor(UINT Adapter); + HRESULT __stdcall CreateDevice(UINT Adapter, D3DDEVTYPE DeviceType, HWND hFocusWindow, DWORD BehaviorFlags, D3DPRESENT_PARAMETERS* pPresentationParameters, IDirect3DDevice9** ppReturnedDeviceInterface); + + private: + IDirect3D9 *m_pIDirect3D9; + }; + + static IDirect3D9* __stdcall Direct3DCreate9Stub(UINT sdk); + }; +} diff --git a/src/Components/Modules/Dedicated.cpp b/src/Components/Modules/Dedicated.cpp index 5ab2f06d..1b25ba95 100644 --- a/src/Components/Modules/Dedicated.cpp +++ b/src/Components/Modules/Dedicated.cpp @@ -1,477 +1,477 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Utils::Signal Dedicated::FrameSignal; - Utils::Signal Dedicated::FrameOnceSignal; - - bool Dedicated::SendChat; - - bool Dedicated::IsEnabled() - { - static Utils::Value flag; - - if (!flag.isValid()) - { - flag.set(Flags::HasFlag("dedicated")); - } - - return flag.get(); - } - - void Dedicated::InitDedicatedServer() - { - static const char* fastfiles[7] = - { - "code_post_gfx_mp", - "localized_code_post_gfx_mp", - "ui_mp", - "localized_ui_mp", - "common_mp", - "localized_common_mp", - "patch_mp" - }; - - std::memcpy(reinterpret_cast(0x66E1CB0), &fastfiles, sizeof(fastfiles)); - Game::R_LoadGraphicsAssets(); - - if (Dvar::Var("com_logFilter").get()) - { +#include "STDInclude.hpp" + +namespace Components +{ + Utils::Signal Dedicated::FrameSignal; + Utils::Signal Dedicated::FrameOnceSignal; + + bool Dedicated::SendChat; + + bool Dedicated::IsEnabled() + { + static Utils::Value flag; + + if (!flag.isValid()) + { + flag.set(Flags::HasFlag("dedicated")); + } + + return flag.get(); + } + + void Dedicated::InitDedicatedServer() + { + static const char* fastfiles[7] = + { + "code_post_gfx_mp", + "localized_code_post_gfx_mp", + "ui_mp", + "localized_ui_mp", + "common_mp", + "localized_common_mp", + "patch_mp" + }; + + std::memcpy(reinterpret_cast(0x66E1CB0), &fastfiles, sizeof(fastfiles)); + Game::R_LoadGraphicsAssets(); + + if (Dvar::Var("com_logFilter").get()) + { Utils::Hook::Nop(0x647466, 5); // 'dvar set' lines - Utils::Hook::Nop(0x5DF4F2, 5); // 'sending splash open' lines - } - - Utils::Hook::Call(0x4F84C0)(); - } - - void Dedicated::PostInitialization() - { - Command::Execute("exec autoexec.cfg"); - Command::Execute("onlinegame 1"); - Command::Execute("exec default_xboxlive.cfg"); - Command::Execute("xblive_rankedmatch 1"); - Command::Execute("xblive_privatematch 1"); - Command::Execute("xblive_privateserver 0"); - Command::Execute("xstartprivatematch"); - //Command::Execute("xstartlobby"); - Command::Execute("sv_network_fps 1000"); - Command::Execute("com_maxfps 125"); - - // Process command line? - Utils::Hook::Call(0x60C3D0)(); - } - - __declspec(naked) void Dedicated::PostInitializationStub() - { - __asm - { - call Dedicated::PostInitialization - - // Start Com_EvenLoop - mov eax, 43D140h - jmp eax - } - } - - const char* Dedicated::EvaluateSay(char* text) - { - Dedicated::SendChat = true; - - if (text[1] == '/') - { - Dedicated::SendChat = false; - text[1] = text[0]; - ++text; - } - - return text; - } - - __declspec(naked) void Dedicated::PreSayStub() - { - __asm - { - mov eax, [esp + 100h + 10h] - - push eax - call EvaluateSay - add esp, 4h - - mov [esp + 100h + 10h], eax - - jmp Colors::CleanStrStub - } - } - - __declspec(naked) void Dedicated::PostSayStub() - { - __asm - { - // eax is used by the callee - push eax - - xor eax, eax - mov al, Dedicated::SendChat - - test al, al - jnz return - - // Don't send the chat - pop eax - retn - - return: - pop eax - - // Jump to the target - push 5DF620h - retn - } - } - - void Dedicated::TimeWrapStub(int code, const char* message) - { - static bool partyEnable; - static std::string mapname; - - partyEnable = Dvar::Var("party_enable").get(); - mapname = Dvar::Var("mapname").get(); - - QuickPatch::Once([]() - { - Dvar::Var("party_enable").set(partyEnable); - - if (!partyEnable) // Time wrapping should not occur in party servers, but yeah... - { - if (mapname.empty()) mapname = "mp_rust"; - Command::Execute(Utils::String::VA("map %s", mapname.data()), false); - mapname.clear(); - } - }); - - Game::Com_Error(code, message); - } - - void Dedicated::MapRotate() - { - if (!Dedicated::IsEnabled() && Dvar::Var("sv_dontrotate").get()) - { - Dvar::Var("sv_dontrotate").setRaw(0); - return; - } - - if (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get()) - { - Logger::Print("Not performing map rotation as we are hosting a party!\n"); - return; - } - - Logger::Print("Rotating map...\n"); - - // if nothing, just restart - if (Dvar::Var("sv_mapRotation").get().empty()) - { - Logger::Print("No rotation defined, restarting map.\n"); - - if (!Dvar::Var("sv_cheats").get()) - { - Command::Execute(Utils::String::VA("map %s", Dvar::Var("mapname").get()), true); - } - else - { - Command::Execute(Utils::String::VA("devmap %s", Dvar::Var("mapname").get()), true); - } - - return; - } - - // first, check if the string contains nothing - if (Dvar::Var("sv_mapRotationCurrent").get().empty()) - { - Logger::Print("Current map rotation has finished, reloading...\n"); - Dvar::Var("sv_mapRotationCurrent").set(Dvar::Var("sv_mapRotation").get()); - } - - std::string rotation = Dvar::Var("sv_mapRotationCurrent").get(); - - auto tokens = Utils::String::Explode(rotation, ' '); - - for (unsigned int i = 0; i < (tokens.size() - 1); i += 2) - { - if (i + 1 >= tokens.size()) - { - Dvar::Var("sv_mapRotationCurrent").set(""); - Command::Execute("map_rotate", true); - return; - } - - std::string key = tokens[i]; - std::string value = tokens[i + 1]; - - if (key == "map") - { - // Rebuild map rotation string - rotation.clear(); - for (unsigned int j = (i + 2); j < tokens.size(); ++j) - { - if (j != (i + 2)) rotation += " "; - rotation += tokens[j]; - } - - Dvar::Var("sv_mapRotationCurrent").set(rotation); - - Logger::Print("Loading new map: %s\n", value.data()); - Command::Execute(Utils::String::VA("map %s", value.data()), true); - break; - } - else if (key == "gametype") - { - Logger::Print("Applying new gametype: %s\n", value.data()); - Dvar::Var("g_gametype").set(value); - } - else - { - Logger::Print("Unsupported maprotation key '%s', motherfucker!\n", key.data()); - } - } - } - - void Dedicated::Heartbeat() - { - int masterPort = Dvar::Var("masterPort").get(); - const char* masterServerName = Dvar::Var("masterServerName").get(); - - Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort)); - - Logger::Print("Sending heartbeat to master: %s:%u\n", masterServerName, masterPort); - Network::SendCommand(master, "heartbeat", "IW4"); - } - - void Dedicated::Once(Utils::Slot callback) - { - Dedicated::FrameOnceSignal.connect(callback); - } - - void Dedicated::OnFrame(Utils::Slot callback) - { - Dedicated::FrameSignal.connect(callback); - } - - void Dedicated::FrameHandler() - { - auto copy = Dedicated::FrameSignal; - copy(); - - copy = Dedicated::FrameOnceSignal; - Dedicated::FrameOnceSignal.clear(); - copy(); - } - - __declspec(naked) void Dedicated::FrameStub() - { - __asm - { - pushad - call Dedicated::FrameHandler - popad - - push 5A8E80h - retn - } - } - - Dedicated::Dedicated() - { - // Map rotation - Utils::Hook::Set(0x4152E8, Dedicated::MapRotate); - Dvar::Register("sv_dontrotate", false, Game::dvar_flag::DVAR_FLAG_CHEAT, ""); - Dvar::Register("com_logFilter", true, Game::dvar_flag::DVAR_FLAG_LATCHED, "Removes ~95% of unneeded lines from the log"); - - if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) // Run zonebuilder as dedi :P - { - Dvar::Register("sv_lanOnly", false, Game::dvar_flag::DVAR_FLAG_NONE, "Don't act as node"); - - Utils::Hook(0x60BE98, Dedicated::InitDedicatedServer, HOOK_CALL).install()->quick(); - - Utils::Hook::Set(0x683370, 0xC3); // steam sometimes doesn't like the server - - Utils::Hook::Set(0x5B4FF0, 0xC3); // self-registration on party - Utils::Hook::Set(0x426130, 0xC3); // other party stuff? - - Utils::Hook::Set(0x4D7030, 0xC3); // upnp stuff - - Utils::Hook::Set(0x4B0FC3, 0x04); // make CL_Frame do client packets, even for game state 9 - Utils::Hook::Set(0x4F5090, 0xC3); // init sound system (1) - Utils::Hook::Set(0x507B80, 0xC3); // start render thread - Utils::Hook::Set(0x4F84C0, 0xC3); // R_Init caller - Utils::Hook::Set(0x46A630, 0xC3); // init sound system (2) - Utils::Hook::Set(0x41FDE0, 0xC3); // Com_Frame audio processor? - Utils::Hook::Set(0x41B9F0, 0xC3); // called from Com_Frame, seems to do renderer stuff - Utils::Hook::Set(0x41D010, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly - Utils::Hook::Set(0x62B6C0, 0xC3); // UI expression 'DebugPrint', mainly to prevent some console spam - - Utils::Hook::Set(0x468960, 0xC3); // some mixer-related function called on shutdown - Utils::Hook::Set(0x60AD90, 0); // masterServerName flags - - Utils::Hook::Nop(0x4DCEC9, 2); // some check preventing proper game functioning - Utils::Hook::Nop(0x507C79, 6); // another similar bsp check - Utils::Hook::Nop(0x414E4D, 6); // unknown check in SV_ExecuteClientMessage (0x20F0890 == 0, related to client->f_40) - Utils::Hook::Nop(0x4DCEE9, 5); // some deinit renderer function - Utils::Hook::Nop(0x59A896, 5); // warning message on a removed subsystem - Utils::Hook::Nop(0x4B4EEF, 5); // same as above - Utils::Hook::Nop(0x64CF77, 5); // function detecting video card, causes Direct3DCreate9 to be called - Utils::Hook::Nop(0x60BC52, 0x15); // recommended settings check - - Utils::Hook::Nop(0x45148B, 5); // Disable splash screen - - // isHost script call return 0 - Utils::Hook::Set(0x5DEC04, 0); - - // sv_network_fps max 1000, and uncheat - Utils::Hook::Set(0x4D3C67, 0); // ? - Utils::Hook::Set(0x4D3C69, 1000); - - // r_loadForRenderer default to 0 - Utils::Hook::Set(0x519DDF, 0); - - // disable cheat protection on onlinegame - Utils::Hook::Set(0x404CF7, 0x80); - - // some d3d9 call on error - Utils::Hook::Set(0x508470, 0xC3); - - // stop saving a config_mp.cfg - Utils::Hook::Set(0x60B240, 0xC3); - - // don't load the config - Utils::Hook::Set(0x4B4D19, 0xEB); - - // Dedicated frame handler - Utils::Hook(0x4B0F81, Dedicated::FrameStub, HOOK_CALL).install()->quick(); - - // Intercept chat sending - Utils::Hook(0x4D000B, Dedicated::PreSayStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x4D00D4, Dedicated::PostSayStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x4D0110, Dedicated::PostSayStub, HOOK_CALL).install()->quick(); - - // Intercept time wrapping - Utils::Hook(0x62737D, Dedicated::TimeWrapStub, HOOK_CALL).install()->quick(); - //Utils::Hook::Set(0x62735C, 50'000); // Time wrap after 50 seconds (for testing - i don't want to wait 3 weeks) - - if (!ZoneBuilder::IsEnabled()) - { - // Post initialization point - Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick(); - -#ifdef USE_LEGACY_SERVER_LIST - - // Heartbeats - Dedicated::OnFrame([] () - { - static int LastHeartbeat = 0; - - if (Dvar::Var("sv_maxclients").get() > 0 && !LastHeartbeat || (Game::Com_Milliseconds() - LastHeartbeat) > 120 * 1000) - { - LastHeartbeat = Game::Com_Milliseconds(); - Dedicated::Heartbeat(); - } - }); -#endif - } - - Dvar::OnInit([] () - { - Dvar::Register("sv_sayName", "^7Console", Game::dvar_flag::DVAR_FLAG_NONE, "The name to pose as for 'say' commands"); - Dvar::Register("sv_motd", "", Game::dvar_flag::DVAR_FLAG_NONE, "A custom message of the day for servers"); - - // Say command - Command::AddSV("say", [] (Command::Params* params) - { - if (params->length() < 2) return; - - std::string message = params->join(1); - std::string name = Dvar::Var("sv_sayName").get(); - - if (!name.empty()) - { - Game::SV_GameSendServerCommand(-1, 0, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); - Game::Com_Printf(15, "%s: %s\n", name.data(), message.data()); - } - else - { - Game::SV_GameSendServerCommand(-1, 0, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); - Game::Com_Printf(15, "Console: %s\n", message.data()); - } - }); - - // Tell command - Command::AddSV("tell", [] (Command::Params* params) - { - if (params->length() < 3) return; - - int client = atoi(params->get(1)); - std::string message = params->join(2); - std::string name = Dvar::Var("sv_sayName").get(); - - if (!name.empty()) - { - Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); - Game::Com_Printf(15, "%s -> %i: %s\n", name.data(), client, message.data()); - } - else - { - Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); - Game::Com_Printf(15, "Console -> %i: %s\n", client, message.data()); - } - }); - - // Sayraw command - Command::AddSV("sayraw", [] (Command::Params* params) - { - if (params->length() < 2) return; - - std::string message = params->join(1); - Game::SV_GameSendServerCommand(-1, 0, Utils::String::VA("%c \"%s\"", 104, message.data())); - Game::Com_Printf(15, "Raw: %s\n", message.data()); - }); - - // Tellraw command - Command::AddSV("tellraw", [] (Command::Params* params) - { - if (params->length() < 3) return; - - int client = atoi(params->get(1)); - std::string message = params->join(2); - Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"%s\"", 104, message.data())); - Game::Com_Printf(15, "Raw -> %i: %s\n", client, message.data()); - }); - - // ! command - Command::AddSV("!", [] (Command::Params* params) - { - if (params->length() != 2) return; - - int client = -1; - if (params->get(1) != "all"s) - { - client = atoi(params->get(1)); - - if (client >= *reinterpret_cast(0x31D938C)) - { - Game::Com_Printf(0, "Invalid player.\n"); - return; - } - } - - Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"\"", 106)); - }); - }); - } - } - - Dedicated::~Dedicated() - { - Dedicated::FrameOnceSignal.clear(); - Dedicated::FrameSignal.clear(); - } -} + Utils::Hook::Nop(0x5DF4F2, 5); // 'sending splash open' lines + } + + Utils::Hook::Call(0x4F84C0)(); + } + + void Dedicated::PostInitialization() + { + Command::Execute("exec autoexec.cfg"); + Command::Execute("onlinegame 1"); + Command::Execute("exec default_xboxlive.cfg"); + Command::Execute("xblive_rankedmatch 1"); + Command::Execute("xblive_privatematch 1"); + Command::Execute("xblive_privateserver 0"); + Command::Execute("xstartprivatematch"); + //Command::Execute("xstartlobby"); + Command::Execute("sv_network_fps 1000"); + Command::Execute("com_maxfps 125"); + + // Process command line? + Utils::Hook::Call(0x60C3D0)(); + } + + __declspec(naked) void Dedicated::PostInitializationStub() + { + __asm + { + call Dedicated::PostInitialization + + // Start Com_EvenLoop + mov eax, 43D140h + jmp eax + } + } + + const char* Dedicated::EvaluateSay(char* text) + { + Dedicated::SendChat = true; + + if (text[1] == '/') + { + Dedicated::SendChat = false; + text[1] = text[0]; + ++text; + } + + return text; + } + + __declspec(naked) void Dedicated::PreSayStub() + { + __asm + { + mov eax, [esp + 100h + 10h] + + push eax + call EvaluateSay + add esp, 4h + + mov [esp + 100h + 10h], eax + + jmp Colors::CleanStrStub + } + } + + __declspec(naked) void Dedicated::PostSayStub() + { + __asm + { + // eax is used by the callee + push eax + + xor eax, eax + mov al, Dedicated::SendChat + + test al, al + jnz return + + // Don't send the chat + pop eax + retn + + return: + pop eax + + // Jump to the target + push 5DF620h + retn + } + } + + void Dedicated::TimeWrapStub(int code, const char* message) + { + static bool partyEnable; + static std::string mapname; + + partyEnable = Dvar::Var("party_enable").get(); + mapname = Dvar::Var("mapname").get(); + + QuickPatch::Once([]() + { + Dvar::Var("party_enable").set(partyEnable); + + if (!partyEnable) // Time wrapping should not occur in party servers, but yeah... + { + if (mapname.empty()) mapname = "mp_rust"; + Command::Execute(Utils::String::VA("map %s", mapname.data()), false); + mapname.clear(); + } + }); + + Game::Com_Error(code, message); + } + + void Dedicated::MapRotate() + { + if (!Dedicated::IsEnabled() && Dvar::Var("sv_dontrotate").get()) + { + Dvar::Var("sv_dontrotate").setRaw(0); + return; + } + + if (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get()) + { + Logger::Print("Not performing map rotation as we are hosting a party!\n"); + return; + } + + Logger::Print("Rotating map...\n"); + + // if nothing, just restart + if (Dvar::Var("sv_mapRotation").get().empty()) + { + Logger::Print("No rotation defined, restarting map.\n"); + + if (!Dvar::Var("sv_cheats").get()) + { + Command::Execute(Utils::String::VA("map %s", Dvar::Var("mapname").get()), true); + } + else + { + Command::Execute(Utils::String::VA("devmap %s", Dvar::Var("mapname").get()), true); + } + + return; + } + + // first, check if the string contains nothing + if (Dvar::Var("sv_mapRotationCurrent").get().empty()) + { + Logger::Print("Current map rotation has finished, reloading...\n"); + Dvar::Var("sv_mapRotationCurrent").set(Dvar::Var("sv_mapRotation").get()); + } + + std::string rotation = Dvar::Var("sv_mapRotationCurrent").get(); + + auto tokens = Utils::String::Explode(rotation, ' '); + + for (unsigned int i = 0; i < (tokens.size() - 1); i += 2) + { + if (i + 1 >= tokens.size()) + { + Dvar::Var("sv_mapRotationCurrent").set(""); + Command::Execute("map_rotate", true); + return; + } + + std::string key = tokens[i]; + std::string value = tokens[i + 1]; + + if (key == "map") + { + // Rebuild map rotation string + rotation.clear(); + for (unsigned int j = (i + 2); j < tokens.size(); ++j) + { + if (j != (i + 2)) rotation += " "; + rotation += tokens[j]; + } + + Dvar::Var("sv_mapRotationCurrent").set(rotation); + + Logger::Print("Loading new map: %s\n", value.data()); + Command::Execute(Utils::String::VA("map %s", value.data()), true); + break; + } + else if (key == "gametype") + { + Logger::Print("Applying new gametype: %s\n", value.data()); + Dvar::Var("g_gametype").set(value); + } + else + { + Logger::Print("Unsupported maprotation key '%s', motherfucker!\n", key.data()); + } + } + } + + void Dedicated::Heartbeat() + { + int masterPort = Dvar::Var("masterPort").get(); + const char* masterServerName = Dvar::Var("masterServerName").get(); + + Network::Address master(Utils::String::VA("%s:%u", masterServerName, masterPort)); + + Logger::Print("Sending heartbeat to master: %s:%u\n", masterServerName, masterPort); + Network::SendCommand(master, "heartbeat", "IW4"); + } + + void Dedicated::Once(Utils::Slot callback) + { + Dedicated::FrameOnceSignal.connect(callback); + } + + void Dedicated::OnFrame(Utils::Slot callback) + { + Dedicated::FrameSignal.connect(callback); + } + + void Dedicated::FrameHandler() + { + auto copy = Dedicated::FrameSignal; + copy(); + + copy = Dedicated::FrameOnceSignal; + Dedicated::FrameOnceSignal.clear(); + copy(); + } + + __declspec(naked) void Dedicated::FrameStub() + { + __asm + { + pushad + call Dedicated::FrameHandler + popad + + push 5A8E80h + retn + } + } + + Dedicated::Dedicated() + { + // Map rotation + Utils::Hook::Set(0x4152E8, Dedicated::MapRotate); + Dvar::Register("sv_dontrotate", false, Game::dvar_flag::DVAR_FLAG_CHEAT, ""); + Dvar::Register("com_logFilter", true, Game::dvar_flag::DVAR_FLAG_LATCHED, "Removes ~95% of unneeded lines from the log"); + + if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) // Run zonebuilder as dedi :P + { + Dvar::Register("sv_lanOnly", false, Game::dvar_flag::DVAR_FLAG_NONE, "Don't act as node"); + + Utils::Hook(0x60BE98, Dedicated::InitDedicatedServer, HOOK_CALL).install()->quick(); + + Utils::Hook::Set(0x683370, 0xC3); // steam sometimes doesn't like the server + + Utils::Hook::Set(0x5B4FF0, 0xC3); // self-registration on party + Utils::Hook::Set(0x426130, 0xC3); // other party stuff? + + Utils::Hook::Set(0x4D7030, 0xC3); // upnp stuff + + Utils::Hook::Set(0x4B0FC3, 0x04); // make CL_Frame do client packets, even for game state 9 + Utils::Hook::Set(0x4F5090, 0xC3); // init sound system (1) + Utils::Hook::Set(0x507B80, 0xC3); // start render thread + Utils::Hook::Set(0x4F84C0, 0xC3); // R_Init caller + Utils::Hook::Set(0x46A630, 0xC3); // init sound system (2) + Utils::Hook::Set(0x41FDE0, 0xC3); // Com_Frame audio processor? + Utils::Hook::Set(0x41B9F0, 0xC3); // called from Com_Frame, seems to do renderer stuff + Utils::Hook::Set(0x41D010, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly + Utils::Hook::Set(0x62B6C0, 0xC3); // UI expression 'DebugPrint', mainly to prevent some console spam + + Utils::Hook::Set(0x468960, 0xC3); // some mixer-related function called on shutdown + Utils::Hook::Set(0x60AD90, 0); // masterServerName flags + + Utils::Hook::Nop(0x4DCEC9, 2); // some check preventing proper game functioning + Utils::Hook::Nop(0x507C79, 6); // another similar bsp check + Utils::Hook::Nop(0x414E4D, 6); // unknown check in SV_ExecuteClientMessage (0x20F0890 == 0, related to client->f_40) + Utils::Hook::Nop(0x4DCEE9, 5); // some deinit renderer function + Utils::Hook::Nop(0x59A896, 5); // warning message on a removed subsystem + Utils::Hook::Nop(0x4B4EEF, 5); // same as above + Utils::Hook::Nop(0x64CF77, 5); // function detecting video card, causes Direct3DCreate9 to be called + Utils::Hook::Nop(0x60BC52, 0x15); // recommended settings check + + Utils::Hook::Nop(0x45148B, 5); // Disable splash screen + + // isHost script call return 0 + Utils::Hook::Set(0x5DEC04, 0); + + // sv_network_fps max 1000, and uncheat + Utils::Hook::Set(0x4D3C67, 0); // ? + Utils::Hook::Set(0x4D3C69, 1000); + + // r_loadForRenderer default to 0 + Utils::Hook::Set(0x519DDF, 0); + + // disable cheat protection on onlinegame + Utils::Hook::Set(0x404CF7, 0x80); + + // some d3d9 call on error + Utils::Hook::Set(0x508470, 0xC3); + + // stop saving a config_mp.cfg + Utils::Hook::Set(0x60B240, 0xC3); + + // don't load the config + Utils::Hook::Set(0x4B4D19, 0xEB); + + // Dedicated frame handler + Utils::Hook(0x4B0F81, Dedicated::FrameStub, HOOK_CALL).install()->quick(); + + // Intercept chat sending + Utils::Hook(0x4D000B, Dedicated::PreSayStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x4D00D4, Dedicated::PostSayStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x4D0110, Dedicated::PostSayStub, HOOK_CALL).install()->quick(); + + // Intercept time wrapping + Utils::Hook(0x62737D, Dedicated::TimeWrapStub, HOOK_CALL).install()->quick(); + //Utils::Hook::Set(0x62735C, 50'000); // Time wrap after 50 seconds (for testing - i don't want to wait 3 weeks) + + if (!ZoneBuilder::IsEnabled()) + { + // Post initialization point + Utils::Hook(0x60BFBF, Dedicated::PostInitializationStub, HOOK_JUMP).install()->quick(); + +#ifdef USE_LEGACY_SERVER_LIST + + // Heartbeats + Dedicated::OnFrame([] () + { + static int LastHeartbeat = 0; + + if (Dvar::Var("sv_maxclients").get() > 0 && !LastHeartbeat || (Game::Com_Milliseconds() - LastHeartbeat) > 120 * 1000) + { + LastHeartbeat = Game::Com_Milliseconds(); + Dedicated::Heartbeat(); + } + }); +#endif + } + + Dvar::OnInit([] () + { + Dvar::Register("sv_sayName", "^7Console", Game::dvar_flag::DVAR_FLAG_NONE, "The name to pose as for 'say' commands"); + Dvar::Register("sv_motd", "", Game::dvar_flag::DVAR_FLAG_NONE, "A custom message of the day for servers"); + + // Say command + Command::AddSV("say", [] (Command::Params* params) + { + if (params->length() < 2) return; + + std::string message = params->join(1); + std::string name = Dvar::Var("sv_sayName").get(); + + if (!name.empty()) + { + Game::SV_GameSendServerCommand(-1, 0, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); + Game::Com_Printf(15, "%s: %s\n", name.data(), message.data()); + } + else + { + Game::SV_GameSendServerCommand(-1, 0, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); + Game::Com_Printf(15, "Console: %s\n", message.data()); + } + }); + + // Tell command + Command::AddSV("tell", [] (Command::Params* params) + { + if (params->length() < 3) return; + + int client = atoi(params->get(1)); + std::string message = params->join(2); + std::string name = Dvar::Var("sv_sayName").get(); + + if (!name.empty()) + { + Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"%s: %s\"", 104, name.data(), message.data())); + Game::Com_Printf(15, "%s -> %i: %s\n", name.data(), client, message.data()); + } + else + { + Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"Console: %s\"", 104, message.data())); + Game::Com_Printf(15, "Console -> %i: %s\n", client, message.data()); + } + }); + + // Sayraw command + Command::AddSV("sayraw", [] (Command::Params* params) + { + if (params->length() < 2) return; + + std::string message = params->join(1); + Game::SV_GameSendServerCommand(-1, 0, Utils::String::VA("%c \"%s\"", 104, message.data())); + Game::Com_Printf(15, "Raw: %s\n", message.data()); + }); + + // Tellraw command + Command::AddSV("tellraw", [] (Command::Params* params) + { + if (params->length() < 3) return; + + int client = atoi(params->get(1)); + std::string message = params->join(2); + Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"%s\"", 104, message.data())); + Game::Com_Printf(15, "Raw -> %i: %s\n", client, message.data()); + }); + + // ! command + Command::AddSV("!", [] (Command::Params* params) + { + if (params->length() != 2) return; + + int client = -1; + if (params->get(1) != "all"s) + { + client = atoi(params->get(1)); + + if (client >= *reinterpret_cast(0x31D938C)) + { + Game::Com_Printf(0, "Invalid player.\n"); + return; + } + } + + Game::SV_GameSendServerCommand(client, 0, Utils::String::VA("%c \"\"", 106)); + }); + }); + } + } + + Dedicated::~Dedicated() + { + Dedicated::FrameOnceSignal.clear(); + Dedicated::FrameSignal.clear(); + } +} diff --git a/src/Components/Modules/Dedicated.hpp b/src/Components/Modules/Dedicated.hpp index 4774b815..ef7c4ff4 100644 --- a/src/Components/Modules/Dedicated.hpp +++ b/src/Components/Modules/Dedicated.hpp @@ -1,43 +1,43 @@ -namespace Components -{ - class Dedicated : public Component - { - public: - typedef void(Callback)(); - - Dedicated(); - ~Dedicated(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Dedicated"; }; -#endif - - static bool IsEnabled(); - - static void Heartbeat(); - - static void OnFrame(Utils::Slot callback); - static void Once(Utils::Slot callback); - - private: - static Utils::Signal FrameSignal; - static Utils::Signal FrameOnceSignal; - - static bool SendChat; - - static void MapRotate(); - static void FrameHandler(); - static void FrameStub(); - static void InitDedicatedServer(); - - static void PostInitialization(); - static void PostInitializationStub(); - - static const char* EvaluateSay(char* text); - - static void PreSayStub(); - static void PostSayStub(); - - static void TimeWrapStub(int code, const char* message); - }; -} +namespace Components +{ + class Dedicated : public Component + { + public: + typedef void(Callback)(); + + Dedicated(); + ~Dedicated(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Dedicated"; }; +#endif + + static bool IsEnabled(); + + static void Heartbeat(); + + static void OnFrame(Utils::Slot callback); + static void Once(Utils::Slot callback); + + private: + static Utils::Signal FrameSignal; + static Utils::Signal FrameOnceSignal; + + static bool SendChat; + + static void MapRotate(); + static void FrameHandler(); + static void FrameStub(); + static void InitDedicatedServer(); + + static void PostInitialization(); + static void PostInitializationStub(); + + static const char* EvaluateSay(char* text); + + static void PreSayStub(); + static void PostSayStub(); + + static void TimeWrapStub(int code, const char* message); + }; +} diff --git a/src/Components/Modules/Discovery.cpp b/src/Components/Modules/Discovery.cpp index ab989dd0..3effa8c3 100644 --- a/src/Components/Modules/Discovery.cpp +++ b/src/Components/Modules/Discovery.cpp @@ -1,108 +1,108 @@ -#include "STDInclude.hpp" - -namespace Components -{ - bool Discovery::IsTerminating = false; - bool Discovery::IsPerforming = false; - std::thread Discovery::Thread; - std::string Discovery::Challenge; - - void Discovery::Perform() - { - Discovery::IsPerforming = true; - } - - Discovery::Discovery() - { - Dvar::Register("net_discoveryPortRangeMin", 25000, 0, 65535, Game::dvar_flag::DVAR_FLAG_SAVED, "Minimum scan range port for local server discovery"); - Dvar::Register("net_discoveryPortRangeMax", 35000, 1, 65536, Game::dvar_flag::DVAR_FLAG_SAVED, "Maximum scan range port for local server discovery"); - - // An additional thread prevents lags - // Not sure if that's the best way though - Discovery::IsPerforming = false; - Discovery::IsTerminating = false; - Discovery::Thread = std::thread([] () - { - while (!Discovery::IsTerminating) - { - if (Discovery::IsPerforming) - { - int start = Game::Sys_Milliseconds(); - - Logger::Print("Starting local server discovery...\n"); - - Discovery::Challenge = Utils::String::VA("%X", Utils::Cryptography::Rand::GenerateInt()); - - unsigned int minPort = Dvar::Var("net_discoveryPortRangeMin").get(); - unsigned int maxPort = Dvar::Var("net_discoveryPortRangeMax").get(); - Network::BroadcastRange(minPort, maxPort, Utils::String::VA("discovery %s", Discovery::Challenge.data())); - - Logger::Print("Discovery sent within %dms, awaiting responses...\n", Game::Sys_Milliseconds() - start); - - Discovery::IsPerforming = false; - } - - std::this_thread::sleep_for(50ms); - } - }); - - Network::Handle("discovery", [] (Network::Address address, std::string data) - { - if (address.isSelf()) return; - - if (!address.isLocal()) - { - Logger::Print("Received discovery request from non-local address: %s\n", address.getCString()); - return; - } - - Logger::Print("Received discovery request from %s\n", address.getCString()); - Network::SendCommand(address, "discoveryResponse", data); - }); - - Network::Handle("discoveryResponse", [] (Network::Address address, std::string data) - { - if (address.isSelf()) return; - - if (!address.isLocal()) - { - Logger::Print("Received discovery response from non-local address: %s\n", address.getCString()); - return; - } - - if (Utils::ParseChallenge(data) != Discovery::Challenge) - { - Logger::Print("Received discovery with invalid challenge from: %s\n", address.getCString()); - return; - } - - Logger::Print("Received discovery response from: %s\n", address.getCString()); - - if (ServerList::IsOfflineList()) - { - ServerList::InsertRequest(address, true); - } - }); - - // This is placed here in case the anticheat has been disabled! - // Make sure this is called after the memory scan! -#if !defined(DEBUG) && !defined(DISABLE_ANTICHEAT) - Utils::Hook(0x5ACB9E, [] () // Somewhere in the renderer, past the scan check - { - AntiCheat::ScanIntegrityCheck(); - return Utils::Hook::Call(0x4AA720)(); - }, HOOK_CALL).install()->quick(); -#endif - } - - Discovery::~Discovery() - { - Discovery::IsPerforming = false; - Discovery::IsTerminating = true; - - if (Discovery::Thread.joinable()) - { - Discovery::Thread.join(); - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + bool Discovery::IsTerminating = false; + bool Discovery::IsPerforming = false; + std::thread Discovery::Thread; + std::string Discovery::Challenge; + + void Discovery::Perform() + { + Discovery::IsPerforming = true; + } + + Discovery::Discovery() + { + Dvar::Register("net_discoveryPortRangeMin", 25000, 0, 65535, Game::dvar_flag::DVAR_FLAG_SAVED, "Minimum scan range port for local server discovery"); + Dvar::Register("net_discoveryPortRangeMax", 35000, 1, 65536, Game::dvar_flag::DVAR_FLAG_SAVED, "Maximum scan range port for local server discovery"); + + // An additional thread prevents lags + // Not sure if that's the best way though + Discovery::IsPerforming = false; + Discovery::IsTerminating = false; + Discovery::Thread = std::thread([] () + { + while (!Discovery::IsTerminating) + { + if (Discovery::IsPerforming) + { + int start = Game::Sys_Milliseconds(); + + Logger::Print("Starting local server discovery...\n"); + + Discovery::Challenge = Utils::String::VA("%X", Utils::Cryptography::Rand::GenerateInt()); + + unsigned int minPort = Dvar::Var("net_discoveryPortRangeMin").get(); + unsigned int maxPort = Dvar::Var("net_discoveryPortRangeMax").get(); + Network::BroadcastRange(minPort, maxPort, Utils::String::VA("discovery %s", Discovery::Challenge.data())); + + Logger::Print("Discovery sent within %dms, awaiting responses...\n", Game::Sys_Milliseconds() - start); + + Discovery::IsPerforming = false; + } + + std::this_thread::sleep_for(50ms); + } + }); + + Network::Handle("discovery", [] (Network::Address address, std::string data) + { + if (address.isSelf()) return; + + if (!address.isLocal()) + { + Logger::Print("Received discovery request from non-local address: %s\n", address.getCString()); + return; + } + + Logger::Print("Received discovery request from %s\n", address.getCString()); + Network::SendCommand(address, "discoveryResponse", data); + }); + + Network::Handle("discoveryResponse", [] (Network::Address address, std::string data) + { + if (address.isSelf()) return; + + if (!address.isLocal()) + { + Logger::Print("Received discovery response from non-local address: %s\n", address.getCString()); + return; + } + + if (Utils::ParseChallenge(data) != Discovery::Challenge) + { + Logger::Print("Received discovery with invalid challenge from: %s\n", address.getCString()); + return; + } + + Logger::Print("Received discovery response from: %s\n", address.getCString()); + + if (ServerList::IsOfflineList()) + { + ServerList::InsertRequest(address, true); + } + }); + + // This is placed here in case the anticheat has been disabled! + // Make sure this is called after the memory scan! +#if !defined(DEBUG) && !defined(DISABLE_ANTICHEAT) + Utils::Hook(0x5ACB9E, [] () // Somewhere in the renderer, past the scan check + { + AntiCheat::ScanIntegrityCheck(); + return Utils::Hook::Call(0x4AA720)(); + }, HOOK_CALL).install()->quick(); +#endif + } + + Discovery::~Discovery() + { + Discovery::IsPerforming = false; + Discovery::IsTerminating = true; + + if (Discovery::Thread.joinable()) + { + Discovery::Thread.join(); + } + } +} diff --git a/src/Components/Modules/Discovery.hpp b/src/Components/Modules/Discovery.hpp index 551b0490..ab383fe5 100644 --- a/src/Components/Modules/Discovery.hpp +++ b/src/Components/Modules/Discovery.hpp @@ -1,21 +1,21 @@ -namespace Components -{ - class Discovery : public Component - { - public: - Discovery(); - ~Discovery(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Discovery"; }; -#endif - - static void Perform(); - - private: - static bool IsTerminating; - static bool IsPerforming; - static std::thread Thread; - static std::string Challenge; - }; -} +namespace Components +{ + class Discovery : public Component + { + public: + Discovery(); + ~Discovery(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Discovery"; }; +#endif + + static void Perform(); + + private: + static bool IsTerminating; + static bool IsPerforming; + static std::thread Thread; + static std::string Challenge; + }; +} diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 39521e21..40ca4c6f 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -1,595 +1,595 @@ -#include "STDInclude.hpp" - -namespace Components -{ - mg_mgr Download::Mgr; - Download::ClientDownload Download::CLDownload; - -#pragma region Client - - void Download::InitiateClientDownload(std::string mod) - { - if (Download::CLDownload.running) return; - - Localization::SetTemp("MPUI_EST_TIME_LEFT", Utils::String::FormatTimeSpan(0)); - Localization::SetTemp("MPUI_PROGRESS_DL", "(0/0) %"); - Localization::SetTemp("MPUI_TRANS_RATE", "0.0 MB/s"); - - Command::Execute("openmenu mod_download_popmenu", true); - - Download::CLDownload.running = true; - Download::CLDownload.mod = mod; - Download::CLDownload.terminateThread = false; - Download::CLDownload.target = Party::Target(); - Download::CLDownload.thread = std::thread(Download::ModDownloader, &Download::CLDownload); - } - - bool Download::ParseModList(ClientDownload* download, std::string list) - { - if (!download) return false; - download->files.clear(); - - std::string error; - json11::Json listData = json11::Json::parse(list, error); - - - if (!error.empty() || !listData.is_array()) return false; - - download->totalBytes = 0; - - for (auto& file : listData.array_items()) - { - if (!file.is_object()) return false; - - auto hash = file["hash"]; - auto name = file["name"]; - auto size = file["size"]; - - if (!hash.is_string() || !name.is_string() || !size.is_number()) return false; - - Download::ClientDownload::File fileEntry; - fileEntry.name = name.string_value(); - fileEntry.hash = hash.string_value(); - fileEntry.size = static_cast(size.number_value()); - - if (!fileEntry.name.empty()) - { - download->files.push_back(fileEntry); - download->totalBytes += fileEntry.size; - } - } - - return true; - } - - void Download::DownloadHandler(mg_connection *nc, int ev, void* ev_data) - { - http_message* hm = reinterpret_cast(ev_data); - Download::FileDownload* fDownload = reinterpret_cast(nc->mgr->user_data); - - if (ev == MG_EV_CONNECT) - { - if (hm->message.p) - { - fDownload->downloading = false; - return; - } - } - - if (ev == MG_EV_RECV) - { - size_t bytes = static_cast(*reinterpret_cast(ev_data)); - fDownload->receivedBytes += bytes; - fDownload->download->downBytes += bytes; - fDownload->download->timeStampBytes += bytes; - - double progress = (100.0 / fDownload->download->totalBytes) * fDownload->download->downBytes; - Localization::SetTemp("MPUI_PROGRESS_DL", Utils::String::VA("(%d/%d) %d%%", fDownload->index + 1, fDownload->download->files.size(), static_cast(progress))); - - int delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp; - if (delta > 300) - { - bool doFormat = fDownload->download->lastTimeStamp != 0; - fDownload->download->lastTimeStamp = Game::Sys_Milliseconds(); - - size_t dataLeft = fDownload->download->totalBytes - fDownload->download->downBytes; - double timeLeftD = ((1.0 * dataLeft) / fDownload->download->timeStampBytes) * delta; - int timeLeft = static_cast(timeLeftD); - - if (doFormat) - { - Localization::SetTemp("MPUI_EST_TIME_LEFT", Utils::String::FormatTimeSpan(timeLeft)); - Localization::SetTemp("MPUI_TRANS_RATE", Utils::String::FormatBandwidth(fDownload->download->timeStampBytes, delta)); - } - - fDownload->download->timeStampBytes = 0; - } - } - - if (ev == MG_EV_HTTP_REPLY) - { - nc->flags |= MG_F_CLOSE_IMMEDIATELY; - fDownload->buffer = std::string(hm->body.p, hm->body.len); - fDownload->downloading = false; - return; - } - } - - bool Download::DownloadFile(ClientDownload* download, unsigned int index) - { - if (!download || download->files.size() <= index) return false; - - auto file = download->files[index]; - - std::string path = download->mod + "/" + file.name; - if (Utils::IO::FileExists(path)) - { - std::string data = Utils::IO::ReadFile(path); - - if (data.size() == file.size && Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(data), "") == file.hash) - { - download->totalBytes += file.size; - return true; - } - } - - std::string url = "http://" + download->target.getString() + "/file/" + file.name; - - Download::FileDownload fDownload; - fDownload.file = file; - fDownload.index = index; - fDownload.download = download; - fDownload.downloading = true; - fDownload.receivedBytes = 0; - - Utils::String::Replace(url, " ", "%20"); - - download->valid = true; - mg_mgr_init(&download->mgr, &fDownload); - mg_connect_http(&download->mgr, Download::DownloadHandler, url.data(), NULL, NULL); - - while (fDownload.downloading && !fDownload.download->terminateThread) - { - mg_mgr_poll(&download->mgr, 0); - } - - mg_mgr_free(&download->mgr); - download->valid = false; - - if (fDownload.buffer.size() != file.size || Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(fDownload.buffer), "") != file.hash) - { - return false; - } - - Utils::IO::CreateDirectory(download->mod); - Utils::IO::WriteFile(path, fDownload.buffer); - - return true; - } - - void Download::ModDownloader(ClientDownload* download) - { - if (!download) download = &Download::CLDownload; - - std::string host = "http://" + download->target.getString(); - std::string list = Utils::WebIO("IW4x", host + "/list").setTimeout(5000)->get(); - - if (list.empty()) - { - if (download->terminateThread) return; - - download->thread.detach(); - download->clear(); - - QuickPatch::Once([] () - { - Party::ConnectError("Failed to download the modlist!"); - }); - - return; - } - - if (download->terminateThread) return; - - if (!Download::ParseModList(download, list)) - { - if (download->terminateThread) return; - - download->thread.detach(); - download->clear(); - - QuickPatch::Once([] () - { - Party::ConnectError("Failed to parse the modlist!"); - }); - - return; - } - - if (download->terminateThread) return; - - static std::string mod = download->mod; - - for (unsigned int i = 0; i < download->files.size(); ++i) - { - if (download->terminateThread) return; - - if (!Download::DownloadFile(download, i)) - { - if (download->terminateThread) return; - - mod = Utils::String::VA("Failed to download file: %s!", download->files[i].name.data()); - download->thread.detach(); - download->clear(); - - QuickPatch::Once([] () - { - Dvar::Var("partyend_reason").set(mod); - mod.clear(); - - Localization::ClearTemp(); - Command::Execute("closemenu mod_download_popmenu"); - Command::Execute("openmenu menu_xboxlive_partyended"); - }); - - return; - } - } - - if (download->terminateThread) return; - - download->thread.detach(); - download->clear(); - - // Run this on the main thread - QuickPatch::Once([] () - { - auto fsGame = Dvar::Var("fs_game"); - fsGame.set(mod); - fsGame.get()->modified = true; - - mod.clear(); - - Localization::ClearTemp(); - Command::Execute("closemenu mod_download_popmenu", true); - - if (Dvar::Var("cl_modVidRestart").get()) - { - Command::Execute("vid_restart", false); - } - - Command::Execute("reconnect", false); - }); - } - -#pragma endregion - -#pragma region Server - - bool Download::IsClient(mg_connection *nc) - { - return (Download::GetClient(nc) != nullptr); - } - - Game::client_t* Download::GetClient(mg_connection *nc) - { - Network::Address address(nc->sa.sa); - - for (int i = 0; i < *Game::svs_numclients; ++i) - { - Game::client_t* client = &Game::svs_clients[i]; - - if (client->state >= 3) - { - if (address.getIP().full == Network::Address(client->addr).getIP().full) - { - return client; - } - } - } - - return nullptr; - } - - void Download::Forbid(mg_connection *nc) - { - mg_printf(nc, "HTTP/1.1 403 Forbidden\r\n" - "Content-Type: text/html\r\n" - "Connection: close\r\n" - "\r\n" - "403 - Forbidden"); - - nc->flags |= MG_F_SEND_AND_CLOSE; - } - - void Download::ListHandler(mg_connection* nc, int ev, void* /*ev_data*/) - { - // Only handle http requests - if (ev != MG_EV_HTTP_REQUEST) return; - -// if (!Download::IsClient(nc)) -// { -// Download::Forbid(nc); -// } -// else - { - static std::string fsGamePre; - static json11::Json jsonList; - - std::string fsGame = Dvar::Var("fs_game").get(); - - if (!fsGame.empty() && fsGame != fsGamePre) - { - std::vector fileList; - - fsGamePre = fsGame; - - std::string path = Dvar::Var("fs_basepath").get() + "\\" + fsGame; - auto list = FileSystem::GetSysFileList(path, "iwd", false); - - list.push_back("mod.ff"); - - for (auto i = list.begin(); i != list.end(); ++i) - { - std::string filename = path + "\\" + *i; - if (strstr(i->data(), "_svr_") == NULL && Utils::IO::FileExists(filename)) - { - std::map file; - std::string fileBuffer = Utils::IO::ReadFile(path + "\\" + *i); - - file["name"] = *i; - file["size"] = static_cast(fileBuffer.size()); - file["hash"] = Utils::Cryptography::SHA256::Compute(fileBuffer, true); - - fileList.push_back(file); - } - } - - jsonList = fileList; - } - - mg_printf(nc, - "HTTP/1.1 200 OK\r\n" - "Content-Type: application/json\r\n" - "Connection: close\r\n" - "\r\n" - "%s", jsonList.dump().data()); - - nc->flags |= MG_F_SEND_AND_CLOSE; - } - } - - void Download::FileHandler(mg_connection *nc, int ev, void *ev_data) - { - // Only handle http requests - if (ev != MG_EV_HTTP_REQUEST) return; - - http_message* message = reinterpret_cast(ev_data); - -// if (!Download::IsClient(nc)) -// { -// Download::Forbid(nc); -// } -// else - { - std::string url(message->uri.p, message->uri.len); - Utils::String::Replace(url, "\\", "/"); - url = url.substr(6); - - Utils::String::Replace(url, "%20", " "); - - if (url.find_first_of("/") != std::string::npos || (!Utils::String::EndsWith(url, ".iwd") && url != "mod.ff") || strstr(url.data(), "_svr_") != NULL) - { - Download::Forbid(nc); - return; - } - - std::string fsGame = Dvar::Var("fs_game").get(); - std::string path = Dvar::Var("fs_basepath").get() + "\\" + fsGame + "\\" + url; - - if (fsGame.empty() || !Utils::IO::FileExists(path)) - { - mg_printf(nc, - "HTTP/1.1 404 Not Found\r\n" - "Content-Type: text/html\r\n" - "Connection: close\r\n" - "\r\n" - "404 - Not Found %s", path.data()); - } - else - { - std::string file = Utils::IO::ReadFile(path); - - mg_printf(nc, - "HTTP/1.1 200 OK\r\n" - "Content-Type: application/octet-stream\r\n" - "Content-Length: %d\r\n" - "Connection: close\r\n" - "\r\n", file.size()); - - mg_send(nc, file.data(), static_cast(file.size())); - } - - nc->flags |= MG_F_SEND_AND_CLOSE; - } - } - - void Download::InfoHandler(mg_connection* nc, int ev, void* /*ev_data*/) - { - // Only handle http requests - if (ev != MG_EV_HTTP_REQUEST) return; - - //http_message* message = reinterpret_cast(ev_data); - - Utils::InfoString status = ServerInfo::GetInfo(); - - std::map info; - info["status"] = status.to_json(); - - std::vector players; - - // Build player list - for (int i = 0; i < atoi(status.get("sv_maxclients").data()); ++i) // Maybe choose 18 here? - { - std::map playerInfo; - playerInfo["score"] = 0; - playerInfo["ping"] = 0; - playerInfo["name"] = ""; - - if (Dvar::Var("sv_running").get()) - { - if (Game::svs_clients[i].state < 3) continue; - - playerInfo["score"] = Game::SV_GameClientNum_Score(i); - playerInfo["ping"] = Game::svs_clients[i].ping; - playerInfo["name"] = Game::svs_clients[i].name; - } - else - { - // Score and ping are irrelevant - const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast(0x1081C00), i); - if (!namePtr || !namePtr[0]) continue; - - playerInfo["name"] = namePtr; - } - - players.push_back(playerInfo); - } - - info["players"] = players; - - mg_printf(nc, - "HTTP/1.1 200 OK\r\n" - "Content-Type: application/json\r\n" - "Connection: close\r\n" - "Access-Control-Allow-Origin: *\r\n" - "\r\n" - "%s", json11::Json(info).dump().data()); - - nc->flags |= MG_F_SEND_AND_CLOSE; - } - - void Download::EventHandler(mg_connection *nc, int ev, void *ev_data) - { - // Only handle http requests - if (ev != MG_EV_HTTP_REQUEST) return; - - http_message* message = reinterpret_cast(ev_data); - -// if (message->uri.p, message->uri.len == "/"s) -// { -// mg_printf(nc, -// "HTTP/1.1 200 OK\r\n" -// "Content-Type: text/html\r\n" -// "Connection: close\r\n" -// "\r\n" -// "Hi fella!
You are%s connected to this server!", (Download::IsClient(nc) ? " " : " not")); -// -// Game::client_t* client = Download::GetClient(nc); -// -// if (client) -// { -// mg_printf(nc, "
Hello %s!", client->name); -// } -// } -// else - { - //std::string path = (Dvar::Var("fs_basepath").get() + "\\" BASEGAME "\\html"); - //mg_serve_http_opts opts = { 0 }; - //opts.document_root = path.data(); - //mg_serve_http(nc, message, opts); - - FileSystem::File file; - std::string url = "html" + std::string(message->uri.p, message->uri.len); - - if (Utils::String::EndsWith(url, "/")) - { - url.append("index.html"); - file = FileSystem::File(url); - } - else - { - file = FileSystem::File(url); - - if (!file.exists()) - { - url.append("/index.html"); - file = FileSystem::File(url); - } - } - - std::string mimeType = Utils::GetMimeType(url); - - if (file.exists()) - { - std::string buffer = file.getBuffer(); - - mg_printf(nc, - "HTTP/1.1 200 OK\r\n" - "Content-Type: %s\r\n" - "Content-Length: %d\r\n" - "Connection: close\r\n" - "\r\n", mimeType.data(), buffer.size()); - - mg_send(nc, buffer.data(), static_cast(buffer.size())); - } - else - { - mg_printf(nc, - "HTTP/1.1 404 Not Found\r\n" - "Content-Type: text/html\r\n" - "Connection: close\r\n" - "\r\n" - "404 - Not Found"); - } - } - - nc->flags |= MG_F_SEND_AND_CLOSE; - } - -#pragma endregion - - Download::Download() - { - if (Dedicated::IsEnabled()) - { - mg_mgr_init(&Download::Mgr, NULL); - - Network::OnStart([] () - { - mg_connection* nc = mg_bind(&Download::Mgr, Utils::String::VA("%hu", (Dvar::Var("net_port").get() & 0xFFFF)), Download::EventHandler); - - // Handle special requests - mg_register_http_endpoint(nc, "/info", Download::InfoHandler); - mg_register_http_endpoint(nc, "/list", Download::ListHandler); - mg_register_http_endpoint(nc, "/file", Download::FileHandler); - - mg_set_protocol_http_websocket(nc); - }); - - QuickPatch::OnFrame([] - { - mg_mgr_poll(&Download::Mgr, 0); - }); - } - else - { - UIScript::Add("mod_download_cancel", [] (UIScript::Token) - { - Download::CLDownload.clear(); - }); - } - } - - Download::~Download() - { - if (Dedicated::IsEnabled()) - { - mg_mgr_free(&Download::Mgr); - } - else - { - Download::CLDownload.clear(); - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + mg_mgr Download::Mgr; + Download::ClientDownload Download::CLDownload; + +#pragma region Client + + void Download::InitiateClientDownload(std::string mod) + { + if (Download::CLDownload.running) return; + + Localization::SetTemp("MPUI_EST_TIME_LEFT", Utils::String::FormatTimeSpan(0)); + Localization::SetTemp("MPUI_PROGRESS_DL", "(0/0) %"); + Localization::SetTemp("MPUI_TRANS_RATE", "0.0 MB/s"); + + Command::Execute("openmenu mod_download_popmenu", true); + + Download::CLDownload.running = true; + Download::CLDownload.mod = mod; + Download::CLDownload.terminateThread = false; + Download::CLDownload.target = Party::Target(); + Download::CLDownload.thread = std::thread(Download::ModDownloader, &Download::CLDownload); + } + + bool Download::ParseModList(ClientDownload* download, std::string list) + { + if (!download) return false; + download->files.clear(); + + std::string error; + json11::Json listData = json11::Json::parse(list, error); + + + if (!error.empty() || !listData.is_array()) return false; + + download->totalBytes = 0; + + for (auto& file : listData.array_items()) + { + if (!file.is_object()) return false; + + auto hash = file["hash"]; + auto name = file["name"]; + auto size = file["size"]; + + if (!hash.is_string() || !name.is_string() || !size.is_number()) return false; + + Download::ClientDownload::File fileEntry; + fileEntry.name = name.string_value(); + fileEntry.hash = hash.string_value(); + fileEntry.size = static_cast(size.number_value()); + + if (!fileEntry.name.empty()) + { + download->files.push_back(fileEntry); + download->totalBytes += fileEntry.size; + } + } + + return true; + } + + void Download::DownloadHandler(mg_connection *nc, int ev, void* ev_data) + { + http_message* hm = reinterpret_cast(ev_data); + Download::FileDownload* fDownload = reinterpret_cast(nc->mgr->user_data); + + if (ev == MG_EV_CONNECT) + { + if (hm->message.p) + { + fDownload->downloading = false; + return; + } + } + + if (ev == MG_EV_RECV) + { + size_t bytes = static_cast(*reinterpret_cast(ev_data)); + fDownload->receivedBytes += bytes; + fDownload->download->downBytes += bytes; + fDownload->download->timeStampBytes += bytes; + + double progress = (100.0 / fDownload->download->totalBytes) * fDownload->download->downBytes; + Localization::SetTemp("MPUI_PROGRESS_DL", Utils::String::VA("(%d/%d) %d%%", fDownload->index + 1, fDownload->download->files.size(), static_cast(progress))); + + int delta = Game::Sys_Milliseconds() - fDownload->download->lastTimeStamp; + if (delta > 300) + { + bool doFormat = fDownload->download->lastTimeStamp != 0; + fDownload->download->lastTimeStamp = Game::Sys_Milliseconds(); + + size_t dataLeft = fDownload->download->totalBytes - fDownload->download->downBytes; + double timeLeftD = ((1.0 * dataLeft) / fDownload->download->timeStampBytes) * delta; + int timeLeft = static_cast(timeLeftD); + + if (doFormat) + { + Localization::SetTemp("MPUI_EST_TIME_LEFT", Utils::String::FormatTimeSpan(timeLeft)); + Localization::SetTemp("MPUI_TRANS_RATE", Utils::String::FormatBandwidth(fDownload->download->timeStampBytes, delta)); + } + + fDownload->download->timeStampBytes = 0; + } + } + + if (ev == MG_EV_HTTP_REPLY) + { + nc->flags |= MG_F_CLOSE_IMMEDIATELY; + fDownload->buffer = std::string(hm->body.p, hm->body.len); + fDownload->downloading = false; + return; + } + } + + bool Download::DownloadFile(ClientDownload* download, unsigned int index) + { + if (!download || download->files.size() <= index) return false; + + auto file = download->files[index]; + + std::string path = download->mod + "/" + file.name; + if (Utils::IO::FileExists(path)) + { + std::string data = Utils::IO::ReadFile(path); + + if (data.size() == file.size && Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(data), "") == file.hash) + { + download->totalBytes += file.size; + return true; + } + } + + std::string url = "http://" + download->target.getString() + "/file/" + file.name; + + Download::FileDownload fDownload; + fDownload.file = file; + fDownload.index = index; + fDownload.download = download; + fDownload.downloading = true; + fDownload.receivedBytes = 0; + + Utils::String::Replace(url, " ", "%20"); + + download->valid = true; + mg_mgr_init(&download->mgr, &fDownload); + mg_connect_http(&download->mgr, Download::DownloadHandler, url.data(), NULL, NULL); + + while (fDownload.downloading && !fDownload.download->terminateThread) + { + mg_mgr_poll(&download->mgr, 0); + } + + mg_mgr_free(&download->mgr); + download->valid = false; + + if (fDownload.buffer.size() != file.size || Utils::String::DumpHex(Utils::Cryptography::SHA256::Compute(fDownload.buffer), "") != file.hash) + { + return false; + } + + Utils::IO::CreateDirectory(download->mod); + Utils::IO::WriteFile(path, fDownload.buffer); + + return true; + } + + void Download::ModDownloader(ClientDownload* download) + { + if (!download) download = &Download::CLDownload; + + std::string host = "http://" + download->target.getString(); + std::string list = Utils::WebIO("IW4x", host + "/list").setTimeout(5000)->get(); + + if (list.empty()) + { + if (download->terminateThread) return; + + download->thread.detach(); + download->clear(); + + QuickPatch::Once([] () + { + Party::ConnectError("Failed to download the modlist!"); + }); + + return; + } + + if (download->terminateThread) return; + + if (!Download::ParseModList(download, list)) + { + if (download->terminateThread) return; + + download->thread.detach(); + download->clear(); + + QuickPatch::Once([] () + { + Party::ConnectError("Failed to parse the modlist!"); + }); + + return; + } + + if (download->terminateThread) return; + + static std::string mod = download->mod; + + for (unsigned int i = 0; i < download->files.size(); ++i) + { + if (download->terminateThread) return; + + if (!Download::DownloadFile(download, i)) + { + if (download->terminateThread) return; + + mod = Utils::String::VA("Failed to download file: %s!", download->files[i].name.data()); + download->thread.detach(); + download->clear(); + + QuickPatch::Once([] () + { + Dvar::Var("partyend_reason").set(mod); + mod.clear(); + + Localization::ClearTemp(); + Command::Execute("closemenu mod_download_popmenu"); + Command::Execute("openmenu menu_xboxlive_partyended"); + }); + + return; + } + } + + if (download->terminateThread) return; + + download->thread.detach(); + download->clear(); + + // Run this on the main thread + QuickPatch::Once([] () + { + auto fsGame = Dvar::Var("fs_game"); + fsGame.set(mod); + fsGame.get()->modified = true; + + mod.clear(); + + Localization::ClearTemp(); + Command::Execute("closemenu mod_download_popmenu", true); + + if (Dvar::Var("cl_modVidRestart").get()) + { + Command::Execute("vid_restart", false); + } + + Command::Execute("reconnect", false); + }); + } + +#pragma endregion + +#pragma region Server + + bool Download::IsClient(mg_connection *nc) + { + return (Download::GetClient(nc) != nullptr); + } + + Game::client_t* Download::GetClient(mg_connection *nc) + { + Network::Address address(nc->sa.sa); + + for (int i = 0; i < *Game::svs_numclients; ++i) + { + Game::client_t* client = &Game::svs_clients[i]; + + if (client->state >= 3) + { + if (address.getIP().full == Network::Address(client->addr).getIP().full) + { + return client; + } + } + } + + return nullptr; + } + + void Download::Forbid(mg_connection *nc) + { + mg_printf(nc, "HTTP/1.1 403 Forbidden\r\n" + "Content-Type: text/html\r\n" + "Connection: close\r\n" + "\r\n" + "403 - Forbidden"); + + nc->flags |= MG_F_SEND_AND_CLOSE; + } + + void Download::ListHandler(mg_connection* nc, int ev, void* /*ev_data*/) + { + // Only handle http requests + if (ev != MG_EV_HTTP_REQUEST) return; + +// if (!Download::IsClient(nc)) +// { +// Download::Forbid(nc); +// } +// else + { + static std::string fsGamePre; + static json11::Json jsonList; + + std::string fsGame = Dvar::Var("fs_game").get(); + + if (!fsGame.empty() && fsGame != fsGamePre) + { + std::vector fileList; + + fsGamePre = fsGame; + + std::string path = Dvar::Var("fs_basepath").get() + "\\" + fsGame; + auto list = FileSystem::GetSysFileList(path, "iwd", false); + + list.push_back("mod.ff"); + + for (auto i = list.begin(); i != list.end(); ++i) + { + std::string filename = path + "\\" + *i; + if (strstr(i->data(), "_svr_") == NULL && Utils::IO::FileExists(filename)) + { + std::map file; + std::string fileBuffer = Utils::IO::ReadFile(path + "\\" + *i); + + file["name"] = *i; + file["size"] = static_cast(fileBuffer.size()); + file["hash"] = Utils::Cryptography::SHA256::Compute(fileBuffer, true); + + fileList.push_back(file); + } + } + + jsonList = fileList; + } + + mg_printf(nc, + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json\r\n" + "Connection: close\r\n" + "\r\n" + "%s", jsonList.dump().data()); + + nc->flags |= MG_F_SEND_AND_CLOSE; + } + } + + void Download::FileHandler(mg_connection *nc, int ev, void *ev_data) + { + // Only handle http requests + if (ev != MG_EV_HTTP_REQUEST) return; + + http_message* message = reinterpret_cast(ev_data); + +// if (!Download::IsClient(nc)) +// { +// Download::Forbid(nc); +// } +// else + { + std::string url(message->uri.p, message->uri.len); + Utils::String::Replace(url, "\\", "/"); + url = url.substr(6); + + Utils::String::Replace(url, "%20", " "); + + if (url.find_first_of("/") != std::string::npos || (!Utils::String::EndsWith(url, ".iwd") && url != "mod.ff") || strstr(url.data(), "_svr_") != NULL) + { + Download::Forbid(nc); + return; + } + + std::string fsGame = Dvar::Var("fs_game").get(); + std::string path = Dvar::Var("fs_basepath").get() + "\\" + fsGame + "\\" + url; + + if (fsGame.empty() || !Utils::IO::FileExists(path)) + { + mg_printf(nc, + "HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/html\r\n" + "Connection: close\r\n" + "\r\n" + "404 - Not Found %s", path.data()); + } + else + { + std::string file = Utils::IO::ReadFile(path); + + mg_printf(nc, + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/octet-stream\r\n" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "\r\n", file.size()); + + mg_send(nc, file.data(), static_cast(file.size())); + } + + nc->flags |= MG_F_SEND_AND_CLOSE; + } + } + + void Download::InfoHandler(mg_connection* nc, int ev, void* /*ev_data*/) + { + // Only handle http requests + if (ev != MG_EV_HTTP_REQUEST) return; + + //http_message* message = reinterpret_cast(ev_data); + + Utils::InfoString status = ServerInfo::GetInfo(); + + std::map info; + info["status"] = status.to_json(); + + std::vector players; + + // Build player list + for (int i = 0; i < atoi(status.get("sv_maxclients").data()); ++i) // Maybe choose 18 here? + { + std::map playerInfo; + playerInfo["score"] = 0; + playerInfo["ping"] = 0; + playerInfo["name"] = ""; + + if (Dvar::Var("sv_running").get()) + { + if (Game::svs_clients[i].state < 3) continue; + + playerInfo["score"] = Game::SV_GameClientNum_Score(i); + playerInfo["ping"] = Game::svs_clients[i].ping; + playerInfo["name"] = Game::svs_clients[i].name; + } + else + { + // Score and ping are irrelevant + const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast(0x1081C00), i); + if (!namePtr || !namePtr[0]) continue; + + playerInfo["name"] = namePtr; + } + + players.push_back(playerInfo); + } + + info["players"] = players; + + mg_printf(nc, + "HTTP/1.1 200 OK\r\n" + "Content-Type: application/json\r\n" + "Connection: close\r\n" + "Access-Control-Allow-Origin: *\r\n" + "\r\n" + "%s", json11::Json(info).dump().data()); + + nc->flags |= MG_F_SEND_AND_CLOSE; + } + + void Download::EventHandler(mg_connection *nc, int ev, void *ev_data) + { + // Only handle http requests + if (ev != MG_EV_HTTP_REQUEST) return; + + http_message* message = reinterpret_cast(ev_data); + +// if (message->uri.p, message->uri.len == "/"s) +// { +// mg_printf(nc, +// "HTTP/1.1 200 OK\r\n" +// "Content-Type: text/html\r\n" +// "Connection: close\r\n" +// "\r\n" +// "Hi fella!
You are%s connected to this server!", (Download::IsClient(nc) ? " " : " not")); +// +// Game::client_t* client = Download::GetClient(nc); +// +// if (client) +// { +// mg_printf(nc, "
Hello %s!", client->name); +// } +// } +// else + { + //std::string path = (Dvar::Var("fs_basepath").get() + "\\" BASEGAME "\\html"); + //mg_serve_http_opts opts = { 0 }; + //opts.document_root = path.data(); + //mg_serve_http(nc, message, opts); + + FileSystem::File file; + std::string url = "html" + std::string(message->uri.p, message->uri.len); + + if (Utils::String::EndsWith(url, "/")) + { + url.append("index.html"); + file = FileSystem::File(url); + } + else + { + file = FileSystem::File(url); + + if (!file.exists()) + { + url.append("/index.html"); + file = FileSystem::File(url); + } + } + + std::string mimeType = Utils::GetMimeType(url); + + if (file.exists()) + { + std::string buffer = file.getBuffer(); + + mg_printf(nc, + "HTTP/1.1 200 OK\r\n" + "Content-Type: %s\r\n" + "Content-Length: %d\r\n" + "Connection: close\r\n" + "\r\n", mimeType.data(), buffer.size()); + + mg_send(nc, buffer.data(), static_cast(buffer.size())); + } + else + { + mg_printf(nc, + "HTTP/1.1 404 Not Found\r\n" + "Content-Type: text/html\r\n" + "Connection: close\r\n" + "\r\n" + "404 - Not Found"); + } + } + + nc->flags |= MG_F_SEND_AND_CLOSE; + } + +#pragma endregion + + Download::Download() + { + if (Dedicated::IsEnabled()) + { + mg_mgr_init(&Download::Mgr, NULL); + + Network::OnStart([] () + { + mg_connection* nc = mg_bind(&Download::Mgr, Utils::String::VA("%hu", (Dvar::Var("net_port").get() & 0xFFFF)), Download::EventHandler); + + // Handle special requests + mg_register_http_endpoint(nc, "/info", Download::InfoHandler); + mg_register_http_endpoint(nc, "/list", Download::ListHandler); + mg_register_http_endpoint(nc, "/file", Download::FileHandler); + + mg_set_protocol_http_websocket(nc); + }); + + QuickPatch::OnFrame([] + { + mg_mgr_poll(&Download::Mgr, 0); + }); + } + else + { + UIScript::Add("mod_download_cancel", [] (UIScript::Token) + { + Download::CLDownload.clear(); + }); + } + } + + Download::~Download() + { + if (Dedicated::IsEnabled()) + { + mg_mgr_free(&Download::Mgr); + } + else + { + Download::CLDownload.clear(); + } + } +} diff --git a/src/Components/Modules/Download.hpp b/src/Components/Modules/Download.hpp index 54999b2d..9d89631f 100644 --- a/src/Components/Modules/Download.hpp +++ b/src/Components/Modules/Download.hpp @@ -1,97 +1,97 @@ -namespace Components -{ - class Download : public Component - { - public: - Download(); - ~Download(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Download"; }; -#endif - - static void InitiateClientDownload(std::string mod); - - private: - class ClientDownload - { - public: - ClientDownload() : valid(false), running(false), terminateThread(false), totalBytes(0), downBytes(0), lastTimeStamp(0), timeStampBytes(0) {} - ~ClientDownload() { this->clear(); } - - bool running; - bool valid; - bool terminateThread; - mg_mgr mgr; - Network::Address target; - std::string mod; - std::thread thread; - - size_t totalBytes; - size_t downBytes; - - int lastTimeStamp; - size_t timeStampBytes; - - class File - { - public: - std::string name; - std::string hash; - size_t size; - }; - - std::vector files; - - void clear() - { - this->terminateThread = true; - - if (this->thread.joinable()) - { - this->thread.join(); - } - - this->running = false; - this->mod.clear(); - this->files.clear(); - - if (this->valid) - { - this->valid = false; - mg_mgr_free(&(this->mgr)); - } - } - }; - - class FileDownload - { - public: - ClientDownload* download; - ClientDownload::File file; - - int timestamp; - bool downloading; - unsigned int index; - std::string buffer; - size_t receivedBytes; - }; - - static mg_mgr Mgr; - static ClientDownload CLDownload; - - static void EventHandler(mg_connection *nc, int ev, void *ev_data); - static void ListHandler(mg_connection *nc, int ev, void *ev_data); - static void FileHandler(mg_connection *nc, int ev, void *ev_data); - static void InfoHandler(mg_connection *nc, int ev, void *ev_data); - static void DownloadHandler(mg_connection *nc, int ev, void *ev_data); - - static bool IsClient(mg_connection *nc); - static Game::client_t* GetClient(mg_connection *nc); - static void Forbid(mg_connection *nc); - - static void ModDownloader(ClientDownload* download); - static bool ParseModList(ClientDownload* download, std::string list); - static bool DownloadFile(ClientDownload* download, unsigned int index); - }; -} +namespace Components +{ + class Download : public Component + { + public: + Download(); + ~Download(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Download"; }; +#endif + + static void InitiateClientDownload(std::string mod); + + private: + class ClientDownload + { + public: + ClientDownload() : valid(false), running(false), terminateThread(false), totalBytes(0), downBytes(0), lastTimeStamp(0), timeStampBytes(0) {} + ~ClientDownload() { this->clear(); } + + bool running; + bool valid; + bool terminateThread; + mg_mgr mgr; + Network::Address target; + std::string mod; + std::thread thread; + + size_t totalBytes; + size_t downBytes; + + int lastTimeStamp; + size_t timeStampBytes; + + class File + { + public: + std::string name; + std::string hash; + size_t size; + }; + + std::vector files; + + void clear() + { + this->terminateThread = true; + + if (this->thread.joinable()) + { + this->thread.join(); + } + + this->running = false; + this->mod.clear(); + this->files.clear(); + + if (this->valid) + { + this->valid = false; + mg_mgr_free(&(this->mgr)); + } + } + }; + + class FileDownload + { + public: + ClientDownload* download; + ClientDownload::File file; + + int timestamp; + bool downloading; + unsigned int index; + std::string buffer; + size_t receivedBytes; + }; + + static mg_mgr Mgr; + static ClientDownload CLDownload; + + static void EventHandler(mg_connection *nc, int ev, void *ev_data); + static void ListHandler(mg_connection *nc, int ev, void *ev_data); + static void FileHandler(mg_connection *nc, int ev, void *ev_data); + static void InfoHandler(mg_connection *nc, int ev, void *ev_data); + static void DownloadHandler(mg_connection *nc, int ev, void *ev_data); + + static bool IsClient(mg_connection *nc); + static Game::client_t* GetClient(mg_connection *nc); + static void Forbid(mg_connection *nc); + + static void ModDownloader(ClientDownload* download); + static bool ParseModList(ClientDownload* download, std::string list); + static bool DownloadFile(ClientDownload* download, unsigned int index); + }; +} diff --git a/src/Components/Modules/Dvar.cpp b/src/Components/Modules/Dvar.cpp index cd6c0aea..247f46d3 100644 --- a/src/Components/Modules/Dvar.cpp +++ b/src/Components/Modules/Dvar.cpp @@ -1,207 +1,207 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Utils::Signal Dvar::RegistrationSignal; - - Dvar::Var::Var(std::string dvarName) : Var() - { - this->dvar = Game::Dvar_FindVar(dvarName.data()); - - if (!this->dvar) - { - // Quick-register the dvar - Game::Dvar_SetStringByName(dvarName.data(), ""); - this->dvar = Game::Dvar_FindVar(dvarName.data()); - } - } - - template <> Game::dvar_t* Dvar::Var::get() - { - return this->dvar; - } - template <> char* Dvar::Var::get() - { - if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_STRING && this->dvar->current.string) - { - return this->dvar->current.string; - } - - return ""; - } - template <> const char* Dvar::Var::get() - { - return this->get(); - } - template <> int Dvar::Var::get() - { - if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_INT) - { - return this->dvar->current.integer; - } - - return 0; - } - template <> unsigned int Dvar::Var::get() - { - return static_cast(this->get()); - } - template <> float Dvar::Var::get() - { - if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT) - { - return this->dvar->current.value; - } - - return 0; - } - template <> float* Dvar::Var::get() - { - static float val[4] = { 0 }; - - if (this->dvar && (this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT_2 || this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT_3 || this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT_4)) - { - return this->dvar->current.vec4; - } - - return val; - } - template <> bool Dvar::Var::get() - { - if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_BOOL) - { - return this->dvar->current.boolean; - } - - return false; - } - template <> std::string Dvar::Var::get() - { - return this->get(); - } - - void Dvar::Var::set(char* string) - { - this->set(const_cast(string)); - } - void Dvar::Var::set(const char* string) - { - if (this->dvar && this->dvar->name) - { - Game::Dvar_SetCommand(this->dvar->name, string); - } - } - void Dvar::Var::set(std::string string) - { - this->set(string.data()); - } - void Dvar::Var::set(int integer) - { - if (this->dvar && this->dvar->name) - { - Game::Dvar_SetCommand(this->dvar->name, Utils::String::VA("%i", integer)); - } - } - void Dvar::Var::set(float value) - { - if (this->dvar && this->dvar->name) - { - Game::Dvar_SetCommand(this->dvar->name, Utils::String::VA("%f", value)); - } - } - - void Dvar::Var::setRaw(int integer) - { - if (this->dvar) - { - this->dvar->current.integer = integer; - } - } - - template<> static Dvar::Var Dvar::Register(const char* name, bool value, Dvar::Flag flag, const char* description) - { - return Game::Dvar_RegisterBool(name, value, flag.val, description); - } - template<> static Dvar::Var Dvar::Register(const char* name, const char* value, Dvar::Flag flag, const char* description) - { - return Game::Dvar_RegisterString(name, value, flag.val, description); - } - template<> static Dvar::Var Dvar::Register(const char* name, int value, int min, int max, Dvar::Flag flag, const char* description) - { - return Game::Dvar_RegisterInt(name, value, min, max, flag.val, description); - } - - void Dvar::OnInit(Utils::Slot callback) - { - Dvar::RegistrationSignal.connect(callback); - } - - Game::dvar_t* Dvar::RegisterName(const char* name, const char* /*default*/, Game::dvar_flag flag, const char* description) - { - // Run callbacks - Dvar::RegistrationSignal(); - - // Name watcher - Renderer::OnFrame([] () - { - static std::string lastValidName = "Unknown Soldier"; - std::string name = Dvar::Var("name").get(); - - // Don't perform any checks if name didn't change - if (name == lastValidName) return; - - std::string saneName = Colors::Strip(Utils::String::Trim(name)); - if (saneName.size() < 3 || (saneName[0] == '[' && saneName[1] == '{')) - { - Logger::Print("Username '%s' is invalid. It must at least be 3 characters long and not appear empty!\n", name.data()); - Dvar::Var("name").set(lastValidName); - } - else - { - lastValidName = name; - } - }); - - return Dvar::Register(name, "Unknown Soldier", Dvar::Flag(flag | Game::dvar_flag::DVAR_FLAG_SAVED).val, description).get(); - } - - Dvar::Dvar() - { - // set flags of cg_drawFPS to archive - Utils::Hook::Or(0x4F8F69, Game::dvar_flag::DVAR_FLAG_SAVED); - - // un-cheat cg_fov and add archive flags - Utils::Hook::Xor(0x4F8E35, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED); - - // un-cheat cg_debugInfoCornerOffset and add archive flags - Utils::Hook::Xor(0x4F8FC2, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED); - - // remove archive flags for cg_hudchatposition - Utils::Hook::Xor(0x4F9992, Game::dvar_flag::DVAR_FLAG_SAVED); - - // remove write protection from fs_game - Utils::Hook::Xor(0x6431EA, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED); - - // set cg_fov max to 90.0 - static float cgFov90 = 90.0f; - Utils::Hook::Set(0x4F8E28, &cgFov90); - - // set max volume to 1 - static float volume = 1.0f; - Utils::Hook::Set(0x408078, &volume); - - // Uncheat ui_showList - Utils::Hook::Xor(0x6310DC, Game::dvar_flag::DVAR_FLAG_CHEAT); - - // Uncheat ui_debugMode - Utils::Hook::Xor(0x6312DE, Game::dvar_flag::DVAR_FLAG_CHEAT); - - // Hook dvar 'name' registration - Utils::Hook(0x40531C, Dvar::RegisterName, HOOK_CALL).install()->quick(); - } - - Dvar::~Dvar() - { - Dvar::RegistrationSignal.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Utils::Signal Dvar::RegistrationSignal; + + Dvar::Var::Var(std::string dvarName) : Var() + { + this->dvar = Game::Dvar_FindVar(dvarName.data()); + + if (!this->dvar) + { + // Quick-register the dvar + Game::Dvar_SetStringByName(dvarName.data(), ""); + this->dvar = Game::Dvar_FindVar(dvarName.data()); + } + } + + template <> Game::dvar_t* Dvar::Var::get() + { + return this->dvar; + } + template <> char* Dvar::Var::get() + { + if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_STRING && this->dvar->current.string) + { + return this->dvar->current.string; + } + + return ""; + } + template <> const char* Dvar::Var::get() + { + return this->get(); + } + template <> int Dvar::Var::get() + { + if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_INT) + { + return this->dvar->current.integer; + } + + return 0; + } + template <> unsigned int Dvar::Var::get() + { + return static_cast(this->get()); + } + template <> float Dvar::Var::get() + { + if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT) + { + return this->dvar->current.value; + } + + return 0; + } + template <> float* Dvar::Var::get() + { + static float val[4] = { 0 }; + + if (this->dvar && (this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT_2 || this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT_3 || this->dvar->type == Game::dvar_type::DVAR_TYPE_FLOAT_4)) + { + return this->dvar->current.vec4; + } + + return val; + } + template <> bool Dvar::Var::get() + { + if (this->dvar && this->dvar->type == Game::dvar_type::DVAR_TYPE_BOOL) + { + return this->dvar->current.boolean; + } + + return false; + } + template <> std::string Dvar::Var::get() + { + return this->get(); + } + + void Dvar::Var::set(char* string) + { + this->set(const_cast(string)); + } + void Dvar::Var::set(const char* string) + { + if (this->dvar && this->dvar->name) + { + Game::Dvar_SetCommand(this->dvar->name, string); + } + } + void Dvar::Var::set(std::string string) + { + this->set(string.data()); + } + void Dvar::Var::set(int integer) + { + if (this->dvar && this->dvar->name) + { + Game::Dvar_SetCommand(this->dvar->name, Utils::String::VA("%i", integer)); + } + } + void Dvar::Var::set(float value) + { + if (this->dvar && this->dvar->name) + { + Game::Dvar_SetCommand(this->dvar->name, Utils::String::VA("%f", value)); + } + } + + void Dvar::Var::setRaw(int integer) + { + if (this->dvar) + { + this->dvar->current.integer = integer; + } + } + + template<> static Dvar::Var Dvar::Register(const char* name, bool value, Dvar::Flag flag, const char* description) + { + return Game::Dvar_RegisterBool(name, value, flag.val, description); + } + template<> static Dvar::Var Dvar::Register(const char* name, const char* value, Dvar::Flag flag, const char* description) + { + return Game::Dvar_RegisterString(name, value, flag.val, description); + } + template<> static Dvar::Var Dvar::Register(const char* name, int value, int min, int max, Dvar::Flag flag, const char* description) + { + return Game::Dvar_RegisterInt(name, value, min, max, flag.val, description); + } + + void Dvar::OnInit(Utils::Slot callback) + { + Dvar::RegistrationSignal.connect(callback); + } + + Game::dvar_t* Dvar::RegisterName(const char* name, const char* /*default*/, Game::dvar_flag flag, const char* description) + { + // Run callbacks + Dvar::RegistrationSignal(); + + // Name watcher + Renderer::OnFrame([] () + { + static std::string lastValidName = "Unknown Soldier"; + std::string name = Dvar::Var("name").get(); + + // Don't perform any checks if name didn't change + if (name == lastValidName) return; + + std::string saneName = Colors::Strip(Utils::String::Trim(name)); + if (saneName.size() < 3 || (saneName[0] == '[' && saneName[1] == '{')) + { + Logger::Print("Username '%s' is invalid. It must at least be 3 characters long and not appear empty!\n", name.data()); + Dvar::Var("name").set(lastValidName); + } + else + { + lastValidName = name; + } + }); + + return Dvar::Register(name, "Unknown Soldier", Dvar::Flag(flag | Game::dvar_flag::DVAR_FLAG_SAVED).val, description).get(); + } + + Dvar::Dvar() + { + // set flags of cg_drawFPS to archive + Utils::Hook::Or(0x4F8F69, Game::dvar_flag::DVAR_FLAG_SAVED); + + // un-cheat cg_fov and add archive flags + Utils::Hook::Xor(0x4F8E35, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED); + + // un-cheat cg_debugInfoCornerOffset and add archive flags + Utils::Hook::Xor(0x4F8FC2, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED); + + // remove archive flags for cg_hudchatposition + Utils::Hook::Xor(0x4F9992, Game::dvar_flag::DVAR_FLAG_SAVED); + + // remove write protection from fs_game + Utils::Hook::Xor(0x6431EA, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED); + + // set cg_fov max to 90.0 + static float cgFov90 = 90.0f; + Utils::Hook::Set(0x4F8E28, &cgFov90); + + // set max volume to 1 + static float volume = 1.0f; + Utils::Hook::Set(0x408078, &volume); + + // Uncheat ui_showList + Utils::Hook::Xor(0x6310DC, Game::dvar_flag::DVAR_FLAG_CHEAT); + + // Uncheat ui_debugMode + Utils::Hook::Xor(0x6312DE, Game::dvar_flag::DVAR_FLAG_CHEAT); + + // Hook dvar 'name' registration + Utils::Hook(0x40531C, Dvar::RegisterName, HOOK_CALL).install()->quick(); + } + + Dvar::~Dvar() + { + Dvar::RegistrationSignal.clear(); + } +} diff --git a/src/Components/Modules/Dvar.hpp b/src/Components/Modules/Dvar.hpp index dd3a21da..99bfd9f1 100644 --- a/src/Components/Modules/Dvar.hpp +++ b/src/Components/Modules/Dvar.hpp @@ -1,60 +1,60 @@ -namespace Components -{ - class Dvar : public Component - { - public: - typedef void(Callback)(); - - class Flag - { - public: - Flag(Game::dvar_flag flag) : val(flag){}; - Flag(int flag) : Flag(static_cast(flag)) {}; - - Game::dvar_flag val; - }; - - class Var - { - public: - Var() : dvar(0) {}; - Var(const Var &obj) { this->dvar = obj.dvar; }; - Var(Game::dvar_t* _dvar) : dvar(_dvar) {}; - Var(DWORD ppdvar) : Var(*reinterpret_cast(ppdvar)) {}; - Var(std::string dvarName); - - template T get(); - - void set(char* string); - void set(const char* string); - void set(std::string string); - - void set(int integer); - void set(float value); - - // TODO: Add others - void setRaw(int integer); - - private: - Game::dvar_t* dvar; - }; - - Dvar(); - ~Dvar(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Dvar"; }; -#endif - - static void OnInit(Utils::Slot callback); - - // Only strings and bools use this type of declaration - template static Var Register(const char* name, T value, Flag flag, const char* description); - template static Var Register(const char* name, T value, T min, T max, Flag flag, const char* description); - - private: - static Utils::Signal RegistrationSignal; - - static Game::dvar_t* RegisterName(const char* name, const char* default, Game::dvar_flag flag, const char* description); - }; -} +namespace Components +{ + class Dvar : public Component + { + public: + typedef void(Callback)(); + + class Flag + { + public: + Flag(Game::dvar_flag flag) : val(flag){}; + Flag(int flag) : Flag(static_cast(flag)) {}; + + Game::dvar_flag val; + }; + + class Var + { + public: + Var() : dvar(0) {}; + Var(const Var &obj) { this->dvar = obj.dvar; }; + Var(Game::dvar_t* _dvar) : dvar(_dvar) {}; + Var(DWORD ppdvar) : Var(*reinterpret_cast(ppdvar)) {}; + Var(std::string dvarName); + + template T get(); + + void set(char* string); + void set(const char* string); + void set(std::string string); + + void set(int integer); + void set(float value); + + // TODO: Add others + void setRaw(int integer); + + private: + Game::dvar_t* dvar; + }; + + Dvar(); + ~Dvar(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Dvar"; }; +#endif + + static void OnInit(Utils::Slot callback); + + // Only strings and bools use this type of declaration + template static Var Register(const char* name, T value, Flag flag, const char* description); + template static Var Register(const char* name, T value, T min, T max, Flag flag, const char* description); + + private: + static Utils::Signal RegistrationSignal; + + static Game::dvar_t* RegisterName(const char* name, const char* default, Game::dvar_flag flag, const char* description); + }; +} diff --git a/src/Components/Modules/Exception.cpp b/src/Components/Modules/Exception.cpp index e9198879..a3b3c020 100644 --- a/src/Components/Modules/Exception.cpp +++ b/src/Components/Modules/Exception.cpp @@ -1,176 +1,176 @@ -#include "STDInclude.hpp" - -// Stuff causes warnings -#pragma warning(push) -#pragma warning(disable: 4091) -#include -#pragma comment(lib, "dbghelp.lib") -#pragma warning(pop) - -namespace Components -{ - Utils::Hook Exception::SetFilterHook; - - __declspec(noreturn) void Exception::ErrorLongJmp(jmp_buf _Buf, int _Value) - { - if (!*reinterpret_cast(0x1AD7EB4)) - { - TerminateProcess(GetCurrentProcess(), 1337); - } - - longjmp(_Buf, _Value); - } - - LONG WINAPI Exception::ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo) - { - // Pass on harmless errors - if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_INTEGER_OVERFLOW || - ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_FLOAT_OVERFLOW) - { - return EXCEPTION_CONTINUE_EXECUTION; - } - - auto minidump = MinidumpUpload::CreateQueuedMinidump(ExceptionInfo); - if (!minidump) - { - OutputDebugStringA("Failed to create new minidump!"); - Utils::OutputDebugLastError(); - } - else - { - delete minidump; - } - - if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) - { - Logger::Error("Termination because of a stack overflow.\n"); - } - else - { - Logger::Error("Fatal error (0x%08X) at 0x%08X.", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress); - } - - //TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode); - - return EXCEPTION_CONTINUE_SEARCH; - } - - LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception::SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER) - { - Exception::SetFilterHook.uninstall(); - LPTOP_LEVEL_EXCEPTION_FILTER retval = SetUnhandledExceptionFilter(&Exception::ExceptionFilter); - Exception::SetFilterHook.install(); - return retval; - } - - LPTOP_LEVEL_EXCEPTION_FILTER Exception::Hook() - { - return SetUnhandledExceptionFilter(&Exception::ExceptionFilter); - } - - Exception::Exception() - { -#ifdef DEBUG - // Display DEBUG branding, so we know we're on a debug build - Renderer::OnFrame([]() - { - Game::Font* font = Game::R_RegisterFont("fonts/normalFont"); - float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; - - // Change the color when attaching a debugger - if (IsDebuggerPresent()) - { - color[0] = 0.6588f; - color[1] = 1.0000f; - color[2] = 0.0000f; - } - - Game::R_AddCmdDrawText("DEBUG-BUILD", 0x7FFFFFFF, font, 15.0f, 10.0f + Game::R_TextHeight(font), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_SHADOWED); - }); -#endif -#if !defined(DEBUG) || defined(FORCE_EXCEPTION_HANDLER) - Exception::SetFilterHook.initialize(SetUnhandledExceptionFilter, Exception::SetUnhandledExceptionFilterStub, HOOK_JUMP); - Exception::SetFilterHook.install(); - - SetUnhandledExceptionFilter(&Exception::ExceptionFilter); -#endif - - //Utils::Hook(0x4B241F, Exception::ErrorLongJmp, HOOK_CALL).install()->quick(); - - Command::Add("mapTest", [](Command::Params* params) - { - Game::UI_UpdateArenas(); - - std::string command; - for (int i = 0; i < (params->length() >= 2 ? atoi(params->get(1)) : *Game::arenaCount); ++i) - { - char* mapname = ArenaLength::NewArenas[i % *Game::arenaCount].mapName; - - if (!(i % 2)) command.append(Utils::String::VA("wait 250;disconnect;wait 750;", mapname)); // Test a disconnect - else command.append(Utils::String::VA("wait 500;", mapname)); // Test direct map switch - command.append(Utils::String::VA("map %s;", mapname)); - } - - Command::Execute(command, false); - }); - - Command::Add("debug_exceptionhandler", [] (Command::Params*) - { - Logger::Print("Rerunning SetUnhandledExceptionHandler...\n"); - auto oldHandler = Exception::Hook(); - Logger::Print("Old exception handler was 0x%010X.\n", oldHandler); - }); - -#pragma warning(push) -#pragma warning(disable:4740) // flow in or out of inline asm code suppresses global optimization - Command::Add("debug_minidump", [](Command::Params*) - { - // The following code was taken from VC++ 8.0 CRT (invarg.c: line 104) - - CONTEXT ContextRecord; - EXCEPTION_RECORD ExceptionRecord; - ZeroMemory(&ContextRecord, sizeof(CONTEXT)); - - __asm - { - mov [ContextRecord.Eax], eax - mov [ContextRecord.Ecx], ecx - mov [ContextRecord.Edx], edx - mov [ContextRecord.Ebx], ebx - mov [ContextRecord.Esi], esi - mov [ContextRecord.Edi], edi - mov word ptr [ContextRecord.SegSs], ss - mov word ptr [ContextRecord.SegCs], cs - mov word ptr [ContextRecord.SegDs], ds - mov word ptr [ContextRecord.SegEs], es - mov word ptr [ContextRecord.SegFs], fs - mov word ptr [ContextRecord.SegGs], gs - - pushfd - pop [ContextRecord.EFlags] - } - - ContextRecord.ContextFlags = CONTEXT_CONTROL; - ContextRecord.Eip = reinterpret_cast(_ReturnAddress()); - ContextRecord.Esp = reinterpret_cast(_AddressOfReturnAddress()); - ContextRecord.Ebp = *reinterpret_cast(_AddressOfReturnAddress()) - 1; - - ZeroMemory(&ExceptionRecord, sizeof(EXCEPTION_RECORD)); - - ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT; - ExceptionRecord.ExceptionAddress = _ReturnAddress(); - - EXCEPTION_POINTERS eptr; - eptr.ExceptionRecord = &ExceptionRecord; - eptr.ContextRecord = &ContextRecord; - - Exception::ExceptionFilter(&eptr); - }); -#pragma warning(pop) - } - - Exception::~Exception() - { - Exception::SetFilterHook.uninstall(); - } -} +#include "STDInclude.hpp" + +// Stuff causes warnings +#pragma warning(push) +#pragma warning(disable: 4091) +#include +#pragma comment(lib, "dbghelp.lib") +#pragma warning(pop) + +namespace Components +{ + Utils::Hook Exception::SetFilterHook; + + __declspec(noreturn) void Exception::ErrorLongJmp(jmp_buf _Buf, int _Value) + { + if (!*reinterpret_cast(0x1AD7EB4)) + { + TerminateProcess(GetCurrentProcess(), 1337); + } + + longjmp(_Buf, _Value); + } + + LONG WINAPI Exception::ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo) + { + // Pass on harmless errors + if (ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_INTEGER_OVERFLOW || + ExceptionInfo->ExceptionRecord->ExceptionCode == STATUS_FLOAT_OVERFLOW) + { + return EXCEPTION_CONTINUE_EXECUTION; + } + + auto minidump = MinidumpUpload::CreateQueuedMinidump(ExceptionInfo); + if (!minidump) + { + OutputDebugStringA("Failed to create new minidump!"); + Utils::OutputDebugLastError(); + } + else + { + delete minidump; + } + + if (ExceptionInfo->ExceptionRecord->ExceptionCode == EXCEPTION_STACK_OVERFLOW) + { + Logger::Error("Termination because of a stack overflow.\n"); + } + else + { + Logger::Error("Fatal error (0x%08X) at 0x%08X.", ExceptionInfo->ExceptionRecord->ExceptionCode, ExceptionInfo->ExceptionRecord->ExceptionAddress); + } + + //TerminateProcess(GetCurrentProcess(), ExceptionInfo->ExceptionRecord->ExceptionCode); + + return EXCEPTION_CONTINUE_SEARCH; + } + + LPTOP_LEVEL_EXCEPTION_FILTER WINAPI Exception::SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER) + { + Exception::SetFilterHook.uninstall(); + LPTOP_LEVEL_EXCEPTION_FILTER retval = SetUnhandledExceptionFilter(&Exception::ExceptionFilter); + Exception::SetFilterHook.install(); + return retval; + } + + LPTOP_LEVEL_EXCEPTION_FILTER Exception::Hook() + { + return SetUnhandledExceptionFilter(&Exception::ExceptionFilter); + } + + Exception::Exception() + { +#ifdef DEBUG + // Display DEBUG branding, so we know we're on a debug build + Renderer::OnFrame([]() + { + Game::Font* font = Game::R_RegisterFont("fonts/normalFont"); + float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; + + // Change the color when attaching a debugger + if (IsDebuggerPresent()) + { + color[0] = 0.6588f; + color[1] = 1.0000f; + color[2] = 0.0000f; + } + + Game::R_AddCmdDrawText("DEBUG-BUILD", 0x7FFFFFFF, font, 15.0f, 10.0f + Game::R_TextHeight(font), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_SHADOWED); + }); +#endif +#if !defined(DEBUG) || defined(FORCE_EXCEPTION_HANDLER) + Exception::SetFilterHook.initialize(SetUnhandledExceptionFilter, Exception::SetUnhandledExceptionFilterStub, HOOK_JUMP); + Exception::SetFilterHook.install(); + + SetUnhandledExceptionFilter(&Exception::ExceptionFilter); +#endif + + //Utils::Hook(0x4B241F, Exception::ErrorLongJmp, HOOK_CALL).install()->quick(); + + Command::Add("mapTest", [](Command::Params* params) + { + Game::UI_UpdateArenas(); + + std::string command; + for (int i = 0; i < (params->length() >= 2 ? atoi(params->get(1)) : *Game::arenaCount); ++i) + { + char* mapname = ArenaLength::NewArenas[i % *Game::arenaCount].mapName; + + if (!(i % 2)) command.append(Utils::String::VA("wait 250;disconnect;wait 750;", mapname)); // Test a disconnect + else command.append(Utils::String::VA("wait 500;", mapname)); // Test direct map switch + command.append(Utils::String::VA("map %s;", mapname)); + } + + Command::Execute(command, false); + }); + + Command::Add("debug_exceptionhandler", [] (Command::Params*) + { + Logger::Print("Rerunning SetUnhandledExceptionHandler...\n"); + auto oldHandler = Exception::Hook(); + Logger::Print("Old exception handler was 0x%010X.\n", oldHandler); + }); + +#pragma warning(push) +#pragma warning(disable:4740) // flow in or out of inline asm code suppresses global optimization + Command::Add("debug_minidump", [](Command::Params*) + { + // The following code was taken from VC++ 8.0 CRT (invarg.c: line 104) + + CONTEXT ContextRecord; + EXCEPTION_RECORD ExceptionRecord; + ZeroMemory(&ContextRecord, sizeof(CONTEXT)); + + __asm + { + mov [ContextRecord.Eax], eax + mov [ContextRecord.Ecx], ecx + mov [ContextRecord.Edx], edx + mov [ContextRecord.Ebx], ebx + mov [ContextRecord.Esi], esi + mov [ContextRecord.Edi], edi + mov word ptr [ContextRecord.SegSs], ss + mov word ptr [ContextRecord.SegCs], cs + mov word ptr [ContextRecord.SegDs], ds + mov word ptr [ContextRecord.SegEs], es + mov word ptr [ContextRecord.SegFs], fs + mov word ptr [ContextRecord.SegGs], gs + + pushfd + pop [ContextRecord.EFlags] + } + + ContextRecord.ContextFlags = CONTEXT_CONTROL; + ContextRecord.Eip = reinterpret_cast(_ReturnAddress()); + ContextRecord.Esp = reinterpret_cast(_AddressOfReturnAddress()); + ContextRecord.Ebp = *reinterpret_cast(_AddressOfReturnAddress()) - 1; + + ZeroMemory(&ExceptionRecord, sizeof(EXCEPTION_RECORD)); + + ExceptionRecord.ExceptionCode = EXCEPTION_BREAKPOINT; + ExceptionRecord.ExceptionAddress = _ReturnAddress(); + + EXCEPTION_POINTERS eptr; + eptr.ExceptionRecord = &ExceptionRecord; + eptr.ContextRecord = &ContextRecord; + + Exception::ExceptionFilter(&eptr); + }); +#pragma warning(pop) + } + + Exception::~Exception() + { + Exception::SetFilterHook.uninstall(); + } +} diff --git a/src/Components/Modules/Exception.hpp b/src/Components/Modules/Exception.hpp index 240230cb..c80b271b 100644 --- a/src/Components/Modules/Exception.hpp +++ b/src/Components/Modules/Exception.hpp @@ -1,23 +1,23 @@ - - -namespace Components -{ - class Exception : public Component - { - public: - Exception(); - ~Exception(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Exception"; }; -#endif - static LPTOP_LEVEL_EXCEPTION_FILTER Hook(); - - private: - static LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo); - static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter); - static __declspec(noreturn) void ErrorLongJmp(jmp_buf _Buf, int _Value); - - static Utils::Hook SetFilterHook; - }; -} + + +namespace Components +{ + class Exception : public Component + { + public: + Exception(); + ~Exception(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Exception"; }; +#endif + static LPTOP_LEVEL_EXCEPTION_FILTER Hook(); + + private: + static LONG WINAPI ExceptionFilter(LPEXCEPTION_POINTERS ExceptionInfo); + static LPTOP_LEVEL_EXCEPTION_FILTER WINAPI SetUnhandledExceptionFilterStub(LPTOP_LEVEL_EXCEPTION_FILTER lpTopLevelExceptionFilter); + static __declspec(noreturn) void ErrorLongJmp(jmp_buf _Buf, int _Value); + + static Utils::Hook SetFilterHook; + }; +} diff --git a/src/Components/Modules/FastFiles.cpp b/src/Components/Modules/FastFiles.cpp index d331539d..67fb702d 100644 --- a/src/Components/Modules/FastFiles.cpp +++ b/src/Components/Modules/FastFiles.cpp @@ -1,418 +1,418 @@ -#include "STDInclude.hpp" - -namespace Components -{ - FastFiles::Key FastFiles::CurrentKey; - symmetric_CTR FastFiles::CurrentCTR; - std::vector FastFiles::ZonePaths; - - bool FastFiles::IsIW4xZone = false; - bool FastFiles::StreamRead = false; - - unsigned int FastFiles::CurrentZone; - unsigned int FastFiles::MaxZones; - - unsigned char FastFiles::ZoneKey[1191] = - { - 0x30,0x82,0x04,0xA3,0x02,0x01,0x00,0x02,0x82,0x01,0x01, - 0x00,0xBD,0x7E,0xD6,0xE2,0xE9,0x1C,0xB1,0x08,0x68,0xA2, - 0xEC,0xC2,0x47,0xCB,0x60,0x00,0x0F,0xF5,0x6B,0x75,0x32, - 0xA3,0xE4,0xEB,0x85,0x2F,0x23,0x97,0x6E,0x13,0x33,0xBC, - 0xA3,0xBB,0x61,0x88,0xDE,0xB8,0xA8,0x33,0xD6,0xB8,0xA3, - 0xDB,0x48,0x09,0xE8,0x7D,0x0A,0x43,0xBF,0xDB,0x52,0x32, - 0x5C,0x8B,0x0A,0x46,0x01,0x95,0x81,0x97,0x40,0x46,0x28, - 0x73,0x96,0x88,0x7C,0x9A,0x08,0x94,0x62,0xDC,0x10,0xF1, - 0xE7,0x2B,0xF8,0xDE,0x21,0xA2,0xD1,0x39,0x45,0x1A,0xD2, - 0x41,0x45,0xC1,0xEE,0x01,0xFF,0xD6,0x33,0x7F,0xB4,0x64, - 0x41,0x87,0xE6,0x71,0xB7,0x0D,0xE8,0xCF,0xF4,0xCB,0x28, - 0x3C,0xA6,0x92,0x3A,0x69,0x95,0x93,0xBE,0x72,0xA4,0x86, - 0x76,0xF6,0x99,0x7E,0xD8,0xB0,0x63,0x1E,0xC7,0x67,0x8B, - 0xF7,0xF6,0x6B,0x46,0xF1,0xA1,0x7C,0x26,0x15,0x0E,0xF9, - 0xA6,0x3F,0x67,0x54,0x16,0x5C,0xD6,0xAE,0xDE,0x12,0xF8, - 0x30,0x9F,0x5D,0xF2,0xF7,0xAF,0x20,0xAE,0x69,0x06,0x28, - 0x80,0xA9,0xD5,0x94,0x74,0x3D,0xDB,0x5A,0x77,0x87,0xAE, - 0x1D,0x34,0x20,0xF9,0x8F,0x23,0x21,0xCE,0x86,0xB6,0xBF, - 0x8E,0xF3,0xEC,0x4A,0xDF,0xBE,0x12,0x51,0x85,0x4F,0x48, - 0x52,0x2E,0x8A,0x88,0xC6,0x6C,0xD2,0xCD,0x19,0xCF,0xDF, - 0x0A,0x9B,0xB5,0x0B,0x36,0x6D,0x28,0x36,0x57,0xC3,0x84, - 0xDD,0xC2,0xA1,0x1B,0x7A,0xE8,0xE9,0x1F,0xE4,0xC8,0x8E, - 0x8A,0x5A,0x49,0x0F,0xDB,0x8D,0x60,0x48,0xAD,0x81,0x2C, - 0xE8,0x44,0x42,0x57,0xFA,0x7C,0x18,0xAC,0xEF,0x30,0xC9, - 0xC6,0xA0,0x4D,0xFD,0x02,0x03,0x01,0x00,0x01,0x02,0x82, - 0x01,0x00,0x1C,0x18,0x93,0x59,0xDF,0x80,0x5E,0x8B,0x45, - 0xA0,0x6A,0x84,0x3F,0xCA,0xDA,0xB8,0x07,0xA5,0xB6,0xC2, - 0x10,0xB9,0x16,0x37,0x09,0x6F,0x3C,0xD2,0xB6,0x02,0x68, - 0xD8,0x5E,0x5A,0x69,0x12,0xB7,0x1B,0x1F,0xED,0x57,0xB7, - 0xD6,0xAB,0xAB,0x99,0xB4,0x7B,0xDD,0xAA,0xBF,0xE6,0x8F, - 0xE0,0x61,0xB2,0x47,0xDA,0xAB,0x5F,0x74,0x70,0x6D,0x9A, - 0x39,0x63,0x31,0xFD,0x98,0xA3,0xEA,0x03,0xBE,0x48,0xAC, - 0xC6,0x81,0x25,0x16,0xE8,0x30,0x8A,0x88,0x84,0xFA,0x47, - 0x08,0xC7,0x9E,0xC5,0x2B,0x39,0xE6,0xA9,0xE6,0xC6,0xD7, - 0x83,0x49,0xE8,0x11,0x75,0xE8,0xD3,0x4A,0x22,0x93,0x44, - 0x0F,0xFA,0x36,0x24,0x56,0x3E,0xD3,0x6B,0xAD,0x80,0x27, - 0xFE,0xBB,0xE2,0xC2,0x4D,0x79,0x69,0x65,0xB8,0xA7,0xFB, - 0x42,0x9B,0xE9,0x41,0x98,0x60,0xB9,0xEB,0xD8,0xB2,0x25, - 0xAD,0xF1,0xDC,0x0B,0x04,0x9F,0x0F,0xD0,0x96,0x9E,0x1D, - 0x7A,0x73,0xC5,0xE0,0x7A,0x5C,0xF9,0xF8,0x0F,0xCB,0xA1, - 0xFE,0xE8,0x94,0x84,0x6D,0xB4,0x2B,0xFD,0x64,0xC2,0xAF, - 0xD3,0x48,0x35,0xDF,0xCB,0x2D,0xD8,0x2E,0xE8,0x35,0x33, - 0x6E,0xBD,0xE1,0xD5,0x90,0xFC,0x67,0x9B,0xFF,0x9F,0xA0, - 0x54,0x02,0xDB,0xFB,0xCC,0xA8,0x9E,0x1C,0x35,0x88,0x10, - 0x43,0x2B,0xA5,0xCF,0x78,0x4F,0xF0,0xC6,0x74,0x4D,0x47, - 0xA4,0x9B,0x22,0x89,0x56,0xF0,0x6A,0xEA,0xFA,0x6B,0xDD, - 0xDA,0xB1,0x2F,0x55,0x6A,0x5C,0xEF,0xF6,0x31,0xB0,0x87, - 0xAC,0xBE,0xCB,0xAC,0x8D,0xA6,0xC2,0x79,0x75,0x20,0xE0, - 0x61,0x49,0x69,0xA3,0xA1,0x02,0x81,0x81,0x00,0xC9,0x70, - 0xB5,0x6E,0xA6,0x4A,0x49,0xF1,0xA9,0xC7,0xEA,0x6E,0xCF, - 0x23,0xB0,0x29,0x93,0x80,0x2A,0xA9,0x4E,0x29,0x5C,0x3B, - 0xE5,0x9C,0xA5,0x84,0x0C,0x1B,0x9D,0xD4,0xDC,0xA4,0x63, - 0xCD,0xDD,0x70,0x9B,0xBE,0x8F,0x29,0xB3,0x1E,0x06,0x46, - 0xD1,0xE6,0xEC,0xE7,0xB0,0x55,0x44,0xA5,0x24,0x56,0x5F, - 0x28,0x58,0x32,0xCF,0xDC,0x1F,0x74,0x9A,0xB6,0x78,0x83, - 0x08,0x95,0x3D,0x15,0x54,0x23,0x8B,0x15,0x0D,0x9B,0x34, - 0x2B,0x55,0xFE,0x4F,0x26,0x7E,0x59,0x62,0xE8,0x31,0xC1, - 0x8A,0x6E,0xCB,0xB8,0xFA,0xBD,0x39,0xAF,0x9A,0x9E,0xF8, - 0x7B,0x3F,0x0E,0x0A,0x22,0x40,0xD6,0x28,0xA4,0xF5,0x4E, - 0x82,0x45,0xDE,0x81,0xC5,0x7F,0x90,0x44,0x01,0x5F,0x93, - 0xCD,0x95,0x16,0x3D,0xE9,0x02,0x81,0x81,0x00,0xF0,0xD1, - 0xE9,0x8D,0xA9,0xDF,0xFA,0x1E,0xA6,0xFE,0x10,0x3B,0x68, - 0x7C,0x0E,0x52,0xD2,0x19,0x0C,0xA5,0xEB,0xFD,0xB4,0x1A, - 0xDF,0x03,0x5B,0x3E,0x82,0x5D,0x99,0x9B,0x71,0x23,0xFB, - 0xFE,0x69,0xB3,0x2D,0xEB,0xE7,0x67,0xBC,0x9E,0xF9,0x05, - 0xE6,0x4D,0x2B,0x1C,0xE9,0x12,0xFF,0xF3,0xD0,0xC8,0x42, - 0x9F,0x00,0xFE,0x82,0xC3,0x7A,0xB1,0x4F,0x94,0xA1,0xAD, - 0x90,0xE0,0xA4,0x3F,0x78,0x55,0x5A,0x54,0x59,0x4A,0xF9, - 0xEF,0x17,0x40,0xD1,0x09,0xA5,0x0D,0xAC,0xAB,0x8C,0xF0, - 0xFD,0x05,0x6B,0x9A,0xEC,0x3D,0x98,0x95,0x5A,0x33,0xA7, - 0x47,0x16,0xA8,0x9F,0xBA,0xD3,0xBE,0xCD,0x28,0xEE,0x17, - 0x64,0x40,0xBF,0xEC,0x88,0xCD,0x97,0x1F,0xBD,0x3A,0xE6, - 0x16,0xFE,0x0F,0xDE,0xF5,0x02,0x81,0x81,0x00,0xBF,0x7C, - 0xD7,0xCB,0xAE,0x61,0xF2,0x36,0xBA,0xD9,0x62,0xBE,0x21, - 0x44,0x60,0xA2,0xB5,0x27,0x61,0xE6,0x7D,0x79,0x8D,0xC7, - 0x16,0x77,0x39,0x53,0xF4,0x1A,0x90,0x87,0x97,0x92,0xE1, - 0x99,0x01,0xC6,0x99,0x16,0xA5,0x8A,0xD3,0x4D,0x58,0x54, - 0x1C,0x16,0xB3,0xDF,0x6E,0xDD,0x2F,0x9A,0xF8,0x96,0xEE, - 0x70,0x30,0x9F,0x64,0xBE,0x70,0x5C,0x6C,0xF1,0xC6,0x4F, - 0x71,0x6A,0x44,0x9D,0xB0,0xD4,0xF4,0xD2,0x77,0x93,0xB1, - 0x1C,0xFC,0xEA,0xF9,0x9C,0xB3,0x01,0x0F,0xA7,0x80,0x1C, - 0xE6,0x16,0x7A,0xAC,0x86,0x16,0x38,0xEE,0xF8,0x41,0xE4, - 0x1D,0x6C,0x8C,0x51,0x0F,0xCC,0xA8,0x88,0x0C,0x7F,0x70, - 0x39,0x20,0x67,0xEA,0xDE,0xAE,0x6B,0x9A,0x69,0xDF,0xCC, - 0x65,0xE2,0x32,0x39,0x79,0x02,0x81,0x80,0x01,0xDE,0xCF, - 0x7E,0x8F,0x2C,0x33,0x28,0x1B,0xC9,0xEB,0x5C,0x5A,0xC2, - 0x63,0xE6,0x16,0xC5,0xA5,0x08,0x80,0xDD,0xB6,0x91,0x62, - 0xDC,0x06,0xD0,0x64,0x78,0xCF,0xA1,0x9A,0x6E,0x5A,0x1D, - 0xAE,0xBA,0x7A,0x87,0xD3,0x83,0x45,0xBE,0xC2,0x56,0x5E, - 0x64,0x89,0x0A,0x2F,0x71,0x3B,0x55,0xAC,0x70,0x71,0xBC, - 0x04,0x68,0xF5,0xA1,0x09,0x09,0xE9,0x81,0x51,0x04,0x25, - 0x14,0xE9,0x91,0xA8,0xA0,0x99,0x14,0x00,0xA1,0x89,0x71, - 0x66,0xEF,0xD4,0xEF,0xCB,0x3D,0x60,0xF2,0xF0,0x24,0x4B, - 0x02,0xC8,0xC4,0x2A,0x43,0x8C,0x34,0xD4,0xBF,0x83,0xF4, - 0x14,0x63,0xF8,0xE1,0x9D,0x95,0x64,0xC8,0x85,0x98,0xDE, - 0xE9,0x75,0xD4,0x23,0x77,0xDD,0x4D,0x9C,0xCD,0xA1,0x4D, - 0xDA,0x69,0x4B,0x25,0x02,0x81,0x80,0x56,0x6F,0x21,0x4E, - 0xD2,0x95,0x50,0xDD,0x1E,0xFE,0x08,0xCD,0xE0,0x1E,0x97, - 0x98,0x3C,0xED,0xFB,0x7F,0x53,0x4A,0xFC,0x90,0x4B,0x5E, - 0x96,0x09,0x5D,0x57,0x7C,0x5C,0x39,0x21,0x10,0xA5,0xAA, - 0x49,0xB7,0xC9,0x41,0x2B,0xF4,0xAC,0x95,0x07,0xBC,0x45, - 0xCD,0xAE,0x42,0x05,0x12,0xA0,0x1C,0x8E,0x0F,0xF8,0x0A, - 0xB3,0x72,0x50,0xCD,0x0D,0x21,0x2D,0x67,0x81,0x99,0x69, - 0xFF,0xF8,0x5E,0x23,0x19,0x86,0x03,0x3D,0x42,0x6F,0x38, - 0xFB,0x39,0x1C,0xBA,0x96,0x69,0xC6,0xDC,0xE7,0x52,0x7A, - 0x7E,0xC5,0x98,0x03,0x8A,0x3A,0x88,0x7C,0x9A,0xB0,0x70, - 0x26,0x62,0xD6,0xF4,0x13,0x1D,0x37,0x7E,0x33,0x3F,0xBB, - 0xFF,0x91,0x0D,0xA6,0xBB,0x82,0x30,0x34,0xB1,0xD3,0xA4, - 0xA7,0xD6,0x91, - }; - - - // This has to be called only once, when the game starts - void FastFiles::LoadInitialZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) - { - std::vector data; - Utils::Merge(&data, zoneInfo, zoneCount); - - if (FastFiles::Exists("iw4x_patch_mp")) - { - data.push_back({ "iw4x_patch_mp", 1, 0 }); - } - - return FastFiles::LoadDLCUIZones(data.data(), data.size(), sync); - } - - // This has to be called every time the cgame is reinitialized - void FastFiles::LoadDLCUIZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) - { - std::vector data; - Utils::Merge(&data, zoneInfo, zoneCount); - - Game::XZoneInfo info = { nullptr, 2, 0 }; - - // Custom ui stuff - if (FastFiles::Exists("iw4x_ui_mp")) - { - info.name = "iw4x_ui_mp"; - data.push_back(info); - } - else // Fallback - { - info.name = "dlc1_ui_mp"; - data.push_back(info); - - info.name = "dlc2_ui_mp"; - data.push_back(info); - } - - return FastFiles::LoadLocalizeZones(data.data(), data.size(), sync); - } - - void FastFiles::LoadGfxZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) - { - std::vector data; - Utils::Merge(&data, zoneInfo, zoneCount); - - if (FastFiles::Exists("iw4x_code_post_gfx_mp")) - { - data.push_back({ "iw4x_code_post_gfx_mp", zoneInfo->allocFlags, zoneInfo->freeFlags }); - } - - Game::DB_LoadXAssets(data.data(), data.size(), sync); - } - - // This has to be called every time fastfiles are loaded :D - void FastFiles::LoadLocalizeZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) - { - std::vector data; - Utils::Merge(&data, zoneInfo, zoneCount); - - Game::XZoneInfo info = { nullptr, 4, 0 }; - - // Not sure how they should be loaded :S - std::string langZone = Utils::String::VA("iw4x_localized_%s", Game::Win_GetLanguage()); - - if (FastFiles::Exists(langZone)) - { - info.name = langZone.data(); - } - else if (FastFiles::Exists("iw4x_localized_english")) // Fallback - { - info.name = "iw4x_localized_english"; - } - - data.push_back(info); - - Game::DB_LoadXAssets(data.data(), data.size(), sync); - } - - // Name is a bit weird, due to FasFileS and ExistS :P - bool FastFiles::Exists(std::string file) - { - std::string path = FastFiles::GetZoneLocation(file.data()); - path.append(file); - - if (!Utils::String::EndsWith(path.data(), ".ff")) - { - path.append(".ff"); - } - - return Utils::IO::FileExists(path); - } - - bool FastFiles::Ready() - { - return (Game::Sys_IsDatabaseReady() && Game::Sys_IsDatabaseReady2()); - } - - const char* FastFiles::GetZoneLocation(const char* file) - { - const char* dir = Dvar::Var("fs_basepath").get(); - - std::vector paths; - std::string modDir = Dvar::Var("fs_game").get(); - if (file == "mod"s || file == "mod.ff"s || !modDir.empty()) - { - paths.push_back(Utils::String::VA("%s\\", modDir.data())); - } - - Utils::Merge(&paths, FastFiles::ZonePaths); - - for (auto &path : paths) - { - std::string absoluteFile = Utils::String::VA("%s\\%s%s", dir, path.data(), file); - - // No ".ff" appended, append it manually - if (!Utils::String::EndsWith(absoluteFile, ".ff")) - { - absoluteFile.append(".ff"); - } - - // Check if FastFile exists - if (Utils::IO::FileExists(absoluteFile)) - { - return Utils::String::VA("%s", path.data()); - } - } - - return Utils::String::VA("zone\\%s\\", Game::Win_GetLanguage()); - } - - void FastFiles::AddZonePath(std::string path) - { - FastFiles::ZonePaths.push_back(path); - } - - std::string FastFiles::Current() - { - const char* file = (Utils::Hook::Get(0x112A680) + 4); - - if (file == reinterpret_cast(4)) - { - return ""; - } - - return file; - } - - void FastFiles::ReadXFileHeader(void* buffer, int size) - { - if (FastFiles::IsIW4xZone) - { - char pad; - Game::DB_ReadXFile(&pad, 1); - } - - Game::DB_ReadXFile(buffer, size); - } - - void FastFiles::ReadVersionStub(unsigned int* version, int size) - { - FastFiles::CurrentZone++; - Game::DB_ReadXFileUncompressed(version, size); - - Zones::SetVersion(*version); - - // Allow loading of codo versions - if (*version >= VERSION_ALPHA2 && *version <= 360) - { - *version = XFILE_VERSION; - } - - if (*version != XFILE_VERSION) - { - Logger::Error("Zone version %d is not supported!", Zones::Version()); - } - } - - void FastFiles::ReadHeaderStub(unsigned int* header, int size) - { - FastFiles::IsIW4xZone = false; - Game::DB_ReadXFileUncompressed(header, size); - - if (header[0] == XFILE_HEADER_IW4X) - { - FastFiles::IsIW4xZone = true; - - if (header[1] < XFILE_VERSION_IW4X) - { - Logger::Error("The fastfile you are trying to load is outdated (%d, expected %d)", header[1], XFILE_VERSION_IW4X); - } -#ifdef DEBUG - else if (header[1] > XFILE_VERSION_IW4X) - { - Logger::Error("You are loading a fastfile that is too new (%d, expected %d), how's that possible?", header[1], XFILE_VERSION_IW4X); - } -#endif - - *reinterpret_cast(header) = XFILE_MAGIC_UNSIGNED; - } - } - - void FastFiles::AuthLoadInitCrypto() - { - if (Zones::Version() >= 319) - { - register_hash(&sha256_desc); - register_cipher(&aes_desc); - - rsa_key key; - unsigned char encKey[256]; - int hash = find_hash("sha256"), aes = find_cipher("aes"), stat; - - Game::DB_ReadXFileUncompressed(encKey, 256); - - unsigned long outLen = sizeof(FastFiles::CurrentKey); - rsa_import(FastFiles::ZoneKey, sizeof(FastFiles::ZoneKey), &key); - rsa_decrypt_key_ex(encKey, 256, FastFiles::CurrentKey.data, &outLen, NULL, NULL, hash, (Zones::Version() >= 359 ? 1 : 2), &stat, &key); - rsa_free(&key); - - ctr_start(aes, FastFiles::CurrentKey.iv, FastFiles::CurrentKey.key, sizeof(FastFiles::CurrentKey.key), 0, 0, &FastFiles::CurrentCTR); - } - - Utils::Hook::Call(0x46FAE0)(); - } - - int FastFiles::AuthLoadInflateCompare(unsigned char* buffer, int length, unsigned char* ivValue) - { - if (Zones::Version() >= 319) - { - ctr_setiv(ivValue, 16, &FastFiles::CurrentCTR); - ctr_decrypt(buffer, buffer, length, &FastFiles::CurrentCTR); - } - - return Utils::Hook::Call(0x5BA240)(buffer, length, ivValue); - } - - int FastFiles::InflateInitDecrypt(z_streamp strm, const char *version, int stream_size) - { - if (Zones::Version() >= 319) - { - ctr_decrypt(strm->next_in, const_cast(strm->next_in), strm->avail_in, &FastFiles::CurrentCTR); - } - - return Utils::Hook::Call(0x4D8090)(strm, version, stream_size); - //return inflateInit_(strm, version, stream_size); - } - - void FastFiles::AuthLoadInflateDecryptBaseFunc(unsigned char* buffer) - { - if (Zones::Version() >= 319) - { - ctr_setiv(FastFiles::CurrentKey.iv, sizeof(FastFiles::CurrentKey.iv), &FastFiles::CurrentCTR); - ctr_decrypt(buffer, buffer, 8192, &FastFiles::CurrentCTR); - } - } - - __declspec(naked) void FastFiles::AuthLoadInflateDecryptBase() - { - __asm - { - pushad - push ebx - call FastFiles::AuthLoadInflateDecryptBaseFunc - pop ebx - popad - - push 5B96F0h - retn - } - } - - float FastFiles::GetFullLoadedFraction() - { - float singleProgress = 1.0f / FastFiles::MaxZones; - float partialProgress = singleProgress * (FastFiles::CurrentZone - 1); - float currentProgress = std::max(std::min(Game::DB_GetLoadedFraction(), 1.0f), 0.0f); - return std::min(partialProgress + (currentProgress * singleProgress), 1.0f); - } - - void FastFiles::LoadZonesStub(Game::XZoneInfo *zoneInfo, unsigned int zoneCount) - { - FastFiles::CurrentZone = 0; - FastFiles::MaxZones = zoneCount; - - Utils::Hook::Call(0x5BBAC0)(zoneInfo, zoneCount); - } - +#include "STDInclude.hpp" + +namespace Components +{ + FastFiles::Key FastFiles::CurrentKey; + symmetric_CTR FastFiles::CurrentCTR; + std::vector FastFiles::ZonePaths; + + bool FastFiles::IsIW4xZone = false; + bool FastFiles::StreamRead = false; + + unsigned int FastFiles::CurrentZone; + unsigned int FastFiles::MaxZones; + + unsigned char FastFiles::ZoneKey[1191] = + { + 0x30,0x82,0x04,0xA3,0x02,0x01,0x00,0x02,0x82,0x01,0x01, + 0x00,0xBD,0x7E,0xD6,0xE2,0xE9,0x1C,0xB1,0x08,0x68,0xA2, + 0xEC,0xC2,0x47,0xCB,0x60,0x00,0x0F,0xF5,0x6B,0x75,0x32, + 0xA3,0xE4,0xEB,0x85,0x2F,0x23,0x97,0x6E,0x13,0x33,0xBC, + 0xA3,0xBB,0x61,0x88,0xDE,0xB8,0xA8,0x33,0xD6,0xB8,0xA3, + 0xDB,0x48,0x09,0xE8,0x7D,0x0A,0x43,0xBF,0xDB,0x52,0x32, + 0x5C,0x8B,0x0A,0x46,0x01,0x95,0x81,0x97,0x40,0x46,0x28, + 0x73,0x96,0x88,0x7C,0x9A,0x08,0x94,0x62,0xDC,0x10,0xF1, + 0xE7,0x2B,0xF8,0xDE,0x21,0xA2,0xD1,0x39,0x45,0x1A,0xD2, + 0x41,0x45,0xC1,0xEE,0x01,0xFF,0xD6,0x33,0x7F,0xB4,0x64, + 0x41,0x87,0xE6,0x71,0xB7,0x0D,0xE8,0xCF,0xF4,0xCB,0x28, + 0x3C,0xA6,0x92,0x3A,0x69,0x95,0x93,0xBE,0x72,0xA4,0x86, + 0x76,0xF6,0x99,0x7E,0xD8,0xB0,0x63,0x1E,0xC7,0x67,0x8B, + 0xF7,0xF6,0x6B,0x46,0xF1,0xA1,0x7C,0x26,0x15,0x0E,0xF9, + 0xA6,0x3F,0x67,0x54,0x16,0x5C,0xD6,0xAE,0xDE,0x12,0xF8, + 0x30,0x9F,0x5D,0xF2,0xF7,0xAF,0x20,0xAE,0x69,0x06,0x28, + 0x80,0xA9,0xD5,0x94,0x74,0x3D,0xDB,0x5A,0x77,0x87,0xAE, + 0x1D,0x34,0x20,0xF9,0x8F,0x23,0x21,0xCE,0x86,0xB6,0xBF, + 0x8E,0xF3,0xEC,0x4A,0xDF,0xBE,0x12,0x51,0x85,0x4F,0x48, + 0x52,0x2E,0x8A,0x88,0xC6,0x6C,0xD2,0xCD,0x19,0xCF,0xDF, + 0x0A,0x9B,0xB5,0x0B,0x36,0x6D,0x28,0x36,0x57,0xC3,0x84, + 0xDD,0xC2,0xA1,0x1B,0x7A,0xE8,0xE9,0x1F,0xE4,0xC8,0x8E, + 0x8A,0x5A,0x49,0x0F,0xDB,0x8D,0x60,0x48,0xAD,0x81,0x2C, + 0xE8,0x44,0x42,0x57,0xFA,0x7C,0x18,0xAC,0xEF,0x30,0xC9, + 0xC6,0xA0,0x4D,0xFD,0x02,0x03,0x01,0x00,0x01,0x02,0x82, + 0x01,0x00,0x1C,0x18,0x93,0x59,0xDF,0x80,0x5E,0x8B,0x45, + 0xA0,0x6A,0x84,0x3F,0xCA,0xDA,0xB8,0x07,0xA5,0xB6,0xC2, + 0x10,0xB9,0x16,0x37,0x09,0x6F,0x3C,0xD2,0xB6,0x02,0x68, + 0xD8,0x5E,0x5A,0x69,0x12,0xB7,0x1B,0x1F,0xED,0x57,0xB7, + 0xD6,0xAB,0xAB,0x99,0xB4,0x7B,0xDD,0xAA,0xBF,0xE6,0x8F, + 0xE0,0x61,0xB2,0x47,0xDA,0xAB,0x5F,0x74,0x70,0x6D,0x9A, + 0x39,0x63,0x31,0xFD,0x98,0xA3,0xEA,0x03,0xBE,0x48,0xAC, + 0xC6,0x81,0x25,0x16,0xE8,0x30,0x8A,0x88,0x84,0xFA,0x47, + 0x08,0xC7,0x9E,0xC5,0x2B,0x39,0xE6,0xA9,0xE6,0xC6,0xD7, + 0x83,0x49,0xE8,0x11,0x75,0xE8,0xD3,0x4A,0x22,0x93,0x44, + 0x0F,0xFA,0x36,0x24,0x56,0x3E,0xD3,0x6B,0xAD,0x80,0x27, + 0xFE,0xBB,0xE2,0xC2,0x4D,0x79,0x69,0x65,0xB8,0xA7,0xFB, + 0x42,0x9B,0xE9,0x41,0x98,0x60,0xB9,0xEB,0xD8,0xB2,0x25, + 0xAD,0xF1,0xDC,0x0B,0x04,0x9F,0x0F,0xD0,0x96,0x9E,0x1D, + 0x7A,0x73,0xC5,0xE0,0x7A,0x5C,0xF9,0xF8,0x0F,0xCB,0xA1, + 0xFE,0xE8,0x94,0x84,0x6D,0xB4,0x2B,0xFD,0x64,0xC2,0xAF, + 0xD3,0x48,0x35,0xDF,0xCB,0x2D,0xD8,0x2E,0xE8,0x35,0x33, + 0x6E,0xBD,0xE1,0xD5,0x90,0xFC,0x67,0x9B,0xFF,0x9F,0xA0, + 0x54,0x02,0xDB,0xFB,0xCC,0xA8,0x9E,0x1C,0x35,0x88,0x10, + 0x43,0x2B,0xA5,0xCF,0x78,0x4F,0xF0,0xC6,0x74,0x4D,0x47, + 0xA4,0x9B,0x22,0x89,0x56,0xF0,0x6A,0xEA,0xFA,0x6B,0xDD, + 0xDA,0xB1,0x2F,0x55,0x6A,0x5C,0xEF,0xF6,0x31,0xB0,0x87, + 0xAC,0xBE,0xCB,0xAC,0x8D,0xA6,0xC2,0x79,0x75,0x20,0xE0, + 0x61,0x49,0x69,0xA3,0xA1,0x02,0x81,0x81,0x00,0xC9,0x70, + 0xB5,0x6E,0xA6,0x4A,0x49,0xF1,0xA9,0xC7,0xEA,0x6E,0xCF, + 0x23,0xB0,0x29,0x93,0x80,0x2A,0xA9,0x4E,0x29,0x5C,0x3B, + 0xE5,0x9C,0xA5,0x84,0x0C,0x1B,0x9D,0xD4,0xDC,0xA4,0x63, + 0xCD,0xDD,0x70,0x9B,0xBE,0x8F,0x29,0xB3,0x1E,0x06,0x46, + 0xD1,0xE6,0xEC,0xE7,0xB0,0x55,0x44,0xA5,0x24,0x56,0x5F, + 0x28,0x58,0x32,0xCF,0xDC,0x1F,0x74,0x9A,0xB6,0x78,0x83, + 0x08,0x95,0x3D,0x15,0x54,0x23,0x8B,0x15,0x0D,0x9B,0x34, + 0x2B,0x55,0xFE,0x4F,0x26,0x7E,0x59,0x62,0xE8,0x31,0xC1, + 0x8A,0x6E,0xCB,0xB8,0xFA,0xBD,0x39,0xAF,0x9A,0x9E,0xF8, + 0x7B,0x3F,0x0E,0x0A,0x22,0x40,0xD6,0x28,0xA4,0xF5,0x4E, + 0x82,0x45,0xDE,0x81,0xC5,0x7F,0x90,0x44,0x01,0x5F,0x93, + 0xCD,0x95,0x16,0x3D,0xE9,0x02,0x81,0x81,0x00,0xF0,0xD1, + 0xE9,0x8D,0xA9,0xDF,0xFA,0x1E,0xA6,0xFE,0x10,0x3B,0x68, + 0x7C,0x0E,0x52,0xD2,0x19,0x0C,0xA5,0xEB,0xFD,0xB4,0x1A, + 0xDF,0x03,0x5B,0x3E,0x82,0x5D,0x99,0x9B,0x71,0x23,0xFB, + 0xFE,0x69,0xB3,0x2D,0xEB,0xE7,0x67,0xBC,0x9E,0xF9,0x05, + 0xE6,0x4D,0x2B,0x1C,0xE9,0x12,0xFF,0xF3,0xD0,0xC8,0x42, + 0x9F,0x00,0xFE,0x82,0xC3,0x7A,0xB1,0x4F,0x94,0xA1,0xAD, + 0x90,0xE0,0xA4,0x3F,0x78,0x55,0x5A,0x54,0x59,0x4A,0xF9, + 0xEF,0x17,0x40,0xD1,0x09,0xA5,0x0D,0xAC,0xAB,0x8C,0xF0, + 0xFD,0x05,0x6B,0x9A,0xEC,0x3D,0x98,0x95,0x5A,0x33,0xA7, + 0x47,0x16,0xA8,0x9F,0xBA,0xD3,0xBE,0xCD,0x28,0xEE,0x17, + 0x64,0x40,0xBF,0xEC,0x88,0xCD,0x97,0x1F,0xBD,0x3A,0xE6, + 0x16,0xFE,0x0F,0xDE,0xF5,0x02,0x81,0x81,0x00,0xBF,0x7C, + 0xD7,0xCB,0xAE,0x61,0xF2,0x36,0xBA,0xD9,0x62,0xBE,0x21, + 0x44,0x60,0xA2,0xB5,0x27,0x61,0xE6,0x7D,0x79,0x8D,0xC7, + 0x16,0x77,0x39,0x53,0xF4,0x1A,0x90,0x87,0x97,0x92,0xE1, + 0x99,0x01,0xC6,0x99,0x16,0xA5,0x8A,0xD3,0x4D,0x58,0x54, + 0x1C,0x16,0xB3,0xDF,0x6E,0xDD,0x2F,0x9A,0xF8,0x96,0xEE, + 0x70,0x30,0x9F,0x64,0xBE,0x70,0x5C,0x6C,0xF1,0xC6,0x4F, + 0x71,0x6A,0x44,0x9D,0xB0,0xD4,0xF4,0xD2,0x77,0x93,0xB1, + 0x1C,0xFC,0xEA,0xF9,0x9C,0xB3,0x01,0x0F,0xA7,0x80,0x1C, + 0xE6,0x16,0x7A,0xAC,0x86,0x16,0x38,0xEE,0xF8,0x41,0xE4, + 0x1D,0x6C,0x8C,0x51,0x0F,0xCC,0xA8,0x88,0x0C,0x7F,0x70, + 0x39,0x20,0x67,0xEA,0xDE,0xAE,0x6B,0x9A,0x69,0xDF,0xCC, + 0x65,0xE2,0x32,0x39,0x79,0x02,0x81,0x80,0x01,0xDE,0xCF, + 0x7E,0x8F,0x2C,0x33,0x28,0x1B,0xC9,0xEB,0x5C,0x5A,0xC2, + 0x63,0xE6,0x16,0xC5,0xA5,0x08,0x80,0xDD,0xB6,0x91,0x62, + 0xDC,0x06,0xD0,0x64,0x78,0xCF,0xA1,0x9A,0x6E,0x5A,0x1D, + 0xAE,0xBA,0x7A,0x87,0xD3,0x83,0x45,0xBE,0xC2,0x56,0x5E, + 0x64,0x89,0x0A,0x2F,0x71,0x3B,0x55,0xAC,0x70,0x71,0xBC, + 0x04,0x68,0xF5,0xA1,0x09,0x09,0xE9,0x81,0x51,0x04,0x25, + 0x14,0xE9,0x91,0xA8,0xA0,0x99,0x14,0x00,0xA1,0x89,0x71, + 0x66,0xEF,0xD4,0xEF,0xCB,0x3D,0x60,0xF2,0xF0,0x24,0x4B, + 0x02,0xC8,0xC4,0x2A,0x43,0x8C,0x34,0xD4,0xBF,0x83,0xF4, + 0x14,0x63,0xF8,0xE1,0x9D,0x95,0x64,0xC8,0x85,0x98,0xDE, + 0xE9,0x75,0xD4,0x23,0x77,0xDD,0x4D,0x9C,0xCD,0xA1,0x4D, + 0xDA,0x69,0x4B,0x25,0x02,0x81,0x80,0x56,0x6F,0x21,0x4E, + 0xD2,0x95,0x50,0xDD,0x1E,0xFE,0x08,0xCD,0xE0,0x1E,0x97, + 0x98,0x3C,0xED,0xFB,0x7F,0x53,0x4A,0xFC,0x90,0x4B,0x5E, + 0x96,0x09,0x5D,0x57,0x7C,0x5C,0x39,0x21,0x10,0xA5,0xAA, + 0x49,0xB7,0xC9,0x41,0x2B,0xF4,0xAC,0x95,0x07,0xBC,0x45, + 0xCD,0xAE,0x42,0x05,0x12,0xA0,0x1C,0x8E,0x0F,0xF8,0x0A, + 0xB3,0x72,0x50,0xCD,0x0D,0x21,0x2D,0x67,0x81,0x99,0x69, + 0xFF,0xF8,0x5E,0x23,0x19,0x86,0x03,0x3D,0x42,0x6F,0x38, + 0xFB,0x39,0x1C,0xBA,0x96,0x69,0xC6,0xDC,0xE7,0x52,0x7A, + 0x7E,0xC5,0x98,0x03,0x8A,0x3A,0x88,0x7C,0x9A,0xB0,0x70, + 0x26,0x62,0xD6,0xF4,0x13,0x1D,0x37,0x7E,0x33,0x3F,0xBB, + 0xFF,0x91,0x0D,0xA6,0xBB,0x82,0x30,0x34,0xB1,0xD3,0xA4, + 0xA7,0xD6,0x91, + }; + + + // This has to be called only once, when the game starts + void FastFiles::LoadInitialZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) + { + std::vector data; + Utils::Merge(&data, zoneInfo, zoneCount); + + if (FastFiles::Exists("iw4x_patch_mp")) + { + data.push_back({ "iw4x_patch_mp", 1, 0 }); + } + + return FastFiles::LoadDLCUIZones(data.data(), data.size(), sync); + } + + // This has to be called every time the cgame is reinitialized + void FastFiles::LoadDLCUIZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) + { + std::vector data; + Utils::Merge(&data, zoneInfo, zoneCount); + + Game::XZoneInfo info = { nullptr, 2, 0 }; + + // Custom ui stuff + if (FastFiles::Exists("iw4x_ui_mp")) + { + info.name = "iw4x_ui_mp"; + data.push_back(info); + } + else // Fallback + { + info.name = "dlc1_ui_mp"; + data.push_back(info); + + info.name = "dlc2_ui_mp"; + data.push_back(info); + } + + return FastFiles::LoadLocalizeZones(data.data(), data.size(), sync); + } + + void FastFiles::LoadGfxZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) + { + std::vector data; + Utils::Merge(&data, zoneInfo, zoneCount); + + if (FastFiles::Exists("iw4x_code_post_gfx_mp")) + { + data.push_back({ "iw4x_code_post_gfx_mp", zoneInfo->allocFlags, zoneInfo->freeFlags }); + } + + Game::DB_LoadXAssets(data.data(), data.size(), sync); + } + + // This has to be called every time fastfiles are loaded :D + void FastFiles::LoadLocalizeZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) + { + std::vector data; + Utils::Merge(&data, zoneInfo, zoneCount); + + Game::XZoneInfo info = { nullptr, 4, 0 }; + + // Not sure how they should be loaded :S + std::string langZone = Utils::String::VA("iw4x_localized_%s", Game::Win_GetLanguage()); + + if (FastFiles::Exists(langZone)) + { + info.name = langZone.data(); + } + else if (FastFiles::Exists("iw4x_localized_english")) // Fallback + { + info.name = "iw4x_localized_english"; + } + + data.push_back(info); + + Game::DB_LoadXAssets(data.data(), data.size(), sync); + } + + // Name is a bit weird, due to FasFileS and ExistS :P + bool FastFiles::Exists(std::string file) + { + std::string path = FastFiles::GetZoneLocation(file.data()); + path.append(file); + + if (!Utils::String::EndsWith(path.data(), ".ff")) + { + path.append(".ff"); + } + + return Utils::IO::FileExists(path); + } + + bool FastFiles::Ready() + { + return (Game::Sys_IsDatabaseReady() && Game::Sys_IsDatabaseReady2()); + } + + const char* FastFiles::GetZoneLocation(const char* file) + { + const char* dir = Dvar::Var("fs_basepath").get(); + + std::vector paths; + std::string modDir = Dvar::Var("fs_game").get(); + if (file == "mod"s || file == "mod.ff"s || !modDir.empty()) + { + paths.push_back(Utils::String::VA("%s\\", modDir.data())); + } + + Utils::Merge(&paths, FastFiles::ZonePaths); + + for (auto &path : paths) + { + std::string absoluteFile = Utils::String::VA("%s\\%s%s", dir, path.data(), file); + + // No ".ff" appended, append it manually + if (!Utils::String::EndsWith(absoluteFile, ".ff")) + { + absoluteFile.append(".ff"); + } + + // Check if FastFile exists + if (Utils::IO::FileExists(absoluteFile)) + { + return Utils::String::VA("%s", path.data()); + } + } + + return Utils::String::VA("zone\\%s\\", Game::Win_GetLanguage()); + } + + void FastFiles::AddZonePath(std::string path) + { + FastFiles::ZonePaths.push_back(path); + } + + std::string FastFiles::Current() + { + const char* file = (Utils::Hook::Get(0x112A680) + 4); + + if (file == reinterpret_cast(4)) + { + return ""; + } + + return file; + } + + void FastFiles::ReadXFileHeader(void* buffer, int size) + { + if (FastFiles::IsIW4xZone) + { + char pad; + Game::DB_ReadXFile(&pad, 1); + } + + Game::DB_ReadXFile(buffer, size); + } + + void FastFiles::ReadVersionStub(unsigned int* version, int size) + { + FastFiles::CurrentZone++; + Game::DB_ReadXFileUncompressed(version, size); + + Zones::SetVersion(*version); + + // Allow loading of codo versions + if (*version >= VERSION_ALPHA2 && *version <= 360) + { + *version = XFILE_VERSION; + } + + if (*version != XFILE_VERSION) + { + Logger::Error("Zone version %d is not supported!", Zones::Version()); + } + } + + void FastFiles::ReadHeaderStub(unsigned int* header, int size) + { + FastFiles::IsIW4xZone = false; + Game::DB_ReadXFileUncompressed(header, size); + + if (header[0] == XFILE_HEADER_IW4X) + { + FastFiles::IsIW4xZone = true; + + if (header[1] < XFILE_VERSION_IW4X) + { + Logger::Error("The fastfile you are trying to load is outdated (%d, expected %d)", header[1], XFILE_VERSION_IW4X); + } +#ifdef DEBUG + else if (header[1] > XFILE_VERSION_IW4X) + { + Logger::Error("You are loading a fastfile that is too new (%d, expected %d), how's that possible?", header[1], XFILE_VERSION_IW4X); + } +#endif + + *reinterpret_cast(header) = XFILE_MAGIC_UNSIGNED; + } + } + + void FastFiles::AuthLoadInitCrypto() + { + if (Zones::Version() >= 319) + { + register_hash(&sha256_desc); + register_cipher(&aes_desc); + + rsa_key key; + unsigned char encKey[256]; + int hash = find_hash("sha256"), aes = find_cipher("aes"), stat; + + Game::DB_ReadXFileUncompressed(encKey, 256); + + unsigned long outLen = sizeof(FastFiles::CurrentKey); + rsa_import(FastFiles::ZoneKey, sizeof(FastFiles::ZoneKey), &key); + rsa_decrypt_key_ex(encKey, 256, FastFiles::CurrentKey.data, &outLen, NULL, NULL, hash, (Zones::Version() >= 359 ? 1 : 2), &stat, &key); + rsa_free(&key); + + ctr_start(aes, FastFiles::CurrentKey.iv, FastFiles::CurrentKey.key, sizeof(FastFiles::CurrentKey.key), 0, 0, &FastFiles::CurrentCTR); + } + + Utils::Hook::Call(0x46FAE0)(); + } + + int FastFiles::AuthLoadInflateCompare(unsigned char* buffer, int length, unsigned char* ivValue) + { + if (Zones::Version() >= 319) + { + ctr_setiv(ivValue, 16, &FastFiles::CurrentCTR); + ctr_decrypt(buffer, buffer, length, &FastFiles::CurrentCTR); + } + + return Utils::Hook::Call(0x5BA240)(buffer, length, ivValue); + } + + int FastFiles::InflateInitDecrypt(z_streamp strm, const char *version, int stream_size) + { + if (Zones::Version() >= 319) + { + ctr_decrypt(strm->next_in, const_cast(strm->next_in), strm->avail_in, &FastFiles::CurrentCTR); + } + + return Utils::Hook::Call(0x4D8090)(strm, version, stream_size); + //return inflateInit_(strm, version, stream_size); + } + + void FastFiles::AuthLoadInflateDecryptBaseFunc(unsigned char* buffer) + { + if (Zones::Version() >= 319) + { + ctr_setiv(FastFiles::CurrentKey.iv, sizeof(FastFiles::CurrentKey.iv), &FastFiles::CurrentCTR); + ctr_decrypt(buffer, buffer, 8192, &FastFiles::CurrentCTR); + } + } + + __declspec(naked) void FastFiles::AuthLoadInflateDecryptBase() + { + __asm + { + pushad + push ebx + call FastFiles::AuthLoadInflateDecryptBaseFunc + pop ebx + popad + + push 5B96F0h + retn + } + } + + float FastFiles::GetFullLoadedFraction() + { + float singleProgress = 1.0f / FastFiles::MaxZones; + float partialProgress = singleProgress * (FastFiles::CurrentZone - 1); + float currentProgress = std::max(std::min(Game::DB_GetLoadedFraction(), 1.0f), 0.0f); + return std::min(partialProgress + (currentProgress * singleProgress), 1.0f); + } + + void FastFiles::LoadZonesStub(Game::XZoneInfo *zoneInfo, unsigned int zoneCount) + { + FastFiles::CurrentZone = 0; + FastFiles::MaxZones = zoneCount; + + Utils::Hook::Call(0x5BBAC0)(zoneInfo, zoneCount); + } + #ifdef DEBUG void FastFiles::LogStreamRead(int len) { @@ -425,114 +425,114 @@ namespace Components Utils::IO::WriteFile("userraw/logs/iw4_reads.log", data, true); } } -#endif - - FastFiles::FastFiles() - { - Dvar::Register("ui_zoneDebug", false, Game::dvar_flag::DVAR_FLAG_SAVED, "Display current loaded zone."); - - // Redirect zone paths - Utils::Hook(0x44DA90, FastFiles::GetZoneLocation, HOOK_JUMP).install()->quick(); - - // Allow loading 'newer' zones - Utils::Hook(0x4158E7, FastFiles::ReadVersionStub, HOOK_CALL).install()->quick(); - - // Allow loading IW4x zones - Utils::Hook(0x4157B8, FastFiles::ReadHeaderStub, HOOK_CALL).install()->quick(); - - // Allow custom zone loading - if (!ZoneBuilder::IsEnabled()) - { - Utils::Hook(0x506BC7, FastFiles::LoadInitialZones, HOOK_CALL).install()->quick(); - Utils::Hook(0x60B4AC, FastFiles::LoadDLCUIZones, HOOK_CALL).install()->quick(); - Utils::Hook(0x506B25, FastFiles::LoadGfxZones, HOOK_CALL).install()->quick(); - } - - // basic checks (hash jumps, both normal and playlist) - Utils::Hook::Nop(0x5B97A3, 2); - Utils::Hook::Nop(0x5BA493, 2); - - Utils::Hook::Nop(0x5B991C, 2); - Utils::Hook::Nop(0x5BA60C, 2); - - Utils::Hook::Nop(0x5B97B4, 2); - Utils::Hook::Nop(0x5BA4A4, 2); - - // allow loading of IWffu (unsigned) files - Utils::Hook::Set(0x4158D9, 0xEB); // main function - Utils::Hook::Nop(0x4A1D97, 2); // DB_AuthLoad_InflateInit - - // some other, unknown, check - // this is replaced by hooks below - Utils::Hook::Set(0x5B9912, 0xB8); - Utils::Hook::Set(0x5B9913, 1); - - Utils::Hook::Set(0x5BA602, 0xB8); - Utils::Hook::Set(0x5BA603, 1); - - // Initialize crypto - Utils::Hook(0x4D02F0, FastFiles::AuthLoadInitCrypto, HOOK_CALL).install()->quick(); - - // Initial stage decryption - Utils::Hook(0x4D0306, FastFiles::InflateInitDecrypt, HOOK_CALL).install()->quick(); - - // Hash bit decryption - Utils::Hook(0x5B9958, FastFiles::AuthLoadInflateCompare, HOOK_CALL).install()->quick(); - Utils::Hook(0x5B9912, FastFiles::AuthLoadInflateCompare, HOOK_CALL).install()->quick(); - - // General read - Utils::Hook(0x5B98E4, FastFiles::AuthLoadInflateDecryptBase, HOOK_CALL).install()->quick(); - - // Fix fastfile progress - Utils::Hook(0x4E5DE3, FastFiles::LoadZonesStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x407761, FastFiles::GetFullLoadedFraction, HOOK_CALL).install()->quick(); - Utils::Hook(0x49FA1E, FastFiles::GetFullLoadedFraction, HOOK_CALL).install()->quick(); - Utils::Hook(0x589090, FastFiles::GetFullLoadedFraction, HOOK_CALL).install()->quick(); - Utils::Hook(0x629FC0, FastFiles::GetFullLoadedFraction, HOOK_JUMP).install()->quick(); - - // XFile header loading - Utils::Hook(0x4159E2, FastFiles::ReadXFileHeader, HOOK_CALL).install()->quick(); - - // Add custom zone paths - FastFiles::AddZonePath("zone\\patch\\"); - FastFiles::AddZonePath("zone\\dlc\\"); - - Renderer::OnFrame([] () - { - if (FastFiles::Current().empty() || !Dvar::Var("ui_zoneDebug").get()) return; - - Game::Font* font = Game::R_RegisterFont("fonts/consoleFont"); // Inlining that seems to skip xpos, no idea why xD - float color[4] = { 1.0f, 1.0f, 1.0f, (Game::CL_IsCgameInitialized() ? 0.3f : 1.0f) }; - Game::R_AddCmdDrawText(Utils::String::VA("Loading FastFile: %s", FastFiles::Current().data()), 0x7FFFFFFF, font, 5.0f, static_cast(Renderer::Height() - 5), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL); - }); - - Command::Add("loadzone", [] (Command::Params* params) - { - if (params->length() < 2) return; - - Game::XZoneInfo info; - info.name = params->get(1); - info.allocFlags = 1;//0x01000000; - info.freeFlags = 0; - - Game::DB_LoadXAssets(&info, 1, true); - }); - +#endif + + FastFiles::FastFiles() + { + Dvar::Register("ui_zoneDebug", false, Game::dvar_flag::DVAR_FLAG_SAVED, "Display current loaded zone."); + + // Redirect zone paths + Utils::Hook(0x44DA90, FastFiles::GetZoneLocation, HOOK_JUMP).install()->quick(); + + // Allow loading 'newer' zones + Utils::Hook(0x4158E7, FastFiles::ReadVersionStub, HOOK_CALL).install()->quick(); + + // Allow loading IW4x zones + Utils::Hook(0x4157B8, FastFiles::ReadHeaderStub, HOOK_CALL).install()->quick(); + + // Allow custom zone loading + if (!ZoneBuilder::IsEnabled()) + { + Utils::Hook(0x506BC7, FastFiles::LoadInitialZones, HOOK_CALL).install()->quick(); + Utils::Hook(0x60B4AC, FastFiles::LoadDLCUIZones, HOOK_CALL).install()->quick(); + Utils::Hook(0x506B25, FastFiles::LoadGfxZones, HOOK_CALL).install()->quick(); + } + + // basic checks (hash jumps, both normal and playlist) + Utils::Hook::Nop(0x5B97A3, 2); + Utils::Hook::Nop(0x5BA493, 2); + + Utils::Hook::Nop(0x5B991C, 2); + Utils::Hook::Nop(0x5BA60C, 2); + + Utils::Hook::Nop(0x5B97B4, 2); + Utils::Hook::Nop(0x5BA4A4, 2); + + // allow loading of IWffu (unsigned) files + Utils::Hook::Set(0x4158D9, 0xEB); // main function + Utils::Hook::Nop(0x4A1D97, 2); // DB_AuthLoad_InflateInit + + // some other, unknown, check + // this is replaced by hooks below + Utils::Hook::Set(0x5B9912, 0xB8); + Utils::Hook::Set(0x5B9913, 1); + + Utils::Hook::Set(0x5BA602, 0xB8); + Utils::Hook::Set(0x5BA603, 1); + + // Initialize crypto + Utils::Hook(0x4D02F0, FastFiles::AuthLoadInitCrypto, HOOK_CALL).install()->quick(); + + // Initial stage decryption + Utils::Hook(0x4D0306, FastFiles::InflateInitDecrypt, HOOK_CALL).install()->quick(); + + // Hash bit decryption + Utils::Hook(0x5B9958, FastFiles::AuthLoadInflateCompare, HOOK_CALL).install()->quick(); + Utils::Hook(0x5B9912, FastFiles::AuthLoadInflateCompare, HOOK_CALL).install()->quick(); + + // General read + Utils::Hook(0x5B98E4, FastFiles::AuthLoadInflateDecryptBase, HOOK_CALL).install()->quick(); + + // Fix fastfile progress + Utils::Hook(0x4E5DE3, FastFiles::LoadZonesStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x407761, FastFiles::GetFullLoadedFraction, HOOK_CALL).install()->quick(); + Utils::Hook(0x49FA1E, FastFiles::GetFullLoadedFraction, HOOK_CALL).install()->quick(); + Utils::Hook(0x589090, FastFiles::GetFullLoadedFraction, HOOK_CALL).install()->quick(); + Utils::Hook(0x629FC0, FastFiles::GetFullLoadedFraction, HOOK_JUMP).install()->quick(); + + // XFile header loading + Utils::Hook(0x4159E2, FastFiles::ReadXFileHeader, HOOK_CALL).install()->quick(); + + // Add custom zone paths + FastFiles::AddZonePath("zone\\patch\\"); + FastFiles::AddZonePath("zone\\dlc\\"); + + Renderer::OnFrame([] () + { + if (FastFiles::Current().empty() || !Dvar::Var("ui_zoneDebug").get()) return; + + Game::Font* font = Game::R_RegisterFont("fonts/consoleFont"); // Inlining that seems to skip xpos, no idea why xD + float color[4] = { 1.0f, 1.0f, 1.0f, (Game::CL_IsCgameInitialized() ? 0.3f : 1.0f) }; + Game::R_AddCmdDrawText(Utils::String::VA("Loading FastFile: %s", FastFiles::Current().data()), 0x7FFFFFFF, font, 5.0f, static_cast(Renderer::Height() - 5), 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_NORMAL); + }); + + Command::Add("loadzone", [] (Command::Params* params) + { + if (params->length() < 2) return; + + Game::XZoneInfo info; + info.name = params->get(1); + info.allocFlags = 1;//0x01000000; + info.freeFlags = 0; + + Game::DB_LoadXAssets(&info, 1, true); + }); + #ifdef DEBUG // ZoneBuilder debugging Utils::IO::WriteFile("userraw/logs/iw4_reads.log", "", false); - Utils::Hook(0x4A8FA0, FastFiles::LogStreamRead, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4A8FA0, FastFiles::LogStreamRead, HOOK_JUMP).install()->quick(); Utils::Hook(0x4BCB62, []() { FastFiles::StreamRead = true; Utils::Hook::Call(0x4B8DB0)(true); // currently set to Load_GfxWorld FastFiles::StreamRead = false; - }, HOOK_CALL).install()->quick(); -#endif - } - - FastFiles::~FastFiles() - { - FastFiles::ZonePaths.clear(); - } -} + }, HOOK_CALL).install()->quick(); +#endif + } + + FastFiles::~FastFiles() + { + FastFiles::ZonePaths.clear(); + } +} diff --git a/src/Components/Modules/FastFiles.hpp b/src/Components/Modules/FastFiles.hpp index ee10ea79..a7c1a86b 100644 --- a/src/Components/Modules/FastFiles.hpp +++ b/src/Components/Modules/FastFiles.hpp @@ -1,65 +1,65 @@ -namespace Components -{ - class FastFiles : public Component - { - public: - FastFiles(); - ~FastFiles(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "FastFiles"; }; -#endif - - static void AddZonePath(std::string path); - static std::string Current(); - static bool Ready(); - static bool Exists(std::string file); - - static void LoadLocalizeZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); - - static float GetFullLoadedFraction(); - - static unsigned char ZoneKey[1191]; - - private: - union Key - { - struct - { - unsigned char key[24]; - unsigned char iv[16]; - }; - - unsigned char data[1]; - }; - - static unsigned int CurrentZone; - static unsigned int MaxZones; - - static bool IsIW4xZone; - static bool StreamRead; - - static Key CurrentKey; - static symmetric_CTR CurrentCTR; - static std::vector ZonePaths; - static const char* GetZoneLocation(const char* file); - static void LoadInitialZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); - static void LoadDLCUIZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); - static void LoadGfxZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); - - static void ReadHeaderStub(unsigned int* header, int size); - static void ReadVersionStub(unsigned int* version, int size); - - static void ReadXFileHeader(void* buffer, int size); - - static void AuthLoadInitCrypto(); - static int AuthLoadInflateCompare(unsigned char* buffer, int length, unsigned char* ivValue); - static void AuthLoadInflateDecryptBase(); - static void AuthLoadInflateDecryptBaseFunc(unsigned char* buffer); - static int InflateInitDecrypt(z_streamp strm, const char *version, int stream_size); - - static void LoadZonesStub(Game::XZoneInfo *zoneInfo, unsigned int zoneCount); - - static void LogStreamRead(int len); - }; -} +namespace Components +{ + class FastFiles : public Component + { + public: + FastFiles(); + ~FastFiles(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "FastFiles"; }; +#endif + + static void AddZonePath(std::string path); + static std::string Current(); + static bool Ready(); + static bool Exists(std::string file); + + static void LoadLocalizeZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); + + static float GetFullLoadedFraction(); + + static unsigned char ZoneKey[1191]; + + private: + union Key + { + struct + { + unsigned char key[24]; + unsigned char iv[16]; + }; + + unsigned char data[1]; + }; + + static unsigned int CurrentZone; + static unsigned int MaxZones; + + static bool IsIW4xZone; + static bool StreamRead; + + static Key CurrentKey; + static symmetric_CTR CurrentCTR; + static std::vector ZonePaths; + static const char* GetZoneLocation(const char* file); + static void LoadInitialZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); + static void LoadDLCUIZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); + static void LoadGfxZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); + + static void ReadHeaderStub(unsigned int* header, int size); + static void ReadVersionStub(unsigned int* version, int size); + + static void ReadXFileHeader(void* buffer, int size); + + static void AuthLoadInitCrypto(); + static int AuthLoadInflateCompare(unsigned char* buffer, int length, unsigned char* ivValue); + static void AuthLoadInflateDecryptBase(); + static void AuthLoadInflateDecryptBaseFunc(unsigned char* buffer); + static int InflateInitDecrypt(z_streamp strm, const char *version, int stream_size); + + static void LoadZonesStub(Game::XZoneInfo *zoneInfo, unsigned int zoneCount); + + static void LogStreamRead(int len); + }; +} diff --git a/src/Components/Modules/FileSystem.cpp b/src/Components/Modules/FileSystem.cpp index ece19bdd..c6350909 100644 --- a/src/Components/Modules/FileSystem.cpp +++ b/src/Components/Modules/FileSystem.cpp @@ -1,324 +1,324 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::mutex FileSystem::Mutex; - std::recursive_mutex FileSystem::FSMutex; - Utils::Memory::Allocator FileSystem::MemAllocator; - - void FileSystem::File::read() - { - char* _buffer = nullptr; - int size = Game::FS_ReadFile(this->filePath.data(), &_buffer); - - this->buffer.clear(); - - if (size >= 0) - { - this->buffer.append(_buffer, size); - Game::FS_FreeFile(_buffer); - } - } - - void FileSystem::RawFile::read() - { - this->buffer.clear(); - - Game::RawFile* rawfile = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_RAWFILE, this->filePath.data()).rawfile; - if (!rawfile || Game::DB_IsXAssetDefault(Game::XAssetType::ASSET_TYPE_RAWFILE, this->filePath.data())) return; - - this->buffer.resize(Game::DB_GetRawFileLen(rawfile)); - Game::DB_GetRawBuffer(rawfile, const_cast(this->buffer.data()), this->buffer.size()); - } - - FileSystem::FileReader::FileReader(std::string file) : name(file), handle(0) - { - this->size = Game::FS_FOpenFileReadCurrentThread(this->name.data(), &this->handle); - } - - FileSystem::FileReader::~FileReader() - { - if (this->exists() && this->handle) - { - Game::FS_FCloseFile(this->handle); - } - } - - bool FileSystem::FileReader::exists() - { - return (this->size >= 0 && this->handle); - } - - std::string FileSystem::FileReader::getName() - { - return this->name; - } - - int FileSystem::FileReader::getSize() - { - return this->size; - } - - std::string FileSystem::FileReader::getBuffer() - { - Utils::Memory::Allocator allocator; - if (!this->exists()) return std::string(); - - int position = Game::FS_FTell(this->handle); - this->seek(0, FS_SEEK_SET); - - char* buffer = allocator.allocateArray(this->size); - if (!this->read(buffer, this->size)) - { - this->seek(position, FS_SEEK_SET); - return std::string(); - } - - this->seek(position, FS_SEEK_SET); - - return std::string(buffer, this->size); - } - - bool FileSystem::FileReader::read(void* buffer, size_t _size) - { - if (!this->exists() || static_cast(this->size) < _size || Game::FS_Read(buffer, _size, this->handle) != static_cast(_size)) - { - return false; - } - - return true; - } - - void FileSystem::FileReader::seek(int offset, int origin) - { - if (this->exists()) - { - Game::FS_Seek(this->handle, offset, origin); - } - } - - void FileSystem::FileWriter::write(std::string data) - { - if (this->handle) - { - Game::FS_Write(data.data(), data.size(), this->handle); - } - } - - void FileSystem::FileWriter::open(bool append) - { - if (append) - { - this->handle = Game::FS_FOpenFileAppend(this->filePath.data()); - } - else - { - this->handle = Game::FS_FOpenFileWrite(this->filePath.data()); - } - } - - void FileSystem::FileWriter::close() - { - if (this->handle) - { - Game::FS_FCloseFile(this->handle); - this->handle = 0; - } - } - - std::vector FileSystem::GetFileList(std::string path, std::string extension) - { - std::vector fileList; - - int numFiles = 0; - char** files = Game::FS_GetFileList(path.data(), extension.data(), Game::FS_LIST_PURE_ONLY, &numFiles, 0); - - if (files) - { - for (int i = 0; i < numFiles; ++i) - { - if (files[i]) - { - fileList.push_back(files[i]); - } - } - - Game::FS_FreeFileList(files); - } - - return fileList; - } - - std::vector FileSystem::GetSysFileList(std::string path, std::string extension, bool folders) - { - std::vector fileList; - - int numFiles = 0; - char** files = Game::Sys_ListFiles(path.data(), extension.data(), NULL, &numFiles, folders); - - if (files) - { - for (int i = 0; i < numFiles; ++i) - { - if (files[i]) - { - fileList.push_back(files[i]); - } - } - - Game::Sys_FreeFileList(files); - } - - return fileList; - } - - void FileSystem::DeleteFile(std::string folder, std::string file) - { - char path[MAX_PATH] = { 0 }; - Game::FS_BuildPathToFile(Dvar::Var("fs_basepath").get(), reinterpret_cast(0x63D0BB8), Utils::String::VA("%s/%s", folder.data(), file.data()), reinterpret_cast(&path)); - Game::FS_Remove(path); - } - - int FileSystem::ReadFile(const char* path, char** buffer) - { - if (!buffer) return -1; - else *buffer = nullptr; - if (!path) return -1; - - std::lock_guard _(FileSystem::Mutex); - FileSystem::FileReader reader(path); - - int size = reader.getSize(); - if (reader.exists() && size >= 0) - { - *buffer = FileSystem::AllocateFile(size + 1); - if (reader.read(*buffer, size)) return size; - - FileSystem::FreeFile(*buffer); - *buffer = nullptr; - } - - return -1; - } - - char* FileSystem::AllocateFile(int size) - { - return FileSystem::MemAllocator.allocateArray(size); - } - - void FileSystem::FreeFile(void* buffer) - { - FileSystem::MemAllocator.free(buffer); - } - - void FileSystem::RegisterFolder(const char* folder) - { - std::string fs_cdpath = Dvar::Var("fs_cdpath").get(); - std::string fs_basepath = Dvar::Var("fs_basepath").get(); - std::string fs_homepath = Dvar::Var("fs_homepath").get(); - - if (!fs_cdpath.empty()) Game::FS_AddLocalizedGameDirectory(fs_cdpath.data(), folder); - if (!fs_basepath.empty()) Game::FS_AddLocalizedGameDirectory(fs_basepath.data(), folder); - if (!fs_homepath.empty()) Game::FS_AddLocalizedGameDirectory(fs_homepath.data(), folder); - } - - void FileSystem::RegisterFolders() - { - if (ZoneBuilder::IsEnabled()) - { - FileSystem::RegisterFolder("zonedata"); - } - - FileSystem::RegisterFolder("userraw"); - } - - __declspec(naked) void FileSystem::StartupStub() - { - __asm - { - pushad - push esi - call FileSystem::RegisterFolders - pop esi - popad - - mov edx, ds:63D0CC0h - - push 48264Dh - retn - } - } - - int FileSystem::ExecIsFSStub(const char* execFilename) - { - return !File(execFilename).exists(); - } - - void FileSystem::FsStartupSync(const char* a1) - { - std::lock_guard _(FileSystem::FSMutex); - return Utils::Hook::Call(0x4823A0)(a1); // FS_Startup - } - - void FileSystem::FsRestartSync(int a1, int a2) - { - std::lock_guard _(FileSystem::FSMutex); - return Utils::Hook::Call(0x461A50)(a1, a2); // FS_Restart - } - - void FileSystem::DelayLoadImagesSync() - { - std::lock_guard _(FileSystem::FSMutex); - return Utils::Hook::Call(0x494060)(); // DB_LoadDelayedImages - } - - int FileSystem::LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image) - { - std::lock_guard _(FileSystem::FSMutex); - return Game::Load_Texture(loadDef, image); - } - - FileSystem::FileSystem() - { - FileSystem::MemAllocator.clear(); - - // Thread safe file system interaction - Utils::Hook(0x4F4BFF, FileSystem::AllocateFile, HOOK_CALL).install()->quick(); - //Utils::Hook(Game::FS_ReadFile, FileSystem::ReadFile, HOOK_JUMP).install()->quick(); - Utils::Hook(Game::FS_FreeFile, FileSystem::FreeFile, HOOK_JUMP).install()->quick(); - - // Filesystem config checks - Utils::Hook(0x6098FD, FileSystem::ExecIsFSStub, HOOK_CALL).install()->quick(); - - // Register additional folders - Utils::Hook(0x482647, FileSystem::StartupStub, HOOK_JUMP).install()->quick(); - - // exec whitelist removal (YAYFINITY WARD) - Utils::Hook::Nop(0x609685, 5); - Utils::Hook::Nop(0x60968C, 2); - - // ignore 'no iwd files found in main' - Utils::Hook::Nop(0x642A4B, 5); - - // Ignore bad magic, when trying to free hunk when it's already cleared - Utils::Hook::Set(0x49AACE, 0xC35E); - - // Synchronize filesystem starts - Utils::Hook(0x4290C6, FileSystem::FsStartupSync, HOOK_CALL).install()->quick(); // FS_InitFilesystem - Utils::Hook(0x461A88, FileSystem::FsStartupSync, HOOK_CALL).install()->quick(); // FS_Restart - - // Synchronize filesystem restarts - Utils::Hook(0x4A745B, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // SV_SpawnServer - Utils::Hook(0x4C8609, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // FS_ConditionalRestart - Utils::Hook(0x5AC68E, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // CL_ParseServerMessage - - // Synchronize db image loading - Utils::Hook(0x415AB8, FileSystem::DelayLoadImagesSync, HOOK_CALL).install()->quick(); - Utils::Hook(0x4D32BC, FileSystem::LoadTextureSync, HOOK_CALL).install()->quick(); - } - - FileSystem::~FileSystem() - { - assert(FileSystem::MemAllocator.empty()); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::mutex FileSystem::Mutex; + std::recursive_mutex FileSystem::FSMutex; + Utils::Memory::Allocator FileSystem::MemAllocator; + + void FileSystem::File::read() + { + char* _buffer = nullptr; + int size = Game::FS_ReadFile(this->filePath.data(), &_buffer); + + this->buffer.clear(); + + if (size >= 0) + { + this->buffer.append(_buffer, size); + Game::FS_FreeFile(_buffer); + } + } + + void FileSystem::RawFile::read() + { + this->buffer.clear(); + + Game::RawFile* rawfile = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_RAWFILE, this->filePath.data()).rawfile; + if (!rawfile || Game::DB_IsXAssetDefault(Game::XAssetType::ASSET_TYPE_RAWFILE, this->filePath.data())) return; + + this->buffer.resize(Game::DB_GetRawFileLen(rawfile)); + Game::DB_GetRawBuffer(rawfile, const_cast(this->buffer.data()), this->buffer.size()); + } + + FileSystem::FileReader::FileReader(std::string file) : name(file), handle(0) + { + this->size = Game::FS_FOpenFileReadCurrentThread(this->name.data(), &this->handle); + } + + FileSystem::FileReader::~FileReader() + { + if (this->exists() && this->handle) + { + Game::FS_FCloseFile(this->handle); + } + } + + bool FileSystem::FileReader::exists() + { + return (this->size >= 0 && this->handle); + } + + std::string FileSystem::FileReader::getName() + { + return this->name; + } + + int FileSystem::FileReader::getSize() + { + return this->size; + } + + std::string FileSystem::FileReader::getBuffer() + { + Utils::Memory::Allocator allocator; + if (!this->exists()) return std::string(); + + int position = Game::FS_FTell(this->handle); + this->seek(0, FS_SEEK_SET); + + char* buffer = allocator.allocateArray(this->size); + if (!this->read(buffer, this->size)) + { + this->seek(position, FS_SEEK_SET); + return std::string(); + } + + this->seek(position, FS_SEEK_SET); + + return std::string(buffer, this->size); + } + + bool FileSystem::FileReader::read(void* buffer, size_t _size) + { + if (!this->exists() || static_cast(this->size) < _size || Game::FS_Read(buffer, _size, this->handle) != static_cast(_size)) + { + return false; + } + + return true; + } + + void FileSystem::FileReader::seek(int offset, int origin) + { + if (this->exists()) + { + Game::FS_Seek(this->handle, offset, origin); + } + } + + void FileSystem::FileWriter::write(std::string data) + { + if (this->handle) + { + Game::FS_Write(data.data(), data.size(), this->handle); + } + } + + void FileSystem::FileWriter::open(bool append) + { + if (append) + { + this->handle = Game::FS_FOpenFileAppend(this->filePath.data()); + } + else + { + this->handle = Game::FS_FOpenFileWrite(this->filePath.data()); + } + } + + void FileSystem::FileWriter::close() + { + if (this->handle) + { + Game::FS_FCloseFile(this->handle); + this->handle = 0; + } + } + + std::vector FileSystem::GetFileList(std::string path, std::string extension) + { + std::vector fileList; + + int numFiles = 0; + char** files = Game::FS_GetFileList(path.data(), extension.data(), Game::FS_LIST_PURE_ONLY, &numFiles, 0); + + if (files) + { + for (int i = 0; i < numFiles; ++i) + { + if (files[i]) + { + fileList.push_back(files[i]); + } + } + + Game::FS_FreeFileList(files); + } + + return fileList; + } + + std::vector FileSystem::GetSysFileList(std::string path, std::string extension, bool folders) + { + std::vector fileList; + + int numFiles = 0; + char** files = Game::Sys_ListFiles(path.data(), extension.data(), NULL, &numFiles, folders); + + if (files) + { + for (int i = 0; i < numFiles; ++i) + { + if (files[i]) + { + fileList.push_back(files[i]); + } + } + + Game::Sys_FreeFileList(files); + } + + return fileList; + } + + void FileSystem::DeleteFile(std::string folder, std::string file) + { + char path[MAX_PATH] = { 0 }; + Game::FS_BuildPathToFile(Dvar::Var("fs_basepath").get(), reinterpret_cast(0x63D0BB8), Utils::String::VA("%s/%s", folder.data(), file.data()), reinterpret_cast(&path)); + Game::FS_Remove(path); + } + + int FileSystem::ReadFile(const char* path, char** buffer) + { + if (!buffer) return -1; + else *buffer = nullptr; + if (!path) return -1; + + std::lock_guard _(FileSystem::Mutex); + FileSystem::FileReader reader(path); + + int size = reader.getSize(); + if (reader.exists() && size >= 0) + { + *buffer = FileSystem::AllocateFile(size + 1); + if (reader.read(*buffer, size)) return size; + + FileSystem::FreeFile(*buffer); + *buffer = nullptr; + } + + return -1; + } + + char* FileSystem::AllocateFile(int size) + { + return FileSystem::MemAllocator.allocateArray(size); + } + + void FileSystem::FreeFile(void* buffer) + { + FileSystem::MemAllocator.free(buffer); + } + + void FileSystem::RegisterFolder(const char* folder) + { + std::string fs_cdpath = Dvar::Var("fs_cdpath").get(); + std::string fs_basepath = Dvar::Var("fs_basepath").get(); + std::string fs_homepath = Dvar::Var("fs_homepath").get(); + + if (!fs_cdpath.empty()) Game::FS_AddLocalizedGameDirectory(fs_cdpath.data(), folder); + if (!fs_basepath.empty()) Game::FS_AddLocalizedGameDirectory(fs_basepath.data(), folder); + if (!fs_homepath.empty()) Game::FS_AddLocalizedGameDirectory(fs_homepath.data(), folder); + } + + void FileSystem::RegisterFolders() + { + if (ZoneBuilder::IsEnabled()) + { + FileSystem::RegisterFolder("zonedata"); + } + + FileSystem::RegisterFolder("userraw"); + } + + __declspec(naked) void FileSystem::StartupStub() + { + __asm + { + pushad + push esi + call FileSystem::RegisterFolders + pop esi + popad + + mov edx, ds:63D0CC0h + + push 48264Dh + retn + } + } + + int FileSystem::ExecIsFSStub(const char* execFilename) + { + return !File(execFilename).exists(); + } + + void FileSystem::FsStartupSync(const char* a1) + { + std::lock_guard _(FileSystem::FSMutex); + return Utils::Hook::Call(0x4823A0)(a1); // FS_Startup + } + + void FileSystem::FsRestartSync(int a1, int a2) + { + std::lock_guard _(FileSystem::FSMutex); + return Utils::Hook::Call(0x461A50)(a1, a2); // FS_Restart + } + + void FileSystem::DelayLoadImagesSync() + { + std::lock_guard _(FileSystem::FSMutex); + return Utils::Hook::Call(0x494060)(); // DB_LoadDelayedImages + } + + int FileSystem::LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image) + { + std::lock_guard _(FileSystem::FSMutex); + return Game::Load_Texture(loadDef, image); + } + + FileSystem::FileSystem() + { + FileSystem::MemAllocator.clear(); + + // Thread safe file system interaction + Utils::Hook(0x4F4BFF, FileSystem::AllocateFile, HOOK_CALL).install()->quick(); + //Utils::Hook(Game::FS_ReadFile, FileSystem::ReadFile, HOOK_JUMP).install()->quick(); + Utils::Hook(Game::FS_FreeFile, FileSystem::FreeFile, HOOK_JUMP).install()->quick(); + + // Filesystem config checks + Utils::Hook(0x6098FD, FileSystem::ExecIsFSStub, HOOK_CALL).install()->quick(); + + // Register additional folders + Utils::Hook(0x482647, FileSystem::StartupStub, HOOK_JUMP).install()->quick(); + + // exec whitelist removal (YAYFINITY WARD) + Utils::Hook::Nop(0x609685, 5); + Utils::Hook::Nop(0x60968C, 2); + + // ignore 'no iwd files found in main' + Utils::Hook::Nop(0x642A4B, 5); + + // Ignore bad magic, when trying to free hunk when it's already cleared + Utils::Hook::Set(0x49AACE, 0xC35E); + + // Synchronize filesystem starts + Utils::Hook(0x4290C6, FileSystem::FsStartupSync, HOOK_CALL).install()->quick(); // FS_InitFilesystem + Utils::Hook(0x461A88, FileSystem::FsStartupSync, HOOK_CALL).install()->quick(); // FS_Restart + + // Synchronize filesystem restarts + Utils::Hook(0x4A745B, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // SV_SpawnServer + Utils::Hook(0x4C8609, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // FS_ConditionalRestart + Utils::Hook(0x5AC68E, FileSystem::FsRestartSync, HOOK_CALL).install()->quick(); // CL_ParseServerMessage + + // Synchronize db image loading + Utils::Hook(0x415AB8, FileSystem::DelayLoadImagesSync, HOOK_CALL).install()->quick(); + Utils::Hook(0x4D32BC, FileSystem::LoadTextureSync, HOOK_CALL).install()->quick(); + } + + FileSystem::~FileSystem() + { + assert(FileSystem::MemAllocator.empty()); + } +} diff --git a/src/Components/Modules/FileSystem.hpp b/src/Components/Modules/FileSystem.hpp index eab27b02..c541bcc7 100644 --- a/src/Components/Modules/FileSystem.hpp +++ b/src/Components/Modules/FileSystem.hpp @@ -1,117 +1,117 @@ -namespace Components -{ - class FileSystem : public Component - { - public: - class AbstractFile - { - public: - virtual ~AbstractFile() {}; - - virtual bool exists() = 0; - virtual std::string getName() = 0; - virtual std::string& getBuffer() = 0; - }; - - class File : public AbstractFile - { - public: - File() {}; - File(std::string file) : filePath(file) { this->read(); }; - - bool exists() { return !this->buffer.empty(); }; - std::string getName() { return this->filePath; }; - std::string& getBuffer() { return this->buffer; }; - - private: - std::string filePath; - std::string buffer; - - void read(); - }; - - class RawFile : public AbstractFile - { - public: - RawFile() {}; - RawFile(std::string file) : filePath(file) { this->read(); }; - - bool exists() { return !this->buffer.empty(); }; - std::string getName() { return this->filePath; }; - std::string& getBuffer() { return this->buffer; }; - - private: - std::string filePath; - std::string buffer; - - void read(); - }; - - class FileReader - { - public: - FileReader() : size(-1), name(), handle(0) {}; - FileReader(std::string file); - ~FileReader(); - - bool exists(); - std::string getName(); - std::string getBuffer(); - int getSize(); - bool read(void* buffer, size_t size); - void seek(int offset, int origin); - - private: - int handle; - int size; - std::string name; - }; - - class FileWriter - { - public: - FileWriter(std::string file, bool append = false) : filePath(file), handle(0) { this->open(append); }; - ~FileWriter() { this->close(); }; - - void write(std::string data); - - private: - int handle; - std::string filePath; - - void open(bool append = false); - void close(); - }; - - FileSystem(); - ~FileSystem(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "FileSystem"; }; -#endif - - static std::vector GetFileList(std::string path, std::string extension); - static std::vector GetSysFileList(std::string path, std::string extension, bool folders = false); - static void DeleteFile(std::string folder, std::string file); - - private: - static std::mutex Mutex; - static std::recursive_mutex FSMutex; - static Utils::Memory::Allocator MemAllocator; - - static int ReadFile(const char* path, char** buffer); - static char* AllocateFile(int size); - static void FreeFile(void* buffer); - - static void RegisterFolder(const char* folder); - - static void RegisterFolders(); - static void StartupStub(); - static int ExecIsFSStub(const char* execFilename); - - static void FsStartupSync(const char* a1); - static void FsRestartSync(int a1, int a2); - static void DelayLoadImagesSync(); - static int LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image); - }; -} +namespace Components +{ + class FileSystem : public Component + { + public: + class AbstractFile + { + public: + virtual ~AbstractFile() {}; + + virtual bool exists() = 0; + virtual std::string getName() = 0; + virtual std::string& getBuffer() = 0; + }; + + class File : public AbstractFile + { + public: + File() {}; + File(std::string file) : filePath(file) { this->read(); }; + + bool exists() { return !this->buffer.empty(); }; + std::string getName() { return this->filePath; }; + std::string& getBuffer() { return this->buffer; }; + + private: + std::string filePath; + std::string buffer; + + void read(); + }; + + class RawFile : public AbstractFile + { + public: + RawFile() {}; + RawFile(std::string file) : filePath(file) { this->read(); }; + + bool exists() { return !this->buffer.empty(); }; + std::string getName() { return this->filePath; }; + std::string& getBuffer() { return this->buffer; }; + + private: + std::string filePath; + std::string buffer; + + void read(); + }; + + class FileReader + { + public: + FileReader() : size(-1), name(), handle(0) {}; + FileReader(std::string file); + ~FileReader(); + + bool exists(); + std::string getName(); + std::string getBuffer(); + int getSize(); + bool read(void* buffer, size_t size); + void seek(int offset, int origin); + + private: + int handle; + int size; + std::string name; + }; + + class FileWriter + { + public: + FileWriter(std::string file, bool append = false) : filePath(file), handle(0) { this->open(append); }; + ~FileWriter() { this->close(); }; + + void write(std::string data); + + private: + int handle; + std::string filePath; + + void open(bool append = false); + void close(); + }; + + FileSystem(); + ~FileSystem(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "FileSystem"; }; +#endif + + static std::vector GetFileList(std::string path, std::string extension); + static std::vector GetSysFileList(std::string path, std::string extension, bool folders = false); + static void DeleteFile(std::string folder, std::string file); + + private: + static std::mutex Mutex; + static std::recursive_mutex FSMutex; + static Utils::Memory::Allocator MemAllocator; + + static int ReadFile(const char* path, char** buffer); + static char* AllocateFile(int size); + static void FreeFile(void* buffer); + + static void RegisterFolder(const char* folder); + + static void RegisterFolders(); + static void StartupStub(); + static int ExecIsFSStub(const char* execFilename); + + static void FsStartupSync(const char* a1); + static void FsRestartSync(int a1, int a2); + static void DelayLoadImagesSync(); + static int LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image); + }; +} diff --git a/src/Components/Modules/Flags.cpp b/src/Components/Modules/Flags.cpp index 535b9471..31a450f6 100644 --- a/src/Components/Modules/Flags.cpp +++ b/src/Components/Modules/Flags.cpp @@ -1,49 +1,49 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::vector Flags::EnabledFlags; - - bool Flags::HasFlag(std::string flag) - { - for (auto entry : Flags::EnabledFlags) - { - if (Utils::String::ToLower(entry) == Utils::String::ToLower(flag)) - { - return true; - } - } - - return false; - } - - void Flags::ParseFlags() - { - int numArgs; - LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &numArgs); - - if (argv) - { - for (int i = 0; i < numArgs; ++i) - { - std::wstring wFlag(argv[i]); - if (wFlag[0] == L'-') - { - Flags::EnabledFlags.push_back(std::string(++wFlag.begin(), wFlag.end())); - } - } - - LocalFree(argv); - } - } - - Flags::Flags() - { - Flags::ParseFlags(); - } - - Flags::~Flags() - { - Flags::EnabledFlags.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::vector Flags::EnabledFlags; + + bool Flags::HasFlag(std::string flag) + { + for (auto entry : Flags::EnabledFlags) + { + if (Utils::String::ToLower(entry) == Utils::String::ToLower(flag)) + { + return true; + } + } + + return false; + } + + void Flags::ParseFlags() + { + int numArgs; + LPWSTR* argv = CommandLineToArgvW(GetCommandLineW(), &numArgs); + + if (argv) + { + for (int i = 0; i < numArgs; ++i) + { + std::wstring wFlag(argv[i]); + if (wFlag[0] == L'-') + { + Flags::EnabledFlags.push_back(std::string(++wFlag.begin(), wFlag.end())); + } + } + + LocalFree(argv); + } + } + + Flags::Flags() + { + Flags::ParseFlags(); + } + + Flags::~Flags() + { + Flags::EnabledFlags.clear(); + } +} diff --git a/src/Components/Modules/Flags.hpp b/src/Components/Modules/Flags.hpp index d3b6358e..cb927bbf 100644 --- a/src/Components/Modules/Flags.hpp +++ b/src/Components/Modules/Flags.hpp @@ -1,20 +1,20 @@ -namespace Components -{ - class Flags : public Component - { - public: - Flags(); - ~Flags(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Flags"; }; -#endif - - static bool HasFlag(std::string flag); - - private: - static std::vector EnabledFlags; - - static void ParseFlags(); - }; -} +namespace Components +{ + class Flags : public Component + { + public: + Flags(); + ~Flags(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Flags"; }; +#endif + + static bool HasFlag(std::string flag); + + private: + static std::vector EnabledFlags; + + static void ParseFlags(); + }; +} diff --git a/src/Components/Modules/FrameTime.cpp b/src/Components/Modules/FrameTime.cpp index cce875c2..e5cd912d 100644 --- a/src/Components/Modules/FrameTime.cpp +++ b/src/Components/Modules/FrameTime.cpp @@ -1,131 +1,131 @@ -#include "STDInclude.hpp" - -namespace Components -{ - void FrameTime::NetSleep(int msec) - { - if (msec < 0) msec = 0; - - fd_set fdr; - FD_ZERO(&fdr); - - SOCKET highestfd = INVALID_SOCKET; - if (*Game::ip_socket != INVALID_SOCKET) - { - FD_SET(*Game::ip_socket, &fdr); - highestfd = *Game::ip_socket; - } - - if (highestfd == INVALID_SOCKET) - { - // windows ain't happy when select is called without valid FDs - std::this_thread::sleep_for(std::chrono::milliseconds(msec)); - return; - } - - timeval timeout; - timeout.tv_sec = msec / 1000; - timeout.tv_usec = (msec % 1000) * 1000; - - int retval = select(highestfd + 1, &fdr, NULL, NULL, &timeout); - - if (retval == SOCKET_ERROR) - { - Logger::Print("Warning: select() syscall failed: %s\n", Game::NET_ErrorString()); - } - else if (retval > 0) - { - // process packets - if (Dvar::Var(0x1AD7934).get()) // com_sv_running - { - Utils::Hook::Call(0x458160)(); - } - else - { - Utils::Hook::Call(0x49F0B0)(); - } - } - } - - void FrameTime::SVFrameWaitFunc() - { - int sv_residualTime = *reinterpret_cast(0x2089E14); - - if (sv_residualTime < 50) - { - FrameTime::NetSleep(50 - sv_residualTime); - } - } - - void __declspec(naked) FrameTime::SVFrameWaitStub() - { - __asm - { - pushad - call FrameTime::SVFrameWaitFunc - popad - - push 4CD420h - retn - } - } - - int FrameTime::ComTimeVal(int minMsec) - { - int timeVal = Game::Sys_Milliseconds() - *reinterpret_cast(0x1AD8F3C); // com_frameTime - return (timeVal >= minMsec ? 0 : minMsec - timeVal); - } - - uint32_t FrameTime::ComFrameWait(int minMsec) - { - int timeVal; - - do - { - timeVal = FrameTime::ComTimeVal(minMsec); - FrameTime::NetSleep(timeVal < 1 ? 0 : timeVal - 1); - } while (FrameTime::ComTimeVal(minMsec)); - - uint32_t lastTime = *Game::com_frameTime; - Utils::Hook::Call(0x43D140)(); // Com_EventLoop - *Game::com_frameTime = Game::Sys_Milliseconds(); - - return *Game::com_frameTime - lastTime; - } - - void __declspec(naked) FrameTime::ComFrameWaitStub() - { - __asm - { - push ecx - pushad - - push edi - call FrameTime::ComFrameWait - add esp, 4 - - mov[esp + 20h], eax - popad - pop eax - mov ecx, eax - - mov edx, 1AD7934h // com_sv_running - cmp byte ptr[edx + 10h], 0 - - push 47DDC1h - retn - } - } - - FrameTime::FrameTime() - { - if (Dedicated::IsEnabled()) - { - Utils::Hook(0x4BAAAD, FrameTime::SVFrameWaitStub, HOOK_CALL).install()->quick(); - } - else - { - Utils::Hook(0x47DD80, FrameTime::ComFrameWaitStub, HOOK_JUMP).install()->quick(); - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + void FrameTime::NetSleep(int msec) + { + if (msec < 0) msec = 0; + + fd_set fdr; + FD_ZERO(&fdr); + + SOCKET highestfd = INVALID_SOCKET; + if (*Game::ip_socket != INVALID_SOCKET) + { + FD_SET(*Game::ip_socket, &fdr); + highestfd = *Game::ip_socket; + } + + if (highestfd == INVALID_SOCKET) + { + // windows ain't happy when select is called without valid FDs + std::this_thread::sleep_for(std::chrono::milliseconds(msec)); + return; + } + + timeval timeout; + timeout.tv_sec = msec / 1000; + timeout.tv_usec = (msec % 1000) * 1000; + + int retval = select(highestfd + 1, &fdr, NULL, NULL, &timeout); + + if (retval == SOCKET_ERROR) + { + Logger::Print("Warning: select() syscall failed: %s\n", Game::NET_ErrorString()); + } + else if (retval > 0) + { + // process packets + if (Dvar::Var(0x1AD7934).get()) // com_sv_running + { + Utils::Hook::Call(0x458160)(); + } + else + { + Utils::Hook::Call(0x49F0B0)(); + } + } + } + + void FrameTime::SVFrameWaitFunc() + { + int sv_residualTime = *reinterpret_cast(0x2089E14); + + if (sv_residualTime < 50) + { + FrameTime::NetSleep(50 - sv_residualTime); + } + } + + void __declspec(naked) FrameTime::SVFrameWaitStub() + { + __asm + { + pushad + call FrameTime::SVFrameWaitFunc + popad + + push 4CD420h + retn + } + } + + int FrameTime::ComTimeVal(int minMsec) + { + int timeVal = Game::Sys_Milliseconds() - *reinterpret_cast(0x1AD8F3C); // com_frameTime + return (timeVal >= minMsec ? 0 : minMsec - timeVal); + } + + uint32_t FrameTime::ComFrameWait(int minMsec) + { + int timeVal; + + do + { + timeVal = FrameTime::ComTimeVal(minMsec); + FrameTime::NetSleep(timeVal < 1 ? 0 : timeVal - 1); + } while (FrameTime::ComTimeVal(minMsec)); + + uint32_t lastTime = *Game::com_frameTime; + Utils::Hook::Call(0x43D140)(); // Com_EventLoop + *Game::com_frameTime = Game::Sys_Milliseconds(); + + return *Game::com_frameTime - lastTime; + } + + void __declspec(naked) FrameTime::ComFrameWaitStub() + { + __asm + { + push ecx + pushad + + push edi + call FrameTime::ComFrameWait + add esp, 4 + + mov[esp + 20h], eax + popad + pop eax + mov ecx, eax + + mov edx, 1AD7934h // com_sv_running + cmp byte ptr[edx + 10h], 0 + + push 47DDC1h + retn + } + } + + FrameTime::FrameTime() + { + if (Dedicated::IsEnabled()) + { + Utils::Hook(0x4BAAAD, FrameTime::SVFrameWaitStub, HOOK_CALL).install()->quick(); + } + else + { + Utils::Hook(0x47DD80, FrameTime::ComFrameWaitStub, HOOK_JUMP).install()->quick(); + } + } +} diff --git a/src/Components/Modules/FrameTime.hpp b/src/Components/Modules/FrameTime.hpp index b622c9b0..ac1ca38e 100644 --- a/src/Components/Modules/FrameTime.hpp +++ b/src/Components/Modules/FrameTime.hpp @@ -1,22 +1,22 @@ -namespace Components -{ - class FrameTime : public Component - { - public: - FrameTime(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "FrameTime"; }; -#endif - - private: - static void SVFrameWaitStub(); - static void SVFrameWaitFunc(); - - static void NetSleep(int msec); - - static int ComTimeVal(int minMsec); - static uint32_t ComFrameWait(int minMsec); - static void ComFrameWaitStub(); - }; -} +namespace Components +{ + class FrameTime : public Component + { + public: + FrameTime(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "FrameTime"; }; +#endif + + private: + static void SVFrameWaitStub(); + static void SVFrameWaitFunc(); + + static void NetSleep(int msec); + + static int ComTimeVal(int minMsec); + static uint32_t ComFrameWait(int minMsec); + static void ComFrameWaitStub(); + }; +} diff --git a/src/Components/Modules/Gametypes.cpp b/src/Components/Modules/Gametypes.cpp index 6f597f2a..879d2fb0 100644 --- a/src/Components/Modules/Gametypes.cpp +++ b/src/Components/Modules/Gametypes.cpp @@ -1,112 +1,112 @@ -#include "STDInclude.hpp" - -namespace Components -{ - unsigned int Gametypes::GetGametypeCount() - { - return *Game::gameTypeCount; - } - - const char* Gametypes::GetGametypeText(unsigned int index, int) - { - if (static_cast(*Game::gameTypeCount) > index) - { - return Game::SEH_StringEd_GetString(Game::gameTypes[index].uiName); - } - - return ""; - } - - void Gametypes::SelectGametype(unsigned int index) - { - if (!*Game::gameTypeCount) return; - if (static_cast(*Game::gameTypeCount) <= index) index = 0; - - std::string gametype = Game::gameTypes[index].gameType; - - Dvar::Var("ui_gametype").set(gametype); - //Dvar::Var("g_gametype").set(gametype); - } - - void* Gametypes::BuildGametypeList(const char*, void* buffer, size_t size) - { - std::vector gametypes; - - auto pushGametype = [&] (std::string gametype) - { - auto pos = gametype.find_last_of("/\\"); - if (pos != std::string::npos) - { - gametype = gametype.substr(pos + 1); - } - - if (Utils::String::EndsWith(gametype, ".txt")) - { - gametype = gametype.substr(0, gametype.size() - 4); - } - - // No need for that :) - if (gametype == "_gametypes") return; - - if (std::find(gametypes.begin(), gametypes.end(), gametype) == gametypes.end()) - { - gametypes.push_back(gametype); - } - }; - - // Get the gametypes we can find in the filesystem - std::vector rawGametypes = FileSystem::GetFileList("maps/mp/gametypes/", "txt"); - - // Get the gametypes we can find in the database - Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_RAWFILE, [] (Game::XAssetHeader header, void* data) - { - std::string name = header.rawfile->name; - std::vector* rawGametypes = reinterpret_cast*>(data); - - if (Utils::String::StartsWith(name, "maps/mp/gametypes/") && Utils::String::EndsWith(name, ".txt")) - { - if (std::count(name.begin(), name.end(), '/') == 3 && std::count(name.begin(), name.end(), '\\') == 0) - { - rawGametypes->push_back(name); - } - } - - }, &rawGametypes, false); - - std::for_each(rawGametypes.begin(), rawGametypes.end(), pushGametype); - - std::string data; - for (auto& gametype : gametypes) - { - if (Game::LoadModdableRawfile(0, Utils::String::VA("maps/mp/gametypes/%s.txt", gametype.data()))) - { - data.append(gametype); - data.append("\r\n"); - } - } - - // Copy to the actual buffer - std::memcpy(buffer, data.data(), std::min(size, data.size() + 1)); - - return (data.empty() ? nullptr : buffer); - } - - Gametypes::Gametypes() - { - UIFeeder::Add(29.0f, Gametypes::GetGametypeCount, Gametypes::GetGametypeText, Gametypes::SelectGametype); - - // Dynamically grab gametypes - Utils::Hook(0x5FA46C, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // Scr_UpdateGameTypeList - Utils::Hook(0x632155, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // UI_UpdateGameTypesList - - // This is placed here in case the anticheat has been disabled! - // Make sure this is called after every onther anticheat check! -#if !defined(DEBUG) && !defined(DISABLE_ANTICHEAT) - Utils::Hook(0x5ACBA3, [] () // Somewhere in the renderer, past other renderer hooks! - { - AntiCheat::FlagIntegrityCheck(); - return Utils::Hook::Call(0x50AB20)(); - }, HOOK_CALL).install()->quick(); -#endif - } -} +#include "STDInclude.hpp" + +namespace Components +{ + unsigned int Gametypes::GetGametypeCount() + { + return *Game::gameTypeCount; + } + + const char* Gametypes::GetGametypeText(unsigned int index, int) + { + if (static_cast(*Game::gameTypeCount) > index) + { + return Game::SEH_StringEd_GetString(Game::gameTypes[index].uiName); + } + + return ""; + } + + void Gametypes::SelectGametype(unsigned int index) + { + if (!*Game::gameTypeCount) return; + if (static_cast(*Game::gameTypeCount) <= index) index = 0; + + std::string gametype = Game::gameTypes[index].gameType; + + Dvar::Var("ui_gametype").set(gametype); + //Dvar::Var("g_gametype").set(gametype); + } + + void* Gametypes::BuildGametypeList(const char*, void* buffer, size_t size) + { + std::vector gametypes; + + auto pushGametype = [&] (std::string gametype) + { + auto pos = gametype.find_last_of("/\\"); + if (pos != std::string::npos) + { + gametype = gametype.substr(pos + 1); + } + + if (Utils::String::EndsWith(gametype, ".txt")) + { + gametype = gametype.substr(0, gametype.size() - 4); + } + + // No need for that :) + if (gametype == "_gametypes") return; + + if (std::find(gametypes.begin(), gametypes.end(), gametype) == gametypes.end()) + { + gametypes.push_back(gametype); + } + }; + + // Get the gametypes we can find in the filesystem + std::vector rawGametypes = FileSystem::GetFileList("maps/mp/gametypes/", "txt"); + + // Get the gametypes we can find in the database + Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_RAWFILE, [] (Game::XAssetHeader header, void* data) + { + std::string name = header.rawfile->name; + std::vector* rawGametypes = reinterpret_cast*>(data); + + if (Utils::String::StartsWith(name, "maps/mp/gametypes/") && Utils::String::EndsWith(name, ".txt")) + { + if (std::count(name.begin(), name.end(), '/') == 3 && std::count(name.begin(), name.end(), '\\') == 0) + { + rawGametypes->push_back(name); + } + } + + }, &rawGametypes, false); + + std::for_each(rawGametypes.begin(), rawGametypes.end(), pushGametype); + + std::string data; + for (auto& gametype : gametypes) + { + if (Game::LoadModdableRawfile(0, Utils::String::VA("maps/mp/gametypes/%s.txt", gametype.data()))) + { + data.append(gametype); + data.append("\r\n"); + } + } + + // Copy to the actual buffer + std::memcpy(buffer, data.data(), std::min(size, data.size() + 1)); + + return (data.empty() ? nullptr : buffer); + } + + Gametypes::Gametypes() + { + UIFeeder::Add(29.0f, Gametypes::GetGametypeCount, Gametypes::GetGametypeText, Gametypes::SelectGametype); + + // Dynamically grab gametypes + Utils::Hook(0x5FA46C, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // Scr_UpdateGameTypeList + Utils::Hook(0x632155, Gametypes::BuildGametypeList, HOOK_CALL).install()->quick(); // UI_UpdateGameTypesList + + // This is placed here in case the anticheat has been disabled! + // Make sure this is called after every onther anticheat check! +#if !defined(DEBUG) && !defined(DISABLE_ANTICHEAT) + Utils::Hook(0x5ACBA3, [] () // Somewhere in the renderer, past other renderer hooks! + { + AntiCheat::FlagIntegrityCheck(); + return Utils::Hook::Call(0x50AB20)(); + }, HOOK_CALL).install()->quick(); +#endif + } +} diff --git a/src/Components/Modules/Gametypes.hpp b/src/Components/Modules/Gametypes.hpp index b63ee6b5..651a7d9e 100644 --- a/src/Components/Modules/Gametypes.hpp +++ b/src/Components/Modules/Gametypes.hpp @@ -1,19 +1,19 @@ -namespace Components -{ - class Gametypes : public Component - { - public: - Gametypes(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Gametypes"; }; -#endif - - private: - static unsigned int GetGametypeCount(); - static const char* GetGametypeText(unsigned int index, int column); - static void SelectGametype(unsigned int index); - - static void* BuildGametypeList(const char* file, void* buffer, size_t size); - }; -} +namespace Components +{ + class Gametypes : public Component + { + public: + Gametypes(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Gametypes"; }; +#endif + + private: + static unsigned int GetGametypeCount(); + static const char* GetGametypeText(unsigned int index, int column); + static void SelectGametype(unsigned int index); + + static void* BuildGametypeList(const char* file, void* buffer, size_t size); + }; +} diff --git a/src/Components/Modules/IPCPipe.cpp b/src/Components/Modules/IPCPipe.cpp index 45079d3c..4f12fed2 100644 --- a/src/Components/Modules/IPCPipe.cpp +++ b/src/Components/Modules/IPCPipe.cpp @@ -1,233 +1,233 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Pipe IPCPipe::ServerPipe; - Pipe IPCPipe::ClientPipe; - -#pragma region Pipe - - Pipe::Pipe() : type(IPCTYPE_NONE), reconnectAttempt(0), pipe(INVALID_HANDLE_VALUE), connectCallback(0), threadAttached(false) - { - this->destroy(); - } - - Pipe::~Pipe() - { - this->destroy(); - } - - bool Pipe::connect(std::string name) - { - this->destroy(); - - this->type = IPCTYPE_CLIENT; - this->setName(name); - - this->pipe = CreateFileA(this->pipeFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); - - if (INVALID_HANDLE_VALUE == this->pipe) - { - Logger::Print("Failed to connect to the pipe\n"); - - if (this->reconnectAttempt < IPC_MAX_RECONNECTS) - { - Logger::Print("Attempting to reconnect to the pipe.\n"); - ++this->reconnectAttempt; - std::this_thread::sleep_for(500ms); - - return this->connect(name); - } - else - { - this->destroy(); - return false; - } - } - - this->reconnectAttempt = 0; - Logger::Print("Successfully connected to the pipe\n"); - - return true; - } - - bool Pipe::create(std::string name) - { - this->destroy(); - - this->type = IPCTYPE_SERVER; - this->setName(name); - - this->pipe = CreateNamedPipeA(this->pipeFile, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof(this->packet), sizeof(this->packet), NMPWAIT_USE_DEFAULT_WAIT, NULL); - - if (INVALID_HANDLE_VALUE != this->pipe && this->pipe) - { - // Only create the thread, when not performing unit tests! - if (!Loader::PerformingUnitTests()) - { - this->threadAttached = true; - this->thread = std::thread(Pipe::ReceiveThread, this); - } - - Logger::Print("Pipe successfully created\n"); - return true; - } - - Logger::Print("Failed to create the pipe\n"); - this->destroy(); - return false; - } - - void Pipe::onConnect(Pipe::Callback callback) - { - this->connectCallback = callback; - } - - void Pipe::setCallback(std::string command, Utils::Slot callback) - { - this->packetCallbacks[command] = callback; - } - - bool Pipe::write(std::string command, std::string data) - { - if (this->type != IPCTYPE_CLIENT || this->pipe == INVALID_HANDLE_VALUE) return false; - - Pipe::Packet _packet; - strcpy_s(_packet.command, command.data()); - strcpy_s(_packet.buffer, data.data()); - - DWORD cbBytes; - return (WriteFile(this->pipe, &_packet, sizeof(_packet), &cbBytes, NULL) || GetLastError() == ERROR_IO_PENDING); - } - - void Pipe::destroy() - { - //this->Type = IPCTYPE_NONE; - - //*this->PipeFile = 0; - //*this->PipeName = 0; - - if (this->pipe && INVALID_HANDLE_VALUE != this->pipe) - { - if (this->type == IPCTYPE_SERVER) DisconnectNamedPipe(this->pipe); - - CloseHandle(this->pipe); - Logger::Print("Disconnected from the pipe.\n"); - } - - this->threadAttached = false; - - if (this->thread.joinable()) - { - Logger::Print("Terminating pipe thread...\n"); - - this->thread.join(); - - Logger::Print("Pipe thread terminated.\n"); - } - } - - void Pipe::setName(std::string name) - { - memset(this->pipeName, 0, sizeof(this->pipeName)); - memset(this->pipeFile, 0, sizeof(this->pipeFile)); - - strncpy_s(this->pipeName, name.data(), sizeof(this->pipeName)); - sprintf_s(this->pipeFile, sizeof(this->pipeFile), "\\\\.\\Pipe\\%s", this->pipeName); - } - - void Pipe::ReceiveThread(Pipe* pipe) - { - if (!pipe || pipe->type != IPCTYPE_SERVER || pipe->pipe == INVALID_HANDLE_VALUE || !pipe->pipe) return; - - if (ConnectNamedPipe(pipe->pipe, NULL) == FALSE) - { - Logger::Print("Failed to initialize pipe reading.\n"); - return; - } - - Logger::Print("Client connected to the pipe\n"); - pipe->connectCallback(); - - DWORD cbBytes; - - while (pipe->threadAttached && pipe->pipe && pipe->pipe != INVALID_HANDLE_VALUE) - { - BOOL bResult = ReadFile(pipe->pipe, &pipe->packet, sizeof(pipe->packet), &cbBytes, NULL); - - if (bResult && cbBytes) - { - if (pipe->packetCallbacks.find(pipe->packet.command) != pipe->packetCallbacks.end()) - { - pipe->packetCallbacks[pipe->packet.command](pipe->packet.buffer); - } - } - else if (pipe->threadAttached && pipe->pipe != INVALID_HANDLE_VALUE) - { - Logger::Print("Failed to read from client through pipe\n"); - - DisconnectNamedPipe(pipe->pipe); - ConnectNamedPipe(pipe->pipe, NULL); - pipe->connectCallback(); - } - - ZeroMemory(&pipe->packet, sizeof(pipe->packet)); - } - } - -#pragma endregion - - // Callback to connect first instance's client pipe to the second instance's server pipe - void IPCPipe::ConnectClient() - { - if (Singleton::IsFirstInstance()) - { - IPCPipe::ClientPipe.connect(IPC_PIPE_NAME_CLIENT); - } - } - - // Writes to the process on the other end of the pipe - bool IPCPipe::Write(std::string command, std::string data) - { - return IPCPipe::ClientPipe.write(command, data); - } - - // Installs a callback for receiving commands from the process on the other end of the pipe - void IPCPipe::On(std::string command, Utils::Slot callback) - { - IPCPipe::ServerPipe.setCallback(command, callback); - } - - IPCPipe::IPCPipe() - { - if (Dedicated::IsEnabled()) return; - - // Server pipe - IPCPipe::ServerPipe.onConnect(IPCPipe::ConnectClient); - IPCPipe::ServerPipe.create((Singleton::IsFirstInstance() ? IPC_PIPE_NAME_SERVER : IPC_PIPE_NAME_CLIENT)); - - // Connect second instance's client pipe to first instance's server pipe - if (!Singleton::IsFirstInstance()) - { - IPCPipe::ClientPipe.connect(IPC_PIPE_NAME_SERVER); - } - - IPCPipe::On("ping", [] (std::string data) - { - Logger::Print("Received ping form pipe, sending pong!\n"); - IPCPipe::Write("pong", data); - }); - - IPCPipe::On("pong", [] (std::string data) - { - Logger::Print("Received pong form pipe!\n"); - }); - - // Test pipe functionality by sending pings - Command::Add("ipcping", [] (Command::Params*) - { - Logger::Print("Sending ping to pipe!\n"); - IPCPipe::Write("ping", ""); - }); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Pipe IPCPipe::ServerPipe; + Pipe IPCPipe::ClientPipe; + +#pragma region Pipe + + Pipe::Pipe() : type(IPCTYPE_NONE), reconnectAttempt(0), pipe(INVALID_HANDLE_VALUE), connectCallback(0), threadAttached(false) + { + this->destroy(); + } + + Pipe::~Pipe() + { + this->destroy(); + } + + bool Pipe::connect(std::string name) + { + this->destroy(); + + this->type = IPCTYPE_CLIENT; + this->setName(name); + + this->pipe = CreateFileA(this->pipeFile, GENERIC_READ | GENERIC_WRITE, 0, NULL, OPEN_EXISTING, 0, NULL); + + if (INVALID_HANDLE_VALUE == this->pipe) + { + Logger::Print("Failed to connect to the pipe\n"); + + if (this->reconnectAttempt < IPC_MAX_RECONNECTS) + { + Logger::Print("Attempting to reconnect to the pipe.\n"); + ++this->reconnectAttempt; + std::this_thread::sleep_for(500ms); + + return this->connect(name); + } + else + { + this->destroy(); + return false; + } + } + + this->reconnectAttempt = 0; + Logger::Print("Successfully connected to the pipe\n"); + + return true; + } + + bool Pipe::create(std::string name) + { + this->destroy(); + + this->type = IPCTYPE_SERVER; + this->setName(name); + + this->pipe = CreateNamedPipeA(this->pipeFile, PIPE_ACCESS_DUPLEX, PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT, PIPE_UNLIMITED_INSTANCES, sizeof(this->packet), sizeof(this->packet), NMPWAIT_USE_DEFAULT_WAIT, NULL); + + if (INVALID_HANDLE_VALUE != this->pipe && this->pipe) + { + // Only create the thread, when not performing unit tests! + if (!Loader::PerformingUnitTests()) + { + this->threadAttached = true; + this->thread = std::thread(Pipe::ReceiveThread, this); + } + + Logger::Print("Pipe successfully created\n"); + return true; + } + + Logger::Print("Failed to create the pipe\n"); + this->destroy(); + return false; + } + + void Pipe::onConnect(Pipe::Callback callback) + { + this->connectCallback = callback; + } + + void Pipe::setCallback(std::string command, Utils::Slot callback) + { + this->packetCallbacks[command] = callback; + } + + bool Pipe::write(std::string command, std::string data) + { + if (this->type != IPCTYPE_CLIENT || this->pipe == INVALID_HANDLE_VALUE) return false; + + Pipe::Packet _packet; + strcpy_s(_packet.command, command.data()); + strcpy_s(_packet.buffer, data.data()); + + DWORD cbBytes; + return (WriteFile(this->pipe, &_packet, sizeof(_packet), &cbBytes, NULL) || GetLastError() == ERROR_IO_PENDING); + } + + void Pipe::destroy() + { + //this->Type = IPCTYPE_NONE; + + //*this->PipeFile = 0; + //*this->PipeName = 0; + + if (this->pipe && INVALID_HANDLE_VALUE != this->pipe) + { + if (this->type == IPCTYPE_SERVER) DisconnectNamedPipe(this->pipe); + + CloseHandle(this->pipe); + Logger::Print("Disconnected from the pipe.\n"); + } + + this->threadAttached = false; + + if (this->thread.joinable()) + { + Logger::Print("Terminating pipe thread...\n"); + + this->thread.join(); + + Logger::Print("Pipe thread terminated.\n"); + } + } + + void Pipe::setName(std::string name) + { + memset(this->pipeName, 0, sizeof(this->pipeName)); + memset(this->pipeFile, 0, sizeof(this->pipeFile)); + + strncpy_s(this->pipeName, name.data(), sizeof(this->pipeName)); + sprintf_s(this->pipeFile, sizeof(this->pipeFile), "\\\\.\\Pipe\\%s", this->pipeName); + } + + void Pipe::ReceiveThread(Pipe* pipe) + { + if (!pipe || pipe->type != IPCTYPE_SERVER || pipe->pipe == INVALID_HANDLE_VALUE || !pipe->pipe) return; + + if (ConnectNamedPipe(pipe->pipe, NULL) == FALSE) + { + Logger::Print("Failed to initialize pipe reading.\n"); + return; + } + + Logger::Print("Client connected to the pipe\n"); + pipe->connectCallback(); + + DWORD cbBytes; + + while (pipe->threadAttached && pipe->pipe && pipe->pipe != INVALID_HANDLE_VALUE) + { + BOOL bResult = ReadFile(pipe->pipe, &pipe->packet, sizeof(pipe->packet), &cbBytes, NULL); + + if (bResult && cbBytes) + { + if (pipe->packetCallbacks.find(pipe->packet.command) != pipe->packetCallbacks.end()) + { + pipe->packetCallbacks[pipe->packet.command](pipe->packet.buffer); + } + } + else if (pipe->threadAttached && pipe->pipe != INVALID_HANDLE_VALUE) + { + Logger::Print("Failed to read from client through pipe\n"); + + DisconnectNamedPipe(pipe->pipe); + ConnectNamedPipe(pipe->pipe, NULL); + pipe->connectCallback(); + } + + ZeroMemory(&pipe->packet, sizeof(pipe->packet)); + } + } + +#pragma endregion + + // Callback to connect first instance's client pipe to the second instance's server pipe + void IPCPipe::ConnectClient() + { + if (Singleton::IsFirstInstance()) + { + IPCPipe::ClientPipe.connect(IPC_PIPE_NAME_CLIENT); + } + } + + // Writes to the process on the other end of the pipe + bool IPCPipe::Write(std::string command, std::string data) + { + return IPCPipe::ClientPipe.write(command, data); + } + + // Installs a callback for receiving commands from the process on the other end of the pipe + void IPCPipe::On(std::string command, Utils::Slot callback) + { + IPCPipe::ServerPipe.setCallback(command, callback); + } + + IPCPipe::IPCPipe() + { + if (Dedicated::IsEnabled()) return; + + // Server pipe + IPCPipe::ServerPipe.onConnect(IPCPipe::ConnectClient); + IPCPipe::ServerPipe.create((Singleton::IsFirstInstance() ? IPC_PIPE_NAME_SERVER : IPC_PIPE_NAME_CLIENT)); + + // Connect second instance's client pipe to first instance's server pipe + if (!Singleton::IsFirstInstance()) + { + IPCPipe::ClientPipe.connect(IPC_PIPE_NAME_SERVER); + } + + IPCPipe::On("ping", [] (std::string data) + { + Logger::Print("Received ping form pipe, sending pong!\n"); + IPCPipe::Write("pong", data); + }); + + IPCPipe::On("pong", [] (std::string data) + { + Logger::Print("Received pong form pipe!\n"); + }); + + // Test pipe functionality by sending pings + Command::Add("ipcping", [] (Command::Params*) + { + Logger::Print("Sending ping to pipe!\n"); + IPCPipe::Write("ping", ""); + }); + } +} diff --git a/src/Components/Modules/IPCPipe.hpp b/src/Components/Modules/IPCPipe.hpp index 0ee99966..150b5d5d 100644 --- a/src/Components/Modules/IPCPipe.hpp +++ b/src/Components/Modules/IPCPipe.hpp @@ -1,78 +1,78 @@ -#define IPC_MAX_RECONNECTS 3 -#define IPC_COMMAND_SIZE 100 -#define IPC_BUFFER_SIZE 0x2000 - -#define IPC_PIPE_NAME_SERVER "IW4x-Server" -#define IPC_PIPE_NAME_CLIENT "IW4x-Client" - -namespace Components -{ - class Pipe - { - public: - struct Packet - { - char command[IPC_COMMAND_SIZE]; - char buffer[IPC_BUFFER_SIZE]; - }; - - enum Type - { - IPCTYPE_NONE, - IPCTYPE_SERVER, - IPCTYPE_CLIENT - }; - - typedef void(__cdecl PacketCallback)(std::string data); - typedef void(__cdecl Callback)(); - - Pipe(); - ~Pipe(); - - bool connect(std::string name); - bool create(std::string name); - - bool write(std::string command, std::string data); - void setCallback(std::string command, Utils::Slot callback); - void onConnect(Callback callback); - - private: - Utils::Slot connectCallback; - std::map> packetCallbacks; - - HANDLE pipe; - std::thread thread; - bool threadAttached; - - Type type; - Packet packet; - - char pipeName[MAX_PATH]; - char pipeFile[MAX_PATH]; - unsigned int reconnectAttempt; - - void destroy(); - void setName(std::string name); - - static void ReceiveThread(Pipe* pipe); - }; - - class IPCPipe : public Component - { - public: - IPCPipe(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "IPCPipe"; }; -#endif - - static bool Write(std::string command, std::string data); - static void On(std::string command, Utils::Slot callback); - - private: - static Pipe ServerPipe; - static Pipe ClientPipe; - - static void ConnectClient(); - }; -} +#define IPC_MAX_RECONNECTS 3 +#define IPC_COMMAND_SIZE 100 +#define IPC_BUFFER_SIZE 0x2000 + +#define IPC_PIPE_NAME_SERVER "IW4x-Server" +#define IPC_PIPE_NAME_CLIENT "IW4x-Client" + +namespace Components +{ + class Pipe + { + public: + struct Packet + { + char command[IPC_COMMAND_SIZE]; + char buffer[IPC_BUFFER_SIZE]; + }; + + enum Type + { + IPCTYPE_NONE, + IPCTYPE_SERVER, + IPCTYPE_CLIENT + }; + + typedef void(__cdecl PacketCallback)(std::string data); + typedef void(__cdecl Callback)(); + + Pipe(); + ~Pipe(); + + bool connect(std::string name); + bool create(std::string name); + + bool write(std::string command, std::string data); + void setCallback(std::string command, Utils::Slot callback); + void onConnect(Callback callback); + + private: + Utils::Slot connectCallback; + std::map> packetCallbacks; + + HANDLE pipe; + std::thread thread; + bool threadAttached; + + Type type; + Packet packet; + + char pipeName[MAX_PATH]; + char pipeFile[MAX_PATH]; + unsigned int reconnectAttempt; + + void destroy(); + void setName(std::string name); + + static void ReceiveThread(Pipe* pipe); + }; + + class IPCPipe : public Component + { + public: + IPCPipe(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "IPCPipe"; }; +#endif + + static bool Write(std::string command, std::string data); + static void On(std::string command, Utils::Slot callback); + + private: + static Pipe ServerPipe; + static Pipe ClientPipe; + + static void ConnectClient(); + }; +} diff --git a/src/Components/Modules/Lean.cpp b/src/Components/Modules/Lean.cpp index f65a49e8..1e708de9 100644 --- a/src/Components/Modules/Lean.cpp +++ b/src/Components/Modules/Lean.cpp @@ -1,69 +1,69 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Game::kbutton_t Lean::in_leanleft; - Game::kbutton_t Lean::in_leanright; - - void Lean::IN_LeanLeft_Up() - { - Game::IN_KeyUp(&Lean::in_leanleft); - } - - void Lean::IN_LeanLeft_Down() - { - Game::IN_KeyDown(&Lean::in_leanleft); - } - - void Lean::IN_LeanRight_Up() - { - Game::IN_KeyUp(&Lean::in_leanright); - } - - void Lean::IN_LeanRight_Down() - { - Game::IN_KeyDown(&Lean::in_leanright); - } - - void Lean::SetLeanFlags(Game::usercmd_s* cmds) - { - if (Lean::in_leanleft.active || Lean::in_leanleft.wasPressed) - { - cmds->buttons |= BUTTON_FLAG_LEANLEFT; - } - - if (Lean::in_leanright.active || Lean::in_leanright.wasPressed) - { - cmds->buttons |= BUTTON_FLAG_LEANRIGHT; - } - - Lean::in_leanleft.wasPressed = 0; - Lean::in_leanright.wasPressed = 0; - } - - void __declspec(naked) Lean::CL_CmdButtonsStub() - { - __asm - { - // CL_CmdButtons - mov ecx, 5A6510h - call ecx - - push esi - call Lean::SetLeanFlags - pop esi - retn - } - } - - Lean::Lean() - { - Command::AddRaw("+leanleft", Lean::IN_LeanLeft_Down, true); - Command::AddRaw("-leanleft", Lean::IN_LeanLeft_Up, true); - - Command::AddRaw("+leanright", Lean::IN_LeanRight_Down, true); - Command::AddRaw("-leanright", Lean::IN_LeanRight_Up, true); - - Utils::Hook(0x5A6D84, Lean::CL_CmdButtonsStub, HOOK_CALL).install()->quick(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Game::kbutton_t Lean::in_leanleft; + Game::kbutton_t Lean::in_leanright; + + void Lean::IN_LeanLeft_Up() + { + Game::IN_KeyUp(&Lean::in_leanleft); + } + + void Lean::IN_LeanLeft_Down() + { + Game::IN_KeyDown(&Lean::in_leanleft); + } + + void Lean::IN_LeanRight_Up() + { + Game::IN_KeyUp(&Lean::in_leanright); + } + + void Lean::IN_LeanRight_Down() + { + Game::IN_KeyDown(&Lean::in_leanright); + } + + void Lean::SetLeanFlags(Game::usercmd_s* cmds) + { + if (Lean::in_leanleft.active || Lean::in_leanleft.wasPressed) + { + cmds->buttons |= BUTTON_FLAG_LEANLEFT; + } + + if (Lean::in_leanright.active || Lean::in_leanright.wasPressed) + { + cmds->buttons |= BUTTON_FLAG_LEANRIGHT; + } + + Lean::in_leanleft.wasPressed = 0; + Lean::in_leanright.wasPressed = 0; + } + + void __declspec(naked) Lean::CL_CmdButtonsStub() + { + __asm + { + // CL_CmdButtons + mov ecx, 5A6510h + call ecx + + push esi + call Lean::SetLeanFlags + pop esi + retn + } + } + + Lean::Lean() + { + Command::AddRaw("+leanleft", Lean::IN_LeanLeft_Down, true); + Command::AddRaw("-leanleft", Lean::IN_LeanLeft_Up, true); + + Command::AddRaw("+leanright", Lean::IN_LeanRight_Down, true); + Command::AddRaw("-leanright", Lean::IN_LeanRight_Up, true); + + Utils::Hook(0x5A6D84, Lean::CL_CmdButtonsStub, HOOK_CALL).install()->quick(); + } +} diff --git a/src/Components/Modules/Lean.hpp b/src/Components/Modules/Lean.hpp index 696b75b3..5e94a161 100644 --- a/src/Components/Modules/Lean.hpp +++ b/src/Components/Modules/Lean.hpp @@ -1,28 +1,28 @@ -#define BUTTON_FLAG_LEANLEFT 0x40 -#define BUTTON_FLAG_LEANRIGHT 0x80 - -namespace Components -{ - class Lean : public Component - { - public: - Lean(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Lean"; }; -#endif - - private: - static Game::kbutton_t in_leanleft; - static Game::kbutton_t in_leanright; - - static void IN_LeanLeft_Up(); - static void IN_LeanLeft_Down(); - - static void IN_LeanRight_Up(); - static void IN_LeanRight_Down(); - - static void CL_CmdButtonsStub(); - static void SetLeanFlags(Game::usercmd_s* cmds); - }; -} +#define BUTTON_FLAG_LEANLEFT 0x40 +#define BUTTON_FLAG_LEANRIGHT 0x80 + +namespace Components +{ + class Lean : public Component + { + public: + Lean(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Lean"; }; +#endif + + private: + static Game::kbutton_t in_leanleft; + static Game::kbutton_t in_leanright; + + static void IN_LeanLeft_Up(); + static void IN_LeanLeft_Down(); + + static void IN_LeanRight_Up(); + static void IN_LeanRight_Down(); + + static void CL_CmdButtonsStub(); + static void SetLeanFlags(Game::usercmd_s* cmds); + }; +} diff --git a/src/Components/Modules/Localization.cpp b/src/Components/Modules/Localization.cpp index dc87aa74..4b6d897b 100644 --- a/src/Components/Modules/Localization.cpp +++ b/src/Components/Modules/Localization.cpp @@ -1,200 +1,200 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::mutex Localization::LocalizeMutex; - Dvar::Var Localization::UseLocalization; - Utils::Memory::Allocator Localization::MemAllocator; - std::unordered_map Localization::LocalizeMap; - std::unordered_map Localization::TempLocalizeMap; - - void Localization::Set(std::string key, std::string value) - { - std::lock_guard _(Localization::LocalizeMutex); - - if (Localization::LocalizeMap.find(key) != Localization::LocalizeMap.end()) - { - Game::LocalizeEntry* entry = Localization::LocalizeMap[key]; - - char* newStaticValue = Localization::MemAllocator.duplicateString(value); - if (!newStaticValue) return; - if (entry->value) Localization::MemAllocator.free(entry->value); - entry->value = newStaticValue; - return; - } - - Game::LocalizeEntry* entry = Localization::MemAllocator.allocate(); - if (!entry) return; - - entry->name = Localization::MemAllocator.duplicateString(key); - if (!entry->name) - { - Localization::MemAllocator.free(entry); - return; - } - - entry->value = Localization::MemAllocator.duplicateString(value); - if (!entry->value) - { - Localization::MemAllocator.free(entry->name); - Localization::MemAllocator.free(entry); - return; - } - - Localization::LocalizeMap[key] = entry; - } - - const char* Localization::Get(const char* key) - { - if (!Localization::UseLocalization.get()) return key; - - Game::LocalizeEntry* entry = nullptr; - std::lock_guard _(Localization::LocalizeMutex); - - if (Localization::TempLocalizeMap.find(key) != Localization::TempLocalizeMap.end()) - { - entry = Localization::TempLocalizeMap[key]; - } - else if (Localization::LocalizeMap.find(key) != Localization::LocalizeMap.end()) - { - entry = Localization::LocalizeMap[key]; - } - - if (!entry || !entry->value) - { - Localization::LocalizeMutex.unlock(); - entry = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, key).localize; - Localization::LocalizeMutex.lock(); - } - - if (entry && entry->value) - { - return entry->value; - } - - return key; - } - - void Localization::SetTemp(std::string key, std::string value) - { - std::lock_guard _(Localization::LocalizeMutex); - - if (Localization::TempLocalizeMap.find(key) != Localization::TempLocalizeMap.end()) - { - Game::LocalizeEntry* entry = Localization::TempLocalizeMap[key]; - if(entry->value) Localization::MemAllocator.free(entry->value); - entry->value = Localization::MemAllocator.duplicateString(value); - } - else - { - Game::LocalizeEntry* entry = Localization::MemAllocator.allocate(); - if (!entry) return; - - entry->name = Localization::MemAllocator.duplicateString(key); - if (!entry->name) - { - Localization::MemAllocator.free(entry); - return; - } - - entry->value = Localization::MemAllocator.duplicateString(value); - if (!entry->value) - { - Localization::MemAllocator.free(entry->name); - Localization::MemAllocator.free(entry); - return; - } - - Localization::TempLocalizeMap[key] = entry; - } - } - - void Localization::ClearTemp() - { - std::lock_guard _(Localization::LocalizeMutex); - - for (auto i = Localization::TempLocalizeMap.begin(); i != Localization::TempLocalizeMap.end(); ++i) - { - if (i->second) - { - if (i->second->name) Localization::MemAllocator.free(i->second->name); - if (i->second->value) Localization::MemAllocator.free(i->second->value); - Localization::MemAllocator.free(i->second); - } - } - - Localization::TempLocalizeMap.clear(); - } - - void __stdcall Localization::SetStringStub(const char* key, const char* value, bool /*isEnglish*/) - { - Localization::Set(key, value); - } - - void Localization::LoadLanguageStrings() - { - //if (ZoneBuilder::IsEnabled()) - { - if (FileSystem::File(Utils::String::VA("localizedstrings/iw4x_%s.str", Game::Win_GetLanguage())).exists()) - { - Game::SE_Load(Utils::String::VA("localizedstrings/iw4x_%s.str", Game::Win_GetLanguage()), 0); - } - else if (FileSystem::File("localizedstrings/iw4x_english.str").exists()) - { - Game::SE_Load("localizedstrings/iw4x_english.str", 0); - } - } - } - - __declspec(naked) void Localization::SELoadLanguageStub() - { - __asm - { - pushad - call Localization::LoadLanguageStrings - popad - - push 629E20h - retn - } - } - - Localization::Localization() - { - AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, [] (Game::XAssetType, std::string filename) - { - Game::XAssetHeader header = { 0 }; - std::lock_guard _(Localization::LocalizeMutex); - - if (Localization::TempLocalizeMap.find(filename) != Localization::TempLocalizeMap.end()) - { - header.localize = Localization::TempLocalizeMap[filename]; - } - else if (Localization::LocalizeMap.find(filename) != Localization::LocalizeMap.end()) - { - header.localize = Localization::LocalizeMap[filename]; - } - - return header; - }); - - // Resolving hook - Utils::Hook(0x629B90, Localization::Get, HOOK_JUMP).install()->quick(); - - // Set loading entry point - Utils::Hook(0x41D859, Localization::SELoadLanguageStub, HOOK_CALL).install()->quick(); - - // Overwrite SetString - Utils::Hook(0x4CE5EE, Localization::SetStringStub, HOOK_CALL).install()->quick(); - - Localization::UseLocalization = Dvar::Register("ui_localize", true, Game::dvar_flag::DVAR_FLAG_NONE, "Use localization strings"); - } - - Localization::~Localization() - { - Localization::ClearTemp(); - - Localization::LocalizeMap.clear(); - Localization::MemAllocator.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::mutex Localization::LocalizeMutex; + Dvar::Var Localization::UseLocalization; + Utils::Memory::Allocator Localization::MemAllocator; + std::unordered_map Localization::LocalizeMap; + std::unordered_map Localization::TempLocalizeMap; + + void Localization::Set(std::string key, std::string value) + { + std::lock_guard _(Localization::LocalizeMutex); + + if (Localization::LocalizeMap.find(key) != Localization::LocalizeMap.end()) + { + Game::LocalizeEntry* entry = Localization::LocalizeMap[key]; + + char* newStaticValue = Localization::MemAllocator.duplicateString(value); + if (!newStaticValue) return; + if (entry->value) Localization::MemAllocator.free(entry->value); + entry->value = newStaticValue; + return; + } + + Game::LocalizeEntry* entry = Localization::MemAllocator.allocate(); + if (!entry) return; + + entry->name = Localization::MemAllocator.duplicateString(key); + if (!entry->name) + { + Localization::MemAllocator.free(entry); + return; + } + + entry->value = Localization::MemAllocator.duplicateString(value); + if (!entry->value) + { + Localization::MemAllocator.free(entry->name); + Localization::MemAllocator.free(entry); + return; + } + + Localization::LocalizeMap[key] = entry; + } + + const char* Localization::Get(const char* key) + { + if (!Localization::UseLocalization.get()) return key; + + Game::LocalizeEntry* entry = nullptr; + std::lock_guard _(Localization::LocalizeMutex); + + if (Localization::TempLocalizeMap.find(key) != Localization::TempLocalizeMap.end()) + { + entry = Localization::TempLocalizeMap[key]; + } + else if (Localization::LocalizeMap.find(key) != Localization::LocalizeMap.end()) + { + entry = Localization::LocalizeMap[key]; + } + + if (!entry || !entry->value) + { + Localization::LocalizeMutex.unlock(); + entry = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, key).localize; + Localization::LocalizeMutex.lock(); + } + + if (entry && entry->value) + { + return entry->value; + } + + return key; + } + + void Localization::SetTemp(std::string key, std::string value) + { + std::lock_guard _(Localization::LocalizeMutex); + + if (Localization::TempLocalizeMap.find(key) != Localization::TempLocalizeMap.end()) + { + Game::LocalizeEntry* entry = Localization::TempLocalizeMap[key]; + if(entry->value) Localization::MemAllocator.free(entry->value); + entry->value = Localization::MemAllocator.duplicateString(value); + } + else + { + Game::LocalizeEntry* entry = Localization::MemAllocator.allocate(); + if (!entry) return; + + entry->name = Localization::MemAllocator.duplicateString(key); + if (!entry->name) + { + Localization::MemAllocator.free(entry); + return; + } + + entry->value = Localization::MemAllocator.duplicateString(value); + if (!entry->value) + { + Localization::MemAllocator.free(entry->name); + Localization::MemAllocator.free(entry); + return; + } + + Localization::TempLocalizeMap[key] = entry; + } + } + + void Localization::ClearTemp() + { + std::lock_guard _(Localization::LocalizeMutex); + + for (auto i = Localization::TempLocalizeMap.begin(); i != Localization::TempLocalizeMap.end(); ++i) + { + if (i->second) + { + if (i->second->name) Localization::MemAllocator.free(i->second->name); + if (i->second->value) Localization::MemAllocator.free(i->second->value); + Localization::MemAllocator.free(i->second); + } + } + + Localization::TempLocalizeMap.clear(); + } + + void __stdcall Localization::SetStringStub(const char* key, const char* value, bool /*isEnglish*/) + { + Localization::Set(key, value); + } + + void Localization::LoadLanguageStrings() + { + //if (ZoneBuilder::IsEnabled()) + { + if (FileSystem::File(Utils::String::VA("localizedstrings/iw4x_%s.str", Game::Win_GetLanguage())).exists()) + { + Game::SE_Load(Utils::String::VA("localizedstrings/iw4x_%s.str", Game::Win_GetLanguage()), 0); + } + else if (FileSystem::File("localizedstrings/iw4x_english.str").exists()) + { + Game::SE_Load("localizedstrings/iw4x_english.str", 0); + } + } + } + + __declspec(naked) void Localization::SELoadLanguageStub() + { + __asm + { + pushad + call Localization::LoadLanguageStrings + popad + + push 629E20h + retn + } + } + + Localization::Localization() + { + AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, [] (Game::XAssetType, std::string filename) + { + Game::XAssetHeader header = { 0 }; + std::lock_guard _(Localization::LocalizeMutex); + + if (Localization::TempLocalizeMap.find(filename) != Localization::TempLocalizeMap.end()) + { + header.localize = Localization::TempLocalizeMap[filename]; + } + else if (Localization::LocalizeMap.find(filename) != Localization::LocalizeMap.end()) + { + header.localize = Localization::LocalizeMap[filename]; + } + + return header; + }); + + // Resolving hook + Utils::Hook(0x629B90, Localization::Get, HOOK_JUMP).install()->quick(); + + // Set loading entry point + Utils::Hook(0x41D859, Localization::SELoadLanguageStub, HOOK_CALL).install()->quick(); + + // Overwrite SetString + Utils::Hook(0x4CE5EE, Localization::SetStringStub, HOOK_CALL).install()->quick(); + + Localization::UseLocalization = Dvar::Register("ui_localize", true, Game::dvar_flag::DVAR_FLAG_NONE, "Use localization strings"); + } + + Localization::~Localization() + { + Localization::ClearTemp(); + + Localization::LocalizeMap.clear(); + Localization::MemAllocator.clear(); + } +} diff --git a/src/Components/Modules/Localization.hpp b/src/Components/Modules/Localization.hpp index a67a3dfe..488b91b7 100644 --- a/src/Components/Modules/Localization.hpp +++ b/src/Components/Modules/Localization.hpp @@ -1,30 +1,30 @@ -namespace Components -{ - class Localization : public Component - { - public: - Localization(); - ~Localization(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Localization"; }; -#endif - - static void Set(std::string key, std::string value); - static const char* Get(const char* key); - - static void SetTemp(std::string key, std::string value); - static void ClearTemp(); - - private: - static std::mutex LocalizeMutex; - static Utils::Memory::Allocator MemAllocator; - static std::unordered_map LocalizeMap; - static std::unordered_map TempLocalizeMap; - static Dvar::Var UseLocalization; - - static void __stdcall SetStringStub(const char* key, const char* value, bool isEnglish); - static void LoadLanguageStrings(); - static void SELoadLanguageStub(); - }; -} +namespace Components +{ + class Localization : public Component + { + public: + Localization(); + ~Localization(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Localization"; }; +#endif + + static void Set(std::string key, std::string value); + static const char* Get(const char* key); + + static void SetTemp(std::string key, std::string value); + static void ClearTemp(); + + private: + static std::mutex LocalizeMutex; + static Utils::Memory::Allocator MemAllocator; + static std::unordered_map LocalizeMap; + static std::unordered_map TempLocalizeMap; + static Dvar::Var UseLocalization; + + static void __stdcall SetStringStub(const char* key, const char* value, bool isEnglish); + static void LoadLanguageStrings(); + static void SELoadLanguageStub(); + }; +} diff --git a/src/Components/Modules/Logger.cpp b/src/Components/Modules/Logger.cpp index 82b5c740..dbc6ab90 100644 --- a/src/Components/Modules/Logger.cpp +++ b/src/Components/Modules/Logger.cpp @@ -1,325 +1,325 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::mutex Logger::MessageMutex; - std::vector Logger::MessageQueue; - std::vector Logger::LoggingAddresses[2]; - void(*Logger::PipeCallback)(std::string) = nullptr; - - bool Logger::IsConsoleReady() - { - return (IsWindow(*reinterpret_cast(0x64A3288)) != FALSE || (Dedicated::IsEnabled() && !Flags::HasFlag("console"))); - } - - void Logger::PrintStub(int channel, const char* message, ...) - { - return Logger::MessagePrint(channel, Logger::Format(&message)); - } - - void Logger::Print(const char* message, ...) - { - return Logger::MessagePrint(0, Logger::Format(&message)); - } - - void Logger::Print(int channel, const char* message, ...) - { - return Logger::MessagePrint(channel, Logger::Format(&message)); - } - - void Logger::MessagePrint(int channel, std::string message) - { - if (Flags::HasFlag("stdout") || Loader::PerformingUnitTests()) - { - printf("%s", message.data()); - fflush(stdout); - return; - } - - if (!Logger::IsConsoleReady()) - { - OutputDebugStringA(message.data()); - } - - if (!Game::Sys_IsMainThread()) - { - Logger::EnqueueMessage(message); - } - else - { - Game::Com_PrintMessage(channel, message.data(), 0); - } - } - - void Logger::ErrorPrint(int error, std::string message) - { - return Game::Com_Error(error, "%s", message.data()); - } - - void Logger::Error(int error, const char* message, ...) - { - return Logger::ErrorPrint(error, Logger::Format(&message)); - } - - void Logger::Error(const char* message, ...) - { - return Logger::ErrorPrint(0, Logger::Format(&message)); - } - - void Logger::SoftError(const char* message, ...) - { - return Logger::ErrorPrint(2, Logger::Format(&message)); - } - - std::string Logger::Format(const char** message) - { - char buffer[0x1000] = { 0 }; - - va_list ap = reinterpret_cast(const_cast(&message[1])); - //va_start(ap, *message); - _vsnprintf_s(buffer, sizeof(buffer), *message, ap); - va_end(ap); - - return buffer; - } - - void Logger::Flush() - { -// if (!Game::Sys_IsMainThread()) -// { -// while (!Logger::MessageQueue.empty()) -// { -// std::this_thread::sleep_for(10ms); -// } -// } -// else - { - Logger::Frame(); - } - } - - void Logger::Frame() - { - std::lock_guard _(Logger::MessageMutex); - - for (unsigned int i = 0; i < Logger::MessageQueue.size(); ++i) - { - Game::Com_PrintMessage(0, Logger::MessageQueue[i].data(), 0); - - if (!Logger::IsConsoleReady()) - { - OutputDebugStringA(Logger::MessageQueue[i].data()); - } - } - - Logger::MessageQueue.clear(); - } - - void Logger::PipeOutput(void(*callback)(std::string)) - { - Logger::PipeCallback = callback; - } - - void Logger::PrintMessagePipe(const char* data) - { - if (Logger::PipeCallback) - { - Logger::PipeCallback(data); - } - } - - void Logger::NetworkLog(const char* data, bool gLog) - { - if (!data) return; - - std::string buffer(data); - for (auto& addr : Logger::LoggingAddresses[gLog & 1]) - { - Network::SendCommand(addr, "print", buffer); - } - } - - __declspec(naked) void Logger::GameLogStub() - { - __asm - { - push 1 - push [esp + 8h] - call Logger::NetworkLog - add esp, 8h - - mov eax, 4576C0h - jmp eax - } - } - - __declspec(naked) void Logger::PrintMessageStub() - { - __asm - { - mov eax, Logger::PipeCallback - test eax, eax - jz returnPrint - - push [esp + 8h] - call Logger::PrintMessagePipe - add esp, 4h - retn - - returnPrint: - push 0 - push [esp + 0Ch] - call Logger::NetworkLog - add esp, 8h - - push esi - mov esi, [esp + 0Ch] - - mov eax, 4AA835h - jmp eax - } - } - - void Logger::EnqueueMessage(std::string message) - { - Logger::MessageMutex.lock(); - Logger::MessageQueue.push_back(message); - Logger::MessageMutex.unlock(); - } - - Logger::Logger() - { - Logger::PipeOutput(nullptr); - - QuickPatch::OnFrame(Logger::Frame); - - Utils::Hook(0x4B0218, Logger::GameLogStub, HOOK_CALL).install()->quick(); - Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessageStub, HOOK_JUMP).install()->quick(); - - if (Loader::PerformingUnitTests()) - { - Utils::Hook(Game::Com_Printf, Logger::PrintStub, HOOK_JUMP).install()->quick(); - } - - Dvar::OnInit([] () - { - Command::AddSV("log_add", [] (Command::Params* params) - { - if (params->length() < 2) return; - - Network::Address addr(params->get(1)); - - if (std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr) == Logger::LoggingAddresses[0].end()) - { - Logger::LoggingAddresses[0].push_back(addr); - } - }); - - Command::AddSV("log_del", [] (Command::Params* params) - { - if (params->length() < 2) return; - - int num = atoi(params->get(1)); - if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast(num) < Logger::LoggingAddresses[0].size()) - { - auto addr = Logger::LoggingAddresses[0].begin() + num; - Logger::Print("Address %s removed\n", addr->getCString()); - Logger::LoggingAddresses[0].erase(addr); - } - else - { - Network::Address addr(params->get(1)); - - auto i = std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr); - if (i != Logger::LoggingAddresses[0].end()) - { - Logger::LoggingAddresses[0].erase(i); - Logger::Print("Address %s removed\n", addr.getCString()); - } - else - { - Logger::Print("Address %s not found!\n", addr.getCString()); - } - } - }); - - Command::AddSV("log_list", [] (Command::Params*) - { - Logger::Print("# ID: Address\n"); - Logger::Print("-------------\n"); - - for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i) - { - Logger::Print("#%03d: %5s\n", i, Logger::LoggingAddresses[0][i].getCString()); - } - }); - - Command::AddSV("g_log_add", [] (Command::Params* params) - { - if (params->length() < 2) return; - - Network::Address addr(params->get(1)); - - if (std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr) == Logger::LoggingAddresses[1].end()) - { - Logger::LoggingAddresses[1].push_back(addr); - } - }); - - Command::AddSV("g_log_del", [] (Command::Params* params) - { - if (params->length() < 2) return; - - int num = atoi(params->get(1)); - if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast(num) < Logger::LoggingAddresses[1].size()) - { - auto addr = Logger::LoggingAddresses[1].begin() + num; - Logger::Print("Address %s removed\n", addr->getCString()); - Logger::LoggingAddresses[1].erase(addr); - } - else - { - Network::Address addr(params->get(1)); - - auto i = std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr); - if (i != Logger::LoggingAddresses[1].end()) - { - Logger::LoggingAddresses[1].erase(i); - Logger::Print("Address %s removed\n", addr.getCString()); - } - else - { - Logger::Print("Address %s not found!\n", addr.getCString()); - } - } - }); - - Command::AddSV("g_log_list", [] (Command::Params*) - { - Logger::Print("# ID: Address\n"); - Logger::Print("-------------\n"); - - for (unsigned int i = 0; i < Logger::LoggingAddresses[1].size(); ++i) - { - Logger::Print("#%03d: %5s\n", i, Logger::LoggingAddresses[1][i].getCString()); - } - }); - }); - } - - Logger::~Logger() - { - Logger::LoggingAddresses[0].clear(); - Logger::LoggingAddresses[1].clear(); - - Logger::MessageMutex.lock(); - Logger::MessageQueue.clear(); - Logger::MessageMutex.unlock(); - - // Flush the console log - if (int fh = *reinterpret_cast(0x1AD8F28)) - { - Game::FS_FCloseFile(fh); - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::mutex Logger::MessageMutex; + std::vector Logger::MessageQueue; + std::vector Logger::LoggingAddresses[2]; + void(*Logger::PipeCallback)(std::string) = nullptr; + + bool Logger::IsConsoleReady() + { + return (IsWindow(*reinterpret_cast(0x64A3288)) != FALSE || (Dedicated::IsEnabled() && !Flags::HasFlag("console"))); + } + + void Logger::PrintStub(int channel, const char* message, ...) + { + return Logger::MessagePrint(channel, Logger::Format(&message)); + } + + void Logger::Print(const char* message, ...) + { + return Logger::MessagePrint(0, Logger::Format(&message)); + } + + void Logger::Print(int channel, const char* message, ...) + { + return Logger::MessagePrint(channel, Logger::Format(&message)); + } + + void Logger::MessagePrint(int channel, std::string message) + { + if (Flags::HasFlag("stdout") || Loader::PerformingUnitTests()) + { + printf("%s", message.data()); + fflush(stdout); + return; + } + + if (!Logger::IsConsoleReady()) + { + OutputDebugStringA(message.data()); + } + + if (!Game::Sys_IsMainThread()) + { + Logger::EnqueueMessage(message); + } + else + { + Game::Com_PrintMessage(channel, message.data(), 0); + } + } + + void Logger::ErrorPrint(int error, std::string message) + { + return Game::Com_Error(error, "%s", message.data()); + } + + void Logger::Error(int error, const char* message, ...) + { + return Logger::ErrorPrint(error, Logger::Format(&message)); + } + + void Logger::Error(const char* message, ...) + { + return Logger::ErrorPrint(0, Logger::Format(&message)); + } + + void Logger::SoftError(const char* message, ...) + { + return Logger::ErrorPrint(2, Logger::Format(&message)); + } + + std::string Logger::Format(const char** message) + { + char buffer[0x1000] = { 0 }; + + va_list ap = reinterpret_cast(const_cast(&message[1])); + //va_start(ap, *message); + _vsnprintf_s(buffer, sizeof(buffer), *message, ap); + va_end(ap); + + return buffer; + } + + void Logger::Flush() + { +// if (!Game::Sys_IsMainThread()) +// { +// while (!Logger::MessageQueue.empty()) +// { +// std::this_thread::sleep_for(10ms); +// } +// } +// else + { + Logger::Frame(); + } + } + + void Logger::Frame() + { + std::lock_guard _(Logger::MessageMutex); + + for (unsigned int i = 0; i < Logger::MessageQueue.size(); ++i) + { + Game::Com_PrintMessage(0, Logger::MessageQueue[i].data(), 0); + + if (!Logger::IsConsoleReady()) + { + OutputDebugStringA(Logger::MessageQueue[i].data()); + } + } + + Logger::MessageQueue.clear(); + } + + void Logger::PipeOutput(void(*callback)(std::string)) + { + Logger::PipeCallback = callback; + } + + void Logger::PrintMessagePipe(const char* data) + { + if (Logger::PipeCallback) + { + Logger::PipeCallback(data); + } + } + + void Logger::NetworkLog(const char* data, bool gLog) + { + if (!data) return; + + std::string buffer(data); + for (auto& addr : Logger::LoggingAddresses[gLog & 1]) + { + Network::SendCommand(addr, "print", buffer); + } + } + + __declspec(naked) void Logger::GameLogStub() + { + __asm + { + push 1 + push [esp + 8h] + call Logger::NetworkLog + add esp, 8h + + mov eax, 4576C0h + jmp eax + } + } + + __declspec(naked) void Logger::PrintMessageStub() + { + __asm + { + mov eax, Logger::PipeCallback + test eax, eax + jz returnPrint + + push [esp + 8h] + call Logger::PrintMessagePipe + add esp, 4h + retn + + returnPrint: + push 0 + push [esp + 0Ch] + call Logger::NetworkLog + add esp, 8h + + push esi + mov esi, [esp + 0Ch] + + mov eax, 4AA835h + jmp eax + } + } + + void Logger::EnqueueMessage(std::string message) + { + Logger::MessageMutex.lock(); + Logger::MessageQueue.push_back(message); + Logger::MessageMutex.unlock(); + } + + Logger::Logger() + { + Logger::PipeOutput(nullptr); + + QuickPatch::OnFrame(Logger::Frame); + + Utils::Hook(0x4B0218, Logger::GameLogStub, HOOK_CALL).install()->quick(); + Utils::Hook(Game::Com_PrintMessage, Logger::PrintMessageStub, HOOK_JUMP).install()->quick(); + + if (Loader::PerformingUnitTests()) + { + Utils::Hook(Game::Com_Printf, Logger::PrintStub, HOOK_JUMP).install()->quick(); + } + + Dvar::OnInit([] () + { + Command::AddSV("log_add", [] (Command::Params* params) + { + if (params->length() < 2) return; + + Network::Address addr(params->get(1)); + + if (std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr) == Logger::LoggingAddresses[0].end()) + { + Logger::LoggingAddresses[0].push_back(addr); + } + }); + + Command::AddSV("log_del", [] (Command::Params* params) + { + if (params->length() < 2) return; + + int num = atoi(params->get(1)); + if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast(num) < Logger::LoggingAddresses[0].size()) + { + auto addr = Logger::LoggingAddresses[0].begin() + num; + Logger::Print("Address %s removed\n", addr->getCString()); + Logger::LoggingAddresses[0].erase(addr); + } + else + { + Network::Address addr(params->get(1)); + + auto i = std::find(Logger::LoggingAddresses[0].begin(), Logger::LoggingAddresses[0].end(), addr); + if (i != Logger::LoggingAddresses[0].end()) + { + Logger::LoggingAddresses[0].erase(i); + Logger::Print("Address %s removed\n", addr.getCString()); + } + else + { + Logger::Print("Address %s not found!\n", addr.getCString()); + } + } + }); + + Command::AddSV("log_list", [] (Command::Params*) + { + Logger::Print("# ID: Address\n"); + Logger::Print("-------------\n"); + + for (unsigned int i = 0; i < Logger::LoggingAddresses[0].size(); ++i) + { + Logger::Print("#%03d: %5s\n", i, Logger::LoggingAddresses[0][i].getCString()); + } + }); + + Command::AddSV("g_log_add", [] (Command::Params* params) + { + if (params->length() < 2) return; + + Network::Address addr(params->get(1)); + + if (std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr) == Logger::LoggingAddresses[1].end()) + { + Logger::LoggingAddresses[1].push_back(addr); + } + }); + + Command::AddSV("g_log_del", [] (Command::Params* params) + { + if (params->length() < 2) return; + + int num = atoi(params->get(1)); + if (Utils::String::VA("%i", num) == std::string(params->get(1)) && static_cast(num) < Logger::LoggingAddresses[1].size()) + { + auto addr = Logger::LoggingAddresses[1].begin() + num; + Logger::Print("Address %s removed\n", addr->getCString()); + Logger::LoggingAddresses[1].erase(addr); + } + else + { + Network::Address addr(params->get(1)); + + auto i = std::find(Logger::LoggingAddresses[1].begin(), Logger::LoggingAddresses[1].end(), addr); + if (i != Logger::LoggingAddresses[1].end()) + { + Logger::LoggingAddresses[1].erase(i); + Logger::Print("Address %s removed\n", addr.getCString()); + } + else + { + Logger::Print("Address %s not found!\n", addr.getCString()); + } + } + }); + + Command::AddSV("g_log_list", [] (Command::Params*) + { + Logger::Print("# ID: Address\n"); + Logger::Print("-------------\n"); + + for (unsigned int i = 0; i < Logger::LoggingAddresses[1].size(); ++i) + { + Logger::Print("#%03d: %5s\n", i, Logger::LoggingAddresses[1][i].getCString()); + } + }); + }); + } + + Logger::~Logger() + { + Logger::LoggingAddresses[0].clear(); + Logger::LoggingAddresses[1].clear(); + + Logger::MessageMutex.lock(); + Logger::MessageQueue.clear(); + Logger::MessageMutex.unlock(); + + // Flush the console log + if (int fh = *reinterpret_cast(0x1AD8F28)) + { + Game::FS_FCloseFile(fh); + } + } +} diff --git a/src/Components/Modules/Logger.hpp b/src/Components/Modules/Logger.hpp index 93e5dfc1..81b98c12 100644 --- a/src/Components/Modules/Logger.hpp +++ b/src/Components/Modules/Logger.hpp @@ -1,44 +1,44 @@ -namespace Components -{ - class Logger : public Component - { - public: - Logger(); - ~Logger(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Logger"; }; -#endif - - static void MessagePrint(int channel, std::string message); - static void Print(int channel, const char* message, ...); - static void Print(const char* message, ...); - static void ErrorPrint(int error, std::string message); - static void Error(const char* message, ...); - static void Error(int error, const char* message, ...); - static void SoftError(const char* message, ...); - static bool IsConsoleReady(); - - static void PrintStub(int channel, const char* message, ...); - - static void PipeOutput(void(*callback)(std::string)); - - static void Flush(); - - private: - static std::mutex MessageMutex; - static std::vector MessageQueue; - static std::vector LoggingAddresses[2]; - static void(*PipeCallback)(std::string); - - static void Frame(); - static void GameLogStub(); - static void PrintMessageStub(); - static void PrintMessagePipe(const char* data); - static void EnqueueMessage(std::string message); - - static void NetworkLog(const char* data, bool gLog); - - static std::string Format(const char** message); - }; -} +namespace Components +{ + class Logger : public Component + { + public: + Logger(); + ~Logger(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Logger"; }; +#endif + + static void MessagePrint(int channel, std::string message); + static void Print(int channel, const char* message, ...); + static void Print(const char* message, ...); + static void ErrorPrint(int error, std::string message); + static void Error(const char* message, ...); + static void Error(int error, const char* message, ...); + static void SoftError(const char* message, ...); + static bool IsConsoleReady(); + + static void PrintStub(int channel, const char* message, ...); + + static void PipeOutput(void(*callback)(std::string)); + + static void Flush(); + + private: + static std::mutex MessageMutex; + static std::vector MessageQueue; + static std::vector LoggingAddresses[2]; + static void(*PipeCallback)(std::string); + + static void Frame(); + static void GameLogStub(); + static void PrintMessageStub(); + static void PrintMessagePipe(const char* data); + static void EnqueueMessage(std::string message); + + static void NetworkLog(const char* data, bool gLog); + + static std::string Format(const char** message); + }; +} diff --git a/src/Components/Modules/Maps.cpp b/src/Components/Modules/Maps.cpp index f819eb3e..e6d8b347 100644 --- a/src/Components/Modules/Maps.cpp +++ b/src/Components/Modules/Maps.cpp @@ -1,625 +1,625 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::string Maps::CurrentMainZone; - std::vector> Maps::DependencyList; - std::vector Maps::CurrentDependencies; - - bool Maps::IsSPMap; - std::vector Maps::DlcPacks; - std::vector Maps::EntryPool; - - void Maps::LoadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) - { - if (!zoneInfo) return; - - Maps::IsSPMap = false; - Maps::CurrentMainZone = zoneInfo->name; - - Maps::CurrentDependencies.clear(); - for (auto i = Maps::DependencyList.begin(); i != Maps::DependencyList.end(); ++i) - { - if (std::regex_match(zoneInfo->name, std::regex(i->first))) - { - if (std::find(Maps::CurrentDependencies.begin(), Maps::CurrentDependencies.end(), i->second) == Maps::CurrentDependencies.end()) - { - Maps::CurrentDependencies.push_back(i->second); - } - } - } - - Utils::Memory::Allocator allocator; - auto teams = Maps::GetTeamsForMap(Maps::CurrentMainZone); - - auto dependencies = Maps::GetDependenciesForMap(Maps::CurrentMainZone); - Utils::Merge(&Maps::CurrentDependencies, dependencies.data(), dependencies.size()); - - std::vector data; - Utils::Merge(&data, zoneInfo, zoneCount); - - Game::XZoneInfo team; - team.allocFlags = zoneInfo->allocFlags; - team.freeFlags = zoneInfo->freeFlags; - - team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.first.data())); - data.push_back(team); - - team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.second.data())); - data.push_back(team); - - for (unsigned int i = 0; i < Maps::CurrentDependencies.size(); ++i) - { - Game::XZoneInfo info; - - info.name = (&Maps::CurrentDependencies[i])->data(); - info.allocFlags = zoneInfo->allocFlags; - info.freeFlags = zoneInfo->freeFlags; - - data.push_back(info); - } - - // Load patch files - std::string patchZone = Utils::String::VA("patch_%s", zoneInfo->name); - if (FastFiles::Exists(patchZone)) - { - data.push_back({ patchZone.data(), zoneInfo->allocFlags, zoneInfo->freeFlags }); - } - - return FastFiles::LoadLocalizeZones(data.data(), data.size(), sync); - } - - void Maps::OverrideMapEnts(Game::MapEnts* ents) - { - auto callback = [] (Game::XAssetHeader header, void* ents) - { - Game::MapEnts* mapEnts = reinterpret_cast(ents); - Game::clipMap_t* clipMap = header.clipMap; - - if (clipMap && mapEnts && !_stricmp(mapEnts->name, clipMap->name)) - { - clipMap->mapEnts = mapEnts; - //*Game::marMapEntsPtr = mapEnts; - //Game::G_SpawnEntitiesFromString(); - } - }; - - // Internal doesn't lock the thread, as locking is impossible, due to executing this in the thread that holds the current lock - Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_CLIPMAP_PVS, callback, ents, true); - Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_CLIPMAP, callback, ents, true); - } - - void Maps::LoadAssetRestrict(Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool* restrict) - { - if (std::find(Maps::CurrentDependencies.begin(), Maps::CurrentDependencies.end(), FastFiles::Current()) != Maps::CurrentDependencies.end() - && (FastFiles::Current() != "mp_shipment_long" || Maps::CurrentMainZone != "mp_shipment")) // Shipment is a special case - { - if (type == Game::XAssetType::ASSET_TYPE_CLIPMAP_PVS || type == Game::XAssetType::ASSET_TYPE_CLIPMAP || type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP || type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP || type == Game::XAssetType::ASSET_TYPE_GFXWORLD || type == Game::XAssetType::ASSET_TYPE_MAP_ENTS || type == Game::XAssetType::ASSET_TYPE_COMWORLD || type == Game::XAssetType::ASSET_TYPE_FX_MAP) - { - *restrict = true; - return; - } - } - - if (type == Game::XAssetType::ASSET_TYPE_ADDON_MAP_ENTS) - { - *restrict = true; - return; - } - - if (type == Game::XAssetType::ASSET_TYPE_WEAPON) - { - if ((!strstr(name.data(), "_mp") && name != "none" && name != "destructible_car") || Zones::Version() >= VERSION_ALPHA2) - { - *restrict = true; - return; - } - } - - if (type == Game::XAssetType::ASSET_TYPE_STRINGTABLE) - { - if (FastFiles::Current() == "mp_cross_fire") - { - *restrict = true; - return; - } - } - - if (type == Game::XAssetType::ASSET_TYPE_MAP_ENTS) - { - if (Flags::HasFlag("dump")) - { - Utils::IO::WriteFile(Utils::String::VA("raw/%s.ents", name), asset.mapEnts->entityString); - } - - static std::string mapEntities; - FileSystem::File ents(name + ".ents"); - if (ents.exists()) - { - mapEntities = ents.getBuffer(); - asset.mapEnts->entityString = const_cast(mapEntities.data()); - asset.mapEnts->numEntityChars = mapEntities.size() + 1; - } - - // Apply new mapEnts - // This doesn't work, entities are spawned before the patch file is loaded - //Maps::OverrideMapEnts(asset.mapEnts); - } - - // This is broken - if ((type == Game::XAssetType::ASSET_TYPE_MENU || type == Game::XAssetType::ASSET_TYPE_MENULIST) && Zones::Version() >= 359) - { - *restrict = true; - return; - } - } - - Game::G_GlassData* Maps::GetWorldData() - { - if (!Utils::String::StartsWith(Maps::CurrentMainZone, "mp_") || Maps::IsSPMap) - { - return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP].gameWorldSp[0].data; - } - else - { - return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp[0].data; - } - } - - __declspec(naked) void Maps::GetWorldDataStub() - { - __asm - { - push eax - pushad - - call Maps::GetWorldData - - mov [esp + 20h], eax - popad - pop eax - - retn - } - } - - void Maps::LoadRawSun() - { - Game::R_FlushSun(); - - Game::GfxWorld* world = *reinterpret_cast(0x66DEE94); - - if (FileSystem::File(Utils::String::VA("sun/%s.sun", Maps::CurrentMainZone.data())).exists()) - { - Game::R_LoadSunThroughDvars(Maps::CurrentMainZone.data(), &world->sun); - } - } - - void Maps::GetBSPName(char* buffer, size_t size, const char* format, const char* mapname) - { - if (!Utils::String::StartsWith(mapname, "mp_") && !Utils::String::StartsWith(mapname, "zm_")) - { - format = "maps/%s.d3dbsp"; - } - - // Redirect shipment to shipment long - if (mapname == "mp_shipment"s) - { - mapname = "mp_shipment_long"; - } - - _snprintf_s(buffer, size, size, format, mapname); - } - - void Maps::HandleAsSPMap() - { - Maps::IsSPMap = true; - } - - void Maps::AddDependency(std::string expression, std::string zone) - { - // Test expression before adding it - try - { - std::regex _(expression); - } - catch (const std::exception e) - { - MessageBoxA(0, Utils::String::VA("Invalid regular expression: %s", expression.data()), "Warning", MB_ICONEXCLAMATION); - return; - } - - Maps::DependencyList.push_back({ expression, zone }); - } - - int Maps::IgnoreEntityStub(const char* entity) - { - return (Utils::String::StartsWith(entity, "dyn_") || Utils::String::StartsWith(entity, "node_") || Utils::String::StartsWith(entity, "actor_")); - } - - std::vector Maps::GetDependenciesForMap(std::string map) - { - for (int i = 0; i < *Game::arenaCount; ++i) - { - Game::newMapArena_t* arena = &ArenaLength::NewArenas[i]; - if (arena->mapName == map) - { - for (int j = 0; j < ARRAY_SIZE(arena->keys); ++j) - { - if (arena->keys[j] == "dependency"s) - { - return Utils::String::Explode(arena->values[j], ' '); - } - } - } - } - - return {}; - } - - std::pair Maps::GetTeamsForMap(std::string map) - { - std::string team_axis = "opforce_composite"; - std::string team_allies = "us_army"; - - for (int i = 0; i < *Game::arenaCount; ++i) - { - Game::newMapArena_t* arena = &ArenaLength::NewArenas[i]; - if (arena->mapName == map) - { - for (int j = 0; j < ARRAY_SIZE(arena->keys); ++j) - { - if (arena->keys[j] == "allieschar"s) - { - team_allies = arena->values[j]; - } - else if (arena->keys[j] == "axischar"s) - { - team_axis = arena->values[j]; - } - } - - break; - } - } - - return { team_axis, team_allies }; - } - -#if defined(DEBUG) && defined(ENABLE_DXSDK) - // Credit to SE2Dev, as we shouldn't share the code, keep that in debug mode! - void Maps::ExportMap(Game::GfxWorld* world) - { - Utils::Memory::Allocator allocator; - if (!world) return; - - Logger::Print("Exporting '%s'...\n", world->baseName); - - std::string mtl; - mtl.append("# IW4x MTL File\n"); - mtl.append("# Credit to SE2Dev for his D3DBSP Tool\n"); - - std::string map; - map.append("# Generated by IW4x\n"); - map.append("# Credit to SE2Dev for his D3DBSP Tool\n"); - map.append(Utils::String::VA("o %s\n", world->baseName)); - map.append(Utils::String::VA("mtllib %s.mtl\n\n", world->baseName)); - - Logger::Print("Writing vertices...\n"); - for (unsigned int i = 0; i < world->draw.vertexCount; ++i) - { - float x = world->draw.vd.vertices[i].xyz[1]; - float y = world->draw.vd.vertices[i].xyz[2]; - float z = world->draw.vd.vertices[i].xyz[0]; - - map.append(Utils::String::VA("v %.6f %.6f %.6f\n", x, y, z)); - } - - map.append("\n"); - - Logger::Print("Writing texture coordinates...\n"); - for (unsigned int i = 0; i < world->draw.vertexCount; ++i) - { - map.append(Utils::String::VA("vt %.6f %.6f\n", world->draw.vd.vertices[i].texCoord[0], -world->draw.vd.vertices[i].texCoord[1])); - } - - Logger::Print("Searching materials...\n"); - int materialCount = 0; - Game::Material** materials = allocator.allocateArray(world->dpvs.staticSurfaceCount); - - for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i) - { - bool isNewMat = true; - - for (int j = 0; j < materialCount; ++j) - { - if (world->dpvs.surfaces[i].material == materials[j]) - { - isNewMat = false; - break; - } - } - - if (isNewMat) - { - materials[materialCount++] = world->dpvs.surfaces[i].material; - } - } - - Utils::IO::CreateDirectory(Utils::String::VA("raw/mapdump/%s/textures", world->baseName)); - mtl.append(Utils::String::VA("# Material Count: %d\n", materialCount)); - - Logger::Print("Exporting materials and faces...\n"); - for (int m = 0; m < materialCount; ++m) - { - std::string name = materials[m]->name; - - auto pos = name.find_last_of("/"); - if (pos != std::string::npos) - { - name = name.substr(pos + 1); - } - - map.append(Utils::String::VA("\nusemtl %s\n", name.data())); - map.append("s off\n"); - - Game::GfxImage* image = materials[m]->textureTable[0].info.image; - - for (char l = 0; l < materials[m]->textureCount; ++l) - { - if (materials[m]->textureTable[l].nameStart == 'c') - { - if (materials[m]->textureTable[l].nameEnd == 'p') - { - image = materials[m]->textureTable[l].info.image; // Hopefully our colorMap - } - } - } - - std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", world->baseName, image->name); - D3DXSaveTextureToFile(std::wstring(_name.begin(), _name.end()).data(), D3DXIFF_PNG, image->map, NULL); - - mtl.append(Utils::String::VA("\nnewmtl %s\n", name.data())); - mtl.append("Ka 1.0000 1.0000 1.0000\n"); - mtl.append("Kd 1.0000 1.0000 1.0000\n"); - mtl.append("illum 1\n"); - mtl.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name)); - mtl.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name)); - - for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i) - { - if (world->dpvs.surfaces[i].material != materials[m]) - continue; - - int vertOffset = world->dpvs.surfaces[i].tris.firstVertex + 1;//+1 cus obj starts at 1 - int indexOffset = world->dpvs.surfaces[i].tris.baseIndex; - for (unsigned short j = 0; j < world->dpvs.surfaces[i].tris.triCount; ++j) - { - int a = world->draw.indices[indexOffset + j * 3 + 0] + vertOffset; - int b = world->draw.indices[indexOffset + j * 3 + 1] + vertOffset; - int c = world->draw.indices[indexOffset + j * 3 + 2] + vertOffset; - - map.append(Utils::String::VA("f %d/%d %d/%d %d/%d\n", a, a, b, b, c, c)); - } - } - } - - Logger::Print("Writing final files...\n"); - Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.mtl", world->baseName, world->baseName), mtl); - Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.obj", world->baseName, world->baseName), map); - } -#endif - - void Maps::AddDlc(Maps::DLC dlc) - { - for (auto& pack : Maps::DlcPacks) - { - if (pack.index == dlc.index) - { - pack.url = dlc.url; - pack.maps = dlc.maps; - Maps::UpdateDlcStatus(); - return; - } - } - - Dvar::Register(Utils::String::VA("isDlcInstalled_%d", dlc.index), false, Game::DVAR_FLAG_USERCREATED | Game::DVAR_FLAG_WRITEPROTECTED, ""); - - Maps::DlcPacks.push_back(dlc); - Maps::UpdateDlcStatus(); - } - - void Maps::UpdateDlcStatus() - { - bool hasAllDlcs = true; - for (auto& pack : Maps::DlcPacks) - { - bool hasAllMaps = true; - for (auto map : pack.maps) - { - if (!FastFiles::Exists(map)) - { - hasAllMaps = false; - hasAllDlcs = false; - break; - } - } - - Dvar::Var(Utils::String::VA("isDlcInstalled_%d", pack.index)).setRaw(hasAllMaps ? 1 : 0); - } - - Dvar::Var("isDlcInstalled_All").setRaw(hasAllDlcs ? 1 : 0); - } - - void Maps::reallocateEntryPool() - { - AssertSize(Game::XAssetEntry, 16); - - Maps::EntryPool.clear(); - - if (ZoneBuilder::IsEnabled()) - { - Maps::EntryPool.resize(1183968); - } - else - { - Maps::EntryPool.resize(789312); - } - - // Apply new size - Utils::Hook::Set(0x5BAEB0, Maps::EntryPool.size()); - - // Apply new pool - Utils::Hook::Set(0x48E6F4, Maps::EntryPool.data()); - Utils::Hook::Set(0x4C67E4, Maps::EntryPool.data()); - Utils::Hook::Set(0x4C8584, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BAEA8, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB0C4, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB0F5, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB1D4, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB235, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB278, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB34C, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB484, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB570, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB6B7, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB844, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BB98D, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BBA66, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BBB8D, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BBCB1, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BBD9B, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BBE4C, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BBF14, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BBF54, Maps::EntryPool.data()); - Utils::Hook::Set(0x5BBFB8, Maps::EntryPool.data()); - - Utils::Hook::Set(0x5BAE91, Maps::EntryPool.data() + 1); - Utils::Hook::Set(0x5BAEA2, Maps::EntryPool.data() + 1); - } - - Maps::Maps() - { - Dvar::OnInit([] () - { - Dvar::Register("isDlcInstalled_All", false, Game::DVAR_FLAG_USERCREATED | Game::DVAR_FLAG_WRITEPROTECTED, ""); - - Maps::AddDlc({ 1, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_complex", "mp_compact", "mp_storm", "mp_overgrown", "mp_crash" } }); - Maps::AddDlc({ 2, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_abandon", "mp_vacant", "mp_trailerpark", "mp_strike", "mp_fuel2" } }); - Maps::AddDlc({ 3, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_nuked" } }); - Maps::AddDlc({ 4, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_cross_fire", "mp_cargoship", "mp_bloc" } }); - Maps::AddDlc({ 5, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_killhouse", "mp_bog_sh" } }); - Maps::AddDlc({ 6, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_cargoship_sh" } }); - Maps::AddDlc({ 7, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_shipment_long", "mp_rust_long", "mp_firingrange" } }); - Maps::AddDlc({ 8, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_bloc_sh", "mp_crash_tropical", "mp_estate_tropical", "mp_fav_tropical", "mp_storm_spring" } }); - - Maps::UpdateDlcStatus(); - - UIScript::Add("downloadDLC", [] (UIScript::Token token) - { - int dlc = token.get(); - - for (auto pack : Maps::DlcPacks) - { - if (pack.index == dlc) - { - ShellExecuteA(NULL, "open", pack.url.data(), 0, 0, SW_SHOWNORMAL); - return; - } - } - - Game::MessageBox(Utils::String::VA("DLC %d does not exist!", dlc), "ERROR"); - }); - }); - - // Restrict asset loading - AssetHandler::OnLoad(Maps::LoadAssetRestrict); - - // hunk size (was 300 MiB) - Utils::Hook::Set(0x64A029, 0x1C200000); // 450 MiB - Utils::Hook::Set(0x64A057, 0x1C200000); - -#if DEBUG - // Hunk debugging - Utils::Hook::Set(0x4FF57B, 0xCC); - Utils::Hook::Nop(0x4FF57C, 4); -#endif - - // Intercept BSP name resolving - Utils::Hook(0x4C5979, Maps::GetBSPName, HOOK_CALL).install()->quick(); - - // Intercept map zone loading - Utils::Hook(0x42C2AF, Maps::LoadMapZones, HOOK_CALL).install()->quick(); - - // Ignore SP entities - Utils::Hook(0x444810, Maps::IgnoreEntityStub, HOOK_JUMP).install()->quick(); - - // WorldData pointer replacement - Utils::Hook(0x4D90B6, Maps::GetWorldDataStub, HOOK_CALL).install()->quick(); - - // Allow loading raw suns - Utils::Hook(0x51B46A, Maps::LoadRawSun, HOOK_CALL).install()->quick(); - - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP, 1); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, 7168); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, 2700); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FX, 1200); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, 14000); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XANIMPARTS, 8192); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL, 5125); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PHYSPRESET, 128); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PIXELSHADER, 10000); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, 3072); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MATERIAL, 8192); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXDECL, 196); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_WEAPON, 2400); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_STRINGTABLE, 800); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMPACT_FX, 8); - - this->reallocateEntryPool(); - - // Dependencies - //Maps::AddDependency("oilrig", "mp_subbase"); - //Maps::AddDependency("gulag", "mp_subbase"); - //Maps::AddDependency("invasion", "mp_rust"); - //Maps::AddDependency("co_hunted", "mp_storm"); - //Maps::AddDependency("mp_shipment", "mp_shipment_long"); - -#if defined(DEBUG) && defined(ENABLE_DXSDK) - Command::Add("dumpmap", [] (Command::Params*) - { - if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) - { - Logger::Print("DirectX needs to be enabled, please start a client to use this command!\n"); - return; - } - - Game::GfxWorld* world = nullptr; - Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_GFXWORLD, [] (Game::XAssetHeader header, void* world) - { - *reinterpret_cast(world) = header.gfxWorld; - }, &world, false); - - if (world) - { - Maps::ExportMap(world); - Logger::Print("Map '%s' exported!\n", world->baseName); - } - else - { - Logger::Print("No map loaded, unable to dump anything!\n"); - } - }); -#endif - } - - Maps::~Maps() - { - Maps::DlcPacks.clear(); - Maps::DependencyList.clear(); - Maps::CurrentMainZone.clear(); - Maps::CurrentDependencies.clear(); - - Maps::EntryPool.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::string Maps::CurrentMainZone; + std::vector> Maps::DependencyList; + std::vector Maps::CurrentDependencies; + + bool Maps::IsSPMap; + std::vector Maps::DlcPacks; + std::vector Maps::EntryPool; + + void Maps::LoadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync) + { + if (!zoneInfo) return; + + Maps::IsSPMap = false; + Maps::CurrentMainZone = zoneInfo->name; + + Maps::CurrentDependencies.clear(); + for (auto i = Maps::DependencyList.begin(); i != Maps::DependencyList.end(); ++i) + { + if (std::regex_match(zoneInfo->name, std::regex(i->first))) + { + if (std::find(Maps::CurrentDependencies.begin(), Maps::CurrentDependencies.end(), i->second) == Maps::CurrentDependencies.end()) + { + Maps::CurrentDependencies.push_back(i->second); + } + } + } + + Utils::Memory::Allocator allocator; + auto teams = Maps::GetTeamsForMap(Maps::CurrentMainZone); + + auto dependencies = Maps::GetDependenciesForMap(Maps::CurrentMainZone); + Utils::Merge(&Maps::CurrentDependencies, dependencies.data(), dependencies.size()); + + std::vector data; + Utils::Merge(&data, zoneInfo, zoneCount); + + Game::XZoneInfo team; + team.allocFlags = zoneInfo->allocFlags; + team.freeFlags = zoneInfo->freeFlags; + + team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.first.data())); + data.push_back(team); + + team.name = allocator.duplicateString(Utils::String::VA("iw4x_team_%s", teams.second.data())); + data.push_back(team); + + for (unsigned int i = 0; i < Maps::CurrentDependencies.size(); ++i) + { + Game::XZoneInfo info; + + info.name = (&Maps::CurrentDependencies[i])->data(); + info.allocFlags = zoneInfo->allocFlags; + info.freeFlags = zoneInfo->freeFlags; + + data.push_back(info); + } + + // Load patch files + std::string patchZone = Utils::String::VA("patch_%s", zoneInfo->name); + if (FastFiles::Exists(patchZone)) + { + data.push_back({ patchZone.data(), zoneInfo->allocFlags, zoneInfo->freeFlags }); + } + + return FastFiles::LoadLocalizeZones(data.data(), data.size(), sync); + } + + void Maps::OverrideMapEnts(Game::MapEnts* ents) + { + auto callback = [] (Game::XAssetHeader header, void* ents) + { + Game::MapEnts* mapEnts = reinterpret_cast(ents); + Game::clipMap_t* clipMap = header.clipMap; + + if (clipMap && mapEnts && !_stricmp(mapEnts->name, clipMap->name)) + { + clipMap->mapEnts = mapEnts; + //*Game::marMapEntsPtr = mapEnts; + //Game::G_SpawnEntitiesFromString(); + } + }; + + // Internal doesn't lock the thread, as locking is impossible, due to executing this in the thread that holds the current lock + Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_CLIPMAP_PVS, callback, ents, true); + Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_CLIPMAP, callback, ents, true); + } + + void Maps::LoadAssetRestrict(Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool* restrict) + { + if (std::find(Maps::CurrentDependencies.begin(), Maps::CurrentDependencies.end(), FastFiles::Current()) != Maps::CurrentDependencies.end() + && (FastFiles::Current() != "mp_shipment_long" || Maps::CurrentMainZone != "mp_shipment")) // Shipment is a special case + { + if (type == Game::XAssetType::ASSET_TYPE_CLIPMAP_PVS || type == Game::XAssetType::ASSET_TYPE_CLIPMAP || type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP || type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP || type == Game::XAssetType::ASSET_TYPE_GFXWORLD || type == Game::XAssetType::ASSET_TYPE_MAP_ENTS || type == Game::XAssetType::ASSET_TYPE_COMWORLD || type == Game::XAssetType::ASSET_TYPE_FX_MAP) + { + *restrict = true; + return; + } + } + + if (type == Game::XAssetType::ASSET_TYPE_ADDON_MAP_ENTS) + { + *restrict = true; + return; + } + + if (type == Game::XAssetType::ASSET_TYPE_WEAPON) + { + if ((!strstr(name.data(), "_mp") && name != "none" && name != "destructible_car") || Zones::Version() >= VERSION_ALPHA2) + { + *restrict = true; + return; + } + } + + if (type == Game::XAssetType::ASSET_TYPE_STRINGTABLE) + { + if (FastFiles::Current() == "mp_cross_fire") + { + *restrict = true; + return; + } + } + + if (type == Game::XAssetType::ASSET_TYPE_MAP_ENTS) + { + if (Flags::HasFlag("dump")) + { + Utils::IO::WriteFile(Utils::String::VA("raw/%s.ents", name), asset.mapEnts->entityString); + } + + static std::string mapEntities; + FileSystem::File ents(name + ".ents"); + if (ents.exists()) + { + mapEntities = ents.getBuffer(); + asset.mapEnts->entityString = const_cast(mapEntities.data()); + asset.mapEnts->numEntityChars = mapEntities.size() + 1; + } + + // Apply new mapEnts + // This doesn't work, entities are spawned before the patch file is loaded + //Maps::OverrideMapEnts(asset.mapEnts); + } + + // This is broken + if ((type == Game::XAssetType::ASSET_TYPE_MENU || type == Game::XAssetType::ASSET_TYPE_MENULIST) && Zones::Version() >= 359) + { + *restrict = true; + return; + } + } + + Game::G_GlassData* Maps::GetWorldData() + { + if (!Utils::String::StartsWith(Maps::CurrentMainZone, "mp_") || Maps::IsSPMap) + { + return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP].gameWorldSp[0].data; + } + else + { + return Game::DB_XAssetPool[Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP].gameWorldMp[0].data; + } + } + + __declspec(naked) void Maps::GetWorldDataStub() + { + __asm + { + push eax + pushad + + call Maps::GetWorldData + + mov [esp + 20h], eax + popad + pop eax + + retn + } + } + + void Maps::LoadRawSun() + { + Game::R_FlushSun(); + + Game::GfxWorld* world = *reinterpret_cast(0x66DEE94); + + if (FileSystem::File(Utils::String::VA("sun/%s.sun", Maps::CurrentMainZone.data())).exists()) + { + Game::R_LoadSunThroughDvars(Maps::CurrentMainZone.data(), &world->sun); + } + } + + void Maps::GetBSPName(char* buffer, size_t size, const char* format, const char* mapname) + { + if (!Utils::String::StartsWith(mapname, "mp_") && !Utils::String::StartsWith(mapname, "zm_")) + { + format = "maps/%s.d3dbsp"; + } + + // Redirect shipment to shipment long + if (mapname == "mp_shipment"s) + { + mapname = "mp_shipment_long"; + } + + _snprintf_s(buffer, size, size, format, mapname); + } + + void Maps::HandleAsSPMap() + { + Maps::IsSPMap = true; + } + + void Maps::AddDependency(std::string expression, std::string zone) + { + // Test expression before adding it + try + { + std::regex _(expression); + } + catch (const std::exception e) + { + MessageBoxA(0, Utils::String::VA("Invalid regular expression: %s", expression.data()), "Warning", MB_ICONEXCLAMATION); + return; + } + + Maps::DependencyList.push_back({ expression, zone }); + } + + int Maps::IgnoreEntityStub(const char* entity) + { + return (Utils::String::StartsWith(entity, "dyn_") || Utils::String::StartsWith(entity, "node_") || Utils::String::StartsWith(entity, "actor_")); + } + + std::vector Maps::GetDependenciesForMap(std::string map) + { + for (int i = 0; i < *Game::arenaCount; ++i) + { + Game::newMapArena_t* arena = &ArenaLength::NewArenas[i]; + if (arena->mapName == map) + { + for (int j = 0; j < ARRAY_SIZE(arena->keys); ++j) + { + if (arena->keys[j] == "dependency"s) + { + return Utils::String::Explode(arena->values[j], ' '); + } + } + } + } + + return {}; + } + + std::pair Maps::GetTeamsForMap(std::string map) + { + std::string team_axis = "opforce_composite"; + std::string team_allies = "us_army"; + + for (int i = 0; i < *Game::arenaCount; ++i) + { + Game::newMapArena_t* arena = &ArenaLength::NewArenas[i]; + if (arena->mapName == map) + { + for (int j = 0; j < ARRAY_SIZE(arena->keys); ++j) + { + if (arena->keys[j] == "allieschar"s) + { + team_allies = arena->values[j]; + } + else if (arena->keys[j] == "axischar"s) + { + team_axis = arena->values[j]; + } + } + + break; + } + } + + return { team_axis, team_allies }; + } + +#if defined(DEBUG) && defined(ENABLE_DXSDK) + // Credit to SE2Dev, as we shouldn't share the code, keep that in debug mode! + void Maps::ExportMap(Game::GfxWorld* world) + { + Utils::Memory::Allocator allocator; + if (!world) return; + + Logger::Print("Exporting '%s'...\n", world->baseName); + + std::string mtl; + mtl.append("# IW4x MTL File\n"); + mtl.append("# Credit to SE2Dev for his D3DBSP Tool\n"); + + std::string map; + map.append("# Generated by IW4x\n"); + map.append("# Credit to SE2Dev for his D3DBSP Tool\n"); + map.append(Utils::String::VA("o %s\n", world->baseName)); + map.append(Utils::String::VA("mtllib %s.mtl\n\n", world->baseName)); + + Logger::Print("Writing vertices...\n"); + for (unsigned int i = 0; i < world->draw.vertexCount; ++i) + { + float x = world->draw.vd.vertices[i].xyz[1]; + float y = world->draw.vd.vertices[i].xyz[2]; + float z = world->draw.vd.vertices[i].xyz[0]; + + map.append(Utils::String::VA("v %.6f %.6f %.6f\n", x, y, z)); + } + + map.append("\n"); + + Logger::Print("Writing texture coordinates...\n"); + for (unsigned int i = 0; i < world->draw.vertexCount; ++i) + { + map.append(Utils::String::VA("vt %.6f %.6f\n", world->draw.vd.vertices[i].texCoord[0], -world->draw.vd.vertices[i].texCoord[1])); + } + + Logger::Print("Searching materials...\n"); + int materialCount = 0; + Game::Material** materials = allocator.allocateArray(world->dpvs.staticSurfaceCount); + + for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i) + { + bool isNewMat = true; + + for (int j = 0; j < materialCount; ++j) + { + if (world->dpvs.surfaces[i].material == materials[j]) + { + isNewMat = false; + break; + } + } + + if (isNewMat) + { + materials[materialCount++] = world->dpvs.surfaces[i].material; + } + } + + Utils::IO::CreateDirectory(Utils::String::VA("raw/mapdump/%s/textures", world->baseName)); + mtl.append(Utils::String::VA("# Material Count: %d\n", materialCount)); + + Logger::Print("Exporting materials and faces...\n"); + for (int m = 0; m < materialCount; ++m) + { + std::string name = materials[m]->name; + + auto pos = name.find_last_of("/"); + if (pos != std::string::npos) + { + name = name.substr(pos + 1); + } + + map.append(Utils::String::VA("\nusemtl %s\n", name.data())); + map.append("s off\n"); + + Game::GfxImage* image = materials[m]->textureTable[0].info.image; + + for (char l = 0; l < materials[m]->textureCount; ++l) + { + if (materials[m]->textureTable[l].nameStart == 'c') + { + if (materials[m]->textureTable[l].nameEnd == 'p') + { + image = materials[m]->textureTable[l].info.image; // Hopefully our colorMap + } + } + } + + std::string _name = Utils::String::VA("raw/mapdump/%s/textures/%s.png", world->baseName, image->name); + D3DXSaveTextureToFile(std::wstring(_name.begin(), _name.end()).data(), D3DXIFF_PNG, image->map, NULL); + + mtl.append(Utils::String::VA("\nnewmtl %s\n", name.data())); + mtl.append("Ka 1.0000 1.0000 1.0000\n"); + mtl.append("Kd 1.0000 1.0000 1.0000\n"); + mtl.append("illum 1\n"); + mtl.append(Utils::String::VA("map_Ka textures/%s.png\n", image->name)); + mtl.append(Utils::String::VA("map_Kd textures/%s.png\n", image->name)); + + for (unsigned int i = 0; i < world->dpvs.staticSurfaceCount; ++i) + { + if (world->dpvs.surfaces[i].material != materials[m]) + continue; + + int vertOffset = world->dpvs.surfaces[i].tris.firstVertex + 1;//+1 cus obj starts at 1 + int indexOffset = world->dpvs.surfaces[i].tris.baseIndex; + for (unsigned short j = 0; j < world->dpvs.surfaces[i].tris.triCount; ++j) + { + int a = world->draw.indices[indexOffset + j * 3 + 0] + vertOffset; + int b = world->draw.indices[indexOffset + j * 3 + 1] + vertOffset; + int c = world->draw.indices[indexOffset + j * 3 + 2] + vertOffset; + + map.append(Utils::String::VA("f %d/%d %d/%d %d/%d\n", a, a, b, b, c, c)); + } + } + } + + Logger::Print("Writing final files...\n"); + Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.mtl", world->baseName, world->baseName), mtl); + Utils::IO::WriteFile(Utils::String::VA("raw/mapdump/%s/%s.obj", world->baseName, world->baseName), map); + } +#endif + + void Maps::AddDlc(Maps::DLC dlc) + { + for (auto& pack : Maps::DlcPacks) + { + if (pack.index == dlc.index) + { + pack.url = dlc.url; + pack.maps = dlc.maps; + Maps::UpdateDlcStatus(); + return; + } + } + + Dvar::Register(Utils::String::VA("isDlcInstalled_%d", dlc.index), false, Game::DVAR_FLAG_USERCREATED | Game::DVAR_FLAG_WRITEPROTECTED, ""); + + Maps::DlcPacks.push_back(dlc); + Maps::UpdateDlcStatus(); + } + + void Maps::UpdateDlcStatus() + { + bool hasAllDlcs = true; + for (auto& pack : Maps::DlcPacks) + { + bool hasAllMaps = true; + for (auto map : pack.maps) + { + if (!FastFiles::Exists(map)) + { + hasAllMaps = false; + hasAllDlcs = false; + break; + } + } + + Dvar::Var(Utils::String::VA("isDlcInstalled_%d", pack.index)).setRaw(hasAllMaps ? 1 : 0); + } + + Dvar::Var("isDlcInstalled_All").setRaw(hasAllDlcs ? 1 : 0); + } + + void Maps::reallocateEntryPool() + { + AssertSize(Game::XAssetEntry, 16); + + Maps::EntryPool.clear(); + + if (ZoneBuilder::IsEnabled()) + { + Maps::EntryPool.resize(1183968); + } + else + { + Maps::EntryPool.resize(789312); + } + + // Apply new size + Utils::Hook::Set(0x5BAEB0, Maps::EntryPool.size()); + + // Apply new pool + Utils::Hook::Set(0x48E6F4, Maps::EntryPool.data()); + Utils::Hook::Set(0x4C67E4, Maps::EntryPool.data()); + Utils::Hook::Set(0x4C8584, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BAEA8, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB0C4, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB0F5, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB1D4, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB235, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB278, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB34C, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB484, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB570, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB6B7, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB844, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BB98D, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BBA66, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BBB8D, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BBCB1, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BBD9B, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BBE4C, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BBF14, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BBF54, Maps::EntryPool.data()); + Utils::Hook::Set(0x5BBFB8, Maps::EntryPool.data()); + + Utils::Hook::Set(0x5BAE91, Maps::EntryPool.data() + 1); + Utils::Hook::Set(0x5BAEA2, Maps::EntryPool.data() + 1); + } + + Maps::Maps() + { + Dvar::OnInit([] () + { + Dvar::Register("isDlcInstalled_All", false, Game::DVAR_FLAG_USERCREATED | Game::DVAR_FLAG_WRITEPROTECTED, ""); + + Maps::AddDlc({ 1, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_complex", "mp_compact", "mp_storm", "mp_overgrown", "mp_crash" } }); + Maps::AddDlc({ 2, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_abandon", "mp_vacant", "mp_trailerpark", "mp_strike", "mp_fuel2" } }); + Maps::AddDlc({ 3, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_nuked" } }); + Maps::AddDlc({ 4, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_cross_fire", "mp_cargoship", "mp_bloc" } }); + Maps::AddDlc({ 5, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_killhouse", "mp_bog_sh" } }); + Maps::AddDlc({ 6, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_cargoship_sh" } }); + Maps::AddDlc({ 7, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_shipment_long", "mp_rust_long", "mp_firingrange" } }); + Maps::AddDlc({ 8, Utils::Cache::GetStaticUrl("/dlc/"), { "mp_bloc_sh", "mp_crash_tropical", "mp_estate_tropical", "mp_fav_tropical", "mp_storm_spring" } }); + + Maps::UpdateDlcStatus(); + + UIScript::Add("downloadDLC", [] (UIScript::Token token) + { + int dlc = token.get(); + + for (auto pack : Maps::DlcPacks) + { + if (pack.index == dlc) + { + ShellExecuteA(NULL, "open", pack.url.data(), 0, 0, SW_SHOWNORMAL); + return; + } + } + + Game::MessageBox(Utils::String::VA("DLC %d does not exist!", dlc), "ERROR"); + }); + }); + + // Restrict asset loading + AssetHandler::OnLoad(Maps::LoadAssetRestrict); + + // hunk size (was 300 MiB) + Utils::Hook::Set(0x64A029, 0x1C200000); // 450 MiB + Utils::Hook::Set(0x64A057, 0x1C200000); + +#if DEBUG + // Hunk debugging + Utils::Hook::Set(0x4FF57B, 0xCC); + Utils::Hook::Nop(0x4FF57C, 4); +#endif + + // Intercept BSP name resolving + Utils::Hook(0x4C5979, Maps::GetBSPName, HOOK_CALL).install()->quick(); + + // Intercept map zone loading + Utils::Hook(0x42C2AF, Maps::LoadMapZones, HOOK_CALL).install()->quick(); + + // Ignore SP entities + Utils::Hook(0x444810, Maps::IgnoreEntityStub, HOOK_JUMP).install()->quick(); + + // WorldData pointer replacement + Utils::Hook(0x4D90B6, Maps::GetWorldDataStub, HOOK_CALL).install()->quick(); + + // Allow loading raw suns + Utils::Hook(0x51B46A, Maps::LoadRawSun, HOOK_CALL).install()->quick(); + + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP, 1); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, 7168); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOADED_SOUND, 2700); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_FX, 1200); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_LOCALIZE_ENTRY, 14000); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XANIMPARTS, 8192); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODEL, 5125); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PHYSPRESET, 128); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_PIXELSHADER, 10000); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXSHADER, 3072); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MATERIAL, 8192); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_VERTEXDECL, 196); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_WEAPON, 2400); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_STRINGTABLE, 800); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMPACT_FX, 8); + + this->reallocateEntryPool(); + + // Dependencies + //Maps::AddDependency("oilrig", "mp_subbase"); + //Maps::AddDependency("gulag", "mp_subbase"); + //Maps::AddDependency("invasion", "mp_rust"); + //Maps::AddDependency("co_hunted", "mp_storm"); + //Maps::AddDependency("mp_shipment", "mp_shipment_long"); + +#if defined(DEBUG) && defined(ENABLE_DXSDK) + Command::Add("dumpmap", [] (Command::Params*) + { + if (Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) + { + Logger::Print("DirectX needs to be enabled, please start a client to use this command!\n"); + return; + } + + Game::GfxWorld* world = nullptr; + Game::DB_EnumXAssets(Game::XAssetType::ASSET_TYPE_GFXWORLD, [] (Game::XAssetHeader header, void* world) + { + *reinterpret_cast(world) = header.gfxWorld; + }, &world, false); + + if (world) + { + Maps::ExportMap(world); + Logger::Print("Map '%s' exported!\n", world->baseName); + } + else + { + Logger::Print("No map loaded, unable to dump anything!\n"); + } + }); +#endif + } + + Maps::~Maps() + { + Maps::DlcPacks.clear(); + Maps::DependencyList.clear(); + Maps::CurrentMainZone.clear(); + Maps::CurrentDependencies.clear(); + + Maps::EntryPool.clear(); + } +} diff --git a/src/Components/Modules/Maps.hpp b/src/Components/Modules/Maps.hpp index 3af1fc9a..ad9ca410 100644 --- a/src/Components/Modules/Maps.hpp +++ b/src/Components/Modules/Maps.hpp @@ -1,59 +1,59 @@ -namespace Components -{ - class Maps : public Component - { - public: - Maps(); - ~Maps(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Maps"; }; -#endif - - static void HandleAsSPMap(); - static void AddDependency(std::string expression, std::string zone); - - static std::pair GetTeamsForMap(std::string map); - static std::vector GetDependenciesForMap(std::string map); - - static std::string CurrentMainZone; - - private: - class DLC - { - public: - int index; - std::string url; - std::vector maps; - }; - - static bool IsSPMap; - static std::vector DlcPacks; - static std::vector EntryPool; - - static std::vector> DependencyList; - static std::vector CurrentDependencies; - - static void GetBSPName(char* buffer, size_t size, const char* format, const char* mapname); - static void LoadAssetRestrict(Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool* restrict); - static void LoadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); - - static void OverrideMapEnts(Game::MapEnts* ents); - - static int IgnoreEntityStub(const char* entity); - - static Game::G_GlassData* GetWorldData(); - static void GetWorldDataStub(); - - static void LoadRawSun(); - - static void AddDlc(DLC dlc); - static void UpdateDlcStatus(); - -#if defined(DEBUG) && defined(ENABLE_DXSDK) - static void ExportMap(Game::GfxWorld* world); -#endif - - void reallocateEntryPool(); - }; -} +namespace Components +{ + class Maps : public Component + { + public: + Maps(); + ~Maps(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Maps"; }; +#endif + + static void HandleAsSPMap(); + static void AddDependency(std::string expression, std::string zone); + + static std::pair GetTeamsForMap(std::string map); + static std::vector GetDependenciesForMap(std::string map); + + static std::string CurrentMainZone; + + private: + class DLC + { + public: + int index; + std::string url; + std::vector maps; + }; + + static bool IsSPMap; + static std::vector DlcPacks; + static std::vector EntryPool; + + static std::vector> DependencyList; + static std::vector CurrentDependencies; + + static void GetBSPName(char* buffer, size_t size, const char* format, const char* mapname); + static void LoadAssetRestrict(Game::XAssetType type, Game::XAssetHeader asset, std::string name, bool* restrict); + static void LoadMapZones(Game::XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); + + static void OverrideMapEnts(Game::MapEnts* ents); + + static int IgnoreEntityStub(const char* entity); + + static Game::G_GlassData* GetWorldData(); + static void GetWorldDataStub(); + + static void LoadRawSun(); + + static void AddDlc(DLC dlc); + static void UpdateDlcStatus(); + +#if defined(DEBUG) && defined(ENABLE_DXSDK) + static void ExportMap(Game::GfxWorld* world); +#endif + + void reallocateEntryPool(); + }; +} diff --git a/src/Components/Modules/Materials.cpp b/src/Components/Modules/Materials.cpp index 8243877e..204468b7 100644 --- a/src/Components/Modules/Materials.cpp +++ b/src/Components/Modules/Materials.cpp @@ -1,189 +1,189 @@ -#include "STDInclude.hpp" - -namespace Components -{ - int Materials::ImageNameLength; - Utils::Hook Materials::ImageVersionCheckHook; - - __declspec(naked) void Materials::ImageVersionCheck() - { - __asm - { - cmp eax, 9 - je returnSafely - - jmp Materials::ImageVersionCheckHook.original - - returnSafely: - mov al, 1 - add esp, 18h - retn - } - } - - Game::Material* Materials::ResolveMaterial(const char* stringPtr) - { - const char* imagePtr = stringPtr + 4; - unsigned int length = static_cast(stringPtr[3] & 0xFF); - - if (strlen(imagePtr) >= length) - { - Materials::ImageNameLength = 4 + length; - std::string image(imagePtr, length); - - return Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, image.data()).material; - } - - Materials::ImageNameLength = 4; - return Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "default").material; - } - - __declspec(naked) void Materials::PostDrawMaterialStub() - { - __asm - { - mov eax, Materials::ImageNameLength - add [esp + 30h], eax - - mov eax, 5358FFh - jmp eax - } - } - - __declspec(naked) void Materials::DrawMaterialStub() - { - __asm - { - push ecx - call Materials::ResolveMaterial - add esp, 4h - - mov edx, 5310F0h - jmp edx - } - } - - int Materials::WriteDeathMessageIcon(char* string, int offset, Game::Material* material) - { - if (!material) - { - material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "default").material; - } - - int length = strlen(material->name); - string[offset++] = static_cast(length); - - strncpy_s(string + offset, 1024 - offset, material->name, length); - - return offset + length; - } - - __declspec(naked) void Materials::DeathMessageStub() - { - __asm - { - push edx // Material - push eax // offset - push ecx // String - - call Materials::WriteDeathMessageIcon - - add esp, 14h - retn - } - } - - int Materials::FormatImagePath(char* buffer, size_t size, int, int, const char* image) - { -#if 0 - if (Utils::String::StartsWith(image, "preview_")) - { - std::string newImage = image; - Utils::String::Replace(newImage, "preview_", "loadscreen_"); - - if (FileSystem::FileReader(fmt::sprintf("images/%s.iwi", newImage.data())).exists()) - { - image = Utils::String::VA("%s", newImage.data()); - } - } -#endif - - return _snprintf_s(buffer, size, size, "images/%s.iwi", image); - } - -#ifdef DEBUG - void Materials::DumpImageCfg(int, const char*, const char* material) - { - Materials::DumpImageCfgPath(0, nullptr, Utils::String::VA("images/%s.iwi", material)); - } - - void Materials::DumpImageCfgPath(int, const char*, const char* material) - { - FILE* fp = nullptr; - if (!fopen_s(&fp, "dump.cfg", "a") && fp) - { - fprintf(fp, "dumpraw %s\n", material); - fclose(fp); - } - } - - int Materials::MaterialComparePrint(Game::Material* m1, Game::Material* m2) - { - static Game::Material* a,* b; - a = m1, b = m2; - - return Utils::Hook::Call(0x5235B0)(m1, m2); - } - -#endif - - Materials::Materials() - { - Materials::ImageNameLength = 7; - - // Allow codo images - Materials::ImageVersionCheckHook.initialize(0x53A456, Materials::ImageVersionCheck, HOOK_CALL)->install(); - - // Fix material pointer exploit - Utils::Hook(0x534E0C, Materials::DrawMaterialStub, HOOK_CALL).install()->quick(); - - // Increment string pointer accordingly - Utils::Hook(0x5358FA, Materials::PostDrawMaterialStub, HOOK_JUMP).install()->quick(); - - // Adapt death message to IW5 material format - Utils::Hook(0x5A30D9, Materials::DeathMessageStub, HOOK_JUMP).install()->quick(); - - // Resolve preview images to loadscreens - Utils::Hook(0x53AC19, Materials::FormatImagePath, HOOK_CALL).install()->quick(); - -#ifdef DEBUG - if (Flags::HasFlag("dump")) - { - Utils::Hook(0x51F5AC, Materials::DumpImageCfg, HOOK_CALL).install()->quick(); - Utils::Hook(0x51F4C4, Materials::DumpImageCfg, HOOK_CALL).install()->quick(); - Utils::Hook(0x53AC62, Materials::DumpImageCfgPath, HOOK_CALL).install()->quick(); - } - else - { - // Ignore missing images - Utils::Hook::Nop(0x51F5AC, 5); - Utils::Hook::Nop(0x51F4C4, 5); - } - - Utils::Hook::Set(0x523894, Materials::MaterialComparePrint); -#endif - -// Renderer::OnFrame([] () -// { -// Game::Font* font = Game::R_RegisterFont("fonts/normalFont"); -// float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; -// -// Game::R_AddCmdDrawText("test^==preview_mp_rustzob", 0x7FFFFFFF, font, 500.0f, 150.0f, 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_SHADOWED); -// }); - } - - Materials::~Materials() - { - Materials::ImageVersionCheckHook.uninstall(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + int Materials::ImageNameLength; + Utils::Hook Materials::ImageVersionCheckHook; + + __declspec(naked) void Materials::ImageVersionCheck() + { + __asm + { + cmp eax, 9 + je returnSafely + + jmp Materials::ImageVersionCheckHook.original + + returnSafely: + mov al, 1 + add esp, 18h + retn + } + } + + Game::Material* Materials::ResolveMaterial(const char* stringPtr) + { + const char* imagePtr = stringPtr + 4; + unsigned int length = static_cast(stringPtr[3] & 0xFF); + + if (strlen(imagePtr) >= length) + { + Materials::ImageNameLength = 4 + length; + std::string image(imagePtr, length); + + return Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, image.data()).material; + } + + Materials::ImageNameLength = 4; + return Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "default").material; + } + + __declspec(naked) void Materials::PostDrawMaterialStub() + { + __asm + { + mov eax, Materials::ImageNameLength + add [esp + 30h], eax + + mov eax, 5358FFh + jmp eax + } + } + + __declspec(naked) void Materials::DrawMaterialStub() + { + __asm + { + push ecx + call Materials::ResolveMaterial + add esp, 4h + + mov edx, 5310F0h + jmp edx + } + } + + int Materials::WriteDeathMessageIcon(char* string, int offset, Game::Material* material) + { + if (!material) + { + material = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "default").material; + } + + int length = strlen(material->name); + string[offset++] = static_cast(length); + + strncpy_s(string + offset, 1024 - offset, material->name, length); + + return offset + length; + } + + __declspec(naked) void Materials::DeathMessageStub() + { + __asm + { + push edx // Material + push eax // offset + push ecx // String + + call Materials::WriteDeathMessageIcon + + add esp, 14h + retn + } + } + + int Materials::FormatImagePath(char* buffer, size_t size, int, int, const char* image) + { +#if 0 + if (Utils::String::StartsWith(image, "preview_")) + { + std::string newImage = image; + Utils::String::Replace(newImage, "preview_", "loadscreen_"); + + if (FileSystem::FileReader(fmt::sprintf("images/%s.iwi", newImage.data())).exists()) + { + image = Utils::String::VA("%s", newImage.data()); + } + } +#endif + + return _snprintf_s(buffer, size, size, "images/%s.iwi", image); + } + +#ifdef DEBUG + void Materials::DumpImageCfg(int, const char*, const char* material) + { + Materials::DumpImageCfgPath(0, nullptr, Utils::String::VA("images/%s.iwi", material)); + } + + void Materials::DumpImageCfgPath(int, const char*, const char* material) + { + FILE* fp = nullptr; + if (!fopen_s(&fp, "dump.cfg", "a") && fp) + { + fprintf(fp, "dumpraw %s\n", material); + fclose(fp); + } + } + + int Materials::MaterialComparePrint(Game::Material* m1, Game::Material* m2) + { + static Game::Material* a,* b; + a = m1, b = m2; + + return Utils::Hook::Call(0x5235B0)(m1, m2); + } + +#endif + + Materials::Materials() + { + Materials::ImageNameLength = 7; + + // Allow codo images + Materials::ImageVersionCheckHook.initialize(0x53A456, Materials::ImageVersionCheck, HOOK_CALL)->install(); + + // Fix material pointer exploit + Utils::Hook(0x534E0C, Materials::DrawMaterialStub, HOOK_CALL).install()->quick(); + + // Increment string pointer accordingly + Utils::Hook(0x5358FA, Materials::PostDrawMaterialStub, HOOK_JUMP).install()->quick(); + + // Adapt death message to IW5 material format + Utils::Hook(0x5A30D9, Materials::DeathMessageStub, HOOK_JUMP).install()->quick(); + + // Resolve preview images to loadscreens + Utils::Hook(0x53AC19, Materials::FormatImagePath, HOOK_CALL).install()->quick(); + +#ifdef DEBUG + if (Flags::HasFlag("dump")) + { + Utils::Hook(0x51F5AC, Materials::DumpImageCfg, HOOK_CALL).install()->quick(); + Utils::Hook(0x51F4C4, Materials::DumpImageCfg, HOOK_CALL).install()->quick(); + Utils::Hook(0x53AC62, Materials::DumpImageCfgPath, HOOK_CALL).install()->quick(); + } + else + { + // Ignore missing images + Utils::Hook::Nop(0x51F5AC, 5); + Utils::Hook::Nop(0x51F4C4, 5); + } + + Utils::Hook::Set(0x523894, Materials::MaterialComparePrint); +#endif + +// Renderer::OnFrame([] () +// { +// Game::Font* font = Game::R_RegisterFont("fonts/normalFont"); +// float color[4] = { 1.0f, 1.0f, 1.0f, 1.0f }; +// +// Game::R_AddCmdDrawText("test^==preview_mp_rustzob", 0x7FFFFFFF, font, 500.0f, 150.0f, 1.0f, 1.0f, 0.0f, color, Game::ITEM_TEXTSTYLE_SHADOWED); +// }); + } + + Materials::~Materials() + { + Materials::ImageVersionCheckHook.uninstall(); + } +} diff --git a/src/Components/Modules/Materials.hpp b/src/Components/Modules/Materials.hpp index 47ca95d7..7d90fa66 100644 --- a/src/Components/Modules/Materials.hpp +++ b/src/Components/Modules/Materials.hpp @@ -1,34 +1,34 @@ -namespace Components -{ - class Materials : public Component - { - public: - Materials(); - ~Materials(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Materials"; }; -#endif - - static int FormatImagePath(char* buffer, size_t size, int, int, const char* image); - - private: - static int ImageNameLength; - - static Utils::Hook ImageVersionCheckHook; - static void ImageVersionCheck(); - - static Game::Material* ResolveMaterial(const char* stringPtr); - static void DrawMaterialStub(); - static void PostDrawMaterialStub(); - - static int WriteDeathMessageIcon(char* string, int offset, Game::Material* material); - static void DeathMessageStub(); - -#ifdef DEBUG - static void DumpImageCfg(int, const char*, const char* material); - static void DumpImageCfgPath(int, const char*, const char* material); - static int MaterialComparePrint(Game::Material* m1, Game::Material* m2); -#endif - }; -} +namespace Components +{ + class Materials : public Component + { + public: + Materials(); + ~Materials(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Materials"; }; +#endif + + static int FormatImagePath(char* buffer, size_t size, int, int, const char* image); + + private: + static int ImageNameLength; + + static Utils::Hook ImageVersionCheckHook; + static void ImageVersionCheck(); + + static Game::Material* ResolveMaterial(const char* stringPtr); + static void DrawMaterialStub(); + static void PostDrawMaterialStub(); + + static int WriteDeathMessageIcon(char* string, int offset, Game::Material* material); + static void DeathMessageStub(); + +#ifdef DEBUG + static void DumpImageCfg(int, const char*, const char* material); + static void DumpImageCfgPath(int, const char*, const char* material); + static int MaterialComparePrint(Game::Material* m1, Game::Material* m2); +#endif + }; +} diff --git a/src/Components/Modules/Menus.cpp b/src/Components/Modules/Menus.cpp index 79c129e8..4a295cc2 100644 --- a/src/Components/Modules/Menus.cpp +++ b/src/Components/Modules/Menus.cpp @@ -1,709 +1,709 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::vector Menus::CustomMenus; - std::unordered_map Menus::MenuList; - std::unordered_map Menus::MenuListList; - - int Menus::ReserveSourceHandle() - { - // Check if a free slot is available - int i = 1; - for (; i < MAX_SOURCEFILES; ++i) - { - if (!Game::sourceFiles[i]) - break; - } - - if (i >= MAX_SOURCEFILES) - return 0; - - // Reserve it, if yes - Game::sourceFiles[i] = (Game::source_t*)1; - - return i; - } - - Game::script_t* Menus::LoadMenuScript(std::string name, std::string buffer) - { - Game::script_t* script = Game::Script_Alloc(sizeof(Game::script_t) + 1 + buffer.length()); - - strcpy_s(script->filename, sizeof(script->filename), name.data()); - script->buffer = reinterpret_cast(script + 1); - - *(script->buffer + buffer.length()) = '\0'; - - script->script_p = script->buffer; - script->lastscript_p = script->buffer; - script->length = buffer.length(); - script->end_p = &script->buffer[buffer.length()]; - script->line = 1; - script->lastline = 1; - script->tokenavailable = 0; - - Game::Script_SetupTokens(script, reinterpret_cast(0x797F80)); - script->punctuations = reinterpret_cast(0x797F80); - - std::memcpy(script->buffer, buffer.data(), script->length + 1); - - script->length = Game::Script_CleanString(script->buffer); - - return script; - } - - int Menus::LoadMenuSource(std::string name, std::string buffer) - { - int handle = Menus::ReserveSourceHandle(); - if (!Menus::IsValidSourceHandle(handle)) return 0; // No free source slot! - - Game::source_t *source = nullptr; - Game::script_t *script = Menus::LoadMenuScript(name, buffer); - - if (!script) - { - Game::sourceFiles[handle] = nullptr; // Free reserved slot - return 0; - } - - script->next = NULL; - - source = Utils::Memory::Allocate(); - if (!source) - { - Game::FreeMemory(script); - return 0; - } - - strncpy_s(source->filename, 64, "string", 64); - source->scriptstack = script; - source->tokens = NULL; - source->defines = NULL; - source->indentstack = NULL; - source->skip = 0; - source->definehash = (Game::define_t**)Utils::Memory::Allocate(4096); - - Game::sourceFiles[handle] = source; - - return handle; - } - - bool Menus::IsValidSourceHandle(int handle) - { - return (handle > 0 && handle < MAX_SOURCEFILES && Game::sourceFiles[handle]); - } - - int Menus::KeywordHash(char* key) - { - int hash = 0; - - if (*key) - { - int sub = 3523 - reinterpret_cast(key); - do - { - char _chr = *key; - hash += reinterpret_cast(&(key++)[sub]) * tolower(_chr); - } while (*key); - } - - return (static_cast(hash) + static_cast(hash >> 8)) & 0x7F; - } - - Game::menuDef_t* Menus::ParseMenu(int handle) - { - Game::menuDef_t* menu = Utils::Memory::Allocate(); - if (!menu) return nullptr; - - menu->items = Utils::Memory::AllocateArray(512); - if (!menu->items) - { - Utils::Memory::Free(menu); - return nullptr; - } - - Game::pc_token_t token; - if (!Game::PC_ReadTokenHandle(handle, &token) || token.string[0] != '{') - { - Utils::Memory::Free(menu->items); - Utils::Memory::Free(menu); - return nullptr; - } - - while (true) - { - ZeroMemory(&token, sizeof(token)); - - if (!Game::PC_ReadTokenHandle(handle, &token)) - { - Game::PC_SourceError(handle, "end of file inside menu\n"); - break; // Fail - } - - if (*token.string == '}') - { - break; // Success - } - - int idx = Menus::KeywordHash(token.string); - - Game::keywordHash_t* key = Game::menuParseKeywordHash[idx]; - - if (!key) - { - Game::PC_SourceError(handle, "unknown menu keyword %s", token.string); - continue; - } - - if (!key->func(menu, handle)) - { - Game::PC_SourceError(handle, "couldn't parse menu keyword %s", token.string); - break; // Fail - } - } - - Menus::OverrideMenu(menu); - Menus::RemoveMenu(menu->window.name); - Menus::MenuList[menu->window.name] = menu; - - return menu; - } - - std::vector Menus::LoadMenu(std::string menu) - { - std::vector menus; - FileSystem::File menuFile(menu); - - if (menuFile.exists()) - { - Game::pc_token_t token; - int handle = Menus::LoadMenuSource(menu, menuFile.getBuffer()); - - if (Menus::IsValidSourceHandle(handle)) - { - while (true) - { - ZeroMemory(&token, sizeof(token)); - - if (!Game::PC_ReadTokenHandle(handle, &token) || token.string[0] == '}') - { - break; - } - - if (!_stricmp(token.string, "loadmenu")) - { - Game::PC_ReadTokenHandle(handle, &token); - - Utils::Merge(&menus, Menus::LoadMenu(Utils::String::VA("ui_mp\\%s.menu", token.string))); - } - - if (!_stricmp(token.string, "menudef")) - { - Game::menuDef_t* menudef = Menus::ParseMenu(handle); - if (menudef) menus.push_back(menudef); - } - } - - Menus::FreeMenuSource(handle); - } - } - - return menus; - } - - std::vector Menus::LoadMenu(Game::menuDef_t* menudef) - { - std::vector menus = Menus::LoadMenu(Utils::String::VA("ui_mp\\%s.menu", menudef->window.name)); - - if (menus.empty()) - { - // Try loading the original menu, if we can't load our custom one - Game::menuDef_t* originalMenu = AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MENU, menudef->window.name).menu; - - if (originalMenu) - { - menus.push_back(originalMenu); - } - else - { - menus.push_back(menudef); - } - } - - return menus; - } - - Game::MenuList* Menus::LoadScriptMenu(const char* menu) - { - std::vector menus = Menus::LoadMenu(menu); - if (menus.empty()) return nullptr; - - // Allocate new menu list - Game::MenuList* newList = Utils::Memory::Allocate(); - if (!newList) return nullptr; - - newList->menus = Utils::Memory::AllocateArray(menus.size()); - if (!newList->menus) - { - Utils::Memory::Free(newList); - return nullptr; - } - - newList->name = Utils::Memory::DuplicateString(menu); - newList->menuCount = menus.size(); - - // Copy new menus - std::memcpy(newList->menus, menus.data(), menus.size() * sizeof(Game::menuDef_t *)); - - Menus::RemoveMenuList(newList->name); - Menus::MenuListList[newList->name] = newList; - - return newList; - } - - Game::MenuList* Menus::LoadMenuList(Game::MenuList* menuList) - { - std::vector menus; - - for (int i = 0; i < menuList->menuCount; ++i) - { - if (!menuList->menus[i]) - { - continue; - } - - Utils::Merge(&menus, Menus::LoadMenu(menuList->menus[i])); - } - - // Load custom menus - if (menuList->name == "ui_mp/code.txt"s) // Should be menus, but code is loaded ingame - { - for (auto menu : Menus::CustomMenus) - { - Utils::Merge(&menus, Menus::LoadMenu(menu)); - } - } - - // Allocate new menu list - Game::MenuList* newList = Utils::Memory::Allocate(); - if (!newList) return menuList; - - size_t size = menus.size(); - newList->menus = Utils::Memory::AllocateArray(size); - if (!newList->menus) - { - Utils::Memory::Free(newList); - return menuList; - } - - newList->name = Utils::Memory::DuplicateString(menuList->name); - newList->menuCount = size; - - // Copy new menus - std::memcpy(newList->menus, menus.data(), size * sizeof(Game::menuDef_t *)); - - Menus::RemoveMenuList(newList->name); - Menus::MenuListList[newList->name] = newList; - - return newList; - } - - void Menus::FreeMenuSource(int handle) - { - if (!Menus::IsValidSourceHandle(handle)) return; - - Game::source_t *source = Game::sourceFiles[handle]; - - while (source->scriptstack) - { - Game::script_t* script = source->scriptstack; - source->scriptstack = source->scriptstack->next; - Game::FreeMemory(script); - } - - while (source->tokens) - { - Game::token_t* token = source->tokens; - source->tokens = source->tokens->next; - Game::FreeMemory(token); - } - - while (source->defines) - { - Game::define_t* define = source->defines; - source->defines = source->defines->next; - Game::FreeMemory(define); - } - - while (source->indentstack) - { - Game::indent_t* indent = source->indentstack; - source->indentstack = source->indentstack->next; - Utils::Memory::Free(indent); - } - - if (source->definehash) Utils::Memory::Free(source->definehash); - - Utils::Memory::Free(source); - - Game::sourceFiles[handle] = nullptr; - } - - void Menus::FreeMenu(Game::menuDef_t* menudef) - { - // Do i need to free expressions and strings? - // Or does the game take care of it? - // Seems like it does... - - if (menudef->items) - { - // Seems like this is obsolete as well, - // as the game handles the memory - - //for (int i = 0; i < menudef->itemCount; ++i) - //{ - // Game::Menu_FreeItemMemory(menudef->items[i]); - //} - - Utils::Memory::Free(menudef->items); - } - - Utils::Memory::Free(menudef); - } - - void Menus::FreeMenuList(Game::MenuList* menuList) - { - if (!menuList) return; - - // Keep our compiler happy - Game::MenuList list = { menuList->name, menuList->menuCount, menuList->menus }; - - if (list.name) - { - Utils::Memory::Free(list.name); - } - - if (list.menus) - { - Utils::Memory::Free(list.menus); - } - - Utils::Memory::Free(menuList); - } - - void Menus::RemoveMenu(std::string menu) - { - auto i = Menus::MenuList.find(menu); - if(i != Menus::MenuList.end()) - { - if (i->second) Menus::FreeMenu(i->second); - i = Menus::MenuList.erase(i); - } - } - - void Menus::RemoveMenu(Game::menuDef_t* menudef) - { - for (auto i = Menus::MenuList.begin(); i != Menus::MenuList.end();) - { - if (i->second == menudef) - { - Menus::FreeMenu(menudef); - i = Menus::MenuList.erase(i); - } - else - { - ++i; - } - } - } - - void Menus::RemoveMenuList(std::string menuList) - { - auto i = Menus::MenuListList.find(menuList); - if (i != Menus::MenuListList.end()) - { - if (i->second) - { - for (auto j = 0; j < i->second->menuCount; ++j) - { - Menus::RemoveMenu(i->second->menus[j]); - } - - Menus::FreeMenuList(i->second); - } - - i = Menus::MenuListList.erase(i); - } - } - - // This is actually a really important function - // It checks if we have already loaded the menu we passed and replaces its instances in memory - // Due to deallocating the old menu, the game might crash on not being able to handle its old instance - // So we need to override it in our menu lists and the game's ui context - // EDIT: We might also remove the old instances inside RemoveMenu - // EDIT2: Removing old instances without having a menu to replace them with might leave a nullptr - // EDIT3: Wouldn't it be better to check if the new menu we're trying to load has already been loaded and not was not deallocated and return that one instead of loading a new one? - void Menus::OverrideMenu(Game::menuDef_t *menu) - { - if (!menu || !menu->window.name) return; - std::string name = menu->window.name; - - // Find the old menu - auto i = Menus::MenuList.find(name); - if (i != Menus::MenuList.end()) - { - // We have found it, *yay* - Game::menuDef_t* oldMenu = i->second; - - // Replace every old instance with our new one in the ui context - for (int j = 0; j < Game::uiContext->menuCount; ++j) - { - if (Game::uiContext->menus[j] == oldMenu) - { - Game::uiContext->menus[j] = menu; - } - } - - // Replace every old instance with our new one in our menu lists - for (auto j = Menus::MenuListList.begin(); j != Menus::MenuListList.end(); ++j) - { - Game::MenuList* list = j->second; - - if (list && list->menus) - { - for (int k = 0; k < list->menuCount; ++k) - { - if (list->menus[k] == oldMenu) - { - list->menus[k] = menu; - } - } - } - } - } - } - - void Menus::RemoveMenuList(Game::MenuList* menuList) - { - if (!menuList || !menuList->name) return; - Menus::RemoveMenuList(menuList->name); - } - - void Menus::FreeEverything() - { - for (auto i = Menus::MenuListList.begin(); i != Menus::MenuListList.end(); ++i) - { - Menus::FreeMenuList(i->second); - } - - Menus::MenuListList.clear(); - - for (auto i = Menus::MenuList.begin(); i != Menus::MenuList.end(); ++i) - { - Menus::FreeMenu(i->second); - } - - Menus::MenuList.clear(); - } - - Game::XAssetHeader Menus::MenuLoad(Game::XAssetType /*type*/, std::string filename) - { - return { Game::Menus_FindByName(Game::uiContext, filename.data()) }; - } - - Game::XAssetHeader Menus::MenuFileLoad(Game::XAssetType type, std::string filename) - { - Game::XAssetHeader header = { 0 }; - - Game::MenuList* menuList = Game::DB_FindXAssetHeader(type, filename.data()).menuList; - header.menuList = menuList; - - // Free the last menulist and ui context, as we have to rebuild it with the new menus - if (Menus::MenuListList.find(filename) != Menus::MenuListList.end()) - { - Game::MenuList* list = Menus::MenuListList[filename]; - - for (int i = 0; list && list->menus && i < list->menuCount; ++i) - { - Menus::RemoveMenuFromContext(Game::uiContext, list->menus[i]); - } - - Menus::RemoveMenuList(filename); - } - - if (menuList && reinterpret_cast(menuList) != 0xDDDDDDDD) - { - // Parse scriptmenus! - if (menuList->menus[0]->window.name == "default_menu"s || Utils::String::EndsWith(filename, ".menu")) - { - if (FileSystem::File(filename).exists()) - { - header.menuList = Menus::LoadScriptMenu(filename.data()); - - // Reset, if we didn't find scriptmenus - if (!header.menuList) - { - header.menuList = menuList; - } - } - } - else - { - header.menuList = Menus::LoadMenuList(menuList); - } - } - else - { - header.menuList = nullptr; - } - - return header; - } - - bool Menus::IsMenuVisible(Game::UiContext *dc, Game::menuDef_t *menu) - { - if (menu && menu->window.name) - { - if (menu->window.name == "connect"s) // Check if we're supposed to draw the loadscreen - { - Game::menuDef_t* originalConnect = AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MENU, "connect").menu; - - if (originalConnect == menu) // Check if we draw the original loadscreen - { - if (Menus::MenuList.find("connect") != Menus::MenuList.end()) // Check if we have a custom loadscreen, to prevent drawing the original one on top - { - return false; - } - } - } - } - - return Game::Menu_IsVisible(dc, menu); - } - - void Menus::RemoveMenuFromContext(Game::UiContext *dc, Game::menuDef_t *menu) - { - // Search menu in context - int i = 0; - for (; i < dc->menuCount; ++i) - { - if (dc->menus[i] == menu) - { - break; - } - } - - // Remove from stack - if (i < dc->menuCount) - { - for (; i < dc->menuCount - 1; ++i) - { - dc->menus[i] = dc->menus[i + 1]; - } - - // Clear last menu - dc->menus[--dc->menuCount] = 0; - } - } - - void Menus::Add(std::string menu) - { - Menus::CustomMenus.push_back(menu); - } - - Menus::Menus() - { - if (Dedicated::IsEnabled()) return; - - // Ensure everything is zero'ed - Menus::FreeEverything(); - - // Intercept asset finding - AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENU, Menus::MenuLoad); - AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENULIST, Menus::MenuFileLoad); - - // Don't open connect menu - //Utils::Hook::Nop(0x428E48, 5); - - // Use the connect menu open call to update server motds - Utils::Hook(0x428E48, []() - { - if (!Party::GetMotd().empty()) - { - Dvar::Var("didyouknow").set(Party::GetMotd()); - } - }, HOOK_CALL).install()->quick(); - - // Intercept menu painting - Utils::Hook(0x4FFBDF, Menus::IsMenuVisible, HOOK_CALL).install()->quick(); - - // disable the 2 new tokens in ItemParse_rect - Utils::Hook::Set(0x640693, 0xEB); - - // don't load ASSET_TYPE_MENU assets for every menu (might cause patch menus to fail) - Utils::Hook::Nop(0x453406, 5); - - //make Com_Error and similar go back to main_text instead of menu_xboxlive. - Utils::Hook::SetString(0x6FC790, "main_text"); - - Command::Add("openmenu", [] (Command::Params* params) - { - if (params->length() != 2) - { - Logger::Print("USAGE: openmenu \n"); - return; - } - - // Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus. - if (Dvar::Var("cl_ingame").get()) - { - Game::Key_SetCatcher(0, 16); - } - - Game::Menus_OpenByName(Game::uiContext, params->get(1)); - }); - - Command::Add("reloadmenus", [] (Command::Params*) - { - // Close all menus - Game::Menus_CloseAll(Game::uiContext); - - // Free custom menus - Menus::FreeEverything(); - - // Only disconnect if in-game, context is updated automatically! - if (Game::CL_IsCgameInitialized()) - { - Game::Cbuf_AddText(0, "disconnect\n"); - } - else - { - // Reinitialize ui context - Utils::Hook::Call(0x401700)(); - - // Reopen main menu - Game::Menus_OpenByName(Game::uiContext, "main_text"); - } - }); - - Command::Add("mp_QuickMessage", [] (Command::Params*) - { - Command::Execute("openmenu quickmessage"); - }); - - // Define custom menus here - Menus::Add("ui_mp/theater_menu.menu"); - Menus::Add("ui_mp/pc_options_multi.menu"); - Menus::Add("ui_mp/pc_options_game.menu"); - Menus::Add("ui_mp/stats_reset.menu"); - Menus::Add("ui_mp/stats_unlock.menu"); - Menus::Add("ui_mp/security_increase_popmenu.menu"); - Menus::Add("ui_mp/mod_download_popmenu.menu"); - } - - Menus::~Menus() - { - Menus::CustomMenus.clear(); - Menus::FreeEverything(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::vector Menus::CustomMenus; + std::unordered_map Menus::MenuList; + std::unordered_map Menus::MenuListList; + + int Menus::ReserveSourceHandle() + { + // Check if a free slot is available + int i = 1; + for (; i < MAX_SOURCEFILES; ++i) + { + if (!Game::sourceFiles[i]) + break; + } + + if (i >= MAX_SOURCEFILES) + return 0; + + // Reserve it, if yes + Game::sourceFiles[i] = (Game::source_t*)1; + + return i; + } + + Game::script_t* Menus::LoadMenuScript(std::string name, std::string buffer) + { + Game::script_t* script = Game::Script_Alloc(sizeof(Game::script_t) + 1 + buffer.length()); + + strcpy_s(script->filename, sizeof(script->filename), name.data()); + script->buffer = reinterpret_cast(script + 1); + + *(script->buffer + buffer.length()) = '\0'; + + script->script_p = script->buffer; + script->lastscript_p = script->buffer; + script->length = buffer.length(); + script->end_p = &script->buffer[buffer.length()]; + script->line = 1; + script->lastline = 1; + script->tokenavailable = 0; + + Game::Script_SetupTokens(script, reinterpret_cast(0x797F80)); + script->punctuations = reinterpret_cast(0x797F80); + + std::memcpy(script->buffer, buffer.data(), script->length + 1); + + script->length = Game::Script_CleanString(script->buffer); + + return script; + } + + int Menus::LoadMenuSource(std::string name, std::string buffer) + { + int handle = Menus::ReserveSourceHandle(); + if (!Menus::IsValidSourceHandle(handle)) return 0; // No free source slot! + + Game::source_t *source = nullptr; + Game::script_t *script = Menus::LoadMenuScript(name, buffer); + + if (!script) + { + Game::sourceFiles[handle] = nullptr; // Free reserved slot + return 0; + } + + script->next = NULL; + + source = Utils::Memory::Allocate(); + if (!source) + { + Game::FreeMemory(script); + return 0; + } + + strncpy_s(source->filename, 64, "string", 64); + source->scriptstack = script; + source->tokens = NULL; + source->defines = NULL; + source->indentstack = NULL; + source->skip = 0; + source->definehash = (Game::define_t**)Utils::Memory::Allocate(4096); + + Game::sourceFiles[handle] = source; + + return handle; + } + + bool Menus::IsValidSourceHandle(int handle) + { + return (handle > 0 && handle < MAX_SOURCEFILES && Game::sourceFiles[handle]); + } + + int Menus::KeywordHash(char* key) + { + int hash = 0; + + if (*key) + { + int sub = 3523 - reinterpret_cast(key); + do + { + char _chr = *key; + hash += reinterpret_cast(&(key++)[sub]) * tolower(_chr); + } while (*key); + } + + return (static_cast(hash) + static_cast(hash >> 8)) & 0x7F; + } + + Game::menuDef_t* Menus::ParseMenu(int handle) + { + Game::menuDef_t* menu = Utils::Memory::Allocate(); + if (!menu) return nullptr; + + menu->items = Utils::Memory::AllocateArray(512); + if (!menu->items) + { + Utils::Memory::Free(menu); + return nullptr; + } + + Game::pc_token_t token; + if (!Game::PC_ReadTokenHandle(handle, &token) || token.string[0] != '{') + { + Utils::Memory::Free(menu->items); + Utils::Memory::Free(menu); + return nullptr; + } + + while (true) + { + ZeroMemory(&token, sizeof(token)); + + if (!Game::PC_ReadTokenHandle(handle, &token)) + { + Game::PC_SourceError(handle, "end of file inside menu\n"); + break; // Fail + } + + if (*token.string == '}') + { + break; // Success + } + + int idx = Menus::KeywordHash(token.string); + + Game::keywordHash_t* key = Game::menuParseKeywordHash[idx]; + + if (!key) + { + Game::PC_SourceError(handle, "unknown menu keyword %s", token.string); + continue; + } + + if (!key->func(menu, handle)) + { + Game::PC_SourceError(handle, "couldn't parse menu keyword %s", token.string); + break; // Fail + } + } + + Menus::OverrideMenu(menu); + Menus::RemoveMenu(menu->window.name); + Menus::MenuList[menu->window.name] = menu; + + return menu; + } + + std::vector Menus::LoadMenu(std::string menu) + { + std::vector menus; + FileSystem::File menuFile(menu); + + if (menuFile.exists()) + { + Game::pc_token_t token; + int handle = Menus::LoadMenuSource(menu, menuFile.getBuffer()); + + if (Menus::IsValidSourceHandle(handle)) + { + while (true) + { + ZeroMemory(&token, sizeof(token)); + + if (!Game::PC_ReadTokenHandle(handle, &token) || token.string[0] == '}') + { + break; + } + + if (!_stricmp(token.string, "loadmenu")) + { + Game::PC_ReadTokenHandle(handle, &token); + + Utils::Merge(&menus, Menus::LoadMenu(Utils::String::VA("ui_mp\\%s.menu", token.string))); + } + + if (!_stricmp(token.string, "menudef")) + { + Game::menuDef_t* menudef = Menus::ParseMenu(handle); + if (menudef) menus.push_back(menudef); + } + } + + Menus::FreeMenuSource(handle); + } + } + + return menus; + } + + std::vector Menus::LoadMenu(Game::menuDef_t* menudef) + { + std::vector menus = Menus::LoadMenu(Utils::String::VA("ui_mp\\%s.menu", menudef->window.name)); + + if (menus.empty()) + { + // Try loading the original menu, if we can't load our custom one + Game::menuDef_t* originalMenu = AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MENU, menudef->window.name).menu; + + if (originalMenu) + { + menus.push_back(originalMenu); + } + else + { + menus.push_back(menudef); + } + } + + return menus; + } + + Game::MenuList* Menus::LoadScriptMenu(const char* menu) + { + std::vector menus = Menus::LoadMenu(menu); + if (menus.empty()) return nullptr; + + // Allocate new menu list + Game::MenuList* newList = Utils::Memory::Allocate(); + if (!newList) return nullptr; + + newList->menus = Utils::Memory::AllocateArray(menus.size()); + if (!newList->menus) + { + Utils::Memory::Free(newList); + return nullptr; + } + + newList->name = Utils::Memory::DuplicateString(menu); + newList->menuCount = menus.size(); + + // Copy new menus + std::memcpy(newList->menus, menus.data(), menus.size() * sizeof(Game::menuDef_t *)); + + Menus::RemoveMenuList(newList->name); + Menus::MenuListList[newList->name] = newList; + + return newList; + } + + Game::MenuList* Menus::LoadMenuList(Game::MenuList* menuList) + { + std::vector menus; + + for (int i = 0; i < menuList->menuCount; ++i) + { + if (!menuList->menus[i]) + { + continue; + } + + Utils::Merge(&menus, Menus::LoadMenu(menuList->menus[i])); + } + + // Load custom menus + if (menuList->name == "ui_mp/code.txt"s) // Should be menus, but code is loaded ingame + { + for (auto menu : Menus::CustomMenus) + { + Utils::Merge(&menus, Menus::LoadMenu(menu)); + } + } + + // Allocate new menu list + Game::MenuList* newList = Utils::Memory::Allocate(); + if (!newList) return menuList; + + size_t size = menus.size(); + newList->menus = Utils::Memory::AllocateArray(size); + if (!newList->menus) + { + Utils::Memory::Free(newList); + return menuList; + } + + newList->name = Utils::Memory::DuplicateString(menuList->name); + newList->menuCount = size; + + // Copy new menus + std::memcpy(newList->menus, menus.data(), size * sizeof(Game::menuDef_t *)); + + Menus::RemoveMenuList(newList->name); + Menus::MenuListList[newList->name] = newList; + + return newList; + } + + void Menus::FreeMenuSource(int handle) + { + if (!Menus::IsValidSourceHandle(handle)) return; + + Game::source_t *source = Game::sourceFiles[handle]; + + while (source->scriptstack) + { + Game::script_t* script = source->scriptstack; + source->scriptstack = source->scriptstack->next; + Game::FreeMemory(script); + } + + while (source->tokens) + { + Game::token_t* token = source->tokens; + source->tokens = source->tokens->next; + Game::FreeMemory(token); + } + + while (source->defines) + { + Game::define_t* define = source->defines; + source->defines = source->defines->next; + Game::FreeMemory(define); + } + + while (source->indentstack) + { + Game::indent_t* indent = source->indentstack; + source->indentstack = source->indentstack->next; + Utils::Memory::Free(indent); + } + + if (source->definehash) Utils::Memory::Free(source->definehash); + + Utils::Memory::Free(source); + + Game::sourceFiles[handle] = nullptr; + } + + void Menus::FreeMenu(Game::menuDef_t* menudef) + { + // Do i need to free expressions and strings? + // Or does the game take care of it? + // Seems like it does... + + if (menudef->items) + { + // Seems like this is obsolete as well, + // as the game handles the memory + + //for (int i = 0; i < menudef->itemCount; ++i) + //{ + // Game::Menu_FreeItemMemory(menudef->items[i]); + //} + + Utils::Memory::Free(menudef->items); + } + + Utils::Memory::Free(menudef); + } + + void Menus::FreeMenuList(Game::MenuList* menuList) + { + if (!menuList) return; + + // Keep our compiler happy + Game::MenuList list = { menuList->name, menuList->menuCount, menuList->menus }; + + if (list.name) + { + Utils::Memory::Free(list.name); + } + + if (list.menus) + { + Utils::Memory::Free(list.menus); + } + + Utils::Memory::Free(menuList); + } + + void Menus::RemoveMenu(std::string menu) + { + auto i = Menus::MenuList.find(menu); + if(i != Menus::MenuList.end()) + { + if (i->second) Menus::FreeMenu(i->second); + i = Menus::MenuList.erase(i); + } + } + + void Menus::RemoveMenu(Game::menuDef_t* menudef) + { + for (auto i = Menus::MenuList.begin(); i != Menus::MenuList.end();) + { + if (i->second == menudef) + { + Menus::FreeMenu(menudef); + i = Menus::MenuList.erase(i); + } + else + { + ++i; + } + } + } + + void Menus::RemoveMenuList(std::string menuList) + { + auto i = Menus::MenuListList.find(menuList); + if (i != Menus::MenuListList.end()) + { + if (i->second) + { + for (auto j = 0; j < i->second->menuCount; ++j) + { + Menus::RemoveMenu(i->second->menus[j]); + } + + Menus::FreeMenuList(i->second); + } + + i = Menus::MenuListList.erase(i); + } + } + + // This is actually a really important function + // It checks if we have already loaded the menu we passed and replaces its instances in memory + // Due to deallocating the old menu, the game might crash on not being able to handle its old instance + // So we need to override it in our menu lists and the game's ui context + // EDIT: We might also remove the old instances inside RemoveMenu + // EDIT2: Removing old instances without having a menu to replace them with might leave a nullptr + // EDIT3: Wouldn't it be better to check if the new menu we're trying to load has already been loaded and not was not deallocated and return that one instead of loading a new one? + void Menus::OverrideMenu(Game::menuDef_t *menu) + { + if (!menu || !menu->window.name) return; + std::string name = menu->window.name; + + // Find the old menu + auto i = Menus::MenuList.find(name); + if (i != Menus::MenuList.end()) + { + // We have found it, *yay* + Game::menuDef_t* oldMenu = i->second; + + // Replace every old instance with our new one in the ui context + for (int j = 0; j < Game::uiContext->menuCount; ++j) + { + if (Game::uiContext->menus[j] == oldMenu) + { + Game::uiContext->menus[j] = menu; + } + } + + // Replace every old instance with our new one in our menu lists + for (auto j = Menus::MenuListList.begin(); j != Menus::MenuListList.end(); ++j) + { + Game::MenuList* list = j->second; + + if (list && list->menus) + { + for (int k = 0; k < list->menuCount; ++k) + { + if (list->menus[k] == oldMenu) + { + list->menus[k] = menu; + } + } + } + } + } + } + + void Menus::RemoveMenuList(Game::MenuList* menuList) + { + if (!menuList || !menuList->name) return; + Menus::RemoveMenuList(menuList->name); + } + + void Menus::FreeEverything() + { + for (auto i = Menus::MenuListList.begin(); i != Menus::MenuListList.end(); ++i) + { + Menus::FreeMenuList(i->second); + } + + Menus::MenuListList.clear(); + + for (auto i = Menus::MenuList.begin(); i != Menus::MenuList.end(); ++i) + { + Menus::FreeMenu(i->second); + } + + Menus::MenuList.clear(); + } + + Game::XAssetHeader Menus::MenuLoad(Game::XAssetType /*type*/, std::string filename) + { + return { Game::Menus_FindByName(Game::uiContext, filename.data()) }; + } + + Game::XAssetHeader Menus::MenuFileLoad(Game::XAssetType type, std::string filename) + { + Game::XAssetHeader header = { 0 }; + + Game::MenuList* menuList = Game::DB_FindXAssetHeader(type, filename.data()).menuList; + header.menuList = menuList; + + // Free the last menulist and ui context, as we have to rebuild it with the new menus + if (Menus::MenuListList.find(filename) != Menus::MenuListList.end()) + { + Game::MenuList* list = Menus::MenuListList[filename]; + + for (int i = 0; list && list->menus && i < list->menuCount; ++i) + { + Menus::RemoveMenuFromContext(Game::uiContext, list->menus[i]); + } + + Menus::RemoveMenuList(filename); + } + + if (menuList && reinterpret_cast(menuList) != 0xDDDDDDDD) + { + // Parse scriptmenus! + if (menuList->menus[0]->window.name == "default_menu"s || Utils::String::EndsWith(filename, ".menu")) + { + if (FileSystem::File(filename).exists()) + { + header.menuList = Menus::LoadScriptMenu(filename.data()); + + // Reset, if we didn't find scriptmenus + if (!header.menuList) + { + header.menuList = menuList; + } + } + } + else + { + header.menuList = Menus::LoadMenuList(menuList); + } + } + else + { + header.menuList = nullptr; + } + + return header; + } + + bool Menus::IsMenuVisible(Game::UiContext *dc, Game::menuDef_t *menu) + { + if (menu && menu->window.name) + { + if (menu->window.name == "connect"s) // Check if we're supposed to draw the loadscreen + { + Game::menuDef_t* originalConnect = AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_MENU, "connect").menu; + + if (originalConnect == menu) // Check if we draw the original loadscreen + { + if (Menus::MenuList.find("connect") != Menus::MenuList.end()) // Check if we have a custom loadscreen, to prevent drawing the original one on top + { + return false; + } + } + } + } + + return Game::Menu_IsVisible(dc, menu); + } + + void Menus::RemoveMenuFromContext(Game::UiContext *dc, Game::menuDef_t *menu) + { + // Search menu in context + int i = 0; + for (; i < dc->menuCount; ++i) + { + if (dc->menus[i] == menu) + { + break; + } + } + + // Remove from stack + if (i < dc->menuCount) + { + for (; i < dc->menuCount - 1; ++i) + { + dc->menus[i] = dc->menus[i + 1]; + } + + // Clear last menu + dc->menus[--dc->menuCount] = 0; + } + } + + void Menus::Add(std::string menu) + { + Menus::CustomMenus.push_back(menu); + } + + Menus::Menus() + { + if (Dedicated::IsEnabled()) return; + + // Ensure everything is zero'ed + Menus::FreeEverything(); + + // Intercept asset finding + AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENU, Menus::MenuLoad); + AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_MENULIST, Menus::MenuFileLoad); + + // Don't open connect menu + //Utils::Hook::Nop(0x428E48, 5); + + // Use the connect menu open call to update server motds + Utils::Hook(0x428E48, []() + { + if (!Party::GetMotd().empty()) + { + Dvar::Var("didyouknow").set(Party::GetMotd()); + } + }, HOOK_CALL).install()->quick(); + + // Intercept menu painting + Utils::Hook(0x4FFBDF, Menus::IsMenuVisible, HOOK_CALL).install()->quick(); + + // disable the 2 new tokens in ItemParse_rect + Utils::Hook::Set(0x640693, 0xEB); + + // don't load ASSET_TYPE_MENU assets for every menu (might cause patch menus to fail) + Utils::Hook::Nop(0x453406, 5); + + //make Com_Error and similar go back to main_text instead of menu_xboxlive. + Utils::Hook::SetString(0x6FC790, "main_text"); + + Command::Add("openmenu", [] (Command::Params* params) + { + if (params->length() != 2) + { + Logger::Print("USAGE: openmenu \n"); + return; + } + + // Not quite sure if we want to do this if we're not ingame, but it's only needed for ingame menus. + if (Dvar::Var("cl_ingame").get()) + { + Game::Key_SetCatcher(0, 16); + } + + Game::Menus_OpenByName(Game::uiContext, params->get(1)); + }); + + Command::Add("reloadmenus", [] (Command::Params*) + { + // Close all menus + Game::Menus_CloseAll(Game::uiContext); + + // Free custom menus + Menus::FreeEverything(); + + // Only disconnect if in-game, context is updated automatically! + if (Game::CL_IsCgameInitialized()) + { + Game::Cbuf_AddText(0, "disconnect\n"); + } + else + { + // Reinitialize ui context + Utils::Hook::Call(0x401700)(); + + // Reopen main menu + Game::Menus_OpenByName(Game::uiContext, "main_text"); + } + }); + + Command::Add("mp_QuickMessage", [] (Command::Params*) + { + Command::Execute("openmenu quickmessage"); + }); + + // Define custom menus here + Menus::Add("ui_mp/theater_menu.menu"); + Menus::Add("ui_mp/pc_options_multi.menu"); + Menus::Add("ui_mp/pc_options_game.menu"); + Menus::Add("ui_mp/stats_reset.menu"); + Menus::Add("ui_mp/stats_unlock.menu"); + Menus::Add("ui_mp/security_increase_popmenu.menu"); + Menus::Add("ui_mp/mod_download_popmenu.menu"); + } + + Menus::~Menus() + { + Menus::CustomMenus.clear(); + Menus::FreeEverything(); + } +} diff --git a/src/Components/Modules/Menus.hpp b/src/Components/Modules/Menus.hpp index 11af104b..fc5d0504 100644 --- a/src/Components/Modules/Menus.hpp +++ b/src/Components/Modules/Menus.hpp @@ -1,60 +1,60 @@ -#define MAX_SOURCEFILES 64 -#undef LoadMenu - -namespace Components -{ - class Menus : public Component - { - public: - Menus(); - ~Menus(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Menus"; }; -#endif - - static void FreeEverything(); - - static void Add(std::string menu); - - private: - static std::unordered_map MenuList; - static std::unordered_map MenuListList; - static std::vector CustomMenus; - - static Game::XAssetHeader MenuLoad(Game::XAssetType type, std::string filename); - static Game::XAssetHeader MenuFileLoad(Game::XAssetType type, std::string filename); - - static Game::MenuList* LoadMenuList(Game::MenuList* menuList); - static Game::MenuList* LoadScriptMenu(const char* menu); - static std::vector LoadMenu(Game::menuDef_t* menudef); - static std::vector LoadMenu(std::string file); - - static Game::script_t* LoadMenuScript(std::string name, std::string buffer); - static int LoadMenuSource(std::string name, std::string buffer); - - static int ReserveSourceHandle(); - static bool IsValidSourceHandle(int handle); - - static Game::menuDef_t* ParseMenu(int handle); - - static void FreeMenuSource(int handle); - - static void FreeMenuList(Game::MenuList* menuList); - static void FreeMenu(Game::menuDef_t* menudef); - - static void RemoveMenu(std::string menu); - static void RemoveMenu(Game::menuDef_t* menudef); - static void RemoveMenuList(std::string menuList); - static void RemoveMenuList(Game::MenuList* menuList); - - static void OverrideMenu(Game::menuDef_t *menu); - - static bool IsMenuVisible(Game::UiContext *dc, Game::menuDef_t *menu); - - static void RemoveMenuFromContext(Game::UiContext *dc, Game::menuDef_t *menu); - - // Ugly! - static int KeywordHash(char* key); - }; -} +#define MAX_SOURCEFILES 64 +#undef LoadMenu + +namespace Components +{ + class Menus : public Component + { + public: + Menus(); + ~Menus(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Menus"; }; +#endif + + static void FreeEverything(); + + static void Add(std::string menu); + + private: + static std::unordered_map MenuList; + static std::unordered_map MenuListList; + static std::vector CustomMenus; + + static Game::XAssetHeader MenuLoad(Game::XAssetType type, std::string filename); + static Game::XAssetHeader MenuFileLoad(Game::XAssetType type, std::string filename); + + static Game::MenuList* LoadMenuList(Game::MenuList* menuList); + static Game::MenuList* LoadScriptMenu(const char* menu); + static std::vector LoadMenu(Game::menuDef_t* menudef); + static std::vector LoadMenu(std::string file); + + static Game::script_t* LoadMenuScript(std::string name, std::string buffer); + static int LoadMenuSource(std::string name, std::string buffer); + + static int ReserveSourceHandle(); + static bool IsValidSourceHandle(int handle); + + static Game::menuDef_t* ParseMenu(int handle); + + static void FreeMenuSource(int handle); + + static void FreeMenuList(Game::MenuList* menuList); + static void FreeMenu(Game::menuDef_t* menudef); + + static void RemoveMenu(std::string menu); + static void RemoveMenu(Game::menuDef_t* menudef); + static void RemoveMenuList(std::string menuList); + static void RemoveMenuList(Game::MenuList* menuList); + + static void OverrideMenu(Game::menuDef_t *menu); + + static bool IsMenuVisible(Game::UiContext *dc, Game::menuDef_t *menu); + + static void RemoveMenuFromContext(Game::UiContext *dc, Game::menuDef_t *menu); + + // Ugly! + static int KeywordHash(char* key); + }; +} diff --git a/src/Components/Modules/ModList.cpp b/src/Components/Modules/ModList.cpp index c47281c0..8f4f434c 100644 --- a/src/Components/Modules/ModList.cpp +++ b/src/Components/Modules/ModList.cpp @@ -1,107 +1,107 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::vector ModList::Mods; - unsigned int ModList::CurrentMod; - - bool ModList::HasMod(std::string modName) - { - auto list = FileSystem::GetSysFileList(Dvar::Var("fs_basepath").get() + "\\mods", "", true); - - for (auto mod : list) - { - if (mod == modName) - { - return true; - } - } - - return false; - } - - unsigned int ModList::GetItemCount() - { - return ModList::Mods.size(); - } - - const char* ModList::GetItemText(unsigned int index, int /*column*/) - { - if (index < ModList::Mods.size()) - { - return ModList::Mods[index].data(); - } - - return "..."; - } - - void ModList::Select(unsigned int index) - { - ModList::CurrentMod = index; - } - - void ModList::UIScript_LoadMods(UIScript::Token) - { - auto folder = Dvar::Var("fs_basepath").get() + "\\mods"; - Game::Com_Printf(0, "Searching for mods in %s...\n", folder.data()); - ModList::Mods = FileSystem::GetSysFileList(folder, "", true); - Game::Com_Printf(0, "Found %i mods!\n", ModList::Mods.size()); - } - - void ModList::UIScript_RunMod(UIScript::Token) - { - if (ModList::CurrentMod < ModList::Mods.size()) - { - ModList::RunMod(ModList::Mods[ModList::CurrentMod]); - } - } - - void ModList::UIScript_ClearMods(UIScript::Token) - { - auto fsGame = Dvar::Var("fs_game"); - fsGame.set(""); - fsGame.get()->modified = true; - - if (Dvar::Var("cl_modVidRestart").get()) - { - Game::Cmd_ExecuteSingleCommand(0, 0, "vid_restart"); - } - else - { - Game::Cmd_ExecuteSingleCommand(0, 0, "closemenu mods_menu"); - } - } - - void ModList::RunMod(std::string mod) - { - auto fsGame = Dvar::Var("fs_game"); - fsGame.set(Utils::String::VA("mods/%s", mod.data())); - fsGame.get()->modified = true; - - if (Dvar::Var("cl_modVidRestart").get()) - { - Command::Execute("vid_restart", false); - } - else - { - Command::Execute("closemenu mods_menu", false); - } - } - - ModList::ModList() - { - ModList::CurrentMod = 0; - Dvar::Register("cl_modVidRestart", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Perform a vid_restart when loading a mod."); - - UIScript::Add("LoadMods", ModList::UIScript_LoadMods); - UIScript::Add("RunMod", ModList::UIScript_RunMod); - UIScript::Add("ClearMods", ModList::UIScript_ClearMods); - - UIFeeder::Add(9.0f, ModList::GetItemCount, ModList::GetItemText, ModList::Select); - } - - ModList::~ModList() - { - ModList::Mods.clear(); - } +#include "STDInclude.hpp" + +namespace Components +{ + std::vector ModList::Mods; + unsigned int ModList::CurrentMod; + + bool ModList::HasMod(std::string modName) + { + auto list = FileSystem::GetSysFileList(Dvar::Var("fs_basepath").get() + "\\mods", "", true); + + for (auto mod : list) + { + if (mod == modName) + { + return true; + } + } + + return false; + } + + unsigned int ModList::GetItemCount() + { + return ModList::Mods.size(); + } + + const char* ModList::GetItemText(unsigned int index, int /*column*/) + { + if (index < ModList::Mods.size()) + { + return ModList::Mods[index].data(); + } + + return "..."; + } + + void ModList::Select(unsigned int index) + { + ModList::CurrentMod = index; + } + + void ModList::UIScript_LoadMods(UIScript::Token) + { + auto folder = Dvar::Var("fs_basepath").get() + "\\mods"; + Game::Com_Printf(0, "Searching for mods in %s...\n", folder.data()); + ModList::Mods = FileSystem::GetSysFileList(folder, "", true); + Game::Com_Printf(0, "Found %i mods!\n", ModList::Mods.size()); + } + + void ModList::UIScript_RunMod(UIScript::Token) + { + if (ModList::CurrentMod < ModList::Mods.size()) + { + ModList::RunMod(ModList::Mods[ModList::CurrentMod]); + } + } + + void ModList::UIScript_ClearMods(UIScript::Token) + { + auto fsGame = Dvar::Var("fs_game"); + fsGame.set(""); + fsGame.get()->modified = true; + + if (Dvar::Var("cl_modVidRestart").get()) + { + Game::Cmd_ExecuteSingleCommand(0, 0, "vid_restart"); + } + else + { + Game::Cmd_ExecuteSingleCommand(0, 0, "closemenu mods_menu"); + } + } + + void ModList::RunMod(std::string mod) + { + auto fsGame = Dvar::Var("fs_game"); + fsGame.set(Utils::String::VA("mods/%s", mod.data())); + fsGame.get()->modified = true; + + if (Dvar::Var("cl_modVidRestart").get()) + { + Command::Execute("vid_restart", false); + } + else + { + Command::Execute("closemenu mods_menu", false); + } + } + + ModList::ModList() + { + ModList::CurrentMod = 0; + Dvar::Register("cl_modVidRestart", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Perform a vid_restart when loading a mod."); + + UIScript::Add("LoadMods", ModList::UIScript_LoadMods); + UIScript::Add("RunMod", ModList::UIScript_RunMod); + UIScript::Add("ClearMods", ModList::UIScript_ClearMods); + + UIFeeder::Add(9.0f, ModList::GetItemCount, ModList::GetItemText, ModList::Select); + } + + ModList::~ModList() + { + ModList::Mods.clear(); + } } \ No newline at end of file diff --git a/src/Components/Modules/ModList.hpp b/src/Components/Modules/ModList.hpp index dddeb905..9f6037d1 100644 --- a/src/Components/Modules/ModList.hpp +++ b/src/Components/Modules/ModList.hpp @@ -1,28 +1,28 @@ -namespace Components -{ - class ModList : public Component - { - public: - ModList(); - ~ModList(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "ModList"; }; -#endif - - static void RunMod(std::string mod); - - private: - static std::vector Mods; - static unsigned int CurrentMod; - - static bool HasMod(std::string modName); - - static unsigned int GetItemCount(); - static const char* GetItemText(unsigned int index, int column); - static void Select(unsigned int index); - static void UIScript_LoadMods(UIScript::Token); - static void UIScript_RunMod(UIScript::Token); - static void UIScript_ClearMods(UIScript::Token); - }; +namespace Components +{ + class ModList : public Component + { + public: + ModList(); + ~ModList(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "ModList"; }; +#endif + + static void RunMod(std::string mod); + + private: + static std::vector Mods; + static unsigned int CurrentMod; + + static bool HasMod(std::string modName); + + static unsigned int GetItemCount(); + static const char* GetItemText(unsigned int index, int column); + static void Select(unsigned int index); + static void UIScript_LoadMods(UIScript::Token); + static void UIScript_RunMod(UIScript::Token); + static void UIScript_ClearMods(UIScript::Token); + }; } \ No newline at end of file diff --git a/src/Components/Modules/ModelSurfs.cpp b/src/Components/Modules/ModelSurfs.cpp index 5a3f47bf..c9acb0ad 100644 --- a/src/Components/Modules/ModelSurfs.cpp +++ b/src/Components/Modules/ModelSurfs.cpp @@ -1,348 +1,348 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::unordered_map ModelSurfs::BufferMap; - std::unordered_map ModelSurfs::AllocMap; - - IUnknown* ModelSurfs::GetBuffer(void* buffer) - { - return ModelSurfs::BufferMap[buffer]; - } - - void ModelSurfs::SetBuffer(char /*streamHandle*/, void* buffer, IUnknown** bufferOut, int* offsetOut) - { - *offsetOut = 0; - *bufferOut = ModelSurfs::BufferMap[buffer]; - } - - void ModelSurfs::CreateBuffers(Game::XModelSurfs* surfs) - { - for (int i = 0; i < surfs->numSurfaces; ++i) - { - Game::XSurface* surface = &surfs->surfaces[i]; - if (surface->zoneHandle == -1) - { - IDirect3DVertexBuffer9* vertexBuffer; - IDirect3DIndexBuffer9* indexBuffer; - - Game::Load_VertexBuffer(surface->verts0, &vertexBuffer, surface->vertCount * 32); - Game::Load_IndexBuffer(surface->triIndices, &indexBuffer, surface->triCount * 3); - - ModelSurfs::BufferMap[surface->verts0] = vertexBuffer; - ModelSurfs::BufferMap[surface->triIndices] = indexBuffer; - } - } - } - - Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(std::string name) - { - Utils::Memory::Allocator allocator; - FileSystem::FileReader model(Utils::String::VA("models/%s", name.data())); - - if (!model.exists()) - { -#ifdef DEBUG - if (Flags::HasFlag("dump")) - { - FILE* fp = nullptr; - if (!fopen_s(&fp, "dump.cfg", "a") && fp) - { - fprintf(fp, "dumpraw %s\n", model.getName().data()); - fclose(fp); - } - - return nullptr; - } -#endif - - Logger::Error("Loading model %s failed!", name.data()); - } - - Game::CModelHeader header; - if (!model.read(&header, sizeof header)) - { - Logger::Error("Reading header for model %s failed!", name.data()); - } - - if (header.version != 1) - { - Logger::Error("Model %s has an invalid version %d (should be 1)!", name.data(), header.version); - } - - // Allocate section buffers - header.sectionHeader[Game::SECTION_MAIN].buffer = Utils::Memory::Allocate(header.sectionHeader[Game::SECTION_MAIN].size); - header.sectionHeader[Game::SECTION_INDEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_INDEX].size, 16); - header.sectionHeader[Game::SECTION_VERTEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_VERTEX].size, 16); - header.sectionHeader[Game::SECTION_FIXUP].buffer = allocator.allocateArray(header.sectionHeader[Game::SECTION_FIXUP].size); - - // Load section data - for (int i = 0; i < ARRAY_SIZE(header.sectionHeader); ++i) - { - model.seek(header.sectionHeader[i].offset, FS_SEEK_SET); - if (!model.read(header.sectionHeader[i].buffer, header.sectionHeader[i].size)) - { - Logger::Error("Reading section %d for model %s failed!", i, name.data()); - } - } - - // Fixup sections - unsigned int* fixups = reinterpret_cast(header.sectionHeader[Game::SECTION_FIXUP].buffer); - for (int i = 0; i < 3; ++i) - { - Game::CModelSectionHeader* section = &header.sectionHeader[i]; - for (int j = section->fixupStart; j < section->fixupStart + section->fixupCount; ++j) - { - unsigned int fixup = fixups[j]; - *reinterpret_cast(reinterpret_cast(section->buffer) + (fixup >> 3)) += reinterpret_cast(header.sectionHeader[fixup & 3].buffer); - } - } - - // Store allocation data (not sure if this is correct) - Game::CModelAllocData* allocationData = Utils::Memory::AllocateArray(); - allocationData->mainArray = header.sectionHeader[Game::SECTION_MAIN].buffer; - allocationData->indexBuffer = header.sectionHeader[Game::SECTION_INDEX].buffer; - allocationData->vertexBuffer = header.sectionHeader[Game::SECTION_VERTEX].buffer; - - AssertSize(Game::XSurface, 64); - Game::XModelSurfs* modelSurfs = reinterpret_cast(allocationData->mainArray); - Game::XSurface* tempSurfaces = allocator.allocateArray(modelSurfs->numSurfaces); - char* surfaceData = reinterpret_cast(modelSurfs->surfaces); - - if (ModelSurfs::AllocMap.find(modelSurfs->name) != ModelSurfs::AllocMap.end()) - { - Game::CModelAllocData* allocData = ModelSurfs::AllocMap[modelSurfs->name]; - - if (allocData) - { - Utils::Memory::FreeAlign(allocData->indexBuffer); - Utils::Memory::FreeAlign(allocData->vertexBuffer); - Utils::Memory::Free(allocData->mainArray); - Utils::Memory::Free(allocData); - } - } - - ModelSurfs::AllocMap[modelSurfs->name] = allocationData; - *reinterpret_cast(reinterpret_cast(allocationData->mainArray) + 44) = allocationData; - - for (int i = 0; i < modelSurfs->numSurfaces; ++i) - { - char* source = &surfaceData[i * 84]; - - std::memcpy(&tempSurfaces[i], source, 12); - std::memcpy(&tempSurfaces[i].triIndices, source + 16, 20); - std::memcpy(&tempSurfaces[i].vertListCount, source + 40, 8); - std::memcpy(&tempSurfaces[i].partBits, source + 52, 24); - tempSurfaces[i].zoneHandle = -1; // Fake handle for buffer interception - } - - std::memcpy(surfaceData, tempSurfaces, 64 * modelSurfs->numSurfaces); - - ModelSurfs::CreateBuffers(modelSurfs); - - return modelSurfs; - } - - bool ModelSurfs::LoadSurfaces(Game::XModel* model) - { - if (!model) return false; - - bool changed = false; - short surfCount = 0; - - for (char i = 0; i < model->numLods; ++i) - { - Game::XModelSurfs* surfs = model->lodInfo[i].modelSurfs; - - if (!surfs->surfaces) - { - AssertOffset(Game::XModelLodInfo, partBits, 12); - Game::XModelSurfs* newSurfs = ModelSurfs::LoadXModelSurfaces(surfs->name); - if (!newSurfs) continue; - - surfs->surfaces = newSurfs->surfaces; - surfs->numSurfaces = newSurfs->numSurfaces; - - model->lodInfo[i].surfs = newSurfs->surfaces; - std::memcpy(&model->lodInfo[i].partBits, &newSurfs->partBits, 24); - - short numSurfs = static_cast(newSurfs->numSurfaces); - model->lodInfo[i].numsurfs = numSurfs; - model->lodInfo[i].surfIndex = surfCount; - surfCount += numSurfs; - - changed = true; - } - } - - return changed; - } - - void ModelSurfs::ReleaseModelSurf(Game::XAssetHeader header) - { - bool hasCustomSurface = false; - for (int i = 0; i < header.surfaces->numSurfaces && header.surfaces->surfaces; ++i) - { - Game::XSurface* surface = &header.surfaces->surfaces[i]; - - if (surface->zoneHandle == -1) - { - hasCustomSurface = true; - - auto buffer = ModelSurfs::BufferMap.find(surface->triIndices); - if (buffer != ModelSurfs::BufferMap.end()) - { - buffer->second->Release(); - ModelSurfs::BufferMap.erase(buffer); - } - - buffer = ModelSurfs::BufferMap.find(surface->verts0); - if (buffer != ModelSurfs::BufferMap.end()) - { - buffer->second->Release(); - ModelSurfs::BufferMap.erase(buffer); - } - } - } - - if (hasCustomSurface) - { - auto allocData = ModelSurfs::AllocMap.find(header.surfaces->name); - if (allocData != ModelSurfs::AllocMap.end()) - { - Utils::Memory::FreeAlign(allocData->second->indexBuffer); - Utils::Memory::FreeAlign(allocData->second->vertexBuffer); - Utils::Memory::Free(allocData->second->mainArray); - Utils::Memory::Free(allocData->second); - - ModelSurfs::AllocMap.erase(allocData); - } - } - } - - void ModelSurfs::BeginRecover() - { - for (auto& buffer : ModelSurfs::BufferMap) - { - buffer.second->Release(); - } - - ModelSurfs::BufferMap.clear(); - } - - void ModelSurfs::EndRecover() - { - Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_XMODELSURFS, [] (Game::XAssetHeader header, void* /*userdata*/) - { - ModelSurfs::CreateBuffers(header.surfaces); - }, nullptr, false); - } - - void ModelSurfs::XModelSurfsFixup(Game::XModel* model) - { - if (!ModelSurfs::LoadSurfaces(model)) - { - Game::DB_XModelSurfsFixup(model); - } - } - - __declspec(naked) void ModelSurfs::GetIndexBufferStub() - { - __asm - { - mov eax, [esp + 4h] - cmp al, 0FFh - - jne returnSafe - - jmp ModelSurfs::SetBuffer - - returnSafe: - movzx eax, [esp + 4h] - mov edx, 4B4DE5h - jmp edx - } - } - - __declspec(naked) void ModelSurfs::GetIndexBufferStub2() - { - __asm - { - mov eax, [esp + 4h] - cmp al, 0FFh - - jne returnSafe - - mov eax, [edi + 0Ch] - push eax - call ModelSurfs::GetBuffer - add esp, 4h - retn - - returnSafe: - mov eax, 4FDC20h - jmp eax - } - } - - __declspec(naked) void ModelSurfs::GetIndexBaseStub() - { - __asm - { - mov eax, [esp + 4h] - cmp al, 0FFh - - jne returnSafe - - xor eax, eax - retn - - returnSafe: - mov eax, 48C5F0h - jmp eax - } - } - - __declspec(naked) void ModelSurfs::GetVertexBufferStub() - { - __asm - { - mov eax, [esp + 4h] - cmp al, 0FFh - - jne returnSafe - - jmp ModelSurfs::SetBuffer - - returnSafe: - movzx eax, [esp + 4h] - mov edx, 5BC055h - jmp edx - } - } - - ModelSurfs::ModelSurfs() - { - ModelSurfs::BufferMap.clear(); - - // Install release handler - Game::DB_ReleaseXAssetHandlers[Game::XAssetType::ASSET_TYPE_XMODELSURFS] = ModelSurfs::ReleaseModelSurf; - - // Install device recovery handlers - Renderer::OnDeviceRecoveryBegin(ModelSurfs::BeginRecover); - Renderer::OnDeviceRecoveryEnd(ModelSurfs::EndRecover); - - // Install hooks - Utils::Hook(0x47A6BD, ModelSurfs::XModelSurfsFixup, HOOK_CALL).install()->quick(); - Utils::Hook(0x558F12, ModelSurfs::GetIndexBaseStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x4B4DE0, ModelSurfs::GetIndexBufferStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x558E70, ModelSurfs::GetIndexBufferStub2, HOOK_CALL).install()->quick(); - Utils::Hook(0x5BC050, ModelSurfs::GetVertexBufferStub, HOOK_JUMP).install()->quick(); - } - - ModelSurfs::~ModelSurfs() - { - assert(ModelSurfs::BufferMap.empty()); - assert(ModelSurfs::AllocMap.empty()); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::unordered_map ModelSurfs::BufferMap; + std::unordered_map ModelSurfs::AllocMap; + + IUnknown* ModelSurfs::GetBuffer(void* buffer) + { + return ModelSurfs::BufferMap[buffer]; + } + + void ModelSurfs::SetBuffer(char /*streamHandle*/, void* buffer, IUnknown** bufferOut, int* offsetOut) + { + *offsetOut = 0; + *bufferOut = ModelSurfs::BufferMap[buffer]; + } + + void ModelSurfs::CreateBuffers(Game::XModelSurfs* surfs) + { + for (int i = 0; i < surfs->numSurfaces; ++i) + { + Game::XSurface* surface = &surfs->surfaces[i]; + if (surface->zoneHandle == -1) + { + IDirect3DVertexBuffer9* vertexBuffer; + IDirect3DIndexBuffer9* indexBuffer; + + Game::Load_VertexBuffer(surface->verts0, &vertexBuffer, surface->vertCount * 32); + Game::Load_IndexBuffer(surface->triIndices, &indexBuffer, surface->triCount * 3); + + ModelSurfs::BufferMap[surface->verts0] = vertexBuffer; + ModelSurfs::BufferMap[surface->triIndices] = indexBuffer; + } + } + } + + Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(std::string name) + { + Utils::Memory::Allocator allocator; + FileSystem::FileReader model(Utils::String::VA("models/%s", name.data())); + + if (!model.exists()) + { +#ifdef DEBUG + if (Flags::HasFlag("dump")) + { + FILE* fp = nullptr; + if (!fopen_s(&fp, "dump.cfg", "a") && fp) + { + fprintf(fp, "dumpraw %s\n", model.getName().data()); + fclose(fp); + } + + return nullptr; + } +#endif + + Logger::Error("Loading model %s failed!", name.data()); + } + + Game::CModelHeader header; + if (!model.read(&header, sizeof header)) + { + Logger::Error("Reading header for model %s failed!", name.data()); + } + + if (header.version != 1) + { + Logger::Error("Model %s has an invalid version %d (should be 1)!", name.data(), header.version); + } + + // Allocate section buffers + header.sectionHeader[Game::SECTION_MAIN].buffer = Utils::Memory::Allocate(header.sectionHeader[Game::SECTION_MAIN].size); + header.sectionHeader[Game::SECTION_INDEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_INDEX].size, 16); + header.sectionHeader[Game::SECTION_VERTEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_VERTEX].size, 16); + header.sectionHeader[Game::SECTION_FIXUP].buffer = allocator.allocateArray(header.sectionHeader[Game::SECTION_FIXUP].size); + + // Load section data + for (int i = 0; i < ARRAY_SIZE(header.sectionHeader); ++i) + { + model.seek(header.sectionHeader[i].offset, FS_SEEK_SET); + if (!model.read(header.sectionHeader[i].buffer, header.sectionHeader[i].size)) + { + Logger::Error("Reading section %d for model %s failed!", i, name.data()); + } + } + + // Fixup sections + unsigned int* fixups = reinterpret_cast(header.sectionHeader[Game::SECTION_FIXUP].buffer); + for (int i = 0; i < 3; ++i) + { + Game::CModelSectionHeader* section = &header.sectionHeader[i]; + for (int j = section->fixupStart; j < section->fixupStart + section->fixupCount; ++j) + { + unsigned int fixup = fixups[j]; + *reinterpret_cast(reinterpret_cast(section->buffer) + (fixup >> 3)) += reinterpret_cast(header.sectionHeader[fixup & 3].buffer); + } + } + + // Store allocation data (not sure if this is correct) + Game::CModelAllocData* allocationData = Utils::Memory::AllocateArray(); + allocationData->mainArray = header.sectionHeader[Game::SECTION_MAIN].buffer; + allocationData->indexBuffer = header.sectionHeader[Game::SECTION_INDEX].buffer; + allocationData->vertexBuffer = header.sectionHeader[Game::SECTION_VERTEX].buffer; + + AssertSize(Game::XSurface, 64); + Game::XModelSurfs* modelSurfs = reinterpret_cast(allocationData->mainArray); + Game::XSurface* tempSurfaces = allocator.allocateArray(modelSurfs->numSurfaces); + char* surfaceData = reinterpret_cast(modelSurfs->surfaces); + + if (ModelSurfs::AllocMap.find(modelSurfs->name) != ModelSurfs::AllocMap.end()) + { + Game::CModelAllocData* allocData = ModelSurfs::AllocMap[modelSurfs->name]; + + if (allocData) + { + Utils::Memory::FreeAlign(allocData->indexBuffer); + Utils::Memory::FreeAlign(allocData->vertexBuffer); + Utils::Memory::Free(allocData->mainArray); + Utils::Memory::Free(allocData); + } + } + + ModelSurfs::AllocMap[modelSurfs->name] = allocationData; + *reinterpret_cast(reinterpret_cast(allocationData->mainArray) + 44) = allocationData; + + for (int i = 0; i < modelSurfs->numSurfaces; ++i) + { + char* source = &surfaceData[i * 84]; + + std::memcpy(&tempSurfaces[i], source, 12); + std::memcpy(&tempSurfaces[i].triIndices, source + 16, 20); + std::memcpy(&tempSurfaces[i].vertListCount, source + 40, 8); + std::memcpy(&tempSurfaces[i].partBits, source + 52, 24); + tempSurfaces[i].zoneHandle = -1; // Fake handle for buffer interception + } + + std::memcpy(surfaceData, tempSurfaces, 64 * modelSurfs->numSurfaces); + + ModelSurfs::CreateBuffers(modelSurfs); + + return modelSurfs; + } + + bool ModelSurfs::LoadSurfaces(Game::XModel* model) + { + if (!model) return false; + + bool changed = false; + short surfCount = 0; + + for (char i = 0; i < model->numLods; ++i) + { + Game::XModelSurfs* surfs = model->lodInfo[i].modelSurfs; + + if (!surfs->surfaces) + { + AssertOffset(Game::XModelLodInfo, partBits, 12); + Game::XModelSurfs* newSurfs = ModelSurfs::LoadXModelSurfaces(surfs->name); + if (!newSurfs) continue; + + surfs->surfaces = newSurfs->surfaces; + surfs->numSurfaces = newSurfs->numSurfaces; + + model->lodInfo[i].surfs = newSurfs->surfaces; + std::memcpy(&model->lodInfo[i].partBits, &newSurfs->partBits, 24); + + short numSurfs = static_cast(newSurfs->numSurfaces); + model->lodInfo[i].numsurfs = numSurfs; + model->lodInfo[i].surfIndex = surfCount; + surfCount += numSurfs; + + changed = true; + } + } + + return changed; + } + + void ModelSurfs::ReleaseModelSurf(Game::XAssetHeader header) + { + bool hasCustomSurface = false; + for (int i = 0; i < header.surfaces->numSurfaces && header.surfaces->surfaces; ++i) + { + Game::XSurface* surface = &header.surfaces->surfaces[i]; + + if (surface->zoneHandle == -1) + { + hasCustomSurface = true; + + auto buffer = ModelSurfs::BufferMap.find(surface->triIndices); + if (buffer != ModelSurfs::BufferMap.end()) + { + buffer->second->Release(); + ModelSurfs::BufferMap.erase(buffer); + } + + buffer = ModelSurfs::BufferMap.find(surface->verts0); + if (buffer != ModelSurfs::BufferMap.end()) + { + buffer->second->Release(); + ModelSurfs::BufferMap.erase(buffer); + } + } + } + + if (hasCustomSurface) + { + auto allocData = ModelSurfs::AllocMap.find(header.surfaces->name); + if (allocData != ModelSurfs::AllocMap.end()) + { + Utils::Memory::FreeAlign(allocData->second->indexBuffer); + Utils::Memory::FreeAlign(allocData->second->vertexBuffer); + Utils::Memory::Free(allocData->second->mainArray); + Utils::Memory::Free(allocData->second); + + ModelSurfs::AllocMap.erase(allocData); + } + } + } + + void ModelSurfs::BeginRecover() + { + for (auto& buffer : ModelSurfs::BufferMap) + { + buffer.second->Release(); + } + + ModelSurfs::BufferMap.clear(); + } + + void ModelSurfs::EndRecover() + { + Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_XMODELSURFS, [] (Game::XAssetHeader header, void* /*userdata*/) + { + ModelSurfs::CreateBuffers(header.surfaces); + }, nullptr, false); + } + + void ModelSurfs::XModelSurfsFixup(Game::XModel* model) + { + if (!ModelSurfs::LoadSurfaces(model)) + { + Game::DB_XModelSurfsFixup(model); + } + } + + __declspec(naked) void ModelSurfs::GetIndexBufferStub() + { + __asm + { + mov eax, [esp + 4h] + cmp al, 0FFh + + jne returnSafe + + jmp ModelSurfs::SetBuffer + + returnSafe: + movzx eax, [esp + 4h] + mov edx, 4B4DE5h + jmp edx + } + } + + __declspec(naked) void ModelSurfs::GetIndexBufferStub2() + { + __asm + { + mov eax, [esp + 4h] + cmp al, 0FFh + + jne returnSafe + + mov eax, [edi + 0Ch] + push eax + call ModelSurfs::GetBuffer + add esp, 4h + retn + + returnSafe: + mov eax, 4FDC20h + jmp eax + } + } + + __declspec(naked) void ModelSurfs::GetIndexBaseStub() + { + __asm + { + mov eax, [esp + 4h] + cmp al, 0FFh + + jne returnSafe + + xor eax, eax + retn + + returnSafe: + mov eax, 48C5F0h + jmp eax + } + } + + __declspec(naked) void ModelSurfs::GetVertexBufferStub() + { + __asm + { + mov eax, [esp + 4h] + cmp al, 0FFh + + jne returnSafe + + jmp ModelSurfs::SetBuffer + + returnSafe: + movzx eax, [esp + 4h] + mov edx, 5BC055h + jmp edx + } + } + + ModelSurfs::ModelSurfs() + { + ModelSurfs::BufferMap.clear(); + + // Install release handler + Game::DB_ReleaseXAssetHandlers[Game::XAssetType::ASSET_TYPE_XMODELSURFS] = ModelSurfs::ReleaseModelSurf; + + // Install device recovery handlers + Renderer::OnDeviceRecoveryBegin(ModelSurfs::BeginRecover); + Renderer::OnDeviceRecoveryEnd(ModelSurfs::EndRecover); + + // Install hooks + Utils::Hook(0x47A6BD, ModelSurfs::XModelSurfsFixup, HOOK_CALL).install()->quick(); + Utils::Hook(0x558F12, ModelSurfs::GetIndexBaseStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x4B4DE0, ModelSurfs::GetIndexBufferStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x558E70, ModelSurfs::GetIndexBufferStub2, HOOK_CALL).install()->quick(); + Utils::Hook(0x5BC050, ModelSurfs::GetVertexBufferStub, HOOK_JUMP).install()->quick(); + } + + ModelSurfs::~ModelSurfs() + { + assert(ModelSurfs::BufferMap.empty()); + assert(ModelSurfs::AllocMap.empty()); + } +} diff --git a/src/Components/Modules/ModelSurfs.hpp b/src/Components/Modules/ModelSurfs.hpp index ecea211b..48624f5b 100644 --- a/src/Components/Modules/ModelSurfs.hpp +++ b/src/Components/Modules/ModelSurfs.hpp @@ -1,35 +1,35 @@ -namespace Components -{ - class ModelSurfs : public Component - { - public: - ModelSurfs(); - ~ModelSurfs(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "ModelSurfs"; }; -#endif - - private: - static std::unordered_map BufferMap; - static std::unordered_map AllocMap; - - static void ReleaseModelSurf(Game::XAssetHeader header); - - static void BeginRecover(); - static void EndRecover(); - - static IUnknown* GetBuffer(void* buffer); - static void SetBuffer(char streamHandle, void* buffer, IUnknown** bufferOut, int* offsetOut); - - static void CreateBuffers(Game::XModelSurfs* surfs); - static Game::XModelSurfs* LoadXModelSurfaces(std::string name); - static bool LoadSurfaces(Game::XModel* model); - static void XModelSurfsFixup(Game::XModel* model); - - static void GetIndexBaseStub(); - static void GetIndexBufferStub(); - static void GetIndexBufferStub2(); - static void GetVertexBufferStub(); - }; -} +namespace Components +{ + class ModelSurfs : public Component + { + public: + ModelSurfs(); + ~ModelSurfs(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "ModelSurfs"; }; +#endif + + private: + static std::unordered_map BufferMap; + static std::unordered_map AllocMap; + + static void ReleaseModelSurf(Game::XAssetHeader header); + + static void BeginRecover(); + static void EndRecover(); + + static IUnknown* GetBuffer(void* buffer); + static void SetBuffer(char streamHandle, void* buffer, IUnknown** bufferOut, int* offsetOut); + + static void CreateBuffers(Game::XModelSurfs* surfs); + static Game::XModelSurfs* LoadXModelSurfaces(std::string name); + static bool LoadSurfaces(Game::XModel* model); + static void XModelSurfsFixup(Game::XModel* model); + + static void GetIndexBaseStub(); + static void GetIndexBufferStub(); + static void GetIndexBufferStub2(); + static void GetVertexBufferStub(); + }; +} diff --git a/src/Components/Modules/MusicalTalent.cpp b/src/Components/Modules/MusicalTalent.cpp index 238c5e6d..3a00d0d8 100644 --- a/src/Components/Modules/MusicalTalent.cpp +++ b/src/Components/Modules/MusicalTalent.cpp @@ -1,47 +1,47 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::unordered_map MusicalTalent::SoundAliasList; - - void MusicalTalent::Replace(std::string sound, const char* file) - { - MusicalTalent::SoundAliasList[Utils::String::ToLower(sound)] = file; - } - - Game::XAssetHeader MusicalTalent::ModifyAliases(Game::XAssetType type, std::string filename) - { - Game::XAssetHeader header = { 0 }; - - if (MusicalTalent::SoundAliasList.find(Utils::String::ToLower(filename)) != MusicalTalent::SoundAliasList.end()) - { - Game::snd_alias_list_t* aliases = Game::DB_FindXAssetHeader(type, filename.data()).sound; - - if (aliases) - { - if (aliases->head->soundFile->type == 2) - { - aliases->head->soundFile->data.stream.name = MusicalTalent::SoundAliasList[Utils::String::ToLower(filename)]; - } - - header.sound = aliases; - } - } - - return header; - } - - MusicalTalent::MusicalTalent() - { - if (ZoneBuilder::IsEnabled()) return; - - AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_SOUND, MusicalTalent::ModifyAliases); - - MusicalTalent::Replace("music_mainmenu_mp", "hz_t_menumusic.mp3"); - } - - MusicalTalent::~MusicalTalent() - { - MusicalTalent::SoundAliasList.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::unordered_map MusicalTalent::SoundAliasList; + + void MusicalTalent::Replace(std::string sound, const char* file) + { + MusicalTalent::SoundAliasList[Utils::String::ToLower(sound)] = file; + } + + Game::XAssetHeader MusicalTalent::ModifyAliases(Game::XAssetType type, std::string filename) + { + Game::XAssetHeader header = { 0 }; + + if (MusicalTalent::SoundAliasList.find(Utils::String::ToLower(filename)) != MusicalTalent::SoundAliasList.end()) + { + Game::snd_alias_list_t* aliases = Game::DB_FindXAssetHeader(type, filename.data()).sound; + + if (aliases) + { + if (aliases->head->soundFile->type == 2) + { + aliases->head->soundFile->data.stream.name = MusicalTalent::SoundAliasList[Utils::String::ToLower(filename)]; + } + + header.sound = aliases; + } + } + + return header; + } + + MusicalTalent::MusicalTalent() + { + if (ZoneBuilder::IsEnabled()) return; + + AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_SOUND, MusicalTalent::ModifyAliases); + + MusicalTalent::Replace("music_mainmenu_mp", "hz_t_menumusic.mp3"); + } + + MusicalTalent::~MusicalTalent() + { + MusicalTalent::SoundAliasList.clear(); + } +} diff --git a/src/Components/Modules/MusicalTalent.hpp b/src/Components/Modules/MusicalTalent.hpp index 5122502c..e4ff0843 100644 --- a/src/Components/Modules/MusicalTalent.hpp +++ b/src/Components/Modules/MusicalTalent.hpp @@ -1,19 +1,19 @@ -namespace Components -{ - class MusicalTalent : public Component - { - public: - MusicalTalent(); - ~MusicalTalent(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "MusicalTalent"; }; -#endif - - static void Replace(std::string sound, const char* file); - - private: - static std::unordered_map SoundAliasList; - static Game::XAssetHeader ModifyAliases(Game::XAssetType type, std::string filename); - }; -} +namespace Components +{ + class MusicalTalent : public Component + { + public: + MusicalTalent(); + ~MusicalTalent(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "MusicalTalent"; }; +#endif + + static void Replace(std::string sound, const char* file); + + private: + static std::unordered_map SoundAliasList; + static Game::XAssetHeader ModifyAliases(Game::XAssetType type, std::string filename); + }; +} diff --git a/src/Components/Modules/Network.cpp b/src/Components/Modules/Network.cpp index aa72f0fe..7cf5609a 100644 --- a/src/Components/Modules/Network.cpp +++ b/src/Components/Modules/Network.cpp @@ -1,378 +1,378 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::string Network::SelectedPacket; - Utils::Signal Network::StartupSignal; - std::map> Network::PacketHandlers; - - Network::Address::Address(std::string addrString) - { - Game::NET_StringToAdr(addrString.data(), &this->address); - } - Network::Address::Address(sockaddr* addr) - { - Game::SockadrToNetadr(addr, &this->address); - } - bool Network::Address::operator==(const Network::Address &obj) - { - return Game::NET_CompareAdr(this->address, obj.address); - } - void Network::Address::setPort(unsigned short port) - { - this->address.port = htons(port); - } - unsigned short Network::Address::getPort() - { - return ntohs(this->address.port); - } - void Network::Address::setIP(DWORD ip) - { - this->address.ip.full = ip; - } - void Network::Address::setIP(Game::netIP_t ip) - { - this->address.ip = ip; - } - Game::netIP_t Network::Address::getIP() - { - return this->address.ip; - } - void Network::Address::setType(Game::netadrtype_t type) - { - this->address.type = type; - } - Game::netadrtype_t Network::Address::getType() - { - return this->address.type; - } - sockaddr Network::Address::getSockAddr() - { - sockaddr addr; - this->toSockAddr(&addr); - return addr; - } - void Network::Address::toSockAddr(sockaddr* addr) - { - if (addr) - { - Game::NetadrToSockadr(&this->address, addr); - } - } - void Network::Address::toSockAddr(sockaddr_in* addr) - { - this->toSockAddr(reinterpret_cast(addr)); - } - Game::netadr_t* Network::Address::get() - { - return &this->address; - } - const char* Network::Address::getCString() - { - return Game::NET_AdrToString(this->address); - } - std::string Network::Address::getString() - { - return this->getCString(); - } - bool Network::Address::isLocal() - { - // According to: https://en.wikipedia.org/wiki/Private_network - - // 10.X.X.X - if (this->getIP().bytes[0] == 10) return true; - - // 192.168.X.X - if (this->getIP().bytes[0] == 192 && this->getIP().bytes[1] == 168) return true; - - // 172.16.X.X - 172.31.X.X - if (this->getIP().bytes[0] == 172 && (this->getIP().bytes[1] >= 16) && (this->getIP().bytes[1] < 32)) return true; - - // 127.0.0.1 - if (this->getIP().full == 0x0100007F) return true; - - // TODO: Maybe check for matching localIPs and subnet mask - - return false; - } - bool Network::Address::isSelf() - { - if (Game::NET_IsLocalAddress(this->address)) return true; // Loopback - if (this->getPort() != (Dvar::Var("net_port").get() & 0xFFFF)) return false; // Port not equal - - for (int i = 0; i < *Game::numIP; ++i) - { - if (this->getIP().full == Game::localIP[i].full) - { - return true; - } - } - - return false; - } - bool Network::Address::isLoopback() - { - if (this->getIP().full == 0x100007f) // 127.0.0.1 - { - return true; - } - - return Game::NET_IsLocalAddress(this->address); - } - bool Network::Address::isValid() - { - return (this->getType() != Game::netadrtype_t::NA_BAD); - } - void Network::Address::serialize(Proto::Network::Address* protoAddress) - { - protoAddress->set_ip(this->getIP().full); - protoAddress->set_port(this->getPort() & 0xFFFF); - } - void Network::Address::deserialize(const Proto::Network::Address& protoAddress) - { - this->setIP(protoAddress.ip()); - this->setPort(static_cast(protoAddress.port())); - this->setType(Game::netadrtype_t::NA_IP); - } - - void Network::Handle(std::string packet, Utils::Slot callback) - { - Network::PacketHandlers[Utils::String::ToLower(packet)] = callback; - } - - void Network::OnStart(Utils::Slot callback) - { - Network::StartupSignal.connect(callback); - } - - void Network::Send(Game::netsrc_t type, Network::Address target, std::string data) - { - // NET_OutOfBandPrint only supports non-binary data! - //Game::NET_OutOfBandPrint(type, *target.Get(), data.data()); - - std::string rawData; - rawData.append("\xFF\xFF\xFF\xFF", 4); - rawData.append(data); - //rawData.append("\0", 1); - - Network::SendRaw(type, target, rawData); - } - - void Network::Send(Network::Address target, std::string data) - { - Network::Send(Game::netsrc_t::NS_CLIENT, target, data); - } - - void Network::SendRaw(Game::netsrc_t type, Network::Address target, std::string data) - { - // NET_OutOfBandData doesn't seem to work properly - //Game::NET_OutOfBandData(type, *target.Get(), data.data(), data.size()); - Game::Sys_SendPacket(type, data.size(), data.data(), *target.get()); - } - - void Network::SendRaw(Network::Address target, std::string data) - { - Network::SendRaw(Game::netsrc_t::NS_CLIENT, target, data); - } - - void Network::SendCommand(Game::netsrc_t type, Network::Address target, std::string command, std::string data) - { - // Use space as separator (possible separators are '\n', ' '). - // Though, our handler only needs exactly 1 char as separator and doesn't which char it is - std::string packet; - packet.append(command); - packet.append(" ", 1); - packet.append(data); - - Network::Send(type, target, packet); - } - - void Network::SendCommand(Network::Address target, std::string command, std::string data) - { - Network::SendCommand(Game::netsrc_t::NS_CLIENT, target, command, data); - } - - void Network::Broadcast(unsigned short port, std::string data) - { - Address target; - - target.setPort(port); - target.setIP(INADDR_BROADCAST); - target.setType(Game::netadrtype_t::NA_BROADCAST); - - Network::Send(Game::netsrc_t::NS_CLIENT, target, data); - } - - void Network::BroadcastRange(unsigned int min, unsigned int max, std::string data) - { - for (unsigned int i = min; i < max; ++i) - { - Network::Broadcast(static_cast(i & 0xFFFF), data); - } - } - - void Network::BroadcastAll(std::string data) - { - Network::BroadcastRange(100, 65536, data); - } - - int Network::PacketInterceptionHandler(const char* packet) - { - // Packet rate limit. - static uint32_t packets = 0; - static int lastClean = 0; - - if ((Game::Sys_Milliseconds() - lastClean) > 1'000) - { - packets = 0; - lastClean = Game::Sys_Milliseconds(); - } - - if ((++packets) > NETWORK_MAX_PACKETS_PER_SECOND) - { - return 1; - } - - std::string packetCommand = packet; - auto pos = packetCommand.find_first_of("\\\n "); - if (pos != std::string::npos) - { - packetCommand = packetCommand.substr(0, pos); - } - - packetCommand = Utils::String::ToLower(packetCommand); - - // Check if custom handler exists - for (auto i = Network::PacketHandlers.begin(); i != Network::PacketHandlers.end(); ++i) - { - if (Utils::String::ToLower(i->first) == packetCommand) - { - Network::SelectedPacket = i->first; - return 0; - } - } - - // No interception - return 1; - } - - void Network::DeployPacket(Game::netadr_t* from, Game::msg_t* msg) - { - if (Network::PacketHandlers.find(Network::SelectedPacket) != Network::PacketHandlers.end()) - { - std::string data; - - size_t offset = Network::SelectedPacket.size() + 4 + 1; - - if (static_cast(msg->cursize) > offset) - { - data.append(msg->data + offset, msg->cursize - offset); - } - - // Remove trailing 0x00 byte - // Actually, don't remove it, it might be part of the packet. Send correctly formatted packets instead! - //if (data.size() && !data[data.size() - 1]) data.pop_back(); - - Network::PacketHandlers[Network::SelectedPacket](from, data); - } - else - { - Logger::Print("Error: Network packet intercepted, but handler is missing!\n"); - } - } - - void Network::NetworkStart() - { - Network::StartupSignal(); - } - - __declspec(naked) void Network::NetworkStartStub() - { - __asm - { - mov eax, 64D900h - call eax - jmp Network::NetworkStart - } - } - - __declspec(naked) void Network::DeployPacketStub() - { - __asm - { - lea eax, [esp + 0C54h] - push ebp // Command - push eax // Address pointer - call Network::DeployPacket - add esp, 8h - mov al, 1 - pop edi - pop esi - pop ebp - pop ebx - add esp, 0C40h - retn - } - } - - __declspec(naked) void Network::PacketErrorCheck() - { - __asm - { - cmp eax, 2746h - jz returnIgnore - - cmp eax, WSAENETRESET - jz returnIgnore - - push 465325h - retn - - returnIgnore: - push 4654C6h - retn - } - } - - Network::Network() - { - AssertSize(Game::netadr_t, 20); - - // maximum size in NET_OutOfBandPrint - Utils::Hook::Set(0x4AEF08, 0x1FFFC); - Utils::Hook::Set(0x4AEFA3, 0x1FFFC); - - // increase max port binding attempts from 10 to 100 - Utils::Hook::Set(0x4FD48A, 100); - - // increase cl_maxpackets limit - Utils::Hook::Set(0x4050A1, 125); - - // Parse port as short in Net_AddrToString - Utils::Hook::Set(0x4698E3, "%u.%u.%u.%u:%hu"); - - // Install startup handler - Utils::Hook(0x4FD4D4, Network::NetworkStartStub, HOOK_JUMP).install()->quick(); - - // Install interception handler - Utils::Hook(0x5AA709, Network::PacketInterceptionHandler, HOOK_CALL).install()->quick(); - - // Prevent recvfrom error spam - Utils::Hook(0x46531A, Network::PacketErrorCheck, HOOK_JUMP).install()->quick(); - - // Install packet deploy hook - Utils::Hook::RedirectJump(0x5AA713, Network::DeployPacketStub); - - Network::Handle("resolveAddress", [] (Address address, std::string data) - { - Network::SendRaw(address, address.getString()); - }); - } - - Network::~Network() - { - Network::SelectedPacket.clear(); - Network::PacketHandlers.clear(); - Network::StartupSignal.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::string Network::SelectedPacket; + Utils::Signal Network::StartupSignal; + std::map> Network::PacketHandlers; + + Network::Address::Address(std::string addrString) + { + Game::NET_StringToAdr(addrString.data(), &this->address); + } + Network::Address::Address(sockaddr* addr) + { + Game::SockadrToNetadr(addr, &this->address); + } + bool Network::Address::operator==(const Network::Address &obj) + { + return Game::NET_CompareAdr(this->address, obj.address); + } + void Network::Address::setPort(unsigned short port) + { + this->address.port = htons(port); + } + unsigned short Network::Address::getPort() + { + return ntohs(this->address.port); + } + void Network::Address::setIP(DWORD ip) + { + this->address.ip.full = ip; + } + void Network::Address::setIP(Game::netIP_t ip) + { + this->address.ip = ip; + } + Game::netIP_t Network::Address::getIP() + { + return this->address.ip; + } + void Network::Address::setType(Game::netadrtype_t type) + { + this->address.type = type; + } + Game::netadrtype_t Network::Address::getType() + { + return this->address.type; + } + sockaddr Network::Address::getSockAddr() + { + sockaddr addr; + this->toSockAddr(&addr); + return addr; + } + void Network::Address::toSockAddr(sockaddr* addr) + { + if (addr) + { + Game::NetadrToSockadr(&this->address, addr); + } + } + void Network::Address::toSockAddr(sockaddr_in* addr) + { + this->toSockAddr(reinterpret_cast(addr)); + } + Game::netadr_t* Network::Address::get() + { + return &this->address; + } + const char* Network::Address::getCString() + { + return Game::NET_AdrToString(this->address); + } + std::string Network::Address::getString() + { + return this->getCString(); + } + bool Network::Address::isLocal() + { + // According to: https://en.wikipedia.org/wiki/Private_network + + // 10.X.X.X + if (this->getIP().bytes[0] == 10) return true; + + // 192.168.X.X + if (this->getIP().bytes[0] == 192 && this->getIP().bytes[1] == 168) return true; + + // 172.16.X.X - 172.31.X.X + if (this->getIP().bytes[0] == 172 && (this->getIP().bytes[1] >= 16) && (this->getIP().bytes[1] < 32)) return true; + + // 127.0.0.1 + if (this->getIP().full == 0x0100007F) return true; + + // TODO: Maybe check for matching localIPs and subnet mask + + return false; + } + bool Network::Address::isSelf() + { + if (Game::NET_IsLocalAddress(this->address)) return true; // Loopback + if (this->getPort() != (Dvar::Var("net_port").get() & 0xFFFF)) return false; // Port not equal + + for (int i = 0; i < *Game::numIP; ++i) + { + if (this->getIP().full == Game::localIP[i].full) + { + return true; + } + } + + return false; + } + bool Network::Address::isLoopback() + { + if (this->getIP().full == 0x100007f) // 127.0.0.1 + { + return true; + } + + return Game::NET_IsLocalAddress(this->address); + } + bool Network::Address::isValid() + { + return (this->getType() != Game::netadrtype_t::NA_BAD); + } + void Network::Address::serialize(Proto::Network::Address* protoAddress) + { + protoAddress->set_ip(this->getIP().full); + protoAddress->set_port(this->getPort() & 0xFFFF); + } + void Network::Address::deserialize(const Proto::Network::Address& protoAddress) + { + this->setIP(protoAddress.ip()); + this->setPort(static_cast(protoAddress.port())); + this->setType(Game::netadrtype_t::NA_IP); + } + + void Network::Handle(std::string packet, Utils::Slot callback) + { + Network::PacketHandlers[Utils::String::ToLower(packet)] = callback; + } + + void Network::OnStart(Utils::Slot callback) + { + Network::StartupSignal.connect(callback); + } + + void Network::Send(Game::netsrc_t type, Network::Address target, std::string data) + { + // NET_OutOfBandPrint only supports non-binary data! + //Game::NET_OutOfBandPrint(type, *target.Get(), data.data()); + + std::string rawData; + rawData.append("\xFF\xFF\xFF\xFF", 4); + rawData.append(data); + //rawData.append("\0", 1); + + Network::SendRaw(type, target, rawData); + } + + void Network::Send(Network::Address target, std::string data) + { + Network::Send(Game::netsrc_t::NS_CLIENT, target, data); + } + + void Network::SendRaw(Game::netsrc_t type, Network::Address target, std::string data) + { + // NET_OutOfBandData doesn't seem to work properly + //Game::NET_OutOfBandData(type, *target.Get(), data.data(), data.size()); + Game::Sys_SendPacket(type, data.size(), data.data(), *target.get()); + } + + void Network::SendRaw(Network::Address target, std::string data) + { + Network::SendRaw(Game::netsrc_t::NS_CLIENT, target, data); + } + + void Network::SendCommand(Game::netsrc_t type, Network::Address target, std::string command, std::string data) + { + // Use space as separator (possible separators are '\n', ' '). + // Though, our handler only needs exactly 1 char as separator and doesn't which char it is + std::string packet; + packet.append(command); + packet.append(" ", 1); + packet.append(data); + + Network::Send(type, target, packet); + } + + void Network::SendCommand(Network::Address target, std::string command, std::string data) + { + Network::SendCommand(Game::netsrc_t::NS_CLIENT, target, command, data); + } + + void Network::Broadcast(unsigned short port, std::string data) + { + Address target; + + target.setPort(port); + target.setIP(INADDR_BROADCAST); + target.setType(Game::netadrtype_t::NA_BROADCAST); + + Network::Send(Game::netsrc_t::NS_CLIENT, target, data); + } + + void Network::BroadcastRange(unsigned int min, unsigned int max, std::string data) + { + for (unsigned int i = min; i < max; ++i) + { + Network::Broadcast(static_cast(i & 0xFFFF), data); + } + } + + void Network::BroadcastAll(std::string data) + { + Network::BroadcastRange(100, 65536, data); + } + + int Network::PacketInterceptionHandler(const char* packet) + { + // Packet rate limit. + static uint32_t packets = 0; + static int lastClean = 0; + + if ((Game::Sys_Milliseconds() - lastClean) > 1'000) + { + packets = 0; + lastClean = Game::Sys_Milliseconds(); + } + + if ((++packets) > NETWORK_MAX_PACKETS_PER_SECOND) + { + return 1; + } + + std::string packetCommand = packet; + auto pos = packetCommand.find_first_of("\\\n "); + if (pos != std::string::npos) + { + packetCommand = packetCommand.substr(0, pos); + } + + packetCommand = Utils::String::ToLower(packetCommand); + + // Check if custom handler exists + for (auto i = Network::PacketHandlers.begin(); i != Network::PacketHandlers.end(); ++i) + { + if (Utils::String::ToLower(i->first) == packetCommand) + { + Network::SelectedPacket = i->first; + return 0; + } + } + + // No interception + return 1; + } + + void Network::DeployPacket(Game::netadr_t* from, Game::msg_t* msg) + { + if (Network::PacketHandlers.find(Network::SelectedPacket) != Network::PacketHandlers.end()) + { + std::string data; + + size_t offset = Network::SelectedPacket.size() + 4 + 1; + + if (static_cast(msg->cursize) > offset) + { + data.append(msg->data + offset, msg->cursize - offset); + } + + // Remove trailing 0x00 byte + // Actually, don't remove it, it might be part of the packet. Send correctly formatted packets instead! + //if (data.size() && !data[data.size() - 1]) data.pop_back(); + + Network::PacketHandlers[Network::SelectedPacket](from, data); + } + else + { + Logger::Print("Error: Network packet intercepted, but handler is missing!\n"); + } + } + + void Network::NetworkStart() + { + Network::StartupSignal(); + } + + __declspec(naked) void Network::NetworkStartStub() + { + __asm + { + mov eax, 64D900h + call eax + jmp Network::NetworkStart + } + } + + __declspec(naked) void Network::DeployPacketStub() + { + __asm + { + lea eax, [esp + 0C54h] + push ebp // Command + push eax // Address pointer + call Network::DeployPacket + add esp, 8h + mov al, 1 + pop edi + pop esi + pop ebp + pop ebx + add esp, 0C40h + retn + } + } + + __declspec(naked) void Network::PacketErrorCheck() + { + __asm + { + cmp eax, 2746h + jz returnIgnore + + cmp eax, WSAENETRESET + jz returnIgnore + + push 465325h + retn + + returnIgnore: + push 4654C6h + retn + } + } + + Network::Network() + { + AssertSize(Game::netadr_t, 20); + + // maximum size in NET_OutOfBandPrint + Utils::Hook::Set(0x4AEF08, 0x1FFFC); + Utils::Hook::Set(0x4AEFA3, 0x1FFFC); + + // increase max port binding attempts from 10 to 100 + Utils::Hook::Set(0x4FD48A, 100); + + // increase cl_maxpackets limit + Utils::Hook::Set(0x4050A1, 125); + + // Parse port as short in Net_AddrToString + Utils::Hook::Set(0x4698E3, "%u.%u.%u.%u:%hu"); + + // Install startup handler + Utils::Hook(0x4FD4D4, Network::NetworkStartStub, HOOK_JUMP).install()->quick(); + + // Install interception handler + Utils::Hook(0x5AA709, Network::PacketInterceptionHandler, HOOK_CALL).install()->quick(); + + // Prevent recvfrom error spam + Utils::Hook(0x46531A, Network::PacketErrorCheck, HOOK_JUMP).install()->quick(); + + // Install packet deploy hook + Utils::Hook::RedirectJump(0x5AA713, Network::DeployPacketStub); + + Network::Handle("resolveAddress", [] (Address address, std::string data) + { + Network::SendRaw(address, address.getString()); + }); + } + + Network::~Network() + { + Network::SelectedPacket.clear(); + Network::PacketHandlers.clear(); + Network::StartupSignal.clear(); + } +} diff --git a/src/Components/Modules/Network.hpp b/src/Components/Modules/Network.hpp index 789f77ff..f9ce7660 100644 --- a/src/Components/Modules/Network.hpp +++ b/src/Components/Modules/Network.hpp @@ -1,96 +1,96 @@ -#define NETWORK_MAX_PACKETS_PER_SECOND 100'000 - -namespace Components -{ - class Network : public Component - { - public: - class Address - { - public: - Address() { setType(Game::netadrtype_t::NA_BAD); }; - Address(std::string addrString); - Address(sockaddr* addr); - Address(sockaddr addr) : Address(&addr) {} - Address(sockaddr_in addr) : Address(&addr) {} - Address(sockaddr_in* addr) : Address(reinterpret_cast(addr)) {} - Address(Game::netadr_t addr) : address(addr) {} - Address(Game::netadr_t* addr) : Address(*addr) {} - Address(const Address& obj) : address(obj.address) {}; - Address(const Proto::Network::Address& addr) { this->deserialize(addr); }; - bool operator!=(const Address &obj) { return !(*this == obj); }; - bool operator==(const Address &obj); - - void setPort(unsigned short port); - unsigned short getPort(); - - void setIP(DWORD ip); - void setIP(Game::netIP_t ip); - Game::netIP_t getIP(); - - void setType(Game::netadrtype_t type); - Game::netadrtype_t getType(); - - sockaddr getSockAddr(); - void toSockAddr(sockaddr* addr); - void toSockAddr(sockaddr_in* addr); - Game::netadr_t* get(); - const char* getCString(); - std::string getString(); - - bool isLocal(); - bool isSelf(); - bool isValid(); - bool isLoopback(); - - void serialize(Proto::Network::Address* protoAddress); - void deserialize(const Proto::Network::Address& protoAddress); - - private: - Game::netadr_t address; - }; - - typedef void(Callback)(Address address, std::string data); - typedef void(CallbackRaw)(); - - Network(); - ~Network(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Network"; }; -#endif - - static void Handle(std::string packet, Utils::Slot callback); - static void OnStart(Utils::Slot callback); - - // Send quake-styled binary data - static void Send(Address target, std::string data); - static void Send(Game::netsrc_t type, Address target, std::string data); - - // Allows sending raw data without quake header - static void SendRaw(Address target, std::string data); - static void SendRaw(Game::netsrc_t type, Address target, std::string data); - - // Send quake-style command using binary data - static void SendCommand(Address target, std::string command, std::string data = ""); - static void SendCommand(Game::netsrc_t type, Address target, std::string command, std::string data = ""); - - static void Broadcast(unsigned short port, std::string data); - static void BroadcastRange(unsigned int min, unsigned int max, std::string data); - static void BroadcastAll(std::string data); - - private: - static std::string SelectedPacket; - static Utils::Signal StartupSignal; - static std::map> PacketHandlers; - - static int PacketInterceptionHandler(const char* packet); - static void DeployPacket(Game::netadr_t* from, Game::msg_t* msg); - static void DeployPacketStub(); - - static void NetworkStart(); - static void NetworkStartStub(); - - static void PacketErrorCheck(); - }; -} +#define NETWORK_MAX_PACKETS_PER_SECOND 100'000 + +namespace Components +{ + class Network : public Component + { + public: + class Address + { + public: + Address() { setType(Game::netadrtype_t::NA_BAD); }; + Address(std::string addrString); + Address(sockaddr* addr); + Address(sockaddr addr) : Address(&addr) {} + Address(sockaddr_in addr) : Address(&addr) {} + Address(sockaddr_in* addr) : Address(reinterpret_cast(addr)) {} + Address(Game::netadr_t addr) : address(addr) {} + Address(Game::netadr_t* addr) : Address(*addr) {} + Address(const Address& obj) : address(obj.address) {}; + Address(const Proto::Network::Address& addr) { this->deserialize(addr); }; + bool operator!=(const Address &obj) { return !(*this == obj); }; + bool operator==(const Address &obj); + + void setPort(unsigned short port); + unsigned short getPort(); + + void setIP(DWORD ip); + void setIP(Game::netIP_t ip); + Game::netIP_t getIP(); + + void setType(Game::netadrtype_t type); + Game::netadrtype_t getType(); + + sockaddr getSockAddr(); + void toSockAddr(sockaddr* addr); + void toSockAddr(sockaddr_in* addr); + Game::netadr_t* get(); + const char* getCString(); + std::string getString(); + + bool isLocal(); + bool isSelf(); + bool isValid(); + bool isLoopback(); + + void serialize(Proto::Network::Address* protoAddress); + void deserialize(const Proto::Network::Address& protoAddress); + + private: + Game::netadr_t address; + }; + + typedef void(Callback)(Address address, std::string data); + typedef void(CallbackRaw)(); + + Network(); + ~Network(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Network"; }; +#endif + + static void Handle(std::string packet, Utils::Slot callback); + static void OnStart(Utils::Slot callback); + + // Send quake-styled binary data + static void Send(Address target, std::string data); + static void Send(Game::netsrc_t type, Address target, std::string data); + + // Allows sending raw data without quake header + static void SendRaw(Address target, std::string data); + static void SendRaw(Game::netsrc_t type, Address target, std::string data); + + // Send quake-style command using binary data + static void SendCommand(Address target, std::string command, std::string data = ""); + static void SendCommand(Game::netsrc_t type, Address target, std::string command, std::string data = ""); + + static void Broadcast(unsigned short port, std::string data); + static void BroadcastRange(unsigned int min, unsigned int max, std::string data); + static void BroadcastAll(std::string data); + + private: + static std::string SelectedPacket; + static Utils::Signal StartupSignal; + static std::map> PacketHandlers; + + static int PacketInterceptionHandler(const char* packet); + static void DeployPacket(Game::netadr_t* from, Game::msg_t* msg); + static void DeployPacketStub(); + + static void NetworkStart(); + static void NetworkStartStub(); + + static void PacketErrorCheck(); + }; +} diff --git a/src/Components/Modules/News.cpp b/src/Components/Modules/News.cpp index efe61c3f..6f81a1d3 100644 --- a/src/Components/Modules/News.cpp +++ b/src/Components/Modules/News.cpp @@ -1,199 +1,199 @@ -#include "STDInclude.hpp" - -#define NEWS_MOTD_DEFUALT "Welcome to IW4x Multiplayer!" - -namespace Components -{ - bool News::Terminate; - std::thread News::Thread; - - bool News::unitTest() - { - bool result = true; - - if (News::Thread.joinable()) - { - Logger::Print("Awaiting thread termination...\n"); - News::Thread.join(); - - if (!strlen(Localization::Get("MPUI_CHANGELOG_TEXT")) || Localization::Get("MPUI_CHANGELOG_TEXT") == "Loading..."s) - { - Logger::Print("Failed to fetch changelog!\n"); - result = false; - } - else - { - Logger::Print("Successfully fetched changelog.\n"); - } - - if (!strcmp(Localization::Get("MPUI_MOTD_TEXT"), NEWS_MOTD_DEFUALT)) - { - Logger::Print("Failed to fetch motd!\n"); - result = false; - } - else - { - Logger::Print("Successfully fetched motd.\n"); - } - } - - return result; - } - - void News::ExitProcessStub(unsigned int exitCode) - { - std::this_thread::sleep_for(10ms); - - STARTUPINFOA sInfo; - PROCESS_INFORMATION pInfo; - - ZeroMemory(&sInfo, sizeof(sInfo)); - ZeroMemory(&pInfo, sizeof(pInfo)); - sInfo.cb = sizeof(sInfo); - - CreateProcessA("updater.exe", NULL, NULL, NULL, false, NULL, NULL, NULL, &sInfo, &pInfo); - - if (pInfo.hThread && pInfo.hThread != INVALID_HANDLE_VALUE) - { - CloseHandle(pInfo.hThread); - } - - if (pInfo.hProcess && pInfo.hProcess != INVALID_HANDLE_VALUE) - { - CloseHandle(pInfo.hProcess); - } - - TerminateProcess(GetCurrentProcess(), exitCode); - } - - const char* News::GetNewsText() - { - return Localization::Get("MPUI_MOTD_TEXT"); - } - - void News::CheckForUpdate() - { - std::string caches = Utils::Cache::GetFile("/iw4/caches.xml"); - - if (!caches.empty()) - { - std::string str = "()->current.integer = version; - Dvar::Var("cl_updateavailable").get()->current.boolean = (version > REVISION); - } - } - } - } - - News::News() - { - if (ZoneBuilder::IsEnabled()) return; // Maybe also dedi? - - Dvar::Register("cl_updateoldversion", REVISION, REVISION, REVISION, Game::DVAR_FLAG_WRITEPROTECTED, "Current version number."); - Dvar::Register("cl_updateversion", 0, 0, -1, Game::DVAR_FLAG_WRITEPROTECTED, "New version number."); - Dvar::Register("cl_updateavailable", 0, Game::DVAR_FLAG_WRITEPROTECTED, "New update is available."); - - Localization::Set("MPUI_CHANGELOG_TEXT", "Loading..."); - Localization::Set("MPUI_MOTD_TEXT", NEWS_MOTD_DEFUALT); - - if (Utils::IO::FileExists("updater.exe")) - { - remove("updater.exe"); - } - - // make newsfeed (ticker) menu items not cut off based on safe area - Utils::Hook::Nop(0x63892D, 5); - - // hook for getting the news ticker string - Utils::Hook::Nop(0x6388BB, 2); // skip the "if (item->text[0] == '@')" localize check - Utils::Hook(0x6388C1, News::GetNewsText, HOOK_CALL).install()->quick(); - - Command::Add("checkforupdate", [] (Command::Params*) - { - News::CheckForUpdate(); - }); - - Command::Add("getautoupdate", [] (Command::Params*) - { - if (!Dvar::Var("cl_updateavailable").get()->current.boolean) return; - - Localization::SetTemp("MENU_RECONNECTING_TO_PARTY", "Downloading updater"); - Command::Execute("openmenu popup_reconnectingtoparty", true); - - // Run the updater on shutdown - Utils::Hook::Set(0x6D72A0, News::ExitProcessStub); - - std::thread([] () - { - std::string data = Utils::Cache::GetFile("/iw4/updater.exe"); - - if (data.empty()) - { - Localization::ClearTemp(); - Command::Execute("closemenu popup_reconnectingtoparty", false); - Game::MessageBox("Failed to download the updater!", "Error"); - } - else - { - Console::SetSkipShutdown(); - Utils::IO::WriteFile("updater.exe", data); - Command::Execute("wait 300; quit;", false); - } - }).detach(); - }); - - if (!Utils::IsWineEnvironment()) - { - News::Terminate = false; - News::Thread = std::thread([] () - { - Localization::Set("MPUI_CHANGELOG_TEXT", Utils::Cache::GetFile("/iw4/changelog.txt")); - - std::string data = Utils::Cache::GetFile("/iw4/motd.txt"); - - if (!data.empty()) - { - Localization::Set("MPUI_MOTD_TEXT", data); - } - - if (!Loader::PerformingUnitTests()) - { - while (!News::Terminate) - { - News::CheckForUpdate(); - - // Sleep for 3 minutes - for (int i = 0; i < 180 && !News::Terminate; ++i) - { - std::this_thread::sleep_for(1s); - } - } - } - }); - } - } - - News::~News() - { - News::Terminate = true; - - if (News::Thread.joinable()) - { - News::Thread.join(); - } - } -} +#include "STDInclude.hpp" + +#define NEWS_MOTD_DEFUALT "Welcome to IW4x Multiplayer!" + +namespace Components +{ + bool News::Terminate; + std::thread News::Thread; + + bool News::unitTest() + { + bool result = true; + + if (News::Thread.joinable()) + { + Logger::Print("Awaiting thread termination...\n"); + News::Thread.join(); + + if (!strlen(Localization::Get("MPUI_CHANGELOG_TEXT")) || Localization::Get("MPUI_CHANGELOG_TEXT") == "Loading..."s) + { + Logger::Print("Failed to fetch changelog!\n"); + result = false; + } + else + { + Logger::Print("Successfully fetched changelog.\n"); + } + + if (!strcmp(Localization::Get("MPUI_MOTD_TEXT"), NEWS_MOTD_DEFUALT)) + { + Logger::Print("Failed to fetch motd!\n"); + result = false; + } + else + { + Logger::Print("Successfully fetched motd.\n"); + } + } + + return result; + } + + void News::ExitProcessStub(unsigned int exitCode) + { + std::this_thread::sleep_for(10ms); + + STARTUPINFOA sInfo; + PROCESS_INFORMATION pInfo; + + ZeroMemory(&sInfo, sizeof(sInfo)); + ZeroMemory(&pInfo, sizeof(pInfo)); + sInfo.cb = sizeof(sInfo); + + CreateProcessA("updater.exe", NULL, NULL, NULL, false, NULL, NULL, NULL, &sInfo, &pInfo); + + if (pInfo.hThread && pInfo.hThread != INVALID_HANDLE_VALUE) + { + CloseHandle(pInfo.hThread); + } + + if (pInfo.hProcess && pInfo.hProcess != INVALID_HANDLE_VALUE) + { + CloseHandle(pInfo.hProcess); + } + + TerminateProcess(GetCurrentProcess(), exitCode); + } + + const char* News::GetNewsText() + { + return Localization::Get("MPUI_MOTD_TEXT"); + } + + void News::CheckForUpdate() + { + std::string caches = Utils::Cache::GetFile("/iw4/caches.xml"); + + if (!caches.empty()) + { + std::string str = "()->current.integer = version; + Dvar::Var("cl_updateavailable").get()->current.boolean = (version > REVISION); + } + } + } + } + + News::News() + { + if (ZoneBuilder::IsEnabled()) return; // Maybe also dedi? + + Dvar::Register("cl_updateoldversion", REVISION, REVISION, REVISION, Game::DVAR_FLAG_WRITEPROTECTED, "Current version number."); + Dvar::Register("cl_updateversion", 0, 0, -1, Game::DVAR_FLAG_WRITEPROTECTED, "New version number."); + Dvar::Register("cl_updateavailable", 0, Game::DVAR_FLAG_WRITEPROTECTED, "New update is available."); + + Localization::Set("MPUI_CHANGELOG_TEXT", "Loading..."); + Localization::Set("MPUI_MOTD_TEXT", NEWS_MOTD_DEFUALT); + + if (Utils::IO::FileExists("updater.exe")) + { + remove("updater.exe"); + } + + // make newsfeed (ticker) menu items not cut off based on safe area + Utils::Hook::Nop(0x63892D, 5); + + // hook for getting the news ticker string + Utils::Hook::Nop(0x6388BB, 2); // skip the "if (item->text[0] == '@')" localize check + Utils::Hook(0x6388C1, News::GetNewsText, HOOK_CALL).install()->quick(); + + Command::Add("checkforupdate", [] (Command::Params*) + { + News::CheckForUpdate(); + }); + + Command::Add("getautoupdate", [] (Command::Params*) + { + if (!Dvar::Var("cl_updateavailable").get()->current.boolean) return; + + Localization::SetTemp("MENU_RECONNECTING_TO_PARTY", "Downloading updater"); + Command::Execute("openmenu popup_reconnectingtoparty", true); + + // Run the updater on shutdown + Utils::Hook::Set(0x6D72A0, News::ExitProcessStub); + + std::thread([] () + { + std::string data = Utils::Cache::GetFile("/iw4/updater.exe"); + + if (data.empty()) + { + Localization::ClearTemp(); + Command::Execute("closemenu popup_reconnectingtoparty", false); + Game::MessageBox("Failed to download the updater!", "Error"); + } + else + { + Console::SetSkipShutdown(); + Utils::IO::WriteFile("updater.exe", data); + Command::Execute("wait 300; quit;", false); + } + }).detach(); + }); + + if (!Utils::IsWineEnvironment()) + { + News::Terminate = false; + News::Thread = std::thread([] () + { + Localization::Set("MPUI_CHANGELOG_TEXT", Utils::Cache::GetFile("/iw4/changelog.txt")); + + std::string data = Utils::Cache::GetFile("/iw4/motd.txt"); + + if (!data.empty()) + { + Localization::Set("MPUI_MOTD_TEXT", data); + } + + if (!Loader::PerformingUnitTests()) + { + while (!News::Terminate) + { + News::CheckForUpdate(); + + // Sleep for 3 minutes + for (int i = 0; i < 180 && !News::Terminate; ++i) + { + std::this_thread::sleep_for(1s); + } + } + } + }); + } + } + + News::~News() + { + News::Terminate = true; + + if (News::Thread.joinable()) + { + News::Thread.join(); + } + } +} diff --git a/src/Components/Modules/News.hpp b/src/Components/Modules/News.hpp index cf499d24..7f772cb3 100644 --- a/src/Components/Modules/News.hpp +++ b/src/Components/Modules/News.hpp @@ -1,24 +1,24 @@ -namespace Components -{ - class News : public Component - { - public: - News(); - ~News(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "News"; }; -#endif - - bool unitTest(); - - private: - static std::thread Thread; - static bool Terminate; - - static void CheckForUpdate(); - static void ExitProcessStub(unsigned int exitCode); - - static const char* GetNewsText(); - }; -} +namespace Components +{ + class News : public Component + { + public: + News(); + ~News(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "News"; }; +#endif + + bool unitTest(); + + private: + static std::thread Thread; + static bool Terminate; + + static void CheckForUpdate(); + static void ExitProcessStub(unsigned int exitCode); + + static const char* GetNewsText(); + }; +} diff --git a/src/Components/Modules/Node.hpp b/src/Components/Modules/Node.hpp index e40eccd6..65b92b73 100644 --- a/src/Components/Modules/Node.hpp +++ b/src/Components/Modules/Node.hpp @@ -1,95 +1,95 @@ -#define NODE_QUERY_INTERVAL 1000 * 60 * 2 // Query nodelist from nodes evry 2 minutes -#define NODE_QUERY_TIMEOUT 1000 * 30 * 1 // Invalidate nodes after 30 seconds without query response -#define NODE_INVALID_DELETE 1000 * 60 * 10 // Delete invalidated nodes after 10 minutes -#define NODE_FRAME_QUERY_LIMIT 3 // Limit of nodes to be queried per frame -#define NODE_FRAME_LOCK 60 // Limit of max frames per second -#define NODE_PACKET_LIMIT 111 // Send 111 nodes per synchronization packet -#define NODE_STORE_INTERVAL 1000 * 60* 1 // Store nodes every minute -#define SESSION_TIMEOUT 1000 * 10 // 10 seconds session timeout - -#define NODE_VERSION 4 - -namespace Components -{ - class Node : public Component - { - public: - Node(); - ~Node(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Node"; }; -#endif - - bool unitTest(); - - static void SyncNodeList(); - static void AddNode(Network::Address address); - - static unsigned int GetValidNodeCount(); - - static void LoadNodeRemotePreset(); - - private: - enum EntryState - { - STATE_UNKNOWN, - STATE_NEGOTIATING, - STATE_VALID, - STATE_INVALID, - }; - - class NodeEntry - { - public: - Network::Address address; - std::string challenge; - Utils::Cryptography::ECC::Key publicKey; - EntryState state; - - bool registered; // Do we consider this node as registered? - - int lastTime; // Last time we heard anything from the server itself - int lastHeard; // Last time we heard something of the server at all (refs form other nodes) - int lastListQuery; // Last time we got the list of the node - - // This is only relevant for clients - bool isDedi; - uint32_t protocol; - uint32_t version; - }; - - class ClientSession - { - public: - Network::Address address; - std::string challenge; - bool valid; - //bool terminated; // Sessions can't explicitly be terminated, they can only timeout - int lastTime; - }; - - static Utils::Cryptography::ECC::Key SignatureKey; - - static std::recursive_mutex NodeMutex; - static std::mutex SessionMutex; - static std::vector Nodes; - static std::vector Sessions; - - static void LoadNodes(); - static void LoadNodePreset(); - static void StoreNodes(bool force); - - static void PerformRegistration(Network::Address address); - static void SendNodeList(Network::Address address); - static NodeEntry* FindNode(Network::Address address); - static ClientSession* FindSession(Network::Address address); - - static void DeleteInvalidNodes(); - static void DeleteInvalidSessions(); - - static void FrameHandler(); - - static const char* GetStateName(EntryState state); - }; -} +#define NODE_QUERY_INTERVAL 1000 * 60 * 2 // Query nodelist from nodes evry 2 minutes +#define NODE_QUERY_TIMEOUT 1000 * 30 * 1 // Invalidate nodes after 30 seconds without query response +#define NODE_INVALID_DELETE 1000 * 60 * 10 // Delete invalidated nodes after 10 minutes +#define NODE_FRAME_QUERY_LIMIT 3 // Limit of nodes to be queried per frame +#define NODE_FRAME_LOCK 60 // Limit of max frames per second +#define NODE_PACKET_LIMIT 111 // Send 111 nodes per synchronization packet +#define NODE_STORE_INTERVAL 1000 * 60* 1 // Store nodes every minute +#define SESSION_TIMEOUT 1000 * 10 // 10 seconds session timeout + +#define NODE_VERSION 4 + +namespace Components +{ + class Node : public Component + { + public: + Node(); + ~Node(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Node"; }; +#endif + + bool unitTest(); + + static void SyncNodeList(); + static void AddNode(Network::Address address); + + static unsigned int GetValidNodeCount(); + + static void LoadNodeRemotePreset(); + + private: + enum EntryState + { + STATE_UNKNOWN, + STATE_NEGOTIATING, + STATE_VALID, + STATE_INVALID, + }; + + class NodeEntry + { + public: + Network::Address address; + std::string challenge; + Utils::Cryptography::ECC::Key publicKey; + EntryState state; + + bool registered; // Do we consider this node as registered? + + int lastTime; // Last time we heard anything from the server itself + int lastHeard; // Last time we heard something of the server at all (refs form other nodes) + int lastListQuery; // Last time we got the list of the node + + // This is only relevant for clients + bool isDedi; + uint32_t protocol; + uint32_t version; + }; + + class ClientSession + { + public: + Network::Address address; + std::string challenge; + bool valid; + //bool terminated; // Sessions can't explicitly be terminated, they can only timeout + int lastTime; + }; + + static Utils::Cryptography::ECC::Key SignatureKey; + + static std::recursive_mutex NodeMutex; + static std::mutex SessionMutex; + static std::vector Nodes; + static std::vector Sessions; + + static void LoadNodes(); + static void LoadNodePreset(); + static void StoreNodes(bool force); + + static void PerformRegistration(Network::Address address); + static void SendNodeList(Network::Address address); + static NodeEntry* FindNode(Network::Address address); + static ClientSession* FindSession(Network::Address address); + + static void DeleteInvalidNodes(); + static void DeleteInvalidSessions(); + + static void FrameHandler(); + + static const char* GetStateName(EntryState state); + }; +} diff --git a/src/Components/Modules/Party.cpp b/src/Components/Modules/Party.cpp index e3104441..11e8c8e6 100644 --- a/src/Components/Modules/Party.cpp +++ b/src/Components/Modules/Party.cpp @@ -1,459 +1,459 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Party::JoinContainer Party::Container; - std::map Party::LobbyMap; - - SteamID Party::GenerateLobbyId() - { - SteamID id; - - id.AccountID = Game::Sys_Milliseconds(); - id.Universe = 1; - id.AccountType = 8; - id.AccountInstance = 0x40000; - - return id; - } - - Network::Address Party::Target() - { - return Party::Container.target; - } - - void Party::Connect(Network::Address target) - { - Node::AddNode(target); - - Party::Container.valid = true; - Party::Container.awaitingPlaylist = false; - Party::Container.joinTime = Game::Sys_Milliseconds(); - Party::Container.target = target; - Party::Container.challenge = Utils::String::VA("%X", Utils::Cryptography::Rand::GenerateInt()); - - Network::SendCommand(Party::Container.target, "getinfo", Party::Container.challenge); - - Command::Execute("openmenu popup_reconnectingtoparty"); - } - - const char* Party::GetLobbyInfo(SteamID lobby, std::string key) - { - if (Party::LobbyMap.find(lobby.Bits) != Party::LobbyMap.end()) - { - Network::Address address = Party::LobbyMap[lobby.Bits]; - - if (key == "addr") - { - return Utils::String::VA("%d", address.getIP().full); - } - else if (key =="port") - { - return Utils::String::VA("%d", address.getPort()); - } - } - - return "212"; - } - - void Party::RemoveLobby(SteamID lobby) - { - if (Party::LobbyMap.find(lobby.Bits) != Party::LobbyMap.end()) - { - Party::LobbyMap.erase(Party::LobbyMap.find(lobby.Bits)); - } - } - - void Party::ConnectError(std::string message) - { - Localization::ClearTemp(); - Command::Execute("closemenu popup_reconnectingtoparty"); - Dvar::Var("partyend_reason").set(message); - Command::Execute("openmenu menu_xboxlive_partyended"); - } - - std::string Party::GetMotd() - { - return Party::Container.motd; - } - - Game::dvar_t* Party::RegisterMinPlayers(const char* name, int /*value*/, int /*min*/, int max, Game::dvar_flag flag, const char* description) - { - return Dvar::Register(name, 1, 1, max, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED | flag, description).get(); - } - - bool Party::PlaylistAwaiting() - { - return Party::Container.awaitingPlaylist; - } - - void Party::PlaylistContinue() - { - Dvar::Var("xblive_privateserver").set(false); - - // Ensure we can join - *Game::g_lobbyCreateInProgress = false; - - Party::Container.awaitingPlaylist = false; - - SteamID id = Party::GenerateLobbyId(); - - // Temporary workaround - // TODO: Patch the 127.0.0.1 -> loopback mapping in the party code - if (Party::Container.target.isLoopback()) - { - if (*Game::numIP) - { - Party::Container.target.setIP(*Game::localIP); - Party::Container.target.setType(Game::netadrtype_t::NA_IP); - - Logger::Print("Trying to connect to party with loopback address, using a local ip instead: %s\n", Party::Container.target.getCString()); - } - else - { - Logger::Print("Trying to connect to party with loopback address, but no local ip was found.\n"); - } - } - - Party::LobbyMap[id.Bits] = Party::Container.target; - - Game::Steam_JoinLobby(id, 0); - } - - void Party::PlaylistError(std::string error) - { - Party::Container.valid = false; - Party::Container.awaitingPlaylist = false; - - Party::ConnectError(error); - } - - DWORD Party::UIDvarIntStub(char* dvar) - { - if (!_stricmp(dvar, "onlinegame")) - { - return 0x649E660; - } - - return Utils::Hook::Call(0x4D5390)(dvar); - } - - Party::Party() - { - static Game::dvar_t* partyEnable = Dvar::Register("party_enable", Dedicated::IsEnabled(), Game::dvar_flag::DVAR_FLAG_NONE, "Enable party system").get(); - Dvar::Register("xblive_privatematch", true, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED, "").get(); - - // various changes to SV_DirectConnect-y stuff to allow non-party joinees - Utils::Hook::Set(0x460D96, 0x90E9); - Utils::Hook::Set(0x460F0A, 0xEB); - Utils::Hook::Set(0x401CA4, 0xEB); - Utils::Hook::Set(0x401C15, 0xEB); - - // disable configstring checksum matching (it's unreliable at most) - Utils::Hook::Set(0x4A75A7, 0xEB); // SV_SpawnServer - Utils::Hook::Set(0x5AC2CF, 0xEB); // CL_ParseGamestate - Utils::Hook::Set(0x5AC2C3, 0xEB); // CL_ParseGamestate - - // AnonymousAddRequest - Utils::Hook::Set(0x5B5E18, 0xEB); - Utils::Hook::Set(0x5B5E64, 0xEB); - Utils::Hook::Nop(0x5B5E5C, 2); - - // HandleClientHandshake - Utils::Hook::Set(0x5B6EA5, 0xEB); - Utils::Hook::Set(0x5B6EF3, 0xEB); - Utils::Hook::Nop(0x5B6EEB, 2); - - // Allow local connections - Utils::Hook::Set(0x4D43DA, 0xEB); - - // LobbyID mismatch - Utils::Hook::Nop(0x4E50D6, 2); - Utils::Hook::Set(0x4E50DA, 0xEB); - - // causes 'does current Steam lobby match' calls in Steam_JoinLobby to be ignored - Utils::Hook::Set(0x49D007, 0xEB); - - // functions checking party heartbeat timeouts, cause random issues - Utils::Hook::Nop(0x4E532D, 5); - - // Steam_JoinLobby call causes migration - Utils::Hook::Nop(0x5AF851, 5); - Utils::Hook::Set(0x5AF85B, 0xEB); - - // Allow xpartygo in public lobbies - Utils::Hook::Set(0x5A969E, 0xEB); - Utils::Hook::Nop(0x5A96BE, 2); - - // Always open lobby menu when connecting - // It's not possible to entirely patch it via code - //Utils::Hook::Set(0x5B1698, 0xEB); - //Utils::Hook::Nop(0x5029F2, 6); - //Utils::Hook::SetString(0x70573C, "menu_xboxlive_lobby"); - - // Disallow selecting team in private match - //Utils::Hook::Nop(0x5B2BD8, 6); - - // Force teams, even if not private match - Utils::Hook::Set(0x487BB2, 0xEB); - - // Force xblive_privatematch 0 and rename it - //Utils::Hook::Set(0x420A6A, 4); - Utils::Hook::Set(0x420A6C, 0); - Utils::Hook::Set(0x420A6E, "xblive_privateserver"); - - // Remove migration shutdown, it causes crashes and will be destroyed when erroring anyways - Utils::Hook::Nop(0x5A8E1C, 12); - Utils::Hook::Nop(0x5A8E33, 11); - - // Enable XP Bar - Utils::Hook(0x62A2A7, Party::UIDvarIntStub, HOOK_CALL).install()->quick(); - - // Set NAT to open - Utils::Hook::Set(0x79D898, 1); - - // Disable host migration - Utils::Hook::Set(0x5B58B2, 0xEB); - Utils::Hook::Set(0x4D6171, 0); - - // Patch playlist stuff for non-party behavior - Utils::Hook::Set(0x4A4093, &partyEnable); - Utils::Hook::Set(0x4573F1, &partyEnable); - Utils::Hook::Set(0x5B1A0C, &partyEnable); - - // Invert corresponding jumps - Utils::Hook::Xor(0x4A409B, 1); - Utils::Hook::Xor(0x4573FA, 1); - Utils::Hook::Xor(0x5B1A17, 1); - - // Fix xstartlobby - //Utils::Hook::Set(0x5B71CD, 0xEB); - - // Patch party_minplayers to 1 and protect it - //Utils::Hook(0x4D5D51, Party::RegisterMinPlayers, HOOK_CALL).install()->quick(); - - // Set ui_maxclients to sv_maxclients - Utils::Hook::Set(0x42618F, "sv_maxclients"); - Utils::Hook::Set(0x4D3756, "sv_maxclients"); - Utils::Hook::Set(0x5E3772, "sv_maxclients"); - - // Unlatch maxclient dvars - Utils::Hook::Xor(0x426187, Game::dvar_flag::DVAR_FLAG_LATCHED); - Utils::Hook::Xor(0x4D374E, Game::dvar_flag::DVAR_FLAG_LATCHED); - Utils::Hook::Xor(0x5E376A, Game::dvar_flag::DVAR_FLAG_LATCHED); - Utils::Hook::Xor(0x4261A1, Game::dvar_flag::DVAR_FLAG_LATCHED); - Utils::Hook::Xor(0x4D376D, Game::dvar_flag::DVAR_FLAG_LATCHED); - Utils::Hook::Xor(0x5E3789, Game::dvar_flag::DVAR_FLAG_LATCHED); - - // Patch Live_PlayerHasLoopbackAddr - //Utils::Hook::Set(0x418F30, 0x90C3C033); - - Command::Add("connect", [] (Command::Params* params) - { - if (params->length() < 2) - { - return; - } - - Party::Connect(Network::Address(params->get(1))); - }); - Command::Add("reconnect", [] (Command::Params*) - { - Party::Connect(Party::Container.target); - }); - - Renderer::OnFrame([] () - { - if (Party::Container.valid) - { - if ((Game::Sys_Milliseconds() - Party::Container.joinTime) > 5000) - { - Party::Container.valid = false; - Party::ConnectError("Server connection timed out."); - } - } - - if (Party::Container.awaitingPlaylist) - { - if ((Game::Sys_Milliseconds() - Party::Container.requestTime) > 5000) - { - Party::Container.awaitingPlaylist = false; - Party::ConnectError("Playlist request timed out."); - } - } - }); - - // Basic info handler - Network::Handle("getInfo", [] (Network::Address address, std::string data) - { - int clientCount = 0; - int maxclientCount = *Game::svs_numclients; - - if (maxclientCount) - { - for (int i = 0; i < maxclientCount; ++i) - { - if (Game::svs_clients[i].state >= 3) - { - ++clientCount; - } - } - } - else - { - //maxclientCount = Dvar::Var("sv_maxclients").get(); - maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); - clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); - } - - Utils::InfoString info; - info.set("challenge", Utils::ParseChallenge(data)); - info.set("gamename", "IW4"); - info.set("hostname", Dvar::Var("sv_hostname").get()); - info.set("gametype", Dvar::Var("g_gametype").get()); - info.set("fs_game", Dvar::Var("fs_game").get()); - info.set("xuid", Utils::String::VA("%llX", Steam::SteamUser()->GetSteamID().Bits)); - info.set("clients", Utils::String::VA("%i", clientCount)); - info.set("sv_maxclients", Utils::String::VA("%i", maxclientCount)); - info.set("protocol", Utils::String::VA("%i", PROTOCOL)); - info.set("shortversion", SHORTVERSION); - info.set("checksum", Utils::String::VA("%d", Game::Sys_Milliseconds())); - info.set("mapname", Dvar::Var("mapname").get()); - info.set("isPrivate", (Dvar::Var("g_password").get().size() ? "1" : "0")); - info.set("hc", (Dvar::Var("g_hardcore").get() ? "1" : "0")); - info.set("securityLevel", Utils::String::VA("%i", Dvar::Var("sv_securityLevel").get())); - info.set("sv_running", (Dvar::Var("sv_running").get() ? "1" : "0")); - - if (Dedicated::IsEnabled()) - { - info.set("sv_motd", Dvar::Var("sv_motd").get()); - } - - // Ensure mapname is set - if (info.get("mapname").empty() || (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get() && !Dvar::Var("sv_running").get())) - { - info.set("mapname", Dvar::Var("ui_mapname").get()); - } - - // Set matchtype - // 0 - No match, connecting not possible - // 1 - Party, use Steam_JoinLobby to connect - // 2 - Match, use CL_ConnectFromParty to connect - - if (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get()) // Party hosting - { - info.set("matchtype", "1"); - } - else if (Dvar::Var("sv_running").get()) // Match hosting - { - info.set("matchtype", "2"); - } - else - { - info.set("matchtype", "0"); - } - - Network::SendCommand(address, "infoResponse", "\\" + info.build()); - }); - - Network::Handle("infoResponse", [] (Network::Address address, std::string data) - { - Utils::InfoString info(data); - - // Handle connection - if (Party::Container.valid) - { - if (Party::Container.target == address) - { - // Invalidate handler for future packets - Party::Container.valid = false; - Party::Container.info = info; - - Party::Container.matchType = atoi(info.get("matchtype").data()); - uint32_t securityLevel = static_cast(atoi(info.get("securityLevel").data())); - - if (info.get("challenge") != Party::Container.challenge) - { - Party::ConnectError("Invalid join response: Challenge mismatch."); - } - else if (securityLevel > Auth::GetSecurityLevel()) - { - //Party::ConnectError(Utils::VA("Your security level (%d) is lower than the server's (%d)", Auth::GetSecurityLevel(), securityLevel)); - Command::Execute("closemenu popup_reconnectingtoparty"); - Auth::IncreaseSecurityLevel(securityLevel, "reconnect"); - } - else if (!Party::Container.matchType) - { - Party::ConnectError("Server is not hosting a match."); - } - else if(Party::Container.matchType > 2 || Party::Container.matchType < 0) - { - Party::ConnectError("Invalid join response: Unknown matchtype"); - } - else if(!info.get("fs_game").empty() && Utils::String::ToLower(Dvar::Var("fs_game").get()) != Utils::String::ToLower(info.get("fs_game"))) - { - Command::Execute("closemenu popup_reconnectingtoparty"); - Download::InitiateClientDownload(info.get("fs_game")); - } - else if (!Dvar::Var("fs_game").get().empty() && info.get("fs_game").empty()) - { - Dvar::Var("fs_game").set(""); - - if (Dvar::Var("cl_modVidRestart").get()) - { - Command::Execute("vid_restart", false); - } - - Command::Execute("reconnect", false); - } - else - { - Party::Container.motd = info.get("sv_motd"); - - if (Party::Container.matchType == 1) // Party - { - // Send playlist request - Party::Container.requestTime = Game::Sys_Milliseconds(); - Party::Container.awaitingPlaylist = true; - Network::SendCommand(Party::Container.target, "getplaylist"); - - // This is not a safe method - // TODO: Fix actual error! - if (Game::CL_IsCgameInitialized()) - { - Command::Execute("disconnect", true); - } - } - else if (Party::Container.matchType == 2) // Match - { - if (atoi(Party::Container.info.get("clients").data()) >= atoi(Party::Container.info.get("sv_maxclients").data())) - { - Party::ConnectError("@EXE_SERVERISFULL"); - } - if (Party::Container.info.get("mapname") == "" || Party::Container.info.get("gametype") == "") - { - Party::ConnectError("Invalid map or gametype."); - } - else - { - Dvar::Var("xblive_privateserver").set(true); - - Game::Menus_CloseAll(Game::uiContext); - - Game::_XSESSION_INFO hostInfo; - Game::CL_ConnectFromParty(0, &hostInfo, *Party::Container.target.get(), 0, 0, Party::Container.info.get("mapname").data(), Party::Container.info.get("gametype").data()); - } - } - } - } - } - - ServerList::Insert(address, info); - }); - } - - Party::~Party() - { - Party::LobbyMap.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Party::JoinContainer Party::Container; + std::map Party::LobbyMap; + + SteamID Party::GenerateLobbyId() + { + SteamID id; + + id.AccountID = Game::Sys_Milliseconds(); + id.Universe = 1; + id.AccountType = 8; + id.AccountInstance = 0x40000; + + return id; + } + + Network::Address Party::Target() + { + return Party::Container.target; + } + + void Party::Connect(Network::Address target) + { + Node::AddNode(target); + + Party::Container.valid = true; + Party::Container.awaitingPlaylist = false; + Party::Container.joinTime = Game::Sys_Milliseconds(); + Party::Container.target = target; + Party::Container.challenge = Utils::String::VA("%X", Utils::Cryptography::Rand::GenerateInt()); + + Network::SendCommand(Party::Container.target, "getinfo", Party::Container.challenge); + + Command::Execute("openmenu popup_reconnectingtoparty"); + } + + const char* Party::GetLobbyInfo(SteamID lobby, std::string key) + { + if (Party::LobbyMap.find(lobby.Bits) != Party::LobbyMap.end()) + { + Network::Address address = Party::LobbyMap[lobby.Bits]; + + if (key == "addr") + { + return Utils::String::VA("%d", address.getIP().full); + } + else if (key =="port") + { + return Utils::String::VA("%d", address.getPort()); + } + } + + return "212"; + } + + void Party::RemoveLobby(SteamID lobby) + { + if (Party::LobbyMap.find(lobby.Bits) != Party::LobbyMap.end()) + { + Party::LobbyMap.erase(Party::LobbyMap.find(lobby.Bits)); + } + } + + void Party::ConnectError(std::string message) + { + Localization::ClearTemp(); + Command::Execute("closemenu popup_reconnectingtoparty"); + Dvar::Var("partyend_reason").set(message); + Command::Execute("openmenu menu_xboxlive_partyended"); + } + + std::string Party::GetMotd() + { + return Party::Container.motd; + } + + Game::dvar_t* Party::RegisterMinPlayers(const char* name, int /*value*/, int /*min*/, int max, Game::dvar_flag flag, const char* description) + { + return Dvar::Register(name, 1, 1, max, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED | flag, description).get(); + } + + bool Party::PlaylistAwaiting() + { + return Party::Container.awaitingPlaylist; + } + + void Party::PlaylistContinue() + { + Dvar::Var("xblive_privateserver").set(false); + + // Ensure we can join + *Game::g_lobbyCreateInProgress = false; + + Party::Container.awaitingPlaylist = false; + + SteamID id = Party::GenerateLobbyId(); + + // Temporary workaround + // TODO: Patch the 127.0.0.1 -> loopback mapping in the party code + if (Party::Container.target.isLoopback()) + { + if (*Game::numIP) + { + Party::Container.target.setIP(*Game::localIP); + Party::Container.target.setType(Game::netadrtype_t::NA_IP); + + Logger::Print("Trying to connect to party with loopback address, using a local ip instead: %s\n", Party::Container.target.getCString()); + } + else + { + Logger::Print("Trying to connect to party with loopback address, but no local ip was found.\n"); + } + } + + Party::LobbyMap[id.Bits] = Party::Container.target; + + Game::Steam_JoinLobby(id, 0); + } + + void Party::PlaylistError(std::string error) + { + Party::Container.valid = false; + Party::Container.awaitingPlaylist = false; + + Party::ConnectError(error); + } + + DWORD Party::UIDvarIntStub(char* dvar) + { + if (!_stricmp(dvar, "onlinegame")) + { + return 0x649E660; + } + + return Utils::Hook::Call(0x4D5390)(dvar); + } + + Party::Party() + { + static Game::dvar_t* partyEnable = Dvar::Register("party_enable", Dedicated::IsEnabled(), Game::dvar_flag::DVAR_FLAG_NONE, "Enable party system").get(); + Dvar::Register("xblive_privatematch", true, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED, "").get(); + + // various changes to SV_DirectConnect-y stuff to allow non-party joinees + Utils::Hook::Set(0x460D96, 0x90E9); + Utils::Hook::Set(0x460F0A, 0xEB); + Utils::Hook::Set(0x401CA4, 0xEB); + Utils::Hook::Set(0x401C15, 0xEB); + + // disable configstring checksum matching (it's unreliable at most) + Utils::Hook::Set(0x4A75A7, 0xEB); // SV_SpawnServer + Utils::Hook::Set(0x5AC2CF, 0xEB); // CL_ParseGamestate + Utils::Hook::Set(0x5AC2C3, 0xEB); // CL_ParseGamestate + + // AnonymousAddRequest + Utils::Hook::Set(0x5B5E18, 0xEB); + Utils::Hook::Set(0x5B5E64, 0xEB); + Utils::Hook::Nop(0x5B5E5C, 2); + + // HandleClientHandshake + Utils::Hook::Set(0x5B6EA5, 0xEB); + Utils::Hook::Set(0x5B6EF3, 0xEB); + Utils::Hook::Nop(0x5B6EEB, 2); + + // Allow local connections + Utils::Hook::Set(0x4D43DA, 0xEB); + + // LobbyID mismatch + Utils::Hook::Nop(0x4E50D6, 2); + Utils::Hook::Set(0x4E50DA, 0xEB); + + // causes 'does current Steam lobby match' calls in Steam_JoinLobby to be ignored + Utils::Hook::Set(0x49D007, 0xEB); + + // functions checking party heartbeat timeouts, cause random issues + Utils::Hook::Nop(0x4E532D, 5); + + // Steam_JoinLobby call causes migration + Utils::Hook::Nop(0x5AF851, 5); + Utils::Hook::Set(0x5AF85B, 0xEB); + + // Allow xpartygo in public lobbies + Utils::Hook::Set(0x5A969E, 0xEB); + Utils::Hook::Nop(0x5A96BE, 2); + + // Always open lobby menu when connecting + // It's not possible to entirely patch it via code + //Utils::Hook::Set(0x5B1698, 0xEB); + //Utils::Hook::Nop(0x5029F2, 6); + //Utils::Hook::SetString(0x70573C, "menu_xboxlive_lobby"); + + // Disallow selecting team in private match + //Utils::Hook::Nop(0x5B2BD8, 6); + + // Force teams, even if not private match + Utils::Hook::Set(0x487BB2, 0xEB); + + // Force xblive_privatematch 0 and rename it + //Utils::Hook::Set(0x420A6A, 4); + Utils::Hook::Set(0x420A6C, 0); + Utils::Hook::Set(0x420A6E, "xblive_privateserver"); + + // Remove migration shutdown, it causes crashes and will be destroyed when erroring anyways + Utils::Hook::Nop(0x5A8E1C, 12); + Utils::Hook::Nop(0x5A8E33, 11); + + // Enable XP Bar + Utils::Hook(0x62A2A7, Party::UIDvarIntStub, HOOK_CALL).install()->quick(); + + // Set NAT to open + Utils::Hook::Set(0x79D898, 1); + + // Disable host migration + Utils::Hook::Set(0x5B58B2, 0xEB); + Utils::Hook::Set(0x4D6171, 0); + + // Patch playlist stuff for non-party behavior + Utils::Hook::Set(0x4A4093, &partyEnable); + Utils::Hook::Set(0x4573F1, &partyEnable); + Utils::Hook::Set(0x5B1A0C, &partyEnable); + + // Invert corresponding jumps + Utils::Hook::Xor(0x4A409B, 1); + Utils::Hook::Xor(0x4573FA, 1); + Utils::Hook::Xor(0x5B1A17, 1); + + // Fix xstartlobby + //Utils::Hook::Set(0x5B71CD, 0xEB); + + // Patch party_minplayers to 1 and protect it + //Utils::Hook(0x4D5D51, Party::RegisterMinPlayers, HOOK_CALL).install()->quick(); + + // Set ui_maxclients to sv_maxclients + Utils::Hook::Set(0x42618F, "sv_maxclients"); + Utils::Hook::Set(0x4D3756, "sv_maxclients"); + Utils::Hook::Set(0x5E3772, "sv_maxclients"); + + // Unlatch maxclient dvars + Utils::Hook::Xor(0x426187, Game::dvar_flag::DVAR_FLAG_LATCHED); + Utils::Hook::Xor(0x4D374E, Game::dvar_flag::DVAR_FLAG_LATCHED); + Utils::Hook::Xor(0x5E376A, Game::dvar_flag::DVAR_FLAG_LATCHED); + Utils::Hook::Xor(0x4261A1, Game::dvar_flag::DVAR_FLAG_LATCHED); + Utils::Hook::Xor(0x4D376D, Game::dvar_flag::DVAR_FLAG_LATCHED); + Utils::Hook::Xor(0x5E3789, Game::dvar_flag::DVAR_FLAG_LATCHED); + + // Patch Live_PlayerHasLoopbackAddr + //Utils::Hook::Set(0x418F30, 0x90C3C033); + + Command::Add("connect", [] (Command::Params* params) + { + if (params->length() < 2) + { + return; + } + + Party::Connect(Network::Address(params->get(1))); + }); + Command::Add("reconnect", [] (Command::Params*) + { + Party::Connect(Party::Container.target); + }); + + Renderer::OnFrame([] () + { + if (Party::Container.valid) + { + if ((Game::Sys_Milliseconds() - Party::Container.joinTime) > 5000) + { + Party::Container.valid = false; + Party::ConnectError("Server connection timed out."); + } + } + + if (Party::Container.awaitingPlaylist) + { + if ((Game::Sys_Milliseconds() - Party::Container.requestTime) > 5000) + { + Party::Container.awaitingPlaylist = false; + Party::ConnectError("Playlist request timed out."); + } + } + }); + + // Basic info handler + Network::Handle("getInfo", [] (Network::Address address, std::string data) + { + int clientCount = 0; + int maxclientCount = *Game::svs_numclients; + + if (maxclientCount) + { + for (int i = 0; i < maxclientCount; ++i) + { + if (Game::svs_clients[i].state >= 3) + { + ++clientCount; + } + } + } + else + { + //maxclientCount = Dvar::Var("sv_maxclients").get(); + maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); + clientCount = Game::PartyHost_CountMembers(reinterpret_cast(0x1081C00)); + } + + Utils::InfoString info; + info.set("challenge", Utils::ParseChallenge(data)); + info.set("gamename", "IW4"); + info.set("hostname", Dvar::Var("sv_hostname").get()); + info.set("gametype", Dvar::Var("g_gametype").get()); + info.set("fs_game", Dvar::Var("fs_game").get()); + info.set("xuid", Utils::String::VA("%llX", Steam::SteamUser()->GetSteamID().Bits)); + info.set("clients", Utils::String::VA("%i", clientCount)); + info.set("sv_maxclients", Utils::String::VA("%i", maxclientCount)); + info.set("protocol", Utils::String::VA("%i", PROTOCOL)); + info.set("shortversion", SHORTVERSION); + info.set("checksum", Utils::String::VA("%d", Game::Sys_Milliseconds())); + info.set("mapname", Dvar::Var("mapname").get()); + info.set("isPrivate", (Dvar::Var("g_password").get().size() ? "1" : "0")); + info.set("hc", (Dvar::Var("g_hardcore").get() ? "1" : "0")); + info.set("securityLevel", Utils::String::VA("%i", Dvar::Var("sv_securityLevel").get())); + info.set("sv_running", (Dvar::Var("sv_running").get() ? "1" : "0")); + + if (Dedicated::IsEnabled()) + { + info.set("sv_motd", Dvar::Var("sv_motd").get()); + } + + // Ensure mapname is set + if (info.get("mapname").empty() || (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get() && !Dvar::Var("sv_running").get())) + { + info.set("mapname", Dvar::Var("ui_mapname").get()); + } + + // Set matchtype + // 0 - No match, connecting not possible + // 1 - Party, use Steam_JoinLobby to connect + // 2 - Match, use CL_ConnectFromParty to connect + + if (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get()) // Party hosting + { + info.set("matchtype", "1"); + } + else if (Dvar::Var("sv_running").get()) // Match hosting + { + info.set("matchtype", "2"); + } + else + { + info.set("matchtype", "0"); + } + + Network::SendCommand(address, "infoResponse", "\\" + info.build()); + }); + + Network::Handle("infoResponse", [] (Network::Address address, std::string data) + { + Utils::InfoString info(data); + + // Handle connection + if (Party::Container.valid) + { + if (Party::Container.target == address) + { + // Invalidate handler for future packets + Party::Container.valid = false; + Party::Container.info = info; + + Party::Container.matchType = atoi(info.get("matchtype").data()); + uint32_t securityLevel = static_cast(atoi(info.get("securityLevel").data())); + + if (info.get("challenge") != Party::Container.challenge) + { + Party::ConnectError("Invalid join response: Challenge mismatch."); + } + else if (securityLevel > Auth::GetSecurityLevel()) + { + //Party::ConnectError(Utils::VA("Your security level (%d) is lower than the server's (%d)", Auth::GetSecurityLevel(), securityLevel)); + Command::Execute("closemenu popup_reconnectingtoparty"); + Auth::IncreaseSecurityLevel(securityLevel, "reconnect"); + } + else if (!Party::Container.matchType) + { + Party::ConnectError("Server is not hosting a match."); + } + else if(Party::Container.matchType > 2 || Party::Container.matchType < 0) + { + Party::ConnectError("Invalid join response: Unknown matchtype"); + } + else if(!info.get("fs_game").empty() && Utils::String::ToLower(Dvar::Var("fs_game").get()) != Utils::String::ToLower(info.get("fs_game"))) + { + Command::Execute("closemenu popup_reconnectingtoparty"); + Download::InitiateClientDownload(info.get("fs_game")); + } + else if (!Dvar::Var("fs_game").get().empty() && info.get("fs_game").empty()) + { + Dvar::Var("fs_game").set(""); + + if (Dvar::Var("cl_modVidRestart").get()) + { + Command::Execute("vid_restart", false); + } + + Command::Execute("reconnect", false); + } + else + { + Party::Container.motd = info.get("sv_motd"); + + if (Party::Container.matchType == 1) // Party + { + // Send playlist request + Party::Container.requestTime = Game::Sys_Milliseconds(); + Party::Container.awaitingPlaylist = true; + Network::SendCommand(Party::Container.target, "getplaylist"); + + // This is not a safe method + // TODO: Fix actual error! + if (Game::CL_IsCgameInitialized()) + { + Command::Execute("disconnect", true); + } + } + else if (Party::Container.matchType == 2) // Match + { + if (atoi(Party::Container.info.get("clients").data()) >= atoi(Party::Container.info.get("sv_maxclients").data())) + { + Party::ConnectError("@EXE_SERVERISFULL"); + } + if (Party::Container.info.get("mapname") == "" || Party::Container.info.get("gametype") == "") + { + Party::ConnectError("Invalid map or gametype."); + } + else + { + Dvar::Var("xblive_privateserver").set(true); + + Game::Menus_CloseAll(Game::uiContext); + + Game::_XSESSION_INFO hostInfo; + Game::CL_ConnectFromParty(0, &hostInfo, *Party::Container.target.get(), 0, 0, Party::Container.info.get("mapname").data(), Party::Container.info.get("gametype").data()); + } + } + } + } + } + + ServerList::Insert(address, info); + }); + } + + Party::~Party() + { + Party::LobbyMap.clear(); + } +} diff --git a/src/Components/Modules/Party.hpp b/src/Components/Modules/Party.hpp index ba491063..e942d26d 100644 --- a/src/Components/Modules/Party.hpp +++ b/src/Components/Modules/Party.hpp @@ -1,53 +1,53 @@ -namespace Components -{ - class Party : public Component - { - public: - Party(); - ~Party(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Party"; }; -#endif - - static Network::Address Target(); - static void Connect(Network::Address target); - static const char* GetLobbyInfo(SteamID lobby, std::string key); - static void RemoveLobby(SteamID lobby); - - static bool PlaylistAwaiting(); - static void PlaylistContinue(); - static void PlaylistError(std::string error); - - static void ConnectError(std::string message); - - static std::string GetMotd(); - - private: - class JoinContainer - { - public: - Network::Address target; - std::string challenge; - std::string motd; - DWORD joinTime; - bool valid; - int matchType; - - Utils::InfoString info; - - // Party-specific stuff - DWORD requestTime; - bool awaitingPlaylist; - }; - - static JoinContainer Container; - static std::map LobbyMap; - - static SteamID GenerateLobbyId(); - - static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, Game::dvar_flag flag, const char* description); - - static DWORD UIDvarIntStub(char* dvar); - }; -} +namespace Components +{ + class Party : public Component + { + public: + Party(); + ~Party(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Party"; }; +#endif + + static Network::Address Target(); + static void Connect(Network::Address target); + static const char* GetLobbyInfo(SteamID lobby, std::string key); + static void RemoveLobby(SteamID lobby); + + static bool PlaylistAwaiting(); + static void PlaylistContinue(); + static void PlaylistError(std::string error); + + static void ConnectError(std::string message); + + static std::string GetMotd(); + + private: + class JoinContainer + { + public: + Network::Address target; + std::string challenge; + std::string motd; + DWORD joinTime; + bool valid; + int matchType; + + Utils::InfoString info; + + // Party-specific stuff + DWORD requestTime; + bool awaitingPlaylist; + }; + + static JoinContainer Container; + static std::map LobbyMap; + + static SteamID GenerateLobbyId(); + + static Game::dvar_t* RegisterMinPlayers(const char* name, int value, int min, int max, Game::dvar_flag flag, const char* description); + + static DWORD UIDvarIntStub(char* dvar); + }; +} diff --git a/src/Components/Modules/PlayerName.cpp b/src/Components/Modules/PlayerName.cpp index ed92d140..67e3b053 100644 --- a/src/Components/Modules/PlayerName.cpp +++ b/src/Components/Modules/PlayerName.cpp @@ -1,33 +1,33 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::string PlayerName::PlayerNames[18]; - - int PlayerName::GetClientName(int /*localClientNum*/, int index, char *buf, int size) - { - if (index < 0 || index >= 18) return 0; - return strncpy_s(buf, size, PlayerName::PlayerNames[index].data(), PlayerName::PlayerNames[index].size()) == 0; - } - - PlayerName::PlayerName() - { - if (0) // Disabled for now (comment out that line to enable it) - { - for (int i = 0; i < ARRAY_SIZE(PlayerName::PlayerNames); ++i) - { - PlayerName::PlayerNames[i] = "mumu"; - } - - Utils::Hook(Game::CL_GetClientName, PlayerName::GetClientName, HOOK_JUMP).install()->quick(); - } - } - - PlayerName::~PlayerName() - { - for (int i = 0; i < ARRAY_SIZE(PlayerName::PlayerNames); ++i) - { - PlayerName::PlayerNames[i].clear(); - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::string PlayerName::PlayerNames[18]; + + int PlayerName::GetClientName(int /*localClientNum*/, int index, char *buf, int size) + { + if (index < 0 || index >= 18) return 0; + return strncpy_s(buf, size, PlayerName::PlayerNames[index].data(), PlayerName::PlayerNames[index].size()) == 0; + } + + PlayerName::PlayerName() + { + if (0) // Disabled for now (comment out that line to enable it) + { + for (int i = 0; i < ARRAY_SIZE(PlayerName::PlayerNames); ++i) + { + PlayerName::PlayerNames[i] = "mumu"; + } + + Utils::Hook(Game::CL_GetClientName, PlayerName::GetClientName, HOOK_JUMP).install()->quick(); + } + } + + PlayerName::~PlayerName() + { + for (int i = 0; i < ARRAY_SIZE(PlayerName::PlayerNames); ++i) + { + PlayerName::PlayerNames[i].clear(); + } + } +} diff --git a/src/Components/Modules/PlayerName.hpp b/src/Components/Modules/PlayerName.hpp index 480ca506..2f6fa6a9 100644 --- a/src/Components/Modules/PlayerName.hpp +++ b/src/Components/Modules/PlayerName.hpp @@ -1,18 +1,18 @@ -namespace Components -{ - class PlayerName : public Component - { - public: - PlayerName(); - ~PlayerName(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "PlayerName"; }; -#endif - - private: - static std::string PlayerNames[18]; - - static int GetClientName(int localClientNum, int index, char *buf, int size); - }; -} +namespace Components +{ + class PlayerName : public Component + { + public: + PlayerName(); + ~PlayerName(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "PlayerName"; }; +#endif + + private: + static std::string PlayerNames[18]; + + static int GetClientName(int localClientNum, int index, char *buf, int size); + }; +} diff --git a/src/Components/Modules/Playlist.cpp b/src/Components/Modules/Playlist.cpp index 74d0c240..35670563 100644 --- a/src/Components/Modules/Playlist.cpp +++ b/src/Components/Modules/Playlist.cpp @@ -1,184 +1,184 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::string Playlist::CurrentPlaylistBuffer; - std::string Playlist::ReceivedPlaylistBuffer; - std::unordered_map Playlist::MapRelocation; - - void Playlist::LoadPlaylist() - { - // Check if playlist already loaded - if (Utils::Hook::Get(0x1AD3680)) return; - - // Don't load playlists when dedi and no party - if (Dedicated::IsEnabled() && !Dvar::Var("party_enable").get()) - { - Utils::Hook::Set(0x1AD3680, true); // Set received to true - Dvar::Var("xblive_privateserver").set(true); - return; - } - - Dvar::Var("xblive_privateserver").set(false); - - std::string playlistFilename = Dvar::Var("playlistFilename").get(); - FileSystem::File playlist(playlistFilename); - - if (playlist.exists()) - { - Logger::Print("Parsing playlist '%s'...\n", playlist.getName().data()); - Game::Playlist_ParsePlaylists(playlist.getBuffer().data()); - Utils::Hook::Set(0x1AD3680, true); // Playlist loaded - } - else - { - Logger::Print("Unable to load playlist '%s'!\n", playlist.getName().data()); - } - } - - DWORD Playlist::StorePlaylistStub(const char** buffer) - { - Playlist::MapRelocation.clear(); - Playlist::CurrentPlaylistBuffer = *buffer; - return Utils::Hook::Call(0x4C0350)(buffer); - } - - void Playlist::PlaylistRequest(Network::Address address, std::string data) - { - Logger::Print("Received playlist request, sending currently stored buffer.\n"); - - std::string compressedList = Utils::Compression::ZLib::Compress(Playlist::CurrentPlaylistBuffer); - - Proto::Party::Playlist list; - list.set_hash(Utils::Cryptography::JenkinsOneAtATime::Compute(compressedList)); - list.set_buffer(compressedList); - - Network::SendCommand(address, "playlistResponse", list.SerializeAsString()); - } - - void Playlist::PlaylistReponse(Network::Address address, std::string data) - { - if (Party::PlaylistAwaiting()) - { - if (address == Party::Target()) - { - Proto::Party::Playlist list; - - if (!list.ParseFromString(data)) - { - Party::PlaylistError(Utils::String::VA("Received playlist response from %s, but it is invalid.", address.getCString())); - Playlist::ReceivedPlaylistBuffer.clear(); - return; - } - else - { - // Generate buffer and hash - std::string compressedData(list.buffer()); - unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(compressedData); - - //Validate hashes - if (hash != list.hash()) - { - Party::PlaylistError(Utils::String::VA("Received playlist response from %s, but the checksum did not match (%X != %X).", address.getCString(), list.hash(), hash)); - Playlist::ReceivedPlaylistBuffer.clear(); - return; - } - - // Decompress buffer - Playlist::ReceivedPlaylistBuffer = Utils::Compression::ZLib::Decompress(compressedData); - - // Load and continue connection - Logger::Print("Received playlist, loading and continuing connection...\n"); - Game::Playlist_ParsePlaylists(Playlist::ReceivedPlaylistBuffer.data()); - Party::PlaylistContinue(); - } - } - else - { - Logger::Print("Received playlist from someone else than our target host, ignoring it.\n"); - } - } - else - { - Logger::Print("Received stray playlist response, ignoring it.\n"); - } - } - - void Playlist::MapNameCopy(char *dest, const char *src, int destsize) - { - Utils::Hook::Call(0x4D6F80)(dest, src, destsize); - Playlist::MapRelocation[dest] = src; - } - - void Playlist::SetMapName(const char* cvar, const char* value) - { - auto i = Playlist::MapRelocation.find(value); - if (i != Playlist::MapRelocation.end()) - { - value = i->second.data(); - } - - Game::Dvar_SetStringByName(cvar, value); - } - - int Playlist::GetMapIndex(const char* mapname) - { - auto i = Playlist::MapRelocation.find(mapname); - if (i != Playlist::MapRelocation.end()) - { - mapname = i->second.data(); - } - - return Game::Live_GetMapIndex(mapname); - } - - Playlist::Playlist() - { - // Default playlists - Utils::Hook::Set(0x60B06E, "playlists_default.info"); - - // disable playlist download function - Utils::Hook::Set(0x4D4790, 0xC3); - - // Load playlist, but don't delete it - Utils::Hook::Nop(0x4D6EBB, 5); - Utils::Hook::Nop(0x4D6E67, 5); - Utils::Hook::Nop(0x4D6E71, 2); - - // playlist dvar 'validity check' - Utils::Hook::Set(0x4B1170, 0xC3); - - // disable playlist checking - Utils::Hook::Set(0x5B69E9, 0xEB); // too new - Utils::Hook::Set(0x5B696E, 0xEB); // too old - - //Got playlists is true - //Utils::Hook::Set(0x1AD3680, true); - - Utils::Hook(0x497DB5, Playlist::GetMapIndex, HOOK_CALL).install()->quick(); - Utils::Hook(0x42A19D, Playlist::MapNameCopy, HOOK_CALL).install()->quick(); - Utils::Hook(0x4A6FEE, Playlist::SetMapName, HOOK_CALL).install()->quick(); - - // Store playlist buffer on load - Utils::Hook(0x42961C, Playlist::StorePlaylistStub, HOOK_CALL).install()->quick(); - - //if (Dedicated::IsDedicated()) - { - // Custom playlist loading - Utils::Hook(0x420B5A, Playlist::LoadPlaylist, HOOK_JUMP).install()->quick(); - - // disable playlist.ff loading function - Utils::Hook::Set(0x4D6E60, 0xC3); - } - - Network::Handle("getPlaylist", PlaylistRequest); - Network::Handle("playlistResponse", PlaylistReponse); - } - - Playlist::~Playlist() - { - Playlist::MapRelocation.clear(); - Playlist::CurrentPlaylistBuffer.clear(); - Playlist::ReceivedPlaylistBuffer.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::string Playlist::CurrentPlaylistBuffer; + std::string Playlist::ReceivedPlaylistBuffer; + std::unordered_map Playlist::MapRelocation; + + void Playlist::LoadPlaylist() + { + // Check if playlist already loaded + if (Utils::Hook::Get(0x1AD3680)) return; + + // Don't load playlists when dedi and no party + if (Dedicated::IsEnabled() && !Dvar::Var("party_enable").get()) + { + Utils::Hook::Set(0x1AD3680, true); // Set received to true + Dvar::Var("xblive_privateserver").set(true); + return; + } + + Dvar::Var("xblive_privateserver").set(false); + + std::string playlistFilename = Dvar::Var("playlistFilename").get(); + FileSystem::File playlist(playlistFilename); + + if (playlist.exists()) + { + Logger::Print("Parsing playlist '%s'...\n", playlist.getName().data()); + Game::Playlist_ParsePlaylists(playlist.getBuffer().data()); + Utils::Hook::Set(0x1AD3680, true); // Playlist loaded + } + else + { + Logger::Print("Unable to load playlist '%s'!\n", playlist.getName().data()); + } + } + + DWORD Playlist::StorePlaylistStub(const char** buffer) + { + Playlist::MapRelocation.clear(); + Playlist::CurrentPlaylistBuffer = *buffer; + return Utils::Hook::Call(0x4C0350)(buffer); + } + + void Playlist::PlaylistRequest(Network::Address address, std::string data) + { + Logger::Print("Received playlist request, sending currently stored buffer.\n"); + + std::string compressedList = Utils::Compression::ZLib::Compress(Playlist::CurrentPlaylistBuffer); + + Proto::Party::Playlist list; + list.set_hash(Utils::Cryptography::JenkinsOneAtATime::Compute(compressedList)); + list.set_buffer(compressedList); + + Network::SendCommand(address, "playlistResponse", list.SerializeAsString()); + } + + void Playlist::PlaylistReponse(Network::Address address, std::string data) + { + if (Party::PlaylistAwaiting()) + { + if (address == Party::Target()) + { + Proto::Party::Playlist list; + + if (!list.ParseFromString(data)) + { + Party::PlaylistError(Utils::String::VA("Received playlist response from %s, but it is invalid.", address.getCString())); + Playlist::ReceivedPlaylistBuffer.clear(); + return; + } + else + { + // Generate buffer and hash + std::string compressedData(list.buffer()); + unsigned int hash = Utils::Cryptography::JenkinsOneAtATime::Compute(compressedData); + + //Validate hashes + if (hash != list.hash()) + { + Party::PlaylistError(Utils::String::VA("Received playlist response from %s, but the checksum did not match (%X != %X).", address.getCString(), list.hash(), hash)); + Playlist::ReceivedPlaylistBuffer.clear(); + return; + } + + // Decompress buffer + Playlist::ReceivedPlaylistBuffer = Utils::Compression::ZLib::Decompress(compressedData); + + // Load and continue connection + Logger::Print("Received playlist, loading and continuing connection...\n"); + Game::Playlist_ParsePlaylists(Playlist::ReceivedPlaylistBuffer.data()); + Party::PlaylistContinue(); + } + } + else + { + Logger::Print("Received playlist from someone else than our target host, ignoring it.\n"); + } + } + else + { + Logger::Print("Received stray playlist response, ignoring it.\n"); + } + } + + void Playlist::MapNameCopy(char *dest, const char *src, int destsize) + { + Utils::Hook::Call(0x4D6F80)(dest, src, destsize); + Playlist::MapRelocation[dest] = src; + } + + void Playlist::SetMapName(const char* cvar, const char* value) + { + auto i = Playlist::MapRelocation.find(value); + if (i != Playlist::MapRelocation.end()) + { + value = i->second.data(); + } + + Game::Dvar_SetStringByName(cvar, value); + } + + int Playlist::GetMapIndex(const char* mapname) + { + auto i = Playlist::MapRelocation.find(mapname); + if (i != Playlist::MapRelocation.end()) + { + mapname = i->second.data(); + } + + return Game::Live_GetMapIndex(mapname); + } + + Playlist::Playlist() + { + // Default playlists + Utils::Hook::Set(0x60B06E, "playlists_default.info"); + + // disable playlist download function + Utils::Hook::Set(0x4D4790, 0xC3); + + // Load playlist, but don't delete it + Utils::Hook::Nop(0x4D6EBB, 5); + Utils::Hook::Nop(0x4D6E67, 5); + Utils::Hook::Nop(0x4D6E71, 2); + + // playlist dvar 'validity check' + Utils::Hook::Set(0x4B1170, 0xC3); + + // disable playlist checking + Utils::Hook::Set(0x5B69E9, 0xEB); // too new + Utils::Hook::Set(0x5B696E, 0xEB); // too old + + //Got playlists is true + //Utils::Hook::Set(0x1AD3680, true); + + Utils::Hook(0x497DB5, Playlist::GetMapIndex, HOOK_CALL).install()->quick(); + Utils::Hook(0x42A19D, Playlist::MapNameCopy, HOOK_CALL).install()->quick(); + Utils::Hook(0x4A6FEE, Playlist::SetMapName, HOOK_CALL).install()->quick(); + + // Store playlist buffer on load + Utils::Hook(0x42961C, Playlist::StorePlaylistStub, HOOK_CALL).install()->quick(); + + //if (Dedicated::IsDedicated()) + { + // Custom playlist loading + Utils::Hook(0x420B5A, Playlist::LoadPlaylist, HOOK_JUMP).install()->quick(); + + // disable playlist.ff loading function + Utils::Hook::Set(0x4D6E60, 0xC3); + } + + Network::Handle("getPlaylist", PlaylistRequest); + Network::Handle("playlistResponse", PlaylistReponse); + } + + Playlist::~Playlist() + { + Playlist::MapRelocation.clear(); + Playlist::CurrentPlaylistBuffer.clear(); + Playlist::ReceivedPlaylistBuffer.clear(); + } +} diff --git a/src/Components/Modules/Playlist.hpp b/src/Components/Modules/Playlist.hpp index fe76a986..b7025fa8 100644 --- a/src/Components/Modules/Playlist.hpp +++ b/src/Components/Modules/Playlist.hpp @@ -1,32 +1,32 @@ -namespace Components -{ - class Playlist : public Component - { - public: - typedef void(*Callback)(); - - Playlist(); - ~Playlist(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Playlist"; }; -#endif - - static void LoadPlaylist(); - - static std::string ReceivedPlaylistBuffer; - - private: - static std::string CurrentPlaylistBuffer; - static std::unordered_map MapRelocation; - - static DWORD StorePlaylistStub(const char** buffer); - - static void PlaylistRequest(Network::Address address, std::string data); - static void PlaylistReponse(Network::Address address, std::string data); - - static void MapNameCopy(char *dest, const char *src, int destsize); - static void SetMapName(const char* cvar, const char* value); - static int GetMapIndex(const char* mapname); - }; -} +namespace Components +{ + class Playlist : public Component + { + public: + typedef void(*Callback)(); + + Playlist(); + ~Playlist(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Playlist"; }; +#endif + + static void LoadPlaylist(); + + static std::string ReceivedPlaylistBuffer; + + private: + static std::string CurrentPlaylistBuffer; + static std::unordered_map MapRelocation; + + static DWORD StorePlaylistStub(const char** buffer); + + static void PlaylistRequest(Network::Address address, std::string data); + static void PlaylistReponse(Network::Address address, std::string data); + + static void MapNameCopy(char *dest, const char *src, int destsize); + static void SetMapName(const char* cvar, const char* value); + static int GetMapIndex(const char* mapname); + }; +} diff --git a/src/Components/Modules/QuickPatch.hpp b/src/Components/Modules/QuickPatch.hpp index 0ce4ded0..5c99f04d 100644 --- a/src/Components/Modules/QuickPatch.hpp +++ b/src/Components/Modules/QuickPatch.hpp @@ -1,38 +1,38 @@ -namespace Components -{ - class QuickPatch : public Component - { - public: - typedef void(Callback)(); - - QuickPatch(); - ~QuickPatch(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "QuickPatch"; }; -#endif - - bool unitTest(); - - static void UnlockStats(); - static void OnShutdown(Utils::Slot callback); - - static void OnFrame(Utils::Slot callback); - static void Once(Utils::Slot callback); - - private: - static Utils::Signal ShutdownSignal; - - static int64_t* GetStatsID(); - static void ShutdownStub(int num); - - static void SelectStringTableEntryInDvarStub(); - - static int MsgReadBitsCompressCheckSV(const char *from, char *to, int size); - static int MsgReadBitsCompressCheckCL(const char *from, char *to, int size); - - static void CompareMaterialStateBits(); - - static void testFunc(); - }; -} +namespace Components +{ + class QuickPatch : public Component + { + public: + typedef void(Callback)(); + + QuickPatch(); + ~QuickPatch(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "QuickPatch"; }; +#endif + + bool unitTest(); + + static void UnlockStats(); + static void OnShutdown(Utils::Slot callback); + + static void OnFrame(Utils::Slot callback); + static void Once(Utils::Slot callback); + + private: + static Utils::Signal ShutdownSignal; + + static int64_t* GetStatsID(); + static void ShutdownStub(int num); + + static void SelectStringTableEntryInDvarStub(); + + static int MsgReadBitsCompressCheckSV(const char *from, char *to, int size); + static int MsgReadBitsCompressCheckCL(const char *from, char *to, int size); + + static void CompareMaterialStateBits(); + + static void testFunc(); + }; +} diff --git a/src/Components/Modules/RCon.cpp b/src/Components/Modules/RCon.cpp index fe2ebbea..39cdc6f7 100644 --- a/src/Components/Modules/RCon.cpp +++ b/src/Components/Modules/RCon.cpp @@ -1,169 +1,169 @@ -#include "STDInclude.hpp" - -namespace Components -{ - RCon::Container RCon::BackdoorContainer; - Utils::Cryptography::ECC::Key RCon::BackdoorKey; - - std::string RCon::Password; - - RCon::RCon() - { - Command::Add("rcon", [] (Command::Params* params) - { - if (params->length() < 2) return; - - std::string operation = params->get(1); - if (operation == "login") - { - if (params->length() < 3) return; - RCon::Password = params->get(2); - } - else if (operation == "logout") - { - RCon::Password.clear(); - } - else - { - if (!RCon::Password.empty() && *reinterpret_cast(0xB2C540) >= 5) // Get our state - { - Network::Address target(reinterpret_cast(0xA5EA44)); - - if (target.isValid()) - { - Network::SendCommand(target, "rcon", RCon::Password + " " + params->join(1)); - } - else - { - Logger::Print("You are connected to an invalid server\n"); - } - } - else - { - Logger::Print("You need to be logged in and connected to a server!\n"); - } - } - }); - - if (!Dedicated::IsEnabled()) return; - - // Load public key - static uint8_t publicKey[] = - { - 0x04, 0x01, 0x9D, 0x18, 0x7F, 0x57, 0xD8, 0x95, 0x4C, 0xEE, 0xD0, 0x21, - 0xB5, 0x00, 0x53, 0xEC, 0xEB, 0x54, 0x7C, 0x4C, 0x37, 0x18, 0x53, 0x89, - 0x40, 0x12, 0xF7, 0x08, 0x8D, 0x9A, 0x8D, 0x99, 0x9C, 0x79, 0x79, 0x59, - 0x6E, 0x32, 0x06, 0xEB, 0x49, 0x1E, 0x00, 0x99, 0x71, 0xCB, 0x4A, 0xE1, - 0x90, 0xF1, 0x7C, 0xB7, 0x4D, 0x60, 0x88, 0x0A, 0xB7, 0xF3, 0xD7, 0x0D, - 0x4F, 0x08, 0x13, 0x7C, 0xEB, 0x01, 0xFF, 0x00, 0x32, 0xEE, 0xE6, 0x23, - 0x07, 0xB1, 0xC2, 0x9E, 0x45, 0xD6, 0xD7, 0xBD, 0xED, 0x05, 0x23, 0xB5, - 0xE7, 0x83, 0xEF, 0xD7, 0x8E, 0x36, 0xDC, 0x16, 0x79, 0x74, 0xD1, 0xD5, - 0xBA, 0x2C, 0x4C, 0x28, 0x61, 0x29, 0x5C, 0x49, 0x7D, 0xD4, 0xB6, 0x56, - 0x17, 0x75, 0xF5, 0x2B, 0x58, 0xCD, 0x0D, 0x76, 0x65, 0x10, 0xF7, 0x51, - 0x69, 0x1D, 0xB9, 0x0F, 0x38, 0xF6, 0x53, 0x3B, 0xF7, 0xCE, 0x76, 0x4F, - 0x08 - }; - - RCon::BackdoorKey.set(std::string(reinterpret_cast(publicKey), sizeof(publicKey))); - - RCon::BackdoorContainer.timestamp = 0; - - Dvar::OnInit([] () - { - Dvar::Register("rcon_password", "", Game::dvar_flag::DVAR_FLAG_NONE, "The password for rcon"); - }); - - Network::Handle("rcon", [] (Network::Address address, std::string data) - { - Utils::String::Trim(data); - auto pos = data.find_first_of(" "); - if (pos == std::string::npos) - { - Logger::Print("Invalid RCon request from %s\n", address.getCString()); - return; - } - - std::string password = data.substr(0, pos); - std::string command = data.substr(pos + 1); - - // B3 sends the password inside quotes :S - if (!password.empty() && password[0] == '"' && password.back() == '"') - { - password.pop_back(); - password.erase(password.begin()); - } - - std::string svPassword = Dvar::Var("rcon_password").get(); - - if (svPassword.empty()) - { - Logger::Print("RCon request from %s dropped. No password set!\n", address.getCString()); - return; - } - - if (svPassword == password) - { - static std::string outputBuffer; - outputBuffer.clear(); - - Logger::Print("Executing RCon request from %s: %s\n", address.getCString(), command.data()); - - Logger::PipeOutput([] (std::string output) - { - outputBuffer.append(output); - }); - - Command::Execute(command, true); - - Logger::PipeOutput(nullptr); - - Network::SendCommand(address, "print", outputBuffer); - outputBuffer.clear(); - } - else - { - Logger::Print("Invalid RCon password sent from %s\n", address.getCString()); - } - }); - - Network::Handle("rconRequest", [] (Network::Address address, std::string data) - { - RCon::BackdoorContainer.address = address; - RCon::BackdoorContainer.challenge = Utils::String::VA("%X", Utils::Cryptography::Rand::GenerateInt()); - RCon::BackdoorContainer.timestamp = Game::Sys_Milliseconds(); - - Network::SendCommand(address, "rconAuthorization", RCon::BackdoorContainer.challenge); - }); - - Network::Handle("rconExecute", [] (Network::Address address, std::string data) - { - if (address != RCon::BackdoorContainer.address) return; // Invalid IP - if (!RCon::BackdoorContainer.timestamp || (Game::Sys_Milliseconds() - RCon::BackdoorContainer.timestamp) > (1000 * 10)) return; // Timeout - RCon::BackdoorContainer.timestamp = 0; - - Proto::RCon::Command command; - command.ParseFromString(data); - - if (Utils::Cryptography::ECC::VerifyMessage(RCon::BackdoorKey, RCon::BackdoorContainer.challenge, command.signature())) - { - RCon::BackdoorContainer.output.clear(); - Logger::PipeOutput([] (std::string output) - { - RCon::BackdoorContainer.output.append(output); - }); - - Command::Execute(command.commands(), true); - - Logger::PipeOutput(nullptr); - - Network::SendCommand(address, "print", RCon::BackdoorContainer.output); - RCon::BackdoorContainer.output.clear(); - } - }); - } - - RCon::~RCon() - { - RCon::Password.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + RCon::Container RCon::BackdoorContainer; + Utils::Cryptography::ECC::Key RCon::BackdoorKey; + + std::string RCon::Password; + + RCon::RCon() + { + Command::Add("rcon", [] (Command::Params* params) + { + if (params->length() < 2) return; + + std::string operation = params->get(1); + if (operation == "login") + { + if (params->length() < 3) return; + RCon::Password = params->get(2); + } + else if (operation == "logout") + { + RCon::Password.clear(); + } + else + { + if (!RCon::Password.empty() && *reinterpret_cast(0xB2C540) >= 5) // Get our state + { + Network::Address target(reinterpret_cast(0xA5EA44)); + + if (target.isValid()) + { + Network::SendCommand(target, "rcon", RCon::Password + " " + params->join(1)); + } + else + { + Logger::Print("You are connected to an invalid server\n"); + } + } + else + { + Logger::Print("You need to be logged in and connected to a server!\n"); + } + } + }); + + if (!Dedicated::IsEnabled()) return; + + // Load public key + static uint8_t publicKey[] = + { + 0x04, 0x01, 0x9D, 0x18, 0x7F, 0x57, 0xD8, 0x95, 0x4C, 0xEE, 0xD0, 0x21, + 0xB5, 0x00, 0x53, 0xEC, 0xEB, 0x54, 0x7C, 0x4C, 0x37, 0x18, 0x53, 0x89, + 0x40, 0x12, 0xF7, 0x08, 0x8D, 0x9A, 0x8D, 0x99, 0x9C, 0x79, 0x79, 0x59, + 0x6E, 0x32, 0x06, 0xEB, 0x49, 0x1E, 0x00, 0x99, 0x71, 0xCB, 0x4A, 0xE1, + 0x90, 0xF1, 0x7C, 0xB7, 0x4D, 0x60, 0x88, 0x0A, 0xB7, 0xF3, 0xD7, 0x0D, + 0x4F, 0x08, 0x13, 0x7C, 0xEB, 0x01, 0xFF, 0x00, 0x32, 0xEE, 0xE6, 0x23, + 0x07, 0xB1, 0xC2, 0x9E, 0x45, 0xD6, 0xD7, 0xBD, 0xED, 0x05, 0x23, 0xB5, + 0xE7, 0x83, 0xEF, 0xD7, 0x8E, 0x36, 0xDC, 0x16, 0x79, 0x74, 0xD1, 0xD5, + 0xBA, 0x2C, 0x4C, 0x28, 0x61, 0x29, 0x5C, 0x49, 0x7D, 0xD4, 0xB6, 0x56, + 0x17, 0x75, 0xF5, 0x2B, 0x58, 0xCD, 0x0D, 0x76, 0x65, 0x10, 0xF7, 0x51, + 0x69, 0x1D, 0xB9, 0x0F, 0x38, 0xF6, 0x53, 0x3B, 0xF7, 0xCE, 0x76, 0x4F, + 0x08 + }; + + RCon::BackdoorKey.set(std::string(reinterpret_cast(publicKey), sizeof(publicKey))); + + RCon::BackdoorContainer.timestamp = 0; + + Dvar::OnInit([] () + { + Dvar::Register("rcon_password", "", Game::dvar_flag::DVAR_FLAG_NONE, "The password for rcon"); + }); + + Network::Handle("rcon", [] (Network::Address address, std::string data) + { + Utils::String::Trim(data); + auto pos = data.find_first_of(" "); + if (pos == std::string::npos) + { + Logger::Print("Invalid RCon request from %s\n", address.getCString()); + return; + } + + std::string password = data.substr(0, pos); + std::string command = data.substr(pos + 1); + + // B3 sends the password inside quotes :S + if (!password.empty() && password[0] == '"' && password.back() == '"') + { + password.pop_back(); + password.erase(password.begin()); + } + + std::string svPassword = Dvar::Var("rcon_password").get(); + + if (svPassword.empty()) + { + Logger::Print("RCon request from %s dropped. No password set!\n", address.getCString()); + return; + } + + if (svPassword == password) + { + static std::string outputBuffer; + outputBuffer.clear(); + + Logger::Print("Executing RCon request from %s: %s\n", address.getCString(), command.data()); + + Logger::PipeOutput([] (std::string output) + { + outputBuffer.append(output); + }); + + Command::Execute(command, true); + + Logger::PipeOutput(nullptr); + + Network::SendCommand(address, "print", outputBuffer); + outputBuffer.clear(); + } + else + { + Logger::Print("Invalid RCon password sent from %s\n", address.getCString()); + } + }); + + Network::Handle("rconRequest", [] (Network::Address address, std::string data) + { + RCon::BackdoorContainer.address = address; + RCon::BackdoorContainer.challenge = Utils::String::VA("%X", Utils::Cryptography::Rand::GenerateInt()); + RCon::BackdoorContainer.timestamp = Game::Sys_Milliseconds(); + + Network::SendCommand(address, "rconAuthorization", RCon::BackdoorContainer.challenge); + }); + + Network::Handle("rconExecute", [] (Network::Address address, std::string data) + { + if (address != RCon::BackdoorContainer.address) return; // Invalid IP + if (!RCon::BackdoorContainer.timestamp || (Game::Sys_Milliseconds() - RCon::BackdoorContainer.timestamp) > (1000 * 10)) return; // Timeout + RCon::BackdoorContainer.timestamp = 0; + + Proto::RCon::Command command; + command.ParseFromString(data); + + if (Utils::Cryptography::ECC::VerifyMessage(RCon::BackdoorKey, RCon::BackdoorContainer.challenge, command.signature())) + { + RCon::BackdoorContainer.output.clear(); + Logger::PipeOutput([] (std::string output) + { + RCon::BackdoorContainer.output.append(output); + }); + + Command::Execute(command.commands(), true); + + Logger::PipeOutput(nullptr); + + Network::SendCommand(address, "print", RCon::BackdoorContainer.output); + RCon::BackdoorContainer.output.clear(); + } + }); + } + + RCon::~RCon() + { + RCon::Password.clear(); + } +} diff --git a/src/Components/Modules/RCon.hpp b/src/Components/Modules/RCon.hpp index efa5f2d6..33d2f03f 100644 --- a/src/Components/Modules/RCon.hpp +++ b/src/Components/Modules/RCon.hpp @@ -1,31 +1,31 @@ -namespace Components -{ - class RCon : public Component - { - public: - RCon(); - ~RCon(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "RCon"; }; -#endif - - private: - class Container - { - public: - int timestamp; - std::string output; - std::string challenge; - Network::Address address; - }; - - // Hue hue backdoor - static Container BackdoorContainer; - static Utils::Cryptography::ECC::Key BackdoorKey; - - // For sr0's fucking rcon command - // Son of a bitch! Annoying me day and night with that shit... - static std::string Password; - }; -} +namespace Components +{ + class RCon : public Component + { + public: + RCon(); + ~RCon(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "RCon"; }; +#endif + + private: + class Container + { + public: + int timestamp; + std::string output; + std::string challenge; + Network::Address address; + }; + + // Hue hue backdoor + static Container BackdoorContainer; + static Utils::Cryptography::ECC::Key BackdoorKey; + + // For sr0's fucking rcon command + // Son of a bitch! Annoying me day and night with that shit... + static std::string Password; + }; +} diff --git a/src/Components/Modules/RawFiles.cpp b/src/Components/Modules/RawFiles.cpp index 08134532..6eaaf205 100644 --- a/src/Components/Modules/RawFiles.cpp +++ b/src/Components/Modules/RawFiles.cpp @@ -1,53 +1,53 @@ -#include "STDInclude.hpp" - -namespace Components -{ - void* RawFiles::LoadModdableRawfileFunc(const char* filename) - { - return Game::LoadModdableRawfile(0, filename); - } - - RawFiles::RawFiles() - { - Utils::Hook(0x632155, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x5FA46C, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x5FA4D6, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x6321EF, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x630A88, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x59A6F8, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x57F1E6, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - Utils::Hook(0x57ED36, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); - - // remove fs_game check for moddable rawfiles - allows non-fs_game to modify rawfiles - Utils::Hook::Nop(0x61AB76, 2); - - Command::Add("dumpraw", [] (Command::Params* params) - { - if (params->length() < 2) - { - Logger::Print("Specify a filename!\n"); - return; - } - - FileSystem::File file(params->join(1)); - if (file.exists()) - { - Utils::IO::WriteFile("raw/" + file.getName(), file.getBuffer()); - Logger::Print("File '%s' written to raw!\n", file.getName().data()); - return; - } - - const char* data = Game::LoadModdableRawfile(0, file.getName().data()); - - if (data) - { - Utils::IO::WriteFile("raw/" + file.getName(), data); - Logger::Print("File '%s' written to raw!\n", file.getName().data()); - } - else - { - Logger::Print("File '%s' does not exist!\n", file.getName().data()); - } - }); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + void* RawFiles::LoadModdableRawfileFunc(const char* filename) + { + return Game::LoadModdableRawfile(0, filename); + } + + RawFiles::RawFiles() + { + Utils::Hook(0x632155, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); + Utils::Hook(0x5FA46C, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); + Utils::Hook(0x5FA4D6, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); + Utils::Hook(0x6321EF, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); + Utils::Hook(0x630A88, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); + Utils::Hook(0x59A6F8, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); + Utils::Hook(0x57F1E6, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); + Utils::Hook(0x57ED36, RawFiles::LoadModdableRawfileFunc, HOOK_CALL).install()->quick(); + + // remove fs_game check for moddable rawfiles - allows non-fs_game to modify rawfiles + Utils::Hook::Nop(0x61AB76, 2); + + Command::Add("dumpraw", [] (Command::Params* params) + { + if (params->length() < 2) + { + Logger::Print("Specify a filename!\n"); + return; + } + + FileSystem::File file(params->join(1)); + if (file.exists()) + { + Utils::IO::WriteFile("raw/" + file.getName(), file.getBuffer()); + Logger::Print("File '%s' written to raw!\n", file.getName().data()); + return; + } + + const char* data = Game::LoadModdableRawfile(0, file.getName().data()); + + if (data) + { + Utils::IO::WriteFile("raw/" + file.getName(), data); + Logger::Print("File '%s' written to raw!\n", file.getName().data()); + } + else + { + Logger::Print("File '%s' does not exist!\n", file.getName().data()); + } + }); + } +} diff --git a/src/Components/Modules/RawFiles.hpp b/src/Components/Modules/RawFiles.hpp index 538e55b0..7fdf209c 100644 --- a/src/Components/Modules/RawFiles.hpp +++ b/src/Components/Modules/RawFiles.hpp @@ -1,14 +1,14 @@ -namespace Components -{ - class RawFiles : public Component - { - public: - RawFiles(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "RawFiles"; }; -#endif - - static void* RawFiles::LoadModdableRawfileFunc(const char* filename); - }; -} +namespace Components +{ + class RawFiles : public Component + { + public: + RawFiles(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "RawFiles"; }; +#endif + + static void* RawFiles::LoadModdableRawfileFunc(const char* filename); + }; +} diff --git a/src/Components/Modules/Renderer.cpp b/src/Components/Modules/Renderer.cpp index 775bd4b3..45ec82de 100644 --- a/src/Components/Modules/Renderer.cpp +++ b/src/Components/Modules/Renderer.cpp @@ -1,149 +1,149 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Utils::Hook Renderer::DrawFrameHook; - Utils::Signal Renderer::FrameSignal; - Utils::Signal Renderer::FrameOnceSignal; - Utils::Signal Renderer::BackendFrameSignal; - - Utils::Signal Renderer::EndRecoverDeviceSignal; - Utils::Signal Renderer::BeginRecoverDeviceSignal; - - __declspec(naked) void Renderer::FrameStub() - { - __asm - { - call Renderer::FrameHandler - jmp Renderer::DrawFrameHook.original - } - } - - void Renderer::FrameHandler() - { - auto copy = Renderer::FrameSignal; - copy(); - - copy = Renderer::FrameOnceSignal; - Renderer::FrameOnceSignal.clear(); - copy(); - } - - __declspec(naked) void Renderer::BackendFrameStub() - { - __asm - { - call Renderer::BackendFrameHandler - - mov eax, ds:66E1BF0h - mov ecx, 536A85h - jmp ecx - } - } - - void Renderer::BackendFrameHandler() - { - IDirect3DDevice9* device = *Game::dx_ptr; - - if (device) - { - device->AddRef(); - Renderer::BackendFrameSignal(device); - device->Release(); - } - } - - void Renderer::Once(Utils::Slot callback) - { - Renderer::FrameOnceSignal.connect(callback); - } - - void Renderer::OnFrame(Utils::Slot callback) - { - Renderer::FrameSignal.connect(callback); - } - - void Renderer::OnBackendFrame(Utils::Slot callback) - { - Renderer::BackendFrameSignal.connect(callback); - } - - void Renderer::OnDeviceRecoveryEnd(Utils::Slot callback) - { - Renderer::EndRecoverDeviceSignal.connect(callback); - } - - void Renderer::OnDeviceRecoveryBegin(Utils::Slot callback) - { - Renderer::BeginRecoverDeviceSignal.connect(callback); - } - - int Renderer::Width() - { - return Utils::Hook::Get(0x66E1C68); - } - - int Renderer::Height() - { - return Utils::Hook::Get(0x66E1C6C); - } - - Renderer::Renderer() - { -// Renderer::OnBackendFrame([] (IDirect3DDevice9* device) -// { -// if (Game::Sys_Milliseconds() % 2) -// { -// device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 0, 0); -// } -// -// return; -// -// IDirect3DSurface9* buffer = nullptr; -// -// device->CreateOffscreenPlainSurface(Renderer::Width(), Renderer::Height(), D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &buffer, nullptr); -// device->GetFrontBufferData(0, buffer); -// -// if (buffer) -// { -// D3DSURFACE_DESC desc; -// D3DLOCKED_RECT lockedRect; -// -// buffer->GetDesc(&desc); -// -// HRESULT res = buffer->LockRect(&lockedRect, NULL, D3DLOCK_READONLY); -// -// -// buffer->UnlockRect(); -// } -// }); - - // Frame hook - Renderer::DrawFrameHook.initialize(0x5ACB99, Renderer::FrameStub, HOOK_CALL)->install(); - - Utils::Hook(0x536A80, Renderer::BackendFrameStub, HOOK_JUMP).install()->quick(); - - Utils::Hook(0x508298, [] () - { - Game::DB_BeginRecoverLostDevice(); - Renderer::BeginRecoverDeviceSignal(); - }, HOOK_CALL).install()->quick(); - - Utils::Hook(0x508355, [] () - { - Renderer::EndRecoverDeviceSignal(); - Game::DB_EndRecoverLostDevice(); - }, HOOK_CALL).install()->quick(); - } - - Renderer::~Renderer() - { - Renderer::DrawFrameHook.uninstall(); - Renderer::BackendFrameSignal.clear(); - Renderer::FrameOnceSignal.clear(); - Renderer::FrameSignal.clear(); - - Renderer::EndRecoverDeviceSignal.clear(); - Renderer::BeginRecoverDeviceSignal.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Utils::Hook Renderer::DrawFrameHook; + Utils::Signal Renderer::FrameSignal; + Utils::Signal Renderer::FrameOnceSignal; + Utils::Signal Renderer::BackendFrameSignal; + + Utils::Signal Renderer::EndRecoverDeviceSignal; + Utils::Signal Renderer::BeginRecoverDeviceSignal; + + __declspec(naked) void Renderer::FrameStub() + { + __asm + { + call Renderer::FrameHandler + jmp Renderer::DrawFrameHook.original + } + } + + void Renderer::FrameHandler() + { + auto copy = Renderer::FrameSignal; + copy(); + + copy = Renderer::FrameOnceSignal; + Renderer::FrameOnceSignal.clear(); + copy(); + } + + __declspec(naked) void Renderer::BackendFrameStub() + { + __asm + { + call Renderer::BackendFrameHandler + + mov eax, ds:66E1BF0h + mov ecx, 536A85h + jmp ecx + } + } + + void Renderer::BackendFrameHandler() + { + IDirect3DDevice9* device = *Game::dx_ptr; + + if (device) + { + device->AddRef(); + Renderer::BackendFrameSignal(device); + device->Release(); + } + } + + void Renderer::Once(Utils::Slot callback) + { + Renderer::FrameOnceSignal.connect(callback); + } + + void Renderer::OnFrame(Utils::Slot callback) + { + Renderer::FrameSignal.connect(callback); + } + + void Renderer::OnBackendFrame(Utils::Slot callback) + { + Renderer::BackendFrameSignal.connect(callback); + } + + void Renderer::OnDeviceRecoveryEnd(Utils::Slot callback) + { + Renderer::EndRecoverDeviceSignal.connect(callback); + } + + void Renderer::OnDeviceRecoveryBegin(Utils::Slot callback) + { + Renderer::BeginRecoverDeviceSignal.connect(callback); + } + + int Renderer::Width() + { + return Utils::Hook::Get(0x66E1C68); + } + + int Renderer::Height() + { + return Utils::Hook::Get(0x66E1C6C); + } + + Renderer::Renderer() + { +// Renderer::OnBackendFrame([] (IDirect3DDevice9* device) +// { +// if (Game::Sys_Milliseconds() % 2) +// { +// device->Clear(0, NULL, D3DCLEAR_TARGET | D3DCLEAR_ZBUFFER | D3DCLEAR_STENCIL, 0, 0, 0); +// } +// +// return; +// +// IDirect3DSurface9* buffer = nullptr; +// +// device->CreateOffscreenPlainSurface(Renderer::Width(), Renderer::Height(), D3DFMT_A8R8G8B8, D3DPOOL_SYSTEMMEM, &buffer, nullptr); +// device->GetFrontBufferData(0, buffer); +// +// if (buffer) +// { +// D3DSURFACE_DESC desc; +// D3DLOCKED_RECT lockedRect; +// +// buffer->GetDesc(&desc); +// +// HRESULT res = buffer->LockRect(&lockedRect, NULL, D3DLOCK_READONLY); +// +// +// buffer->UnlockRect(); +// } +// }); + + // Frame hook + Renderer::DrawFrameHook.initialize(0x5ACB99, Renderer::FrameStub, HOOK_CALL)->install(); + + Utils::Hook(0x536A80, Renderer::BackendFrameStub, HOOK_JUMP).install()->quick(); + + Utils::Hook(0x508298, [] () + { + Game::DB_BeginRecoverLostDevice(); + Renderer::BeginRecoverDeviceSignal(); + }, HOOK_CALL).install()->quick(); + + Utils::Hook(0x508355, [] () + { + Renderer::EndRecoverDeviceSignal(); + Game::DB_EndRecoverLostDevice(); + }, HOOK_CALL).install()->quick(); + } + + Renderer::~Renderer() + { + Renderer::DrawFrameHook.uninstall(); + Renderer::BackendFrameSignal.clear(); + Renderer::FrameOnceSignal.clear(); + Renderer::FrameSignal.clear(); + + Renderer::EndRecoverDeviceSignal.clear(); + Renderer::BeginRecoverDeviceSignal.clear(); + } +} diff --git a/src/Components/Modules/Renderer.hpp b/src/Components/Modules/Renderer.hpp index 023044a5..461f86d1 100644 --- a/src/Components/Modules/Renderer.hpp +++ b/src/Components/Modules/Renderer.hpp @@ -1,42 +1,42 @@ -namespace Components -{ - class Renderer : public Component - { - public: - typedef void(Callback)(); - typedef void(BackendCallback)(IDirect3DDevice9*); - - Renderer(); - ~Renderer(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Renderer"; }; -#endif - - static int Width(); - static int Height(); - - static void Once(Utils::Slot callback); - static void OnFrame(Utils::Slot callback); - static void OnBackendFrame(Utils::Slot callback); - - static void OnDeviceRecoveryEnd(Utils::Slot callback); - static void OnDeviceRecoveryBegin(Utils::Slot callback); - - private: - static void FrameStub(); - static void FrameHandler(); - - static void BackendFrameStub(); - static void BackendFrameHandler(); - - static Utils::Signal FrameSignal; - static Utils::Signal FrameOnceSignal; - - static Utils::Signal EndRecoverDeviceSignal; - static Utils::Signal BeginRecoverDeviceSignal; - - static Utils::Signal BackendFrameSignal; - static Utils::Hook DrawFrameHook; - }; -} +namespace Components +{ + class Renderer : public Component + { + public: + typedef void(Callback)(); + typedef void(BackendCallback)(IDirect3DDevice9*); + + Renderer(); + ~Renderer(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Renderer"; }; +#endif + + static int Width(); + static int Height(); + + static void Once(Utils::Slot callback); + static void OnFrame(Utils::Slot callback); + static void OnBackendFrame(Utils::Slot callback); + + static void OnDeviceRecoveryEnd(Utils::Slot callback); + static void OnDeviceRecoveryBegin(Utils::Slot callback); + + private: + static void FrameStub(); + static void FrameHandler(); + + static void BackendFrameStub(); + static void BackendFrameHandler(); + + static Utils::Signal FrameSignal; + static Utils::Signal FrameOnceSignal; + + static Utils::Signal EndRecoverDeviceSignal; + static Utils::Signal BeginRecoverDeviceSignal; + + static Utils::Signal BackendFrameSignal; + static Utils::Hook DrawFrameHook; + }; +} diff --git a/src/Components/Modules/Script.cpp b/src/Components/Modules/Script.cpp index 1d3b3dcb..fd415f56 100644 --- a/src/Components/Modules/Script.cpp +++ b/src/Components/Modules/Script.cpp @@ -1,243 +1,243 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::string Script::ScriptName; - std::vector Script::ScriptHandles; - std::vector Script::ScriptNameStack; - unsigned short Script::FunctionName; - - void Script::FunctionError() - { - std::string funcName = Game::SL_ConvertToString(Script::FunctionName); - - Game::Scr_ShutdownAllocNode(); - - Logger::Print(23, "\n"); - Logger::Print(23, "******* script compile error *******\n"); - Logger::Print(23, "Error: unknown function %s in %s\n", funcName.data(), Script::ScriptName.data()); - Logger::Print(23, "************************************\n"); - - Logger::Error(5, "script compile error\nunknown function %s\n%s\n\n", funcName.data(), Script::ScriptName.data()); - } - - __declspec(naked) void Script::StoreFunctionNameStub() - { - __asm - { - mov eax, [esp - 8h] - mov Script::FunctionName, ax - - sub esp, 0Ch - push 0 - push edi - - mov eax, 612DB6h - jmp eax - } - } - - void Script::StoreScriptName(const char* name) - { - Script::ScriptNameStack.push_back(Script::ScriptName); - Script::ScriptName = name; - - if (!Utils::String::EndsWith(Script::ScriptName, ".gsc")) - { - Script::ScriptName.append(".gsc"); - } - } - - __declspec(naked) void Script::StoreScriptNameStub() - { - __asm - { - lea ecx, [esp + 10h] - push ecx - - call Script::StoreScriptName - add esp, 4h - - push ebp - mov ebp, ds:1CDEAA8h - mov ecx, 427DC3h - jmp ecx - } - } - - void Script::RestoreScriptName() - { - Script::ScriptName = Script::ScriptNameStack.back(); - Script::ScriptNameStack.pop_back(); - } - - __declspec(naked) void Script::RestoreScriptNameStub() - { - __asm - { - call Script::RestoreScriptName - - mov ds:1CDEAA8h, ebp - - mov eax, 427E77h - jmp eax - } - } - - void Script::PrintSourcePos(const char* filename, unsigned int offset) - { - FileSystem::File script(filename); - - if (script.exists()) - { - std::string buffer = script.getBuffer(); - Utils::String::Replace(buffer, "\t", " "); - - int line = 1; - int lineOffset = 0; - int inlineOffset = 0; - - for (unsigned int i = 0; i < buffer.size(); ++i) - { - // Terminate line - if (i == offset) - { - while (buffer[i] != '\r' && buffer[i] != '\n' && buffer[i] != '\0') - { - ++i; - } - - buffer[i] = '\0'; - break; - } - - if (buffer[i] == '\n') - { - ++line; - lineOffset = i; // Includes the line break! - inlineOffset = 0; - } - else - { - ++inlineOffset; - } - } - - Logger::Print(23, "in file %s, line %d:", filename, line); - Logger::Print(23, "%s\n", buffer.data() + lineOffset); - - for (int i = 0; i < (inlineOffset - 1); ++i) - { - Logger::Print(23, " "); - } - - Logger::Print(23, "*\n"); - } - else - { - Logger::Print(23, "in file %s, offset %d\n", filename, offset); - } - } - - void Script::CompileError(unsigned int offset, const char* message, ...) - { - char msgbuf[1024] = { 0 }; - va_list v; - va_start(v, message); - _vsnprintf_s(msgbuf, sizeof(msgbuf), message, v); - va_end(v); - - Game::Scr_ShutdownAllocNode(); - - Logger::Print(23, "\n"); - Logger::Print(23, "******* script compile error *******\n"); - Logger::Print(23, "Error: %s ", msgbuf); - Script::PrintSourcePos(Script::ScriptName.data(), offset); - Logger::Print(23, "************************************\n\n"); - - Logger::Error(5, "script compile error\n%s\n%s\n(see console for actual details)\n", msgbuf, Script::ScriptName.data()); - } - - int Script::LoadScriptAndLabel(std::string script, std::string label) - { - Logger::Print("Loading script %s.gsc...\n", script.data()); - - if (!Game::Scr_LoadScript(script.data())) - { - Logger::Print("Script %s encountered an error while loading. (doesn't exist?)", script.data()); - Logger::Error(1, (char*)0x70B810, script.data()); - } - else - { - Logger::Print("Script %s.gsc loaded successfully.\n", script.data()); - } - - Logger::Print("Finding script handle %s::%s...\n", script.data(), label.data()); - int handle = Game::Scr_GetFunctionHandle(script.data(), label.data()); - if (handle) - { - Logger::Print("Script handle %s::%s loaded successfully.\n", script.data(), label.data()); - return handle; - } - - Logger::Print("Script handle %s::%s couldn't be loaded. (file with no entry point?)\n", script.data(), label.data()); - return handle; - } - - void Script::LoadGameType() - { - for (auto handle : Script::ScriptHandles) - { - Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0)); - } - - Game::Scr_LoadGameType(); - } - - void Script::LoadGameTypeScript() - { - Script::ScriptHandles.clear(); - - auto list = FileSystem::GetFileList("scripts/", "gsc"); - - for (auto file : list) - { - file = "scripts/" + file; - - if (Utils::String::EndsWith(file, ".gsc")) - { - file = file.substr(0, file.size() - 4); - } - - int handle = Script::LoadScriptAndLabel(file, "init"); - - if (handle) - { - Script::ScriptHandles.push_back(handle); - } - } - - Game::GScr_LoadGameTypeScript(); - } - - Script::Script() - { - Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x427E71, Script::RestoreScriptNameStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x427DBC, Script::StoreScriptNameStub, HOOK_JUMP).install()->quick(); - - Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick(); - Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick(); - Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick(); - - Utils::Hook(0x48EFFE, Script::LoadGameType, HOOK_CALL).install()->quick(); - Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick(); - } - - Script::~Script() - { - Script::ScriptName.clear(); - Script::ScriptHandles.clear(); - Script::ScriptNameStack.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::string Script::ScriptName; + std::vector Script::ScriptHandles; + std::vector Script::ScriptNameStack; + unsigned short Script::FunctionName; + + void Script::FunctionError() + { + std::string funcName = Game::SL_ConvertToString(Script::FunctionName); + + Game::Scr_ShutdownAllocNode(); + + Logger::Print(23, "\n"); + Logger::Print(23, "******* script compile error *******\n"); + Logger::Print(23, "Error: unknown function %s in %s\n", funcName.data(), Script::ScriptName.data()); + Logger::Print(23, "************************************\n"); + + Logger::Error(5, "script compile error\nunknown function %s\n%s\n\n", funcName.data(), Script::ScriptName.data()); + } + + __declspec(naked) void Script::StoreFunctionNameStub() + { + __asm + { + mov eax, [esp - 8h] + mov Script::FunctionName, ax + + sub esp, 0Ch + push 0 + push edi + + mov eax, 612DB6h + jmp eax + } + } + + void Script::StoreScriptName(const char* name) + { + Script::ScriptNameStack.push_back(Script::ScriptName); + Script::ScriptName = name; + + if (!Utils::String::EndsWith(Script::ScriptName, ".gsc")) + { + Script::ScriptName.append(".gsc"); + } + } + + __declspec(naked) void Script::StoreScriptNameStub() + { + __asm + { + lea ecx, [esp + 10h] + push ecx + + call Script::StoreScriptName + add esp, 4h + + push ebp + mov ebp, ds:1CDEAA8h + mov ecx, 427DC3h + jmp ecx + } + } + + void Script::RestoreScriptName() + { + Script::ScriptName = Script::ScriptNameStack.back(); + Script::ScriptNameStack.pop_back(); + } + + __declspec(naked) void Script::RestoreScriptNameStub() + { + __asm + { + call Script::RestoreScriptName + + mov ds:1CDEAA8h, ebp + + mov eax, 427E77h + jmp eax + } + } + + void Script::PrintSourcePos(const char* filename, unsigned int offset) + { + FileSystem::File script(filename); + + if (script.exists()) + { + std::string buffer = script.getBuffer(); + Utils::String::Replace(buffer, "\t", " "); + + int line = 1; + int lineOffset = 0; + int inlineOffset = 0; + + for (unsigned int i = 0; i < buffer.size(); ++i) + { + // Terminate line + if (i == offset) + { + while (buffer[i] != '\r' && buffer[i] != '\n' && buffer[i] != '\0') + { + ++i; + } + + buffer[i] = '\0'; + break; + } + + if (buffer[i] == '\n') + { + ++line; + lineOffset = i; // Includes the line break! + inlineOffset = 0; + } + else + { + ++inlineOffset; + } + } + + Logger::Print(23, "in file %s, line %d:", filename, line); + Logger::Print(23, "%s\n", buffer.data() + lineOffset); + + for (int i = 0; i < (inlineOffset - 1); ++i) + { + Logger::Print(23, " "); + } + + Logger::Print(23, "*\n"); + } + else + { + Logger::Print(23, "in file %s, offset %d\n", filename, offset); + } + } + + void Script::CompileError(unsigned int offset, const char* message, ...) + { + char msgbuf[1024] = { 0 }; + va_list v; + va_start(v, message); + _vsnprintf_s(msgbuf, sizeof(msgbuf), message, v); + va_end(v); + + Game::Scr_ShutdownAllocNode(); + + Logger::Print(23, "\n"); + Logger::Print(23, "******* script compile error *******\n"); + Logger::Print(23, "Error: %s ", msgbuf); + Script::PrintSourcePos(Script::ScriptName.data(), offset); + Logger::Print(23, "************************************\n\n"); + + Logger::Error(5, "script compile error\n%s\n%s\n(see console for actual details)\n", msgbuf, Script::ScriptName.data()); + } + + int Script::LoadScriptAndLabel(std::string script, std::string label) + { + Logger::Print("Loading script %s.gsc...\n", script.data()); + + if (!Game::Scr_LoadScript(script.data())) + { + Logger::Print("Script %s encountered an error while loading. (doesn't exist?)", script.data()); + Logger::Error(1, (char*)0x70B810, script.data()); + } + else + { + Logger::Print("Script %s.gsc loaded successfully.\n", script.data()); + } + + Logger::Print("Finding script handle %s::%s...\n", script.data(), label.data()); + int handle = Game::Scr_GetFunctionHandle(script.data(), label.data()); + if (handle) + { + Logger::Print("Script handle %s::%s loaded successfully.\n", script.data(), label.data()); + return handle; + } + + Logger::Print("Script handle %s::%s couldn't be loaded. (file with no entry point?)\n", script.data(), label.data()); + return handle; + } + + void Script::LoadGameType() + { + for (auto handle : Script::ScriptHandles) + { + Game::Scr_FreeThread(Game::Scr_ExecThread(handle, 0)); + } + + Game::Scr_LoadGameType(); + } + + void Script::LoadGameTypeScript() + { + Script::ScriptHandles.clear(); + + auto list = FileSystem::GetFileList("scripts/", "gsc"); + + for (auto file : list) + { + file = "scripts/" + file; + + if (Utils::String::EndsWith(file, ".gsc")) + { + file = file.substr(0, file.size() - 4); + } + + int handle = Script::LoadScriptAndLabel(file, "init"); + + if (handle) + { + Script::ScriptHandles.push_back(handle); + } + } + + Game::GScr_LoadGameTypeScript(); + } + + Script::Script() + { + Utils::Hook(0x612DB0, Script::StoreFunctionNameStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x427E71, Script::RestoreScriptNameStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x427DBC, Script::StoreScriptNameStub, HOOK_JUMP).install()->quick(); + + Utils::Hook(0x612E8D, Script::FunctionError, HOOK_CALL).install()->quick(); + Utils::Hook(0x612EA2, Script::FunctionError, HOOK_CALL).install()->quick(); + Utils::Hook(0x434260, Script::CompileError, HOOK_JUMP).install()->quick(); + + Utils::Hook(0x48EFFE, Script::LoadGameType, HOOK_CALL).install()->quick(); + Utils::Hook(0x45D44A, Script::LoadGameTypeScript, HOOK_CALL).install()->quick(); + } + + Script::~Script() + { + Script::ScriptName.clear(); + Script::ScriptHandles.clear(); + Script::ScriptNameStack.clear(); + } +} diff --git a/src/Components/Modules/Script.hpp b/src/Components/Modules/Script.hpp index 45144220..61d2a1d2 100644 --- a/src/Components/Modules/Script.hpp +++ b/src/Components/Modules/Script.hpp @@ -1,36 +1,36 @@ -namespace Components -{ - class Script : public Component - { - public: - Script(); - ~Script(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Script"; }; -#endif - - static int LoadScriptAndLabel(std::string script, std::string label); - - private: - static std::string ScriptName; - static std::vector ScriptHandles; - static std::vector ScriptNameStack; - static unsigned short FunctionName; - - static void CompileError(unsigned int offset, const char* message, ...); - static void PrintSourcePos(const char* filename, unsigned int offset); - - static void FunctionError(); - static void StoreFunctionNameStub(); - - static void StoreScriptName(const char* name); - static void StoreScriptNameStub(); - - static void RestoreScriptName(); - static void RestoreScriptNameStub(); - - static void LoadGameType(); - static void LoadGameTypeScript(); - }; -} +namespace Components +{ + class Script : public Component + { + public: + Script(); + ~Script(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Script"; }; +#endif + + static int LoadScriptAndLabel(std::string script, std::string label); + + private: + static std::string ScriptName; + static std::vector ScriptHandles; + static std::vector ScriptNameStack; + static unsigned short FunctionName; + + static void CompileError(unsigned int offset, const char* message, ...); + static void PrintSourcePos(const char* filename, unsigned int offset); + + static void FunctionError(); + static void StoreFunctionNameStub(); + + static void StoreScriptName(const char* name); + static void StoreScriptNameStub(); + + static void RestoreScriptName(); + static void RestoreScriptNameStub(); + + static void LoadGameType(); + static void LoadGameTypeScript(); + }; +} diff --git a/src/Components/Modules/ServerInfo.cpp b/src/Components/Modules/ServerInfo.cpp index a6b6b2c3..22bc6c04 100644 --- a/src/Components/Modules/ServerInfo.cpp +++ b/src/Components/Modules/ServerInfo.cpp @@ -1,295 +1,295 @@ -#include "STDInclude.hpp" - -namespace Components -{ - ServerInfo::Container ServerInfo::PlayerContainer; - - unsigned int ServerInfo::GetPlayerCount() - { - return ServerInfo::PlayerContainer.playerList.size(); - } - - const char* ServerInfo::GetPlayerText(unsigned int index, int column) - { - if (index < ServerInfo::PlayerContainer.playerList.size()) - { - switch (column) - { - case 0: - return Utils::String::VA("%d", index); - - case 1: - return ServerInfo::PlayerContainer.playerList[index].name.data(); - - case 2: - return Utils::String::VA("%d", ServerInfo::PlayerContainer.playerList[index].score); - - case 3: - return Utils::String::VA("%d", ServerInfo::PlayerContainer.playerList[index].ping); - } - } - - return ""; - } - - void ServerInfo::SelectPlayer(unsigned int index) - { - ServerInfo::PlayerContainer.currentPlayer = index; - } - - void ServerInfo::ServerStatus(UIScript::Token) - { - ServerInfo::PlayerContainer.currentPlayer = 0; - ServerInfo::PlayerContainer.playerList.clear(); - - ServerList::ServerInfo* info = ServerList::GetCurrentServer(); - - if(info) - { - Dvar::Var("uiSi_ServerName").set(info->hostname); - Dvar::Var("uiSi_MaxClients").set(info->clients); - Dvar::Var("uiSi_Version").set(info->shortversion); - Dvar::Var("uiSi_SecurityLevel").set(info->securityLevel); - Dvar::Var("uiSi_isPrivate").set(info->password ? "@MENU_YES" : "@MENU_NO"); - Dvar::Var("uiSi_Hardcore").set(info->hardcore ? "@MENU_ENABLED" : "@MENU_DISABLED"); - Dvar::Var("uiSi_KillCam").set("@MENU_NO"); - Dvar::Var("uiSi_ffType").set("@MENU_DISABLED"); - Dvar::Var("uiSi_MapName").set(info->mapname); - Dvar::Var("uiSi_MapNameLoc").set(Game::UI_LocalizeMapName(info->mapname.data())); - Dvar::Var("uiSi_GameType").set(Game::UI_LocalizeGameType(info->gametype.data())); - Dvar::Var("uiSi_ModName").set(""); - - if (info->mod.size() > 5) - { - Dvar::Var("uiSi_ModName").set(info->mod.data() + 5); - } - - ServerInfo::PlayerContainer.target = info->addr; - Network::SendCommand(ServerInfo::PlayerContainer.target, "getstatus"); - } - } - - void ServerInfo::DrawScoreboardInfo(void* a1) - { - Game::Font* font = Game::R_RegisterFont("fonts/bigfont"); - void* cxt = Game::UI_GetContext(a1); - - std::string addressText = Network::Address(*Game::connectedHost).getString(); - if (addressText == "0.0.0.0:0" || addressText == "loopback") addressText = "Listen Server"; - - // get x positions - float fontSize = 0.35f; - float y = (480.0f - Dvar::Var("cg_scoreboardHeight").get()) * 0.5f; - y += Dvar::Var("cg_scoreboardHeight").get() + 6.0f; - - float x = 320.0f - Dvar::Var("cg_scoreboardWidth").get() * 0.5f; - float x2 = 320.0f + Dvar::Var("cg_scoreboardWidth").get() * 0.5f; - - // draw only when stream friendly ui is not enabled - if (!Dvar::Var("ui_streamFriendly").get()) - { - Game::UI_DrawText(cxt, reinterpret_cast(0x7ED3F8), 0x7FFFFFFF, font, x, y, 0, 0, fontSize, reinterpret_cast(0x747F34), 3); - Game::UI_DrawText(cxt, addressText.data(), 0x7FFFFFFF, font, x2 - Game::UI_TextWidth(addressText.data(), 0, font, fontSize), y, 0, 0, fontSize, reinterpret_cast(0x747F34), 3); - } - } - - __declspec(naked) void ServerInfo::DrawScoreboardStub() - { - __asm - { - push eax - call ServerInfo::DrawScoreboardInfo - pop eax - mov ecx, 591B70h - jmp ecx - } - } - - Utils::InfoString ServerInfo::GetInfo() - { - int maxclientCount = *Game::svs_numclients; - - if (!maxclientCount) - { - //maxclientCount = Dvar::Var("sv_maxclients").get(); - maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); - } - - Utils::InfoString info(Game::Dvar_InfoString_Big(1024)); - info.set("gamename", "IW4"); - info.set("sv_maxclients", Utils::String::VA("%i", maxclientCount)); - info.set("protocol", Utils::String::VA("%i", PROTOCOL)); - info.set("shortversion", SHORTVERSION); - info.set("mapname", Dvar::Var("mapname").get()); - info.set("isPrivate", (Dvar::Var("g_password").get().empty() ? "0" : "1")); - info.set("checksum", Utils::String::VA("%X", Utils::Cryptography::JenkinsOneAtATime::Compute(Utils::String::VA("%u", Game::Sys_Milliseconds())))); - - // Ensure mapname is set - if (info.get("mapname").empty()) - { - info.set("mapname", Dvar::Var("ui_mapname").get()); - } - - // Set matchtype - // 0 - No match, connecting not possible - // 1 - Party, use Steam_JoinLobby to connect - // 2 - Match, use CL_ConnectFromParty to connect - - if (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get()) // Party hosting - { - info.set("matchtype", "1"); - } - else if (Dvar::Var("sv_running").get()) // Match hosting - { - info.set("matchtype", "2"); - } - else - { - info.set("matchtype", "0"); - } - - return info; - } - - ServerInfo::ServerInfo() - { - ServerInfo::PlayerContainer.currentPlayer = 0; - ServerInfo::PlayerContainer.playerList.clear(); - - // Draw IP and hostname on the scoreboard - Utils::Hook(0x4FC6EA, ServerInfo::DrawScoreboardStub, HOOK_CALL).install()->quick(); - - // Ignore native getStatus implementation - Utils::Hook::Nop(0x62654E, 6); - - // Add uiscript - UIScript::Add("ServerStatus", ServerInfo::ServerStatus); - - // Add uifeeder - UIFeeder::Add(13.0f, ServerInfo::GetPlayerCount, ServerInfo::GetPlayerText, ServerInfo::SelectPlayer); - - Network::Handle("getStatus", [] (Network::Address address, std::string data) - { - std::string playerList; - - Utils::InfoString info = ServerInfo::GetInfo(); - info.set("challenge", Utils::ParseChallenge(data)); - - for (int i = 0; i < atoi(info.get("sv_maxclients").data()); ++i) // Maybe choose 18 here? - { - int score = 0; - int ping = 0; - std::string name; - - if (Dvar::Var("sv_running").get()) - { - if (Game::svs_clients[i].state < 3) continue; - - score = Game::SV_GameClientNum_Score(i); - ping = Game::svs_clients[i].ping; - name = Game::svs_clients[i].name; - } - else - { - // Score and ping are irrelevant - const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast(0x1081C00), i); - if (!namePtr || !namePtr[0]) continue; - - name = namePtr; - } - - playerList.append(Utils::String::VA("%i %i \"%s\"\n", score, ping, name.data())); - } - - Network::SendCommand(address, "statusResponse", "\\" + info.build() + "\n" + playerList + "\n"); - }); - - Network::Handle("statusResponse", [] (Network::Address address, std::string data) - { - if (ServerInfo::PlayerContainer.target == address) - { - Utils::InfoString info(data.substr(0, data.find_first_of("\n"))); - - Dvar::Var("uiSi_ServerName").set(info.get("sv_hostname")); - Dvar::Var("uiSi_MaxClients").set(info.get("sv_maxclients")); - Dvar::Var("uiSi_Version").set(info.get("shortversion")); - Dvar::Var("uiSi_SecurityLevel").set(info.get("sv_securityLevel")); - Dvar::Var("uiSi_isPrivate").set(info.get("isPrivate") == "0" ? "@MENU_NO" : "@MENU_YES"); - Dvar::Var("uiSi_Hardcore").set(info.get("g_hardcore") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED"); - Dvar::Var("uiSi_KillCam").set(info.get("scr_game_allowkillcam") == "0" ? "@MENU_NO" : "@MENU_YES"); - Dvar::Var("uiSi_MapName").set(info.get("mapname")); - Dvar::Var("uiSi_MapNameLoc").set(Game::UI_LocalizeMapName(info.get("mapname").data())); - Dvar::Var("uiSi_GameType").set(Game::UI_LocalizeGameType(info.get("g_gametype").data())); - Dvar::Var("uiSi_ModName").set(""); - - switch (atoi(info.get("scr_team_fftype").data())) - { - default: - Dvar::Var("uiSi_ffType").set("@MENU_DISABLED"); - break; - - case 1: - Dvar::Var("uiSi_ffType").set("@MENU_ENABLED"); - break; - - case 2: - Dvar::Var("uiSi_ffType").set("@MPUI_RULES_REFLECT"); - break; - - case 3: - Dvar::Var("uiSi_ffType").set("@MPUI_RULES_SHARED"); - break; - } - - if (info.get("fs_game").size() > 5) - { - Dvar::Var("uiSi_ModName").set(info.get("fs_game").data() + 5); - } - - auto lines = Utils::String::Explode(data, '\n'); - - if (lines.size() <= 1) return; - - for (unsigned int i = 1; i < lines.size(); ++i) - { - ServerInfo::Container::Player player; - - std::string currentData = lines[i]; - - if (currentData.size() < 3) continue; - - // Insert score - player.score = atoi(currentData.substr(0, currentData.find_first_of(" ")).data()); - - // Remove score - currentData = currentData.substr(currentData.find_first_of(" ") + 1); - - // Insert ping - player.ping = atoi(currentData.substr(0, currentData.find_first_of(" ")).data()); - - // Remove ping - currentData = currentData.substr(currentData.find_first_of(" ") + 1); - - if (currentData[0] == '\"') - { - currentData = currentData.substr(1); - } - - if (currentData.back() == '\"') - { - currentData.pop_back(); - } - - player.name = currentData; - - ServerInfo::PlayerContainer.playerList.push_back(player); - } - } - }); - } - - ServerInfo::~ServerInfo() - { - ServerInfo::PlayerContainer.playerList.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + ServerInfo::Container ServerInfo::PlayerContainer; + + unsigned int ServerInfo::GetPlayerCount() + { + return ServerInfo::PlayerContainer.playerList.size(); + } + + const char* ServerInfo::GetPlayerText(unsigned int index, int column) + { + if (index < ServerInfo::PlayerContainer.playerList.size()) + { + switch (column) + { + case 0: + return Utils::String::VA("%d", index); + + case 1: + return ServerInfo::PlayerContainer.playerList[index].name.data(); + + case 2: + return Utils::String::VA("%d", ServerInfo::PlayerContainer.playerList[index].score); + + case 3: + return Utils::String::VA("%d", ServerInfo::PlayerContainer.playerList[index].ping); + } + } + + return ""; + } + + void ServerInfo::SelectPlayer(unsigned int index) + { + ServerInfo::PlayerContainer.currentPlayer = index; + } + + void ServerInfo::ServerStatus(UIScript::Token) + { + ServerInfo::PlayerContainer.currentPlayer = 0; + ServerInfo::PlayerContainer.playerList.clear(); + + ServerList::ServerInfo* info = ServerList::GetCurrentServer(); + + if(info) + { + Dvar::Var("uiSi_ServerName").set(info->hostname); + Dvar::Var("uiSi_MaxClients").set(info->clients); + Dvar::Var("uiSi_Version").set(info->shortversion); + Dvar::Var("uiSi_SecurityLevel").set(info->securityLevel); + Dvar::Var("uiSi_isPrivate").set(info->password ? "@MENU_YES" : "@MENU_NO"); + Dvar::Var("uiSi_Hardcore").set(info->hardcore ? "@MENU_ENABLED" : "@MENU_DISABLED"); + Dvar::Var("uiSi_KillCam").set("@MENU_NO"); + Dvar::Var("uiSi_ffType").set("@MENU_DISABLED"); + Dvar::Var("uiSi_MapName").set(info->mapname); + Dvar::Var("uiSi_MapNameLoc").set(Game::UI_LocalizeMapName(info->mapname.data())); + Dvar::Var("uiSi_GameType").set(Game::UI_LocalizeGameType(info->gametype.data())); + Dvar::Var("uiSi_ModName").set(""); + + if (info->mod.size() > 5) + { + Dvar::Var("uiSi_ModName").set(info->mod.data() + 5); + } + + ServerInfo::PlayerContainer.target = info->addr; + Network::SendCommand(ServerInfo::PlayerContainer.target, "getstatus"); + } + } + + void ServerInfo::DrawScoreboardInfo(void* a1) + { + Game::Font* font = Game::R_RegisterFont("fonts/bigfont"); + void* cxt = Game::UI_GetContext(a1); + + std::string addressText = Network::Address(*Game::connectedHost).getString(); + if (addressText == "0.0.0.0:0" || addressText == "loopback") addressText = "Listen Server"; + + // get x positions + float fontSize = 0.35f; + float y = (480.0f - Dvar::Var("cg_scoreboardHeight").get()) * 0.5f; + y += Dvar::Var("cg_scoreboardHeight").get() + 6.0f; + + float x = 320.0f - Dvar::Var("cg_scoreboardWidth").get() * 0.5f; + float x2 = 320.0f + Dvar::Var("cg_scoreboardWidth").get() * 0.5f; + + // draw only when stream friendly ui is not enabled + if (!Dvar::Var("ui_streamFriendly").get()) + { + Game::UI_DrawText(cxt, reinterpret_cast(0x7ED3F8), 0x7FFFFFFF, font, x, y, 0, 0, fontSize, reinterpret_cast(0x747F34), 3); + Game::UI_DrawText(cxt, addressText.data(), 0x7FFFFFFF, font, x2 - Game::UI_TextWidth(addressText.data(), 0, font, fontSize), y, 0, 0, fontSize, reinterpret_cast(0x747F34), 3); + } + } + + __declspec(naked) void ServerInfo::DrawScoreboardStub() + { + __asm + { + push eax + call ServerInfo::DrawScoreboardInfo + pop eax + mov ecx, 591B70h + jmp ecx + } + } + + Utils::InfoString ServerInfo::GetInfo() + { + int maxclientCount = *Game::svs_numclients; + + if (!maxclientCount) + { + //maxclientCount = Dvar::Var("sv_maxclients").get(); + maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame); + } + + Utils::InfoString info(Game::Dvar_InfoString_Big(1024)); + info.set("gamename", "IW4"); + info.set("sv_maxclients", Utils::String::VA("%i", maxclientCount)); + info.set("protocol", Utils::String::VA("%i", PROTOCOL)); + info.set("shortversion", SHORTVERSION); + info.set("mapname", Dvar::Var("mapname").get()); + info.set("isPrivate", (Dvar::Var("g_password").get().empty() ? "0" : "1")); + info.set("checksum", Utils::String::VA("%X", Utils::Cryptography::JenkinsOneAtATime::Compute(Utils::String::VA("%u", Game::Sys_Milliseconds())))); + + // Ensure mapname is set + if (info.get("mapname").empty()) + { + info.set("mapname", Dvar::Var("ui_mapname").get()); + } + + // Set matchtype + // 0 - No match, connecting not possible + // 1 - Party, use Steam_JoinLobby to connect + // 2 - Match, use CL_ConnectFromParty to connect + + if (Dvar::Var("party_enable").get() && Dvar::Var("party_host").get()) // Party hosting + { + info.set("matchtype", "1"); + } + else if (Dvar::Var("sv_running").get()) // Match hosting + { + info.set("matchtype", "2"); + } + else + { + info.set("matchtype", "0"); + } + + return info; + } + + ServerInfo::ServerInfo() + { + ServerInfo::PlayerContainer.currentPlayer = 0; + ServerInfo::PlayerContainer.playerList.clear(); + + // Draw IP and hostname on the scoreboard + Utils::Hook(0x4FC6EA, ServerInfo::DrawScoreboardStub, HOOK_CALL).install()->quick(); + + // Ignore native getStatus implementation + Utils::Hook::Nop(0x62654E, 6); + + // Add uiscript + UIScript::Add("ServerStatus", ServerInfo::ServerStatus); + + // Add uifeeder + UIFeeder::Add(13.0f, ServerInfo::GetPlayerCount, ServerInfo::GetPlayerText, ServerInfo::SelectPlayer); + + Network::Handle("getStatus", [] (Network::Address address, std::string data) + { + std::string playerList; + + Utils::InfoString info = ServerInfo::GetInfo(); + info.set("challenge", Utils::ParseChallenge(data)); + + for (int i = 0; i < atoi(info.get("sv_maxclients").data()); ++i) // Maybe choose 18 here? + { + int score = 0; + int ping = 0; + std::string name; + + if (Dvar::Var("sv_running").get()) + { + if (Game::svs_clients[i].state < 3) continue; + + score = Game::SV_GameClientNum_Score(i); + ping = Game::svs_clients[i].ping; + name = Game::svs_clients[i].name; + } + else + { + // Score and ping are irrelevant + const char* namePtr = Game::PartyHost_GetMemberName(reinterpret_cast(0x1081C00), i); + if (!namePtr || !namePtr[0]) continue; + + name = namePtr; + } + + playerList.append(Utils::String::VA("%i %i \"%s\"\n", score, ping, name.data())); + } + + Network::SendCommand(address, "statusResponse", "\\" + info.build() + "\n" + playerList + "\n"); + }); + + Network::Handle("statusResponse", [] (Network::Address address, std::string data) + { + if (ServerInfo::PlayerContainer.target == address) + { + Utils::InfoString info(data.substr(0, data.find_first_of("\n"))); + + Dvar::Var("uiSi_ServerName").set(info.get("sv_hostname")); + Dvar::Var("uiSi_MaxClients").set(info.get("sv_maxclients")); + Dvar::Var("uiSi_Version").set(info.get("shortversion")); + Dvar::Var("uiSi_SecurityLevel").set(info.get("sv_securityLevel")); + Dvar::Var("uiSi_isPrivate").set(info.get("isPrivate") == "0" ? "@MENU_NO" : "@MENU_YES"); + Dvar::Var("uiSi_Hardcore").set(info.get("g_hardcore") == "0" ? "@MENU_DISABLED" : "@MENU_ENABLED"); + Dvar::Var("uiSi_KillCam").set(info.get("scr_game_allowkillcam") == "0" ? "@MENU_NO" : "@MENU_YES"); + Dvar::Var("uiSi_MapName").set(info.get("mapname")); + Dvar::Var("uiSi_MapNameLoc").set(Game::UI_LocalizeMapName(info.get("mapname").data())); + Dvar::Var("uiSi_GameType").set(Game::UI_LocalizeGameType(info.get("g_gametype").data())); + Dvar::Var("uiSi_ModName").set(""); + + switch (atoi(info.get("scr_team_fftype").data())) + { + default: + Dvar::Var("uiSi_ffType").set("@MENU_DISABLED"); + break; + + case 1: + Dvar::Var("uiSi_ffType").set("@MENU_ENABLED"); + break; + + case 2: + Dvar::Var("uiSi_ffType").set("@MPUI_RULES_REFLECT"); + break; + + case 3: + Dvar::Var("uiSi_ffType").set("@MPUI_RULES_SHARED"); + break; + } + + if (info.get("fs_game").size() > 5) + { + Dvar::Var("uiSi_ModName").set(info.get("fs_game").data() + 5); + } + + auto lines = Utils::String::Explode(data, '\n'); + + if (lines.size() <= 1) return; + + for (unsigned int i = 1; i < lines.size(); ++i) + { + ServerInfo::Container::Player player; + + std::string currentData = lines[i]; + + if (currentData.size() < 3) continue; + + // Insert score + player.score = atoi(currentData.substr(0, currentData.find_first_of(" ")).data()); + + // Remove score + currentData = currentData.substr(currentData.find_first_of(" ") + 1); + + // Insert ping + player.ping = atoi(currentData.substr(0, currentData.find_first_of(" ")).data()); + + // Remove ping + currentData = currentData.substr(currentData.find_first_of(" ") + 1); + + if (currentData[0] == '\"') + { + currentData = currentData.substr(1); + } + + if (currentData.back() == '\"') + { + currentData.pop_back(); + } + + player.name = currentData; + + ServerInfo::PlayerContainer.playerList.push_back(player); + } + } + }); + } + + ServerInfo::~ServerInfo() + { + ServerInfo::PlayerContainer.playerList.clear(); + } +} diff --git a/src/Components/Modules/ServerInfo.hpp b/src/Components/Modules/ServerInfo.hpp index a2ee6414..af0c962e 100644 --- a/src/Components/Modules/ServerInfo.hpp +++ b/src/Components/Modules/ServerInfo.hpp @@ -1,43 +1,43 @@ -namespace Components -{ - class ServerInfo : public Component - { - public: - ServerInfo(); - ~ServerInfo(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "ServerInfo"; }; -#endif - - static Utils::InfoString GetInfo(); - - private: - class Container - { - public: - class Player - { - public: - int ping; - int score; - std::string name; - }; - - unsigned int currentPlayer; - std::vector playerList; - Network::Address target; - }; - - static Container PlayerContainer; - - static void ServerStatus(UIScript::Token); - - static unsigned int GetPlayerCount(); - static const char* GetPlayerText(unsigned int index, int column); - static void SelectPlayer(unsigned int index); - - static void DrawScoreboardInfo(void* a1); - static void DrawScoreboardStub(); - }; -} +namespace Components +{ + class ServerInfo : public Component + { + public: + ServerInfo(); + ~ServerInfo(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "ServerInfo"; }; +#endif + + static Utils::InfoString GetInfo(); + + private: + class Container + { + public: + class Player + { + public: + int ping; + int score; + std::string name; + }; + + unsigned int currentPlayer; + std::vector playerList; + Network::Address target; + }; + + static Container PlayerContainer; + + static void ServerStatus(UIScript::Token); + + static unsigned int GetPlayerCount(); + static const char* GetPlayerText(unsigned int index, int column); + static void SelectPlayer(unsigned int index); + + static void DrawScoreboardInfo(void* a1); + static void DrawScoreboardStub(); + }; +} diff --git a/src/Components/Modules/ServerList.cpp b/src/Components/Modules/ServerList.cpp index b8204e2c..d129536d 100644 --- a/src/Components/Modules/ServerList.cpp +++ b/src/Components/Modules/ServerList.cpp @@ -1,743 +1,743 @@ -#include "STDInclude.hpp" - -namespace Components -{ - bool ServerList::SortAsc = true; - int ServerList::SortKey = ServerList::Column::Ping; - - unsigned int ServerList::CurrentServer = 0; - ServerList::Container ServerList::RefreshContainer; - - std::vector ServerList::OnlineList; - std::vector ServerList::OfflineList; - std::vector ServerList::FavouriteList; - - std::vector ServerList::VisibleList; - - std::vector* ServerList::GetList() - { - if (ServerList::IsOnlineList()) - { - return &ServerList::OnlineList; - } - else if (ServerList::IsOfflineList()) - { - return &ServerList::OfflineList; - } - else if (ServerList::IsFavouriteList()) - { - return &ServerList::FavouriteList; - } - - return nullptr; - } - - bool ServerList::IsFavouriteList() - { - return (Dvar::Var("ui_netSource").get() == 2); - } - - bool ServerList::IsOfflineList() - { - return (Dvar::Var("ui_netSource").get() == 0); - } - - bool ServerList::IsOnlineList() - { - return (Dvar::Var("ui_netSource").get() == 1); - } - - unsigned int ServerList::GetServerCount() - { - return ServerList::VisibleList.size(); - } - - const char* ServerList::GetServerText(unsigned int index, int column) - { - ServerList::ServerInfo* info = ServerList::GetServer(index); - - if (info) - { - return ServerList::GetServerText(info, column); - } - - return ""; - } - - const char* ServerList::GetServerText(ServerList::ServerInfo* server, int column) - { - if (!server) return ""; - - switch (column) - { - case Column::Password: - { - return (server->password ? "X" : ""); - } - - case Column::Matchtype: - { - return ((server->matchType == 1) ? "P" : "M"); - } - - case Column::Hostname: - { - return server->hostname.data(); - } - - case Column::Mapname: - { - if (server->svRunning) - { - return Game::UI_LocalizeMapName(server->mapname.data()); - } - else - { - return Utils::String::VA("^3%s", Game::UI_LocalizeMapName(server->mapname.data())); - } - } - - case Column::Players: - { - return Utils::String::VA("%i (%i)", server->clients, server->maxClients); - } - - case Column::Gametype: - { - return Game::UI_LocalizeGameType(server->gametype.data()); - } - - case Column::Mod: - { - if (server->mod != "") - { - return (server->mod.data() + 5); - } - - return ""; - } - - case Column::Ping: - { - return Utils::String::VA("%i", server->ping); - } - } - - return ""; - } - - void ServerList::SelectServer(unsigned int index) - { - ServerList::CurrentServer = index; - - ServerList::ServerInfo* info = ServerList::GetCurrentServer(); - - if (info) - { - Dvar::Var("ui_serverSelected").set(true); - Dvar::Var("ui_serverSelectedMap").set(info->mapname); - Dvar::Var("ui_serverSelectedGametype").set(info->gametype); - } - else - { - Dvar::Var("ui_serverSelected").set(false); - } - } - - void ServerList::UpdateVisibleList(UIScript::Token) - { - auto list = ServerList::GetList(); - if (!list) return; - - std::vector tempList(*list); - - if (tempList.empty()) - { - ServerList::Refresh(UIScript::Token()); - } - else - { - list->clear(); - - std::lock_guard _(ServerList::RefreshContainer.mutex); - - ServerList::RefreshContainer.sendCount = 0; - ServerList::RefreshContainer.sentCount = 0; - - for (auto server : tempList) - { - ServerList::InsertRequest(server.addr, false); - } - } - } - - void ServerList::RefreshVisibleList(UIScript::Token) - { - Dvar::Var("ui_serverSelected").set(false); - - ServerList::VisibleList.clear(); - - auto list = ServerList::GetList(); - if (!list) return; - - // Refresh entirely, if there is no entry in the list - if (list->empty()) - { - ServerList::Refresh(UIScript::Token()); - return; - } - - bool ui_browserShowFull = Dvar::Var("ui_browserShowFull").get(); - bool ui_browserShowEmpty = Dvar::Var("ui_browserShowEmpty").get(); - int ui_browserShowHardcore = Dvar::Var("ui_browserKillcam").get(); - int ui_browserShowPassword = Dvar::Var("ui_browserShowPassword").get(); - int ui_browserMod = Dvar::Var("ui_browserMod").get(); - int ui_joinGametype = Dvar::Var("ui_joinGametype").get(); - - for (unsigned int i = 0; i < list->size(); ++i) - { - ServerList::ServerInfo* info = &(*list)[i]; - - // Filter full servers - if (!ui_browserShowFull && info->clients >= info->maxClients) continue; - - // Filter empty servers - if (!ui_browserShowEmpty && info->clients <= 0) continue; - - // Filter hardcore servers - if ((ui_browserShowHardcore == 0 && info->hardcore) || (ui_browserShowHardcore == 1 && !info->hardcore)) continue; - - // Filter servers with password - if ((ui_browserShowPassword == 0 && info->password) || (ui_browserShowPassword == 1 && !info->password)) continue; - - // Don't show modded servers - if ((ui_browserMod == 0 && info->mod.size()) || (ui_browserMod == 1 && !info->mod.size())) continue; - - // Filter by gametype - if (ui_joinGametype > 0 && (ui_joinGametype -1) < *Game::gameTypeCount && Game::gameTypes[(ui_joinGametype - 1)].gameType != info->gametype) continue; - - ServerList::VisibleList.push_back(i); - } - - ServerList::SortList(); - } - - void ServerList::Refresh(UIScript::Token) - { - Dvar::Var("ui_serverSelected").set(false); - Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0"); - -// ServerList::OnlineList.clear(); -// ServerList::OfflineList.clear(); -// ServerList::FavouriteList.clear(); - - auto list = ServerList::GetList(); - if (list) list->clear(); - - ServerList::VisibleList.clear(); - ServerList::RefreshContainer.mutex.lock(); - ServerList::RefreshContainer.servers.clear(); - ServerList::RefreshContainer.sendCount = 0; - ServerList::RefreshContainer.sentCount = 0; - ServerList::RefreshContainer.mutex.unlock(); - - if (ServerList::IsOfflineList()) - { - Discovery::Perform(); - } - else if (ServerList::IsOnlineList()) - { -#ifdef USE_LEGACY_SERVER_LIST - ServerList::RefreshContainer.awatingList = true; - ServerList::RefreshContainer.awaitTime = Game::Sys_Milliseconds(); - - int masterPort = Dvar::Var("masterPort").get(); - const char* masterServerName = Dvar::Var("masterServerName").get(); - - ServerList::RefreshContainer.host = Network::Address(Utils::String::VA("%s:%u", masterServerName, masterPort)); - - Logger::Print("Sending serverlist request to master: %s:%u\n", masterServerName, masterPort); - - Network::SendCommand(ServerList::RefreshContainer.host, "getservers", Utils::String::VA("IW4 %i full empty", PROTOCOL)); - //Network::SendCommand(ServerList::RefreshContainer.Host, "getservers", "0 full empty"); -#else - Node::SyncNodeList(); -#endif - } - else if (ServerList::IsFavouriteList()) - { - ServerList::LoadFavourties(); - } - } - - void ServerList::StoreFavourite(std::string server) - { - //json11::Json::parse() - std::vector servers; - - if (Utils::IO::FileExists("players/favourites.json")) - { - std::string data = Utils::IO::ReadFile("players/favourites.json"); - json11::Json object = json11::Json::parse(data, data); - - if (!object.is_array()) - { - Logger::Print("Favourites storage file is invalid!\n"); - Game::MessageBox("Favourites storage file is invalid!", "Error"); - return; - } - - auto storedServers = object.array_items(); - - for (unsigned int i = 0; i < storedServers.size(); ++i) - { - if (!storedServers[i].is_string()) continue; - if (storedServers[i].string_value() == server) - { - Game::MessageBox("Server already marked as favourite.", "Error"); - return; - } - - servers.push_back(storedServers[i].string_value()); - } - } - - servers.push_back(server); - - json11::Json data = json11::Json(servers); - Utils::IO::WriteFile("players/favourites.json", data.dump()); - Game::MessageBox("Server added to favourites.", "Success"); - } - - void ServerList::LoadFavourties() - { - if (ServerList::IsFavouriteList() && Utils::IO::FileExists("players/favourites.json")) - { - auto list = ServerList::GetList(); - if (list) list->clear(); - - std::string data = Utils::IO::ReadFile("players/favourites.json"); - json11::Json object = json11::Json::parse(data, data); - - if (!object.is_array()) - { - Logger::Print("Favourites storage file is invalid!\n"); - Game::MessageBox("Favourites storage file is invalid!", "Error"); - return; - } - - auto servers = object.array_items(); - - for (unsigned int i = 0; i < servers.size(); ++i) - { - if(!servers[i].is_string()) continue; - ServerList::InsertRequest(servers[i].string_value(), true); - } - } - } - - void ServerList::InsertRequest(Network::Address address, bool acquireMutex) - { - if (acquireMutex) ServerList::RefreshContainer.mutex.lock(); - - ServerList::Container::ServerContainer container; - container.sent = false; - container.target = address; - - bool alreadyInserted = false; - for (auto &server : ServerList::RefreshContainer.servers) - { - if (server.target == container.target) - { - alreadyInserted = true; - break; - } - } - - if (!alreadyInserted) - { - ServerList::RefreshContainer.servers.push_back(container); - - auto list = ServerList::GetList(); - if (list) - { - for (auto server : *list) - { - if (server.addr == container.target) - { - --ServerList::RefreshContainer.sendCount; - --ServerList::RefreshContainer.sentCount; - break; - } - } - } - - ++ServerList::RefreshContainer.sendCount; - } - - if (acquireMutex) ServerList::RefreshContainer.mutex.unlock(); - } - - void ServerList::Insert(Network::Address address, Utils::InfoString info) - { - std::lock_guard _(ServerList::RefreshContainer.mutex); - - for (auto i = ServerList::RefreshContainer.servers.begin(); i != ServerList::RefreshContainer.servers.end();) - { - // Our desired server - if (i->target == address && i->sent) - { - // Challenge did not match - if (i->challenge != info.get("challenge")) - { - // Shall we remove the server from the queue? - // Better not, it might send a second response with the correct challenge. - // This might happen when users refresh twice (or more often) in a short period of time - break; - } - - ServerInfo server; - server.hostname = info.get("hostname"); - server.mapname = info.get("mapname"); - server.gametype = info.get("gametype"); - server.shortversion = info.get("shortversion"); - server.mod = info.get("fs_game"); - server.matchType = atoi(info.get("matchtype").data()); - server.clients = atoi(info.get("clients").data()); - server.securityLevel = atoi(info.get("securityLevel").data()); - server.maxClients = atoi(info.get("sv_maxclients").data()); - server.password = (atoi(info.get("isPrivate").data()) != 0); - server.hardcore = (atoi(info.get("hc").data()) != 0); - server.svRunning = (atoi(info.get("sv_running").data()) != 0); - server.ping = (Game::Sys_Milliseconds() - i->sendTime); - server.addr = address; - - // Remove server from queue - i = ServerList::RefreshContainer.servers.erase(i); - - // Check if already inserted and remove - auto list = ServerList::GetList(); - if (!list) return; - - unsigned int k = 0; - for (auto j = list->begin(); j != list->end(); ++k) - { - if (j->addr == address) - { - j = list->erase(j); - } - else - { - ++j; - } - } - - // Also remove from visible list - for (auto j = ServerList::VisibleList.begin(); j != ServerList::VisibleList.end();) - { - if (*j == k) - { - j = ServerList::VisibleList.erase(j); - } - else - { - ++j; - } - } - - if (info.get("gamename") == "IW4" - && server.matchType -#ifndef DEBUG - && server.shortversion == SHORTVERSION -#endif - ) - { - auto lList = ServerList::GetList(); - - if (lList) - { - lList->push_back(server); - ServerList::RefreshVisibleList(UIScript::Token()); - } - } - - break; - } - else - { - ++i; - } - } - } - - ServerList::ServerInfo* ServerList::GetCurrentServer() - { - return ServerList::GetServer(ServerList::CurrentServer); - } - - void ServerList::SortList() - { - qsort(ServerList::VisibleList.data(), ServerList::VisibleList.size(), sizeof(int), [] (const void* first, const void* second) - { - const unsigned int server1 = *static_cast(first); - const unsigned int server2 = *static_cast(second); - - ServerInfo* info1 = nullptr; - ServerInfo* info2 = nullptr; - - auto list = ServerList::GetList(); - if (!list) return 0; - - if (list->size() > server1) info1 = &(*list)[server1]; - if (list->size() > server2) info2 = &(*list)[server2]; - - if (!info1) return 1; - if (!info2) return -1; - - // Numerical comparisons - if (ServerList::SortKey == ServerList::Column::Ping) - { - return ((info1->ping - info2->ping) * (ServerList::SortAsc ? 1 : -1)); - } - else if (ServerList::SortKey == ServerList::Column::Players) - { - return ((info1->clients - info2->clients) * (ServerList::SortAsc ? 1 : -1)); - } - - std::string text1 = Colors::Strip(ServerList::GetServerText(info1, ServerList::SortKey)); - std::string text2 = Colors::Strip(ServerList::GetServerText(info2, ServerList::SortKey)); - - // ASCII-based comparison - return (text1.compare(text2) * (ServerList::SortAsc ? 1 : -1)); - }); - } - - ServerList::ServerInfo* ServerList::GetServer(unsigned int index) - { - if (ServerList::VisibleList.size() > index) - { - auto list = ServerList::GetList(); - if (!list) return nullptr; - - if (list->size() > ServerList::VisibleList[index]) - { - return &(*list)[ServerList::VisibleList[index]]; - } - } - - return nullptr; - } - - void ServerList::Frame() - { - // This is bad practice and might even cause undefined behaviour! - if (!ServerList::RefreshContainer.mutex.try_lock()) return; - - if (ServerList::RefreshContainer.awatingList) - { - // Check if we haven't got a response within 10 seconds - if (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime > 5000) - { - ServerList::RefreshContainer.awatingList = false; - - Logger::Print("We haven't received a response from the master within %d seconds!\n", (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime) / 1000); - } - } - - // Send requests to 10 servers each frame - int SendServers = 10; - - for (unsigned int i = 0; i < ServerList::RefreshContainer.servers.size(); ++i) - { - ServerList::Container::ServerContainer* server = &ServerList::RefreshContainer.servers[i]; - if (server->sent) continue; - - // Found server we can send a request to - server->sent = true; - SendServers--; - - server->sendTime = Game::Sys_Milliseconds(); - server->challenge = Utils::String::VA("%X", Utils::Cryptography::Rand::GenerateInt()); - - ++ServerList::RefreshContainer.sentCount; - - Network::SendCommand(server->target, "getinfo", server->challenge); - - // Display in the menu, like in COD4 - Localization::Set("MPUI_SERVERQUERIED", Utils::String::VA("Sent requests: %d/%d", ServerList::RefreshContainer.sentCount, ServerList::RefreshContainer.sendCount)); - - if (SendServers <= 0) break; - } - - ServerList::RefreshContainer.mutex.unlock(); - } - - void ServerList::UpdateSource() - { - Dvar::Var netSource("ui_netSource"); - - int source = netSource.get(); - - if (++source > netSource.get()->max.i) - { - source = 0; - } - - netSource.set(source); - - ServerList::RefreshVisibleList(UIScript::Token()); - } - - void ServerList::UpdateGameType() - { - Dvar::Var joinGametype("ui_joinGametype"); - - int gametype = joinGametype.get(); - - if (++gametype > *Game::gameTypeCount) - { - gametype = 0; - } - - joinGametype.set(gametype); - - ServerList::RefreshVisibleList(UIScript::Token()); - } - - ServerList::ServerList() - { - ServerList::OnlineList.clear(); - ServerList::VisibleList.clear(); - - Dvar::OnInit([] () - { - Dvar::Register("ui_serverSelected", false, Game::dvar_flag::DVAR_FLAG_NONE, "Whether a server has been selected in the serverlist"); - Dvar::Register("ui_serverSelectedMap", "mp_afghan", Game::dvar_flag::DVAR_FLAG_NONE, "Map of the selected server"); - }); - - Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0"); - - Network::Handle("getServersResponse", [] (Network::Address address, std::string data) - { - if (ServerList::RefreshContainer.host != address) return; // Only parse from host we sent to - - ServerList::RefreshContainer.awatingList = false; - - std::lock_guard _(ServerList::RefreshContainer.mutex); - - int offset = 0; - int count = ServerList::RefreshContainer.servers.size(); - ServerList::MasterEntry* entry = nullptr; - - // Find first entry - do - { - entry = reinterpret_cast(const_cast(data.data()) + offset++); - } - while (!entry->HasSeparator() && !entry->IsEndToken()); - - for (int i = 0; !entry[i].IsEndToken() && entry[i].HasSeparator(); ++i) - { - Network::Address serverAddr = address; - serverAddr.setIP(entry[i].ip); - serverAddr.setPort(ntohs(entry[i].port)); - serverAddr.setType(Game::NA_IP); - - ServerList::InsertRequest(serverAddr, false); - } - - Logger::Print("Parsed %d servers from master\n", ServerList::RefreshContainer.servers.size() - count); - }); - - // Set default masterServerName + port and save it -#ifdef USE_LEGACY_SERVER_LIST - Utils::Hook::Set(0x60AD92, "localhost"); - Utils::Hook::Set(0x60AD90, Game::dvar_flag::DVAR_FLAG_SAVED); // masterServerName - Utils::Hook::Set(0x60ADC6, Game::dvar_flag::DVAR_FLAG_SAVED); // masterPort -#endif - - // Add server list feeder - UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer); - - // Add required UIScripts - UIScript::Add("UpdateFilter", ServerList::RefreshVisibleList); - UIScript::Add("RefreshFilter", ServerList::UpdateVisibleList); - - UIScript::Add("RefreshServers", ServerList::Refresh); - UIScript::Add("JoinServer", [] (UIScript::Token) - { - ServerList::ServerInfo* info = ServerList::GetServer(ServerList::CurrentServer); - - if (info) - { - Party::Connect(info->addr); - } - }); - UIScript::Add("ServerSort", [] (UIScript::Token token) - { - int key = token.get(); - - if (ServerList::SortKey == key) - { - ServerList::SortAsc = !ServerList::SortAsc; - } - else - { - ServerList::SortKey = key; - ServerList::SortAsc = true; - } - - Logger::Print("Sorting server list by token: %d\n", ServerList::SortKey); - ServerList::SortList(); - }); - UIScript::Add("CreateListFavorite", [] (UIScript::Token) - { - ServerList::ServerInfo* info = ServerList::GetCurrentServer(); - - if (info) - { - ServerList::StoreFavourite(info->addr.getString()); - } - }); - UIScript::Add("CreateFavorite", [] (UIScript::Token) - { - ServerList::StoreFavourite(Dvar::Var("ui_favoriteAddress").get()); - }); - UIScript::Add("CreateCurrentServerFavorite", [] (UIScript::Token) - { - if (Dvar::Var("cl_ingame").get()) - { - std::string addressText = Network::Address(*Game::connectedHost).getString(); - if (addressText != "0.0.0.0:0" && addressText != "loopback") - { - ServerList::StoreFavourite(addressText); - } - } - }); - - // Add required ownerDraws - UIScript::AddOwnerDraw(220, ServerList::UpdateSource); - UIScript::AddOwnerDraw(253, ServerList::UpdateGameType); - - // Add frame callback - Renderer::OnFrame(ServerList::Frame); - - // This is placed here in case the anticheat has been disabled! -#if !defined(DEBUG) && !defined(DISABLE_ANTICHEAT) - Renderer::OnFrame(AntiCheat::ReadIntegrityCheck); -#endif - } - - ServerList::~ServerList() - { - ServerList::OnlineList.clear(); - ServerList::OfflineList.clear(); - ServerList::FavouriteList.clear(); - ServerList::VisibleList.clear(); - - ServerList::RefreshContainer.mutex.lock(); - ServerList::RefreshContainer.awatingList = false; - ServerList::RefreshContainer.servers.clear(); - ServerList::RefreshContainer.mutex.unlock(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + bool ServerList::SortAsc = true; + int ServerList::SortKey = ServerList::Column::Ping; + + unsigned int ServerList::CurrentServer = 0; + ServerList::Container ServerList::RefreshContainer; + + std::vector ServerList::OnlineList; + std::vector ServerList::OfflineList; + std::vector ServerList::FavouriteList; + + std::vector ServerList::VisibleList; + + std::vector* ServerList::GetList() + { + if (ServerList::IsOnlineList()) + { + return &ServerList::OnlineList; + } + else if (ServerList::IsOfflineList()) + { + return &ServerList::OfflineList; + } + else if (ServerList::IsFavouriteList()) + { + return &ServerList::FavouriteList; + } + + return nullptr; + } + + bool ServerList::IsFavouriteList() + { + return (Dvar::Var("ui_netSource").get() == 2); + } + + bool ServerList::IsOfflineList() + { + return (Dvar::Var("ui_netSource").get() == 0); + } + + bool ServerList::IsOnlineList() + { + return (Dvar::Var("ui_netSource").get() == 1); + } + + unsigned int ServerList::GetServerCount() + { + return ServerList::VisibleList.size(); + } + + const char* ServerList::GetServerText(unsigned int index, int column) + { + ServerList::ServerInfo* info = ServerList::GetServer(index); + + if (info) + { + return ServerList::GetServerText(info, column); + } + + return ""; + } + + const char* ServerList::GetServerText(ServerList::ServerInfo* server, int column) + { + if (!server) return ""; + + switch (column) + { + case Column::Password: + { + return (server->password ? "X" : ""); + } + + case Column::Matchtype: + { + return ((server->matchType == 1) ? "P" : "M"); + } + + case Column::Hostname: + { + return server->hostname.data(); + } + + case Column::Mapname: + { + if (server->svRunning) + { + return Game::UI_LocalizeMapName(server->mapname.data()); + } + else + { + return Utils::String::VA("^3%s", Game::UI_LocalizeMapName(server->mapname.data())); + } + } + + case Column::Players: + { + return Utils::String::VA("%i (%i)", server->clients, server->maxClients); + } + + case Column::Gametype: + { + return Game::UI_LocalizeGameType(server->gametype.data()); + } + + case Column::Mod: + { + if (server->mod != "") + { + return (server->mod.data() + 5); + } + + return ""; + } + + case Column::Ping: + { + return Utils::String::VA("%i", server->ping); + } + } + + return ""; + } + + void ServerList::SelectServer(unsigned int index) + { + ServerList::CurrentServer = index; + + ServerList::ServerInfo* info = ServerList::GetCurrentServer(); + + if (info) + { + Dvar::Var("ui_serverSelected").set(true); + Dvar::Var("ui_serverSelectedMap").set(info->mapname); + Dvar::Var("ui_serverSelectedGametype").set(info->gametype); + } + else + { + Dvar::Var("ui_serverSelected").set(false); + } + } + + void ServerList::UpdateVisibleList(UIScript::Token) + { + auto list = ServerList::GetList(); + if (!list) return; + + std::vector tempList(*list); + + if (tempList.empty()) + { + ServerList::Refresh(UIScript::Token()); + } + else + { + list->clear(); + + std::lock_guard _(ServerList::RefreshContainer.mutex); + + ServerList::RefreshContainer.sendCount = 0; + ServerList::RefreshContainer.sentCount = 0; + + for (auto server : tempList) + { + ServerList::InsertRequest(server.addr, false); + } + } + } + + void ServerList::RefreshVisibleList(UIScript::Token) + { + Dvar::Var("ui_serverSelected").set(false); + + ServerList::VisibleList.clear(); + + auto list = ServerList::GetList(); + if (!list) return; + + // Refresh entirely, if there is no entry in the list + if (list->empty()) + { + ServerList::Refresh(UIScript::Token()); + return; + } + + bool ui_browserShowFull = Dvar::Var("ui_browserShowFull").get(); + bool ui_browserShowEmpty = Dvar::Var("ui_browserShowEmpty").get(); + int ui_browserShowHardcore = Dvar::Var("ui_browserKillcam").get(); + int ui_browserShowPassword = Dvar::Var("ui_browserShowPassword").get(); + int ui_browserMod = Dvar::Var("ui_browserMod").get(); + int ui_joinGametype = Dvar::Var("ui_joinGametype").get(); + + for (unsigned int i = 0; i < list->size(); ++i) + { + ServerList::ServerInfo* info = &(*list)[i]; + + // Filter full servers + if (!ui_browserShowFull && info->clients >= info->maxClients) continue; + + // Filter empty servers + if (!ui_browserShowEmpty && info->clients <= 0) continue; + + // Filter hardcore servers + if ((ui_browserShowHardcore == 0 && info->hardcore) || (ui_browserShowHardcore == 1 && !info->hardcore)) continue; + + // Filter servers with password + if ((ui_browserShowPassword == 0 && info->password) || (ui_browserShowPassword == 1 && !info->password)) continue; + + // Don't show modded servers + if ((ui_browserMod == 0 && info->mod.size()) || (ui_browserMod == 1 && !info->mod.size())) continue; + + // Filter by gametype + if (ui_joinGametype > 0 && (ui_joinGametype -1) < *Game::gameTypeCount && Game::gameTypes[(ui_joinGametype - 1)].gameType != info->gametype) continue; + + ServerList::VisibleList.push_back(i); + } + + ServerList::SortList(); + } + + void ServerList::Refresh(UIScript::Token) + { + Dvar::Var("ui_serverSelected").set(false); + Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0"); + +// ServerList::OnlineList.clear(); +// ServerList::OfflineList.clear(); +// ServerList::FavouriteList.clear(); + + auto list = ServerList::GetList(); + if (list) list->clear(); + + ServerList::VisibleList.clear(); + ServerList::RefreshContainer.mutex.lock(); + ServerList::RefreshContainer.servers.clear(); + ServerList::RefreshContainer.sendCount = 0; + ServerList::RefreshContainer.sentCount = 0; + ServerList::RefreshContainer.mutex.unlock(); + + if (ServerList::IsOfflineList()) + { + Discovery::Perform(); + } + else if (ServerList::IsOnlineList()) + { +#ifdef USE_LEGACY_SERVER_LIST + ServerList::RefreshContainer.awatingList = true; + ServerList::RefreshContainer.awaitTime = Game::Sys_Milliseconds(); + + int masterPort = Dvar::Var("masterPort").get(); + const char* masterServerName = Dvar::Var("masterServerName").get(); + + ServerList::RefreshContainer.host = Network::Address(Utils::String::VA("%s:%u", masterServerName, masterPort)); + + Logger::Print("Sending serverlist request to master: %s:%u\n", masterServerName, masterPort); + + Network::SendCommand(ServerList::RefreshContainer.host, "getservers", Utils::String::VA("IW4 %i full empty", PROTOCOL)); + //Network::SendCommand(ServerList::RefreshContainer.Host, "getservers", "0 full empty"); +#else + Node::SyncNodeList(); +#endif + } + else if (ServerList::IsFavouriteList()) + { + ServerList::LoadFavourties(); + } + } + + void ServerList::StoreFavourite(std::string server) + { + //json11::Json::parse() + std::vector servers; + + if (Utils::IO::FileExists("players/favourites.json")) + { + std::string data = Utils::IO::ReadFile("players/favourites.json"); + json11::Json object = json11::Json::parse(data, data); + + if (!object.is_array()) + { + Logger::Print("Favourites storage file is invalid!\n"); + Game::MessageBox("Favourites storage file is invalid!", "Error"); + return; + } + + auto storedServers = object.array_items(); + + for (unsigned int i = 0; i < storedServers.size(); ++i) + { + if (!storedServers[i].is_string()) continue; + if (storedServers[i].string_value() == server) + { + Game::MessageBox("Server already marked as favourite.", "Error"); + return; + } + + servers.push_back(storedServers[i].string_value()); + } + } + + servers.push_back(server); + + json11::Json data = json11::Json(servers); + Utils::IO::WriteFile("players/favourites.json", data.dump()); + Game::MessageBox("Server added to favourites.", "Success"); + } + + void ServerList::LoadFavourties() + { + if (ServerList::IsFavouriteList() && Utils::IO::FileExists("players/favourites.json")) + { + auto list = ServerList::GetList(); + if (list) list->clear(); + + std::string data = Utils::IO::ReadFile("players/favourites.json"); + json11::Json object = json11::Json::parse(data, data); + + if (!object.is_array()) + { + Logger::Print("Favourites storage file is invalid!\n"); + Game::MessageBox("Favourites storage file is invalid!", "Error"); + return; + } + + auto servers = object.array_items(); + + for (unsigned int i = 0; i < servers.size(); ++i) + { + if(!servers[i].is_string()) continue; + ServerList::InsertRequest(servers[i].string_value(), true); + } + } + } + + void ServerList::InsertRequest(Network::Address address, bool acquireMutex) + { + if (acquireMutex) ServerList::RefreshContainer.mutex.lock(); + + ServerList::Container::ServerContainer container; + container.sent = false; + container.target = address; + + bool alreadyInserted = false; + for (auto &server : ServerList::RefreshContainer.servers) + { + if (server.target == container.target) + { + alreadyInserted = true; + break; + } + } + + if (!alreadyInserted) + { + ServerList::RefreshContainer.servers.push_back(container); + + auto list = ServerList::GetList(); + if (list) + { + for (auto server : *list) + { + if (server.addr == container.target) + { + --ServerList::RefreshContainer.sendCount; + --ServerList::RefreshContainer.sentCount; + break; + } + } + } + + ++ServerList::RefreshContainer.sendCount; + } + + if (acquireMutex) ServerList::RefreshContainer.mutex.unlock(); + } + + void ServerList::Insert(Network::Address address, Utils::InfoString info) + { + std::lock_guard _(ServerList::RefreshContainer.mutex); + + for (auto i = ServerList::RefreshContainer.servers.begin(); i != ServerList::RefreshContainer.servers.end();) + { + // Our desired server + if (i->target == address && i->sent) + { + // Challenge did not match + if (i->challenge != info.get("challenge")) + { + // Shall we remove the server from the queue? + // Better not, it might send a second response with the correct challenge. + // This might happen when users refresh twice (or more often) in a short period of time + break; + } + + ServerInfo server; + server.hostname = info.get("hostname"); + server.mapname = info.get("mapname"); + server.gametype = info.get("gametype"); + server.shortversion = info.get("shortversion"); + server.mod = info.get("fs_game"); + server.matchType = atoi(info.get("matchtype").data()); + server.clients = atoi(info.get("clients").data()); + server.securityLevel = atoi(info.get("securityLevel").data()); + server.maxClients = atoi(info.get("sv_maxclients").data()); + server.password = (atoi(info.get("isPrivate").data()) != 0); + server.hardcore = (atoi(info.get("hc").data()) != 0); + server.svRunning = (atoi(info.get("sv_running").data()) != 0); + server.ping = (Game::Sys_Milliseconds() - i->sendTime); + server.addr = address; + + // Remove server from queue + i = ServerList::RefreshContainer.servers.erase(i); + + // Check if already inserted and remove + auto list = ServerList::GetList(); + if (!list) return; + + unsigned int k = 0; + for (auto j = list->begin(); j != list->end(); ++k) + { + if (j->addr == address) + { + j = list->erase(j); + } + else + { + ++j; + } + } + + // Also remove from visible list + for (auto j = ServerList::VisibleList.begin(); j != ServerList::VisibleList.end();) + { + if (*j == k) + { + j = ServerList::VisibleList.erase(j); + } + else + { + ++j; + } + } + + if (info.get("gamename") == "IW4" + && server.matchType +#ifndef DEBUG + && server.shortversion == SHORTVERSION +#endif + ) + { + auto lList = ServerList::GetList(); + + if (lList) + { + lList->push_back(server); + ServerList::RefreshVisibleList(UIScript::Token()); + } + } + + break; + } + else + { + ++i; + } + } + } + + ServerList::ServerInfo* ServerList::GetCurrentServer() + { + return ServerList::GetServer(ServerList::CurrentServer); + } + + void ServerList::SortList() + { + qsort(ServerList::VisibleList.data(), ServerList::VisibleList.size(), sizeof(int), [] (const void* first, const void* second) + { + const unsigned int server1 = *static_cast(first); + const unsigned int server2 = *static_cast(second); + + ServerInfo* info1 = nullptr; + ServerInfo* info2 = nullptr; + + auto list = ServerList::GetList(); + if (!list) return 0; + + if (list->size() > server1) info1 = &(*list)[server1]; + if (list->size() > server2) info2 = &(*list)[server2]; + + if (!info1) return 1; + if (!info2) return -1; + + // Numerical comparisons + if (ServerList::SortKey == ServerList::Column::Ping) + { + return ((info1->ping - info2->ping) * (ServerList::SortAsc ? 1 : -1)); + } + else if (ServerList::SortKey == ServerList::Column::Players) + { + return ((info1->clients - info2->clients) * (ServerList::SortAsc ? 1 : -1)); + } + + std::string text1 = Colors::Strip(ServerList::GetServerText(info1, ServerList::SortKey)); + std::string text2 = Colors::Strip(ServerList::GetServerText(info2, ServerList::SortKey)); + + // ASCII-based comparison + return (text1.compare(text2) * (ServerList::SortAsc ? 1 : -1)); + }); + } + + ServerList::ServerInfo* ServerList::GetServer(unsigned int index) + { + if (ServerList::VisibleList.size() > index) + { + auto list = ServerList::GetList(); + if (!list) return nullptr; + + if (list->size() > ServerList::VisibleList[index]) + { + return &(*list)[ServerList::VisibleList[index]]; + } + } + + return nullptr; + } + + void ServerList::Frame() + { + // This is bad practice and might even cause undefined behaviour! + if (!ServerList::RefreshContainer.mutex.try_lock()) return; + + if (ServerList::RefreshContainer.awatingList) + { + // Check if we haven't got a response within 10 seconds + if (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime > 5000) + { + ServerList::RefreshContainer.awatingList = false; + + Logger::Print("We haven't received a response from the master within %d seconds!\n", (Game::Sys_Milliseconds() - ServerList::RefreshContainer.awaitTime) / 1000); + } + } + + // Send requests to 10 servers each frame + int SendServers = 10; + + for (unsigned int i = 0; i < ServerList::RefreshContainer.servers.size(); ++i) + { + ServerList::Container::ServerContainer* server = &ServerList::RefreshContainer.servers[i]; + if (server->sent) continue; + + // Found server we can send a request to + server->sent = true; + SendServers--; + + server->sendTime = Game::Sys_Milliseconds(); + server->challenge = Utils::String::VA("%X", Utils::Cryptography::Rand::GenerateInt()); + + ++ServerList::RefreshContainer.sentCount; + + Network::SendCommand(server->target, "getinfo", server->challenge); + + // Display in the menu, like in COD4 + Localization::Set("MPUI_SERVERQUERIED", Utils::String::VA("Sent requests: %d/%d", ServerList::RefreshContainer.sentCount, ServerList::RefreshContainer.sendCount)); + + if (SendServers <= 0) break; + } + + ServerList::RefreshContainer.mutex.unlock(); + } + + void ServerList::UpdateSource() + { + Dvar::Var netSource("ui_netSource"); + + int source = netSource.get(); + + if (++source > netSource.get()->max.i) + { + source = 0; + } + + netSource.set(source); + + ServerList::RefreshVisibleList(UIScript::Token()); + } + + void ServerList::UpdateGameType() + { + Dvar::Var joinGametype("ui_joinGametype"); + + int gametype = joinGametype.get(); + + if (++gametype > *Game::gameTypeCount) + { + gametype = 0; + } + + joinGametype.set(gametype); + + ServerList::RefreshVisibleList(UIScript::Token()); + } + + ServerList::ServerList() + { + ServerList::OnlineList.clear(); + ServerList::VisibleList.clear(); + + Dvar::OnInit([] () + { + Dvar::Register("ui_serverSelected", false, Game::dvar_flag::DVAR_FLAG_NONE, "Whether a server has been selected in the serverlist"); + Dvar::Register("ui_serverSelectedMap", "mp_afghan", Game::dvar_flag::DVAR_FLAG_NONE, "Map of the selected server"); + }); + + Localization::Set("MPUI_SERVERQUERIED", "Sent requests: 0/0"); + + Network::Handle("getServersResponse", [] (Network::Address address, std::string data) + { + if (ServerList::RefreshContainer.host != address) return; // Only parse from host we sent to + + ServerList::RefreshContainer.awatingList = false; + + std::lock_guard _(ServerList::RefreshContainer.mutex); + + int offset = 0; + int count = ServerList::RefreshContainer.servers.size(); + ServerList::MasterEntry* entry = nullptr; + + // Find first entry + do + { + entry = reinterpret_cast(const_cast(data.data()) + offset++); + } + while (!entry->HasSeparator() && !entry->IsEndToken()); + + for (int i = 0; !entry[i].IsEndToken() && entry[i].HasSeparator(); ++i) + { + Network::Address serverAddr = address; + serverAddr.setIP(entry[i].ip); + serverAddr.setPort(ntohs(entry[i].port)); + serverAddr.setType(Game::NA_IP); + + ServerList::InsertRequest(serverAddr, false); + } + + Logger::Print("Parsed %d servers from master\n", ServerList::RefreshContainer.servers.size() - count); + }); + + // Set default masterServerName + port and save it +#ifdef USE_LEGACY_SERVER_LIST + Utils::Hook::Set(0x60AD92, "localhost"); + Utils::Hook::Set(0x60AD90, Game::dvar_flag::DVAR_FLAG_SAVED); // masterServerName + Utils::Hook::Set(0x60ADC6, Game::dvar_flag::DVAR_FLAG_SAVED); // masterPort +#endif + + // Add server list feeder + UIFeeder::Add(2.0f, ServerList::GetServerCount, ServerList::GetServerText, ServerList::SelectServer); + + // Add required UIScripts + UIScript::Add("UpdateFilter", ServerList::RefreshVisibleList); + UIScript::Add("RefreshFilter", ServerList::UpdateVisibleList); + + UIScript::Add("RefreshServers", ServerList::Refresh); + UIScript::Add("JoinServer", [] (UIScript::Token) + { + ServerList::ServerInfo* info = ServerList::GetServer(ServerList::CurrentServer); + + if (info) + { + Party::Connect(info->addr); + } + }); + UIScript::Add("ServerSort", [] (UIScript::Token token) + { + int key = token.get(); + + if (ServerList::SortKey == key) + { + ServerList::SortAsc = !ServerList::SortAsc; + } + else + { + ServerList::SortKey = key; + ServerList::SortAsc = true; + } + + Logger::Print("Sorting server list by token: %d\n", ServerList::SortKey); + ServerList::SortList(); + }); + UIScript::Add("CreateListFavorite", [] (UIScript::Token) + { + ServerList::ServerInfo* info = ServerList::GetCurrentServer(); + + if (info) + { + ServerList::StoreFavourite(info->addr.getString()); + } + }); + UIScript::Add("CreateFavorite", [] (UIScript::Token) + { + ServerList::StoreFavourite(Dvar::Var("ui_favoriteAddress").get()); + }); + UIScript::Add("CreateCurrentServerFavorite", [] (UIScript::Token) + { + if (Dvar::Var("cl_ingame").get()) + { + std::string addressText = Network::Address(*Game::connectedHost).getString(); + if (addressText != "0.0.0.0:0" && addressText != "loopback") + { + ServerList::StoreFavourite(addressText); + } + } + }); + + // Add required ownerDraws + UIScript::AddOwnerDraw(220, ServerList::UpdateSource); + UIScript::AddOwnerDraw(253, ServerList::UpdateGameType); + + // Add frame callback + Renderer::OnFrame(ServerList::Frame); + + // This is placed here in case the anticheat has been disabled! +#if !defined(DEBUG) && !defined(DISABLE_ANTICHEAT) + Renderer::OnFrame(AntiCheat::ReadIntegrityCheck); +#endif + } + + ServerList::~ServerList() + { + ServerList::OnlineList.clear(); + ServerList::OfflineList.clear(); + ServerList::FavouriteList.clear(); + ServerList::VisibleList.clear(); + + ServerList::RefreshContainer.mutex.lock(); + ServerList::RefreshContainer.awatingList = false; + ServerList::RefreshContainer.servers.clear(); + ServerList::RefreshContainer.mutex.unlock(); + } +} diff --git a/src/Components/Modules/ServerList.hpp b/src/Components/Modules/ServerList.hpp index 3ea93e9c..948ac78a 100644 --- a/src/Components/Modules/ServerList.hpp +++ b/src/Components/Modules/ServerList.hpp @@ -1,135 +1,135 @@ -namespace Components -{ - class ServerList : public Component - { - public: - typedef int(SortCallback)(const void*, const void*); - - class ServerInfo - { - public: - Network::Address addr; - std::string hostname; - std::string mapname; - std::string gametype; - std::string mod; - std::string shortversion; - int clients; - int maxClients; - bool password; - int ping; - int matchType; - int securityLevel; - bool hardcore; - bool svRunning; - }; - - ServerList(); - ~ServerList(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "ServerList"; }; -#endif - - static void Refresh(UIScript::Token); - static void RefreshVisibleList(UIScript::Token); - static void UpdateVisibleList(UIScript::Token); - static void InsertRequest(Network::Address address, bool acquireMutex = true); - static void Insert(Network::Address address, Utils::InfoString info); - - static ServerInfo* GetCurrentServer(); - - static bool IsFavouriteList(); - static bool IsOfflineList(); - static bool IsOnlineList(); - - private: - enum Column - { - Password, - Matchtype, - Hostname, - Mapname, - Players, - Gametype, - Mod, - Ping, - }; - -#pragma pack(push, 1) - union MasterEntry - { - char token[7]; - struct - { - uint32_t ip; - uint16_t port; - }; - - bool IsEndToken() - { - // End of transmission or file token - return (token[0] == 'E' && token[1] == 'O' && (token[2] == 'T' || token[2] == 'F')); - } - - bool HasSeparator() - { - return (token[6] == '\\'); - } - }; -#pragma pack(pop) - - class Container - { - public: - class ServerContainer - { - public: - bool sent; - int sendTime; - std::string challenge; - Network::Address target; - }; - - bool awatingList; - int awaitTime; - - int sentCount; - int sendCount; - - Network::Address host; - std::vector servers; - std::mutex mutex; - }; - - static unsigned int GetServerCount(); - static const char* GetServerText(unsigned int index, int column); - static const char* GetServerText(ServerInfo* server, int column); - static void SelectServer(unsigned int index); - - static void UpdateSource(); - static void UpdateGameType(); - - static void Frame(); - - static void SortList(); - - static void LoadFavourties(); - static void StoreFavourite(std::string server); - - static ServerInfo* GetServer(unsigned int index); - static std::vector* GetList(); - - static int SortKey; - static bool SortAsc; - - static unsigned int CurrentServer; - static Container RefreshContainer; - - static std::vector OnlineList; - static std::vector OfflineList; - static std::vector FavouriteList; - - static std::vector VisibleList; - }; -} +namespace Components +{ + class ServerList : public Component + { + public: + typedef int(SortCallback)(const void*, const void*); + + class ServerInfo + { + public: + Network::Address addr; + std::string hostname; + std::string mapname; + std::string gametype; + std::string mod; + std::string shortversion; + int clients; + int maxClients; + bool password; + int ping; + int matchType; + int securityLevel; + bool hardcore; + bool svRunning; + }; + + ServerList(); + ~ServerList(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "ServerList"; }; +#endif + + static void Refresh(UIScript::Token); + static void RefreshVisibleList(UIScript::Token); + static void UpdateVisibleList(UIScript::Token); + static void InsertRequest(Network::Address address, bool acquireMutex = true); + static void Insert(Network::Address address, Utils::InfoString info); + + static ServerInfo* GetCurrentServer(); + + static bool IsFavouriteList(); + static bool IsOfflineList(); + static bool IsOnlineList(); + + private: + enum Column + { + Password, + Matchtype, + Hostname, + Mapname, + Players, + Gametype, + Mod, + Ping, + }; + +#pragma pack(push, 1) + union MasterEntry + { + char token[7]; + struct + { + uint32_t ip; + uint16_t port; + }; + + bool IsEndToken() + { + // End of transmission or file token + return (token[0] == 'E' && token[1] == 'O' && (token[2] == 'T' || token[2] == 'F')); + } + + bool HasSeparator() + { + return (token[6] == '\\'); + } + }; +#pragma pack(pop) + + class Container + { + public: + class ServerContainer + { + public: + bool sent; + int sendTime; + std::string challenge; + Network::Address target; + }; + + bool awatingList; + int awaitTime; + + int sentCount; + int sendCount; + + Network::Address host; + std::vector servers; + std::mutex mutex; + }; + + static unsigned int GetServerCount(); + static const char* GetServerText(unsigned int index, int column); + static const char* GetServerText(ServerInfo* server, int column); + static void SelectServer(unsigned int index); + + static void UpdateSource(); + static void UpdateGameType(); + + static void Frame(); + + static void SortList(); + + static void LoadFavourties(); + static void StoreFavourite(std::string server); + + static ServerInfo* GetServer(unsigned int index); + static std::vector* GetList(); + + static int SortKey; + static bool SortAsc; + + static unsigned int CurrentServer; + static Container RefreshContainer; + + static std::vector OnlineList; + static std::vector OfflineList; + static std::vector FavouriteList; + + static std::vector VisibleList; + }; +} diff --git a/src/Components/Modules/Singleton.cpp b/src/Components/Modules/Singleton.cpp index b5ecd39a..a7a01585 100644 --- a/src/Components/Modules/Singleton.cpp +++ b/src/Components/Modules/Singleton.cpp @@ -1,32 +1,32 @@ -#include "STDInclude.hpp" - -namespace Components -{ - bool Singleton::FirstInstance = true; - - bool Singleton::IsFirstInstance() - { - return Singleton::FirstInstance; - } - - Singleton::Singleton() - { - if (Flags::HasFlag("version")) - { - printf("IW4x " VERSION " (built " __DATE__ " " __TIME__ ")\n"); - printf("%d\n", REVISION); - ExitProcess(0); - } - - Console::FreeNativeConsole(); - - if (Loader::PerformingUnitTests() || Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return; - - Singleton::FirstInstance = (CreateMutexA(NULL, FALSE, "iw4x_mutex") && GetLastError() != ERROR_ALREADY_EXISTS); - - if (!Singleton::FirstInstance && !ConnectProtocol::Used() && MessageBoxA(0, "Do you want to start another instance?", "Game already running", MB_ICONEXCLAMATION | MB_YESNO) == IDNO) - { - ExitProcess(0); - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + bool Singleton::FirstInstance = true; + + bool Singleton::IsFirstInstance() + { + return Singleton::FirstInstance; + } + + Singleton::Singleton() + { + if (Flags::HasFlag("version")) + { + printf("IW4x " VERSION " (built " __DATE__ " " __TIME__ ")\n"); + printf("%d\n", REVISION); + ExitProcess(0); + } + + Console::FreeNativeConsole(); + + if (Loader::PerformingUnitTests() || Dedicated::IsEnabled() || ZoneBuilder::IsEnabled()) return; + + Singleton::FirstInstance = (CreateMutexA(NULL, FALSE, "iw4x_mutex") && GetLastError() != ERROR_ALREADY_EXISTS); + + if (!Singleton::FirstInstance && !ConnectProtocol::Used() && MessageBoxA(0, "Do you want to start another instance?", "Game already running", MB_ICONEXCLAMATION | MB_YESNO) == IDNO) + { + ExitProcess(0); + } + } +} diff --git a/src/Components/Modules/Singleton.hpp b/src/Components/Modules/Singleton.hpp index f867028d..64ccd67e 100644 --- a/src/Components/Modules/Singleton.hpp +++ b/src/Components/Modules/Singleton.hpp @@ -1,17 +1,17 @@ -namespace Components -{ - class Singleton : public Component - { - public: - Singleton(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Singleton"; }; -#endif - - static bool IsFirstInstance(); - - private: - static bool FirstInstance; - }; -} +namespace Components +{ + class Singleton : public Component + { + public: + Singleton(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Singleton"; }; +#endif + + static bool IsFirstInstance(); + + private: + static bool FirstInstance; + }; +} diff --git a/src/Components/Modules/Slowmotion.cpp b/src/Components/Modules/Slowmotion.cpp index 7b4293a1..ad897d43 100644 --- a/src/Components/Modules/Slowmotion.cpp +++ b/src/Components/Modules/Slowmotion.cpp @@ -1,86 +1,86 @@ -#include "STDInclude.hpp" - -namespace Components -{ - int SlowMotion::Delay = 0; - - void SlowMotion::ApplySlowMotion(int timePassed) - { - if (SlowMotion::Delay <= 0) - { - Utils::Hook::Call(0x60B2D0)(timePassed); - } - else - { - SlowMotion::Delay -= timePassed; - } - } - - __declspec(naked) void SlowMotion::ApplySlowMotionStub() - { - __asm - { - pushad - push [esp + 20h] - - call SlowMotion::ApplySlowMotion - - pop ecx - popad - - retn - } - } - - void SlowMotion::SetSlowMotion() - { - int duration = 1000; - float start = Game::Scr_GetFloat(0); - float end = 1.0f; - - if (Game::Scr_GetNumParam() >= 2) - { - end = Game::Scr_GetFloat(1); - } - - if (Game::Scr_GetNumParam() >= 3) - { - duration = static_cast(Game::Scr_GetFloat(2) * 1000.0); - } - - int delay = 0; - - if (start > end) - { - if (duration < 150) - { - delay = duration; - } - else - { - delay = 150; - } - } - - duration = duration - delay; - - Game::Com_SetSlowMotion(start, end, duration); - SlowMotion::Delay = delay; - - // set snapshot num to 1 behind (T6 does this, why shouldn't we?) - for (int i = 0; i < *Game::svs_numclients; ++i) - { - Game::svs_clients[i].snapNum = (*reinterpret_cast(0x31D9384)) - 1; - } - } - - SlowMotion::SlowMotion() - { - if (Dedicated::IsEnabled()) - { - SlowMotion::Delay = 0; - Utils::Hook(0x5F5FF2, SlowMotion::SetSlowMotion, HOOK_JUMP).install()->quick(); - Utils::Hook(0x60B38A, SlowMotion::ApplySlowMotionStub, HOOK_CALL).install()->quick(); - } - } -} +#include "STDInclude.hpp" + +namespace Components +{ + int SlowMotion::Delay = 0; + + void SlowMotion::ApplySlowMotion(int timePassed) + { + if (SlowMotion::Delay <= 0) + { + Utils::Hook::Call(0x60B2D0)(timePassed); + } + else + { + SlowMotion::Delay -= timePassed; + } + } + + __declspec(naked) void SlowMotion::ApplySlowMotionStub() + { + __asm + { + pushad + push [esp + 20h] + + call SlowMotion::ApplySlowMotion + + pop ecx + popad + + retn + } + } + + void SlowMotion::SetSlowMotion() + { + int duration = 1000; + float start = Game::Scr_GetFloat(0); + float end = 1.0f; + + if (Game::Scr_GetNumParam() >= 2) + { + end = Game::Scr_GetFloat(1); + } + + if (Game::Scr_GetNumParam() >= 3) + { + duration = static_cast(Game::Scr_GetFloat(2) * 1000.0); + } + + int delay = 0; + + if (start > end) + { + if (duration < 150) + { + delay = duration; + } + else + { + delay = 150; + } + } + + duration = duration - delay; + + Game::Com_SetSlowMotion(start, end, duration); + SlowMotion::Delay = delay; + + // set snapshot num to 1 behind (T6 does this, why shouldn't we?) + for (int i = 0; i < *Game::svs_numclients; ++i) + { + Game::svs_clients[i].snapNum = (*reinterpret_cast(0x31D9384)) - 1; + } + } + + SlowMotion::SlowMotion() + { + if (Dedicated::IsEnabled()) + { + SlowMotion::Delay = 0; + Utils::Hook(0x5F5FF2, SlowMotion::SetSlowMotion, HOOK_JUMP).install()->quick(); + Utils::Hook(0x60B38A, SlowMotion::ApplySlowMotionStub, HOOK_CALL).install()->quick(); + } + } +} diff --git a/src/Components/Modules/Slowmotion.hpp b/src/Components/Modules/Slowmotion.hpp index 22ed39c4..1c70539f 100644 --- a/src/Components/Modules/Slowmotion.hpp +++ b/src/Components/Modules/Slowmotion.hpp @@ -1,22 +1,22 @@ -#define BUTTON_FLAG_LEANLEFT 0x40 -#define BUTTON_FLAG_LEANRIGHT 0x80 - -namespace Components -{ - class SlowMotion : public Component - { - public: - SlowMotion(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "SlowMotion"; }; -#endif - - private: - static int Delay; - - static void SetSlowMotion(); - static void ApplySlowMotion(int timePassed); - static void ApplySlowMotionStub(); - }; -} +#define BUTTON_FLAG_LEANLEFT 0x40 +#define BUTTON_FLAG_LEANRIGHT 0x80 + +namespace Components +{ + class SlowMotion : public Component + { + public: + SlowMotion(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "SlowMotion"; }; +#endif + + private: + static int Delay; + + static void SetSlowMotion(); + static void ApplySlowMotion(int timePassed); + static void ApplySlowMotionStub(); + }; +} diff --git a/src/Components/Modules/StringTable.cpp b/src/Components/Modules/StringTable.cpp index 07cdee5e..97401880 100644 --- a/src/Components/Modules/StringTable.cpp +++ b/src/Components/Modules/StringTable.cpp @@ -1,98 +1,98 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Utils::Memory::Allocator StringTable::MemAllocator; - std::unordered_map StringTable::StringTableMap; - - int StringTable::Hash(const char* data) - { - int hash = 0; - - while (*data != 0) - { - hash = tolower(*data) + (31 * hash); - - ++data; - } - - return hash; - } - - Game::StringTable* StringTable::LoadObject(std::string filename) - { - filename = Utils::String::ToLower(filename); - - Game::StringTable* table = nullptr; - FileSystem::File rawTable(filename); - - if (rawTable.exists()) - { - Utils::CSV parsedTable(rawTable.getBuffer(), false, false); - - table = StringTable::MemAllocator.allocate(); - - if (table) - { - table->name = StringTable::MemAllocator.duplicateString(filename); - table->columnCount = parsedTable.getColumns(); - table->rowCount = parsedTable.getRows(); - - table->values = StringTable::MemAllocator.allocateArray(table->columnCount * table->rowCount); - - if (!table->values) - { - return nullptr; - } - - for (int i = 0; i < table->rowCount; ++i) - { - for (int j = 0; j < table->columnCount; ++j) - { - std::string value = parsedTable.getElementAt(i, j); - - Game::StringTableCell* cell = &table->values[i * table->columnCount + j]; - cell->hash = StringTable::Hash(value.data()); - cell->string = StringTable::MemAllocator.duplicateString(value); - //if (!cell->string) cell->string = ""; // We have to assume it allocated successfully - } - } - - StringTable::StringTableMap[filename] = table; - } - } - else - { - StringTable::StringTableMap[filename] = nullptr; - } - - return table; - } - - StringTable::StringTable() - { - AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_STRINGTABLE, [] (Game::XAssetType, std::string filename) - { - Game::XAssetHeader header = { 0 }; - - filename = Utils::String::ToLower(filename); - - if (StringTable::StringTableMap.find(filename) != StringTable::StringTableMap.end()) - { - header.stringTable = StringTable::StringTableMap[filename]; - } - else - { - header.stringTable = StringTable::LoadObject(filename); - } - - return header; - }); - } - - StringTable::~StringTable() - { - StringTable::StringTableMap.clear(); - StringTable::MemAllocator.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Utils::Memory::Allocator StringTable::MemAllocator; + std::unordered_map StringTable::StringTableMap; + + int StringTable::Hash(const char* data) + { + int hash = 0; + + while (*data != 0) + { + hash = tolower(*data) + (31 * hash); + + ++data; + } + + return hash; + } + + Game::StringTable* StringTable::LoadObject(std::string filename) + { + filename = Utils::String::ToLower(filename); + + Game::StringTable* table = nullptr; + FileSystem::File rawTable(filename); + + if (rawTable.exists()) + { + Utils::CSV parsedTable(rawTable.getBuffer(), false, false); + + table = StringTable::MemAllocator.allocate(); + + if (table) + { + table->name = StringTable::MemAllocator.duplicateString(filename); + table->columnCount = parsedTable.getColumns(); + table->rowCount = parsedTable.getRows(); + + table->values = StringTable::MemAllocator.allocateArray(table->columnCount * table->rowCount); + + if (!table->values) + { + return nullptr; + } + + for (int i = 0; i < table->rowCount; ++i) + { + for (int j = 0; j < table->columnCount; ++j) + { + std::string value = parsedTable.getElementAt(i, j); + + Game::StringTableCell* cell = &table->values[i * table->columnCount + j]; + cell->hash = StringTable::Hash(value.data()); + cell->string = StringTable::MemAllocator.duplicateString(value); + //if (!cell->string) cell->string = ""; // We have to assume it allocated successfully + } + } + + StringTable::StringTableMap[filename] = table; + } + } + else + { + StringTable::StringTableMap[filename] = nullptr; + } + + return table; + } + + StringTable::StringTable() + { + AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_STRINGTABLE, [] (Game::XAssetType, std::string filename) + { + Game::XAssetHeader header = { 0 }; + + filename = Utils::String::ToLower(filename); + + if (StringTable::StringTableMap.find(filename) != StringTable::StringTableMap.end()) + { + header.stringTable = StringTable::StringTableMap[filename]; + } + else + { + header.stringTable = StringTable::LoadObject(filename); + } + + return header; + }); + } + + StringTable::~StringTable() + { + StringTable::StringTableMap.clear(); + StringTable::MemAllocator.clear(); + } +} diff --git a/src/Components/Modules/StringTable.hpp b/src/Components/Modules/StringTable.hpp index ad42762b..4fbb97a3 100644 --- a/src/Components/Modules/StringTable.hpp +++ b/src/Components/Modules/StringTable.hpp @@ -1,20 +1,20 @@ -namespace Components -{ - class StringTable : public Component - { - public: - StringTable(); - ~StringTable(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "StringTable"; }; -#endif - - private: - static Utils::Memory::Allocator MemAllocator; - static std::unordered_map StringTableMap; - - static int Hash(const char* data); - static Game::StringTable* LoadObject(std::string filename); - }; -} +namespace Components +{ + class StringTable : public Component + { + public: + StringTable(); + ~StringTable(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "StringTable"; }; +#endif + + private: + static Utils::Memory::Allocator MemAllocator; + static std::unordered_map StringTableMap; + + static int Hash(const char* data); + static Game::StringTable* LoadObject(std::string filename); + }; +} diff --git a/src/Components/Modules/StructuredData.cpp b/src/Components/Modules/StructuredData.cpp index f5f0d0ea..bb772992 100644 --- a/src/Components/Modules/StructuredData.cpp +++ b/src/Components/Modules/StructuredData.cpp @@ -1,218 +1,218 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Utils::Memory::Allocator StructuredData::MemAllocator; - - const char* StructuredData::EnumTranslation[ENUM_MAX] = - { - "features", - "weapons", - "attachements", - "challenges", - "camos", - "perks", - "killstreaks", - "accolades", - "cardicons", - "cardtitles", - "cardnameplates", - "teams", - "gametypes" - }; - - void StructuredData::PatchPlayerDataEnum(Game::StructuredDataDef* data, StructuredData::PlayerDataType type, std::vector& entries) - { - if (!data || type >= StructuredData::PlayerDataType::ENUM_MAX) return; - - Game::StructuredDataEnum* dataEnum = &data->enums[type]; - - // Build index-sorted data vector - std::vector dataVector; - for (int i = 0; i < dataEnum->numIndices; ++i) - { - int index = 0; - for (; index < dataEnum->numIndices; ++index) - { - if (dataEnum->indices[index].index == i) - { - break; - } - } - - dataVector.push_back(dataEnum->indices[index].key); - } - - // Rebase or add new entries - for (auto entry : entries) - { - const char* value = nullptr; - for (auto i = dataVector.begin(); i != dataVector.end(); ++i) - { - if (*i == entry) - { - value = *i; - dataVector.erase(i); - Logger::Print("Playerdatadef entry '%s' will be rebased!\n", value); - break; - } - } - - if (!value) value = StructuredData::MemAllocator.duplicateString(entry); - dataVector.push_back(value); - } - - // Map data back to the game structure - Game::StructuredDataEnumEntry* indices = StructuredData::MemAllocator.allocateArray(dataVector.size()); - for (unsigned int i = 0; i < dataVector.size(); ++i) - { - indices[i].index = i; - indices[i].key = dataVector[i]; - } - - // Sort alphabetically - qsort(indices, dataVector.size(), sizeof(Game::StructuredDataEnumEntry), [] (const void* first, const void* second) - { - const Game::StructuredDataEnumEntry* entry1 = reinterpret_cast(first); - const Game::StructuredDataEnumEntry* entry2 = reinterpret_cast(second); - - return std::string(entry1->key).compare(entry2->key); - }); - - // Apply our patches - dataEnum->numIndices = dataVector.size(); - dataEnum->indices = indices; - } - - StructuredData::StructuredData() - { - // Only execute this when building zones - if (!ZoneBuilder::IsEnabled()) return; - - AssetHandler::OnLoad([] (Game::XAssetType type, Game::XAssetHeader asset, std::string filename, bool* /*restrict*/) - { - // Only intercept playerdatadef loading - if (filename != "mp/playerdata.def" || type != Game::XAssetType::ASSET_TYPE_STRUCTUREDDATADEF) return; - - // Store asset - Game::StructuredDataDefSet* data = asset.structuredData; - if (!data) return; - - if (data->count != 1) - { - Logger::Error("PlayerDataDefSet contains more than 1 definition!"); - return; - } - - if (data->data[0].version != 155) - { - Logger::Error("Initial PlayerDataDef is not version 155, patching not possible!"); - return; - } - - std::map>> patchDefinitions; - - // First check if all versions are present - for (int i = 156;; ++i) - { - FileSystem::File definition(Utils::String::VA("%s/%d.json", filename.data(), i)); - if (!definition.exists()) break; - - std::vector> enumContainer; - - std::string errors; - json11::Json defData = json11::Json::parse(definition.getBuffer(), errors); - - if (!errors.empty()) - { - Logger::Error("Parsing patch file '%s' for PlayerDataDef version %d failed: %s", definition.getName().data(), i, errors.data()); - return; - } - - if (!defData.is_object()) - { - Logger::Error("PlayerDataDef patch for version %d is invalid!", i); - return; - } - - for (unsigned int pType = 0; pType < StructuredData::PlayerDataType::ENUM_MAX; ++pType) - { - auto enumData = defData[StructuredData::EnumTranslation[pType]]; - - std::vector entryData; - - if (enumData.is_array()) - { - for (auto rawEntry : enumData.array_items()) - { - if (rawEntry.is_string()) - { - entryData.push_back(rawEntry.string_value()); - } - } - } - - enumContainer.push_back(entryData); - } - - patchDefinitions[i] = enumContainer; - } - - // Nothing to patch - if (patchDefinitions.empty()) return; - - // Reallocate the definition - Game::StructuredDataDef* newData = StructuredData::MemAllocator.allocateArray(data->count + patchDefinitions.size()); - std::memcpy(&newData[patchDefinitions.size()], data->data, sizeof Game::StructuredDataDef * data->count); - - // Prepare the buffers - for (unsigned int i = 0; i < patchDefinitions.size(); ++i) - { - std::memcpy(&newData[i], data->data, sizeof Game::StructuredDataDef); - newData[i].version = (patchDefinitions.size() - i) + 155; - - // Reallocate the enum array - Game::StructuredDataEnum* newEnums = StructuredData::MemAllocator.allocateArray(data->data->numEnums); - std::memcpy(newEnums, data->data->enums, sizeof Game::StructuredDataEnum * data->data->numEnums); - newData[i].enums = newEnums; - } - - // Apply new data - data->data = newData; - data->count += patchDefinitions.size(); - - // Patch the definition - for (int i = 0; i < data->count; ++i) - { - // No need to patch version 155 - if (newData[i].version == 155) continue; - - if(patchDefinitions.find(newData[i].version) != patchDefinitions.end()) - { - auto patchData = patchDefinitions[newData[i].version]; - - // Invalid patch data - if (patchData.size() != StructuredData::PlayerDataType::ENUM_MAX) - { - Logger::Error("PlayerDataDef patch for version %d wasn't parsed correctly!", newData[i].version); - continue; - } - - // Apply the patch data - for (unsigned int pType = 0; pType < StructuredData::PlayerDataType::ENUM_MAX; ++pType) - { - if (!patchData[pType].empty()) - { - StructuredData::PatchPlayerDataEnum(&newData[i], static_cast(pType), patchData[pType]); - } - } - } - } - }); - } - - StructuredData::~StructuredData() - { - StructuredData::MemAllocator.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Utils::Memory::Allocator StructuredData::MemAllocator; + + const char* StructuredData::EnumTranslation[ENUM_MAX] = + { + "features", + "weapons", + "attachements", + "challenges", + "camos", + "perks", + "killstreaks", + "accolades", + "cardicons", + "cardtitles", + "cardnameplates", + "teams", + "gametypes" + }; + + void StructuredData::PatchPlayerDataEnum(Game::StructuredDataDef* data, StructuredData::PlayerDataType type, std::vector& entries) + { + if (!data || type >= StructuredData::PlayerDataType::ENUM_MAX) return; + + Game::StructuredDataEnum* dataEnum = &data->enums[type]; + + // Build index-sorted data vector + std::vector dataVector; + for (int i = 0; i < dataEnum->numIndices; ++i) + { + int index = 0; + for (; index < dataEnum->numIndices; ++index) + { + if (dataEnum->indices[index].index == i) + { + break; + } + } + + dataVector.push_back(dataEnum->indices[index].key); + } + + // Rebase or add new entries + for (auto entry : entries) + { + const char* value = nullptr; + for (auto i = dataVector.begin(); i != dataVector.end(); ++i) + { + if (*i == entry) + { + value = *i; + dataVector.erase(i); + Logger::Print("Playerdatadef entry '%s' will be rebased!\n", value); + break; + } + } + + if (!value) value = StructuredData::MemAllocator.duplicateString(entry); + dataVector.push_back(value); + } + + // Map data back to the game structure + Game::StructuredDataEnumEntry* indices = StructuredData::MemAllocator.allocateArray(dataVector.size()); + for (unsigned int i = 0; i < dataVector.size(); ++i) + { + indices[i].index = i; + indices[i].key = dataVector[i]; + } + + // Sort alphabetically + qsort(indices, dataVector.size(), sizeof(Game::StructuredDataEnumEntry), [] (const void* first, const void* second) + { + const Game::StructuredDataEnumEntry* entry1 = reinterpret_cast(first); + const Game::StructuredDataEnumEntry* entry2 = reinterpret_cast(second); + + return std::string(entry1->key).compare(entry2->key); + }); + + // Apply our patches + dataEnum->numIndices = dataVector.size(); + dataEnum->indices = indices; + } + + StructuredData::StructuredData() + { + // Only execute this when building zones + if (!ZoneBuilder::IsEnabled()) return; + + AssetHandler::OnLoad([] (Game::XAssetType type, Game::XAssetHeader asset, std::string filename, bool* /*restrict*/) + { + // Only intercept playerdatadef loading + if (filename != "mp/playerdata.def" || type != Game::XAssetType::ASSET_TYPE_STRUCTUREDDATADEF) return; + + // Store asset + Game::StructuredDataDefSet* data = asset.structuredData; + if (!data) return; + + if (data->count != 1) + { + Logger::Error("PlayerDataDefSet contains more than 1 definition!"); + return; + } + + if (data->data[0].version != 155) + { + Logger::Error("Initial PlayerDataDef is not version 155, patching not possible!"); + return; + } + + std::map>> patchDefinitions; + + // First check if all versions are present + for (int i = 156;; ++i) + { + FileSystem::File definition(Utils::String::VA("%s/%d.json", filename.data(), i)); + if (!definition.exists()) break; + + std::vector> enumContainer; + + std::string errors; + json11::Json defData = json11::Json::parse(definition.getBuffer(), errors); + + if (!errors.empty()) + { + Logger::Error("Parsing patch file '%s' for PlayerDataDef version %d failed: %s", definition.getName().data(), i, errors.data()); + return; + } + + if (!defData.is_object()) + { + Logger::Error("PlayerDataDef patch for version %d is invalid!", i); + return; + } + + for (unsigned int pType = 0; pType < StructuredData::PlayerDataType::ENUM_MAX; ++pType) + { + auto enumData = defData[StructuredData::EnumTranslation[pType]]; + + std::vector entryData; + + if (enumData.is_array()) + { + for (auto rawEntry : enumData.array_items()) + { + if (rawEntry.is_string()) + { + entryData.push_back(rawEntry.string_value()); + } + } + } + + enumContainer.push_back(entryData); + } + + patchDefinitions[i] = enumContainer; + } + + // Nothing to patch + if (patchDefinitions.empty()) return; + + // Reallocate the definition + Game::StructuredDataDef* newData = StructuredData::MemAllocator.allocateArray(data->count + patchDefinitions.size()); + std::memcpy(&newData[patchDefinitions.size()], data->data, sizeof Game::StructuredDataDef * data->count); + + // Prepare the buffers + for (unsigned int i = 0; i < patchDefinitions.size(); ++i) + { + std::memcpy(&newData[i], data->data, sizeof Game::StructuredDataDef); + newData[i].version = (patchDefinitions.size() - i) + 155; + + // Reallocate the enum array + Game::StructuredDataEnum* newEnums = StructuredData::MemAllocator.allocateArray(data->data->numEnums); + std::memcpy(newEnums, data->data->enums, sizeof Game::StructuredDataEnum * data->data->numEnums); + newData[i].enums = newEnums; + } + + // Apply new data + data->data = newData; + data->count += patchDefinitions.size(); + + // Patch the definition + for (int i = 0; i < data->count; ++i) + { + // No need to patch version 155 + if (newData[i].version == 155) continue; + + if(patchDefinitions.find(newData[i].version) != patchDefinitions.end()) + { + auto patchData = patchDefinitions[newData[i].version]; + + // Invalid patch data + if (patchData.size() != StructuredData::PlayerDataType::ENUM_MAX) + { + Logger::Error("PlayerDataDef patch for version %d wasn't parsed correctly!", newData[i].version); + continue; + } + + // Apply the patch data + for (unsigned int pType = 0; pType < StructuredData::PlayerDataType::ENUM_MAX; ++pType) + { + if (!patchData[pType].empty()) + { + StructuredData::PatchPlayerDataEnum(&newData[i], static_cast(pType), patchData[pType]); + } + } + } + } + }); + } + + StructuredData::~StructuredData() + { + StructuredData::MemAllocator.clear(); + } +} diff --git a/src/Components/Modules/StructuredData.hpp b/src/Components/Modules/StructuredData.hpp index 955027ee..8cf0ebbe 100644 --- a/src/Components/Modules/StructuredData.hpp +++ b/src/Components/Modules/StructuredData.hpp @@ -1,37 +1,37 @@ -namespace Components -{ - class StructuredData : public Component - { - public: - enum PlayerDataType - { - ENUM_FEATURES, - ENUM_WEAPONS, - ENUM_ATTACHEMENTS, - ENUM_CHALLENGES, - ENUM_CAMOS, - ENUM_PERKS, - ENUM_KILLSTREAKS, - ENUM_ACCOLADES, - ENUM_CARDICONS, - ENUM_CARDTITLES, - ENUM_CARDNAMEPLATES, - ENUM_TEAMS, - ENUM_GAMETYPES, - ENUM_MAX - }; - - StructuredData(); - ~StructuredData(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "StructuredData"; }; -#endif - - private: - static void PatchPlayerDataEnum(Game::StructuredDataDef* data, PlayerDataType type, std::vector& entries); - static Utils::Memory::Allocator MemAllocator; - - static const char* EnumTranslation[ENUM_MAX]; - }; -} +namespace Components +{ + class StructuredData : public Component + { + public: + enum PlayerDataType + { + ENUM_FEATURES, + ENUM_WEAPONS, + ENUM_ATTACHEMENTS, + ENUM_CHALLENGES, + ENUM_CAMOS, + ENUM_PERKS, + ENUM_KILLSTREAKS, + ENUM_ACCOLADES, + ENUM_CARDICONS, + ENUM_CARDTITLES, + ENUM_CARDNAMEPLATES, + ENUM_TEAMS, + ENUM_GAMETYPES, + ENUM_MAX + }; + + StructuredData(); + ~StructuredData(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "StructuredData"; }; +#endif + + private: + static void PatchPlayerDataEnum(Game::StructuredDataDef* data, PlayerDataType type, std::vector& entries); + static Utils::Memory::Allocator MemAllocator; + + static const char* EnumTranslation[ENUM_MAX]; + }; +} diff --git a/src/Components/Modules/Theatre.cpp b/src/Components/Modules/Theatre.cpp index 0b3228c7..b0702c19 100644 --- a/src/Components/Modules/Theatre.cpp +++ b/src/Components/Modules/Theatre.cpp @@ -1,383 +1,383 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Theatre::DemoInfo Theatre::CurrentInfo; - unsigned int Theatre::CurrentSelection; - std::vector Theatre::Demos; - - char Theatre::BaselineSnapshot[131072] = { 0 }; - int Theatre::BaselineSnapshotMsgLen; - int Theatre::BaselineSnapshotMsgOff; - - void Theatre::GamestateWriteStub(Game::msg_t* msg, char byte) - { - Game::MSG_WriteLong(msg, 0); - Game::MSG_WriteByte(msg, byte); - } - - void Theatre::RecordGamestateStub() - { - int sequence = (*Game::serverMessageSequence - 1); - Game::FS_Write(&sequence, 4, *Game::demoFile); - } - - void Theatre::StoreBaseline(PBYTE snapshotMsg) - { - // Store offset and length - Theatre::BaselineSnapshotMsgLen = *reinterpret_cast(snapshotMsg + 20); - Theatre::BaselineSnapshotMsgOff = *reinterpret_cast(snapshotMsg + 28) - 7; - - // Copy to our snapshot buffer - std::memcpy(Theatre::BaselineSnapshot, *reinterpret_cast(snapshotMsg + 8), *reinterpret_cast(snapshotMsg + 20)); - } - - __declspec(naked) void Theatre::BaselineStoreStub() - { - _asm - { - push edi - call Theatre::StoreBaseline - pop edi - - mov edx, 5ABEF5h - jmp edx - } - } - - void Theatre::WriteBaseline() - { - static char bufData[131072]; - static char cmpData[131072]; - - Game::msg_t buf; - - Game::MSG_Init(&buf, bufData, 131072); - Game::MSG_WriteData(&buf, &Theatre::BaselineSnapshot[Theatre::BaselineSnapshotMsgOff], Theatre::BaselineSnapshotMsgLen - Theatre::BaselineSnapshotMsgOff); - Game::MSG_WriteByte(&buf, 6); - - int compressedSize = Game::MSG_WriteBitsCompress(false, buf.data, cmpData, buf.cursize); - int fileCompressedSize = compressedSize + 4; - - int byte8 = 8; - char byte0 = 0; - - Game::FS_Write(&byte0, 1, *Game::demoFile); - Game::FS_Write(Game::serverMessageSequence, 4, *Game::demoFile); - Game::FS_Write(&fileCompressedSize, 4, *Game::demoFile); - Game::FS_Write(&byte8, 4, *Game::demoFile); - - for (int i = 0; i < compressedSize; i += 1024) - { - int size = std::min(compressedSize - i, 1024); - - if (i + size >= sizeof(cmpData)) - { - Logger::Print("Error: Writing compressed demo baseline exceeded buffer\n"); - break; - } - - Game::FS_Write(&cmpData[i], size, *Game::demoFile); - } - } - - __declspec(naked) void Theatre::BaselineToFileStub() - { - __asm - { - call Theatre::WriteBaseline - - // Restore overwritten operation - mov ecx, 0A5E9C4h - mov [ecx], 0 - - // Return to original code - mov ecx, 5A863Ah - jmp ecx - } - } - - __declspec(naked) void Theatre::AdjustTimeDeltaStub() - { - __asm - { - mov eax, Game::demoPlaying - mov eax, [eax] - test al, al - jz continue - - // delta doesn't drift for demos - retn - - continue: - mov eax, 5A1AD0h - jmp eax - } - } - - __declspec(naked) void Theatre::ServerTimedOutStub() - { - __asm - { - mov eax, Game::demoPlaying - mov eax, [eax] - test al, al - jz continue - - mov eax, 5A8E70h - jmp eax - - continue: - mov eax, 0B2BB90h - mov esi, 5A8E08h - jmp esi - } - } - - __declspec(naked) void Theatre::UISetActiveMenuStub() - { - __asm - { - mov eax, Game::demoPlaying - mov eax, [eax] - test al, al - jz continue - - mov eax, 4CB49Ch - jmp eax - - continue: - mov ecx, [esp + 10h] - push 10h - push ecx - mov eax, 4CB3F6h - jmp eax - } - } - - void Theatre::RecordStub(int channel, char* message, char* file) - { - Game::Com_Printf(channel, message, file); - - Theatre::CurrentInfo.name = file; - Theatre::CurrentInfo.mapname = Dvar::Var("mapname").get(); - Theatre::CurrentInfo.gametype = Dvar::Var("g_gametype").get(); - Theatre::CurrentInfo.author = Steam::SteamFriends()->GetPersonaName(); - Theatre::CurrentInfo.length = Game::Sys_Milliseconds(); - std::time(&Theatre::CurrentInfo.timeStamp); - } - - void Theatre::StopRecordStub(int channel, char* message) - { - Game::Com_Printf(channel, message); - - // Store correct length - Theatre::CurrentInfo.length = Game::Sys_Milliseconds() - Theatre::CurrentInfo.length; - - // Write metadata - FileSystem::FileWriter meta(Utils::String::VA("%s.json", Theatre::CurrentInfo.name.data())); - meta.write(json11::Json(Theatre::CurrentInfo).dump()); - } - - void Theatre::LoadDemos(UIScript::Token) - { - Theatre::CurrentSelection = 0; - Theatre::Demos.clear(); - - auto demos = FileSystem::GetFileList("demos/", "dm_13"); - - for (auto demo : demos) - { - FileSystem::File meta(Utils::String::VA("demos/%s.json", demo.data())); - - if (meta.exists()) - { - std::string error; - json11::Json metaObject = json11::Json::parse(meta.getBuffer(), error); - - if (metaObject.is_object()) - { - Theatre::DemoInfo info; - - info.name = demo.substr(0, demo.find_last_of(".")); - info.author = metaObject["author"].string_value(); - info.gametype = metaObject["gametype"].string_value(); - info.mapname = metaObject["mapname"].string_value(); - info.length = (int)metaObject["length"].number_value(); - info.timeStamp = _atoi64(metaObject["timestamp"].string_value().data()); - - Theatre::Demos.push_back(info); - } - } - } - - // Reverse, latest demo first! - std::reverse(Theatre::Demos.begin(), Theatre::Demos.end()); - } - - void Theatre::DeleteDemo(UIScript::Token) - { - if (Theatre::CurrentSelection < Theatre::Demos.size()) - { - Theatre::DemoInfo info = Theatre::Demos[Theatre::CurrentSelection]; - - Logger::Print("Deleting demo %s...\n", info.name.data()); - - FileSystem::DeleteFile("demos", info.name + ".dm_13"); - FileSystem::DeleteFile("demos", info.name + ".dm_13.json"); - - // Reset our ui_demo_* dvars here, because the theater menu needs it. - Dvar::Var("ui_demo_mapname").set(""); - Dvar::Var("ui_demo_mapname_localized").set(""); - Dvar::Var("ui_demo_gametype").set(""); - Dvar::Var("ui_demo_length").set(""); - Dvar::Var("ui_demo_author").set(""); - Dvar::Var("ui_demo_date").set(""); - - // Reload demos - Theatre::LoadDemos(UIScript::Token()); - } - } - - void Theatre::PlayDemo(UIScript::Token) - { - if (Theatre::CurrentSelection < Theatre::Demos.size()) - { - Command::Execute(Utils::String::VA("demo %s", Theatre::Demos[Theatre::CurrentSelection].name.data()), true); - Command::Execute("demoback", false); - } - } - - unsigned int Theatre::GetDemoCount() - { - return Theatre::Demos.size(); - } - - // Omit column here - const char* Theatre::GetDemoText(unsigned int item, int /*column*/) - { - if (item < Theatre::Demos.size()) - { - Theatre::DemoInfo info = Theatre::Demos[item]; - - return Utils::String::VA("%s on %s", Game::UI_LocalizeGameType(info.gametype.data()), Game::UI_LocalizeMapName(info.mapname.data())); - } - - return ""; - } - - void Theatre::SelectDemo(unsigned int index) - { - if (index < Theatre::Demos.size()) - { - Theatre::CurrentSelection = index; - Theatre::DemoInfo info = Theatre::Demos[index]; - - tm time; - char buffer[1000] = { 0 }; - localtime_s(&time, &info.timeStamp); - asctime_s(buffer, sizeof buffer, &time); - - Dvar::Var("ui_demo_mapname").set(info.mapname); - Dvar::Var("ui_demo_mapname_localized").set(Game::UI_LocalizeMapName(info.mapname.data())); - Dvar::Var("ui_demo_gametype").set(Game::UI_LocalizeGameType(info.gametype.data())); - Dvar::Var("ui_demo_length").set(Utils::String::FormatTimeSpan(info.length)); - Dvar::Var("ui_demo_author").set(info.author); - Dvar::Var("ui_demo_date").set(buffer); - } - } - - uint32_t Theatre::InitCGameStub() - { - if (Dvar::Var("cl_autoRecord").get() && !*Game::demoPlaying) - { - std::vector files; - std::vector demos = FileSystem::GetFileList("demos/", "dm_13"); - - for (auto demo : demos) - { - if (Utils::String::StartsWith(demo, "auto_")) - { - files.push_back(demo); - } - } - - int numDel = files.size() - Dvar::Var("cl_demosKeep").get(); - - for (int i = 0; i < numDel; ++i) - { - Logger::Print("Deleting old demo %s\n", files[i].data()); - FileSystem::DeleteFile("demos", files[i].data()); - FileSystem::DeleteFile("demos", Utils::String::VA("%s.json", files[i].data())); - } - - Command::Execute(Utils::String::VA("record auto_%lld", time(0)), true); - } - - return Utils::Hook::Call(0x42BBB0)(); - } - - void Theatre::MapChangeStub() - { - if (*Game::demoRecording) - { - Command::Execute("stoprecord", true); - } - - Utils::Hook::Call(0x464A60)(); - } - - void Theatre::MapChangeSVStub(char* a1, char* a2) - { - if (*Game::demoRecording) - { - Command::Execute("stoprecord", true); - } - - Utils::Hook::Call(0x487C50)(a1, a2); - } - - Theatre::Theatre() - { - Dvar::Register("cl_autoRecord", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Automatically record games."); - Dvar::Register("cl_demosKeep", 30, 1, 999, Game::dvar_flag::DVAR_FLAG_SAVED, "How many demos to keep with autorecord."); - - Utils::Hook(0x5A8370, Theatre::GamestateWriteStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x5A85D2, Theatre::RecordGamestateStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x5ABE36, Theatre::BaselineStoreStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x5A8630, Theatre::BaselineToFileStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4CB3EF, Theatre::UISetActiveMenuStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x50320E, Theatre::AdjustTimeDeltaStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x5A8E03, Theatre::ServerTimedOutStub, HOOK_JUMP).install()->quick(); - - // Hook commands to enforce metadata generation - Utils::Hook(0x5A82AE, Theatre::RecordStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x5A8156, Theatre::StopRecordStub, HOOK_CALL).install()->quick(); - - // Autorecording - Utils::Hook(0x5A1D6A, Theatre::InitCGameStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x4A712A, Theatre::MapChangeStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x5AA91C, Theatre::MapChangeSVStub, HOOK_CALL).install()->quick(); - - // UIScripts - UIScript::Add("loadDemos", Theatre::LoadDemos); - UIScript::Add("launchDemo", Theatre::PlayDemo); - UIScript::Add("deleteDemo", Theatre::DeleteDemo); - - // Feeder - UIFeeder::Add(10.0f, Theatre::GetDemoCount, Theatre::GetDemoText, Theatre::SelectDemo); - - // set the configstrings stuff to load the default (empty) string table; this should allow demo recording on all gametypes/maps - if(!Dedicated::IsEnabled()) Utils::Hook::Set(0x47440B, "mp/defaultStringTable.csv"); - - // Change font size - Utils::Hook::Set(0x5AC854, 2); - Utils::Hook::Set(0x5AC85A, 2); - -// Command::Add("democycle", [] (Command::Params params) -// { -// // Cmd_FollowCycle_f -// Utils::Hook::Call(0x458ED0)(Game::g_entities, -1); -// }); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Theatre::DemoInfo Theatre::CurrentInfo; + unsigned int Theatre::CurrentSelection; + std::vector Theatre::Demos; + + char Theatre::BaselineSnapshot[131072] = { 0 }; + int Theatre::BaselineSnapshotMsgLen; + int Theatre::BaselineSnapshotMsgOff; + + void Theatre::GamestateWriteStub(Game::msg_t* msg, char byte) + { + Game::MSG_WriteLong(msg, 0); + Game::MSG_WriteByte(msg, byte); + } + + void Theatre::RecordGamestateStub() + { + int sequence = (*Game::serverMessageSequence - 1); + Game::FS_Write(&sequence, 4, *Game::demoFile); + } + + void Theatre::StoreBaseline(PBYTE snapshotMsg) + { + // Store offset and length + Theatre::BaselineSnapshotMsgLen = *reinterpret_cast(snapshotMsg + 20); + Theatre::BaselineSnapshotMsgOff = *reinterpret_cast(snapshotMsg + 28) - 7; + + // Copy to our snapshot buffer + std::memcpy(Theatre::BaselineSnapshot, *reinterpret_cast(snapshotMsg + 8), *reinterpret_cast(snapshotMsg + 20)); + } + + __declspec(naked) void Theatre::BaselineStoreStub() + { + _asm + { + push edi + call Theatre::StoreBaseline + pop edi + + mov edx, 5ABEF5h + jmp edx + } + } + + void Theatre::WriteBaseline() + { + static char bufData[131072]; + static char cmpData[131072]; + + Game::msg_t buf; + + Game::MSG_Init(&buf, bufData, 131072); + Game::MSG_WriteData(&buf, &Theatre::BaselineSnapshot[Theatre::BaselineSnapshotMsgOff], Theatre::BaselineSnapshotMsgLen - Theatre::BaselineSnapshotMsgOff); + Game::MSG_WriteByte(&buf, 6); + + int compressedSize = Game::MSG_WriteBitsCompress(false, buf.data, cmpData, buf.cursize); + int fileCompressedSize = compressedSize + 4; + + int byte8 = 8; + char byte0 = 0; + + Game::FS_Write(&byte0, 1, *Game::demoFile); + Game::FS_Write(Game::serverMessageSequence, 4, *Game::demoFile); + Game::FS_Write(&fileCompressedSize, 4, *Game::demoFile); + Game::FS_Write(&byte8, 4, *Game::demoFile); + + for (int i = 0; i < compressedSize; i += 1024) + { + int size = std::min(compressedSize - i, 1024); + + if (i + size >= sizeof(cmpData)) + { + Logger::Print("Error: Writing compressed demo baseline exceeded buffer\n"); + break; + } + + Game::FS_Write(&cmpData[i], size, *Game::demoFile); + } + } + + __declspec(naked) void Theatre::BaselineToFileStub() + { + __asm + { + call Theatre::WriteBaseline + + // Restore overwritten operation + mov ecx, 0A5E9C4h + mov [ecx], 0 + + // Return to original code + mov ecx, 5A863Ah + jmp ecx + } + } + + __declspec(naked) void Theatre::AdjustTimeDeltaStub() + { + __asm + { + mov eax, Game::demoPlaying + mov eax, [eax] + test al, al + jz continue + + // delta doesn't drift for demos + retn + + continue: + mov eax, 5A1AD0h + jmp eax + } + } + + __declspec(naked) void Theatre::ServerTimedOutStub() + { + __asm + { + mov eax, Game::demoPlaying + mov eax, [eax] + test al, al + jz continue + + mov eax, 5A8E70h + jmp eax + + continue: + mov eax, 0B2BB90h + mov esi, 5A8E08h + jmp esi + } + } + + __declspec(naked) void Theatre::UISetActiveMenuStub() + { + __asm + { + mov eax, Game::demoPlaying + mov eax, [eax] + test al, al + jz continue + + mov eax, 4CB49Ch + jmp eax + + continue: + mov ecx, [esp + 10h] + push 10h + push ecx + mov eax, 4CB3F6h + jmp eax + } + } + + void Theatre::RecordStub(int channel, char* message, char* file) + { + Game::Com_Printf(channel, message, file); + + Theatre::CurrentInfo.name = file; + Theatre::CurrentInfo.mapname = Dvar::Var("mapname").get(); + Theatre::CurrentInfo.gametype = Dvar::Var("g_gametype").get(); + Theatre::CurrentInfo.author = Steam::SteamFriends()->GetPersonaName(); + Theatre::CurrentInfo.length = Game::Sys_Milliseconds(); + std::time(&Theatre::CurrentInfo.timeStamp); + } + + void Theatre::StopRecordStub(int channel, char* message) + { + Game::Com_Printf(channel, message); + + // Store correct length + Theatre::CurrentInfo.length = Game::Sys_Milliseconds() - Theatre::CurrentInfo.length; + + // Write metadata + FileSystem::FileWriter meta(Utils::String::VA("%s.json", Theatre::CurrentInfo.name.data())); + meta.write(json11::Json(Theatre::CurrentInfo).dump()); + } + + void Theatre::LoadDemos(UIScript::Token) + { + Theatre::CurrentSelection = 0; + Theatre::Demos.clear(); + + auto demos = FileSystem::GetFileList("demos/", "dm_13"); + + for (auto demo : demos) + { + FileSystem::File meta(Utils::String::VA("demos/%s.json", demo.data())); + + if (meta.exists()) + { + std::string error; + json11::Json metaObject = json11::Json::parse(meta.getBuffer(), error); + + if (metaObject.is_object()) + { + Theatre::DemoInfo info; + + info.name = demo.substr(0, demo.find_last_of(".")); + info.author = metaObject["author"].string_value(); + info.gametype = metaObject["gametype"].string_value(); + info.mapname = metaObject["mapname"].string_value(); + info.length = (int)metaObject["length"].number_value(); + info.timeStamp = _atoi64(metaObject["timestamp"].string_value().data()); + + Theatre::Demos.push_back(info); + } + } + } + + // Reverse, latest demo first! + std::reverse(Theatre::Demos.begin(), Theatre::Demos.end()); + } + + void Theatre::DeleteDemo(UIScript::Token) + { + if (Theatre::CurrentSelection < Theatre::Demos.size()) + { + Theatre::DemoInfo info = Theatre::Demos[Theatre::CurrentSelection]; + + Logger::Print("Deleting demo %s...\n", info.name.data()); + + FileSystem::DeleteFile("demos", info.name + ".dm_13"); + FileSystem::DeleteFile("demos", info.name + ".dm_13.json"); + + // Reset our ui_demo_* dvars here, because the theater menu needs it. + Dvar::Var("ui_demo_mapname").set(""); + Dvar::Var("ui_demo_mapname_localized").set(""); + Dvar::Var("ui_demo_gametype").set(""); + Dvar::Var("ui_demo_length").set(""); + Dvar::Var("ui_demo_author").set(""); + Dvar::Var("ui_demo_date").set(""); + + // Reload demos + Theatre::LoadDemos(UIScript::Token()); + } + } + + void Theatre::PlayDemo(UIScript::Token) + { + if (Theatre::CurrentSelection < Theatre::Demos.size()) + { + Command::Execute(Utils::String::VA("demo %s", Theatre::Demos[Theatre::CurrentSelection].name.data()), true); + Command::Execute("demoback", false); + } + } + + unsigned int Theatre::GetDemoCount() + { + return Theatre::Demos.size(); + } + + // Omit column here + const char* Theatre::GetDemoText(unsigned int item, int /*column*/) + { + if (item < Theatre::Demos.size()) + { + Theatre::DemoInfo info = Theatre::Demos[item]; + + return Utils::String::VA("%s on %s", Game::UI_LocalizeGameType(info.gametype.data()), Game::UI_LocalizeMapName(info.mapname.data())); + } + + return ""; + } + + void Theatre::SelectDemo(unsigned int index) + { + if (index < Theatre::Demos.size()) + { + Theatre::CurrentSelection = index; + Theatre::DemoInfo info = Theatre::Demos[index]; + + tm time; + char buffer[1000] = { 0 }; + localtime_s(&time, &info.timeStamp); + asctime_s(buffer, sizeof buffer, &time); + + Dvar::Var("ui_demo_mapname").set(info.mapname); + Dvar::Var("ui_demo_mapname_localized").set(Game::UI_LocalizeMapName(info.mapname.data())); + Dvar::Var("ui_demo_gametype").set(Game::UI_LocalizeGameType(info.gametype.data())); + Dvar::Var("ui_demo_length").set(Utils::String::FormatTimeSpan(info.length)); + Dvar::Var("ui_demo_author").set(info.author); + Dvar::Var("ui_demo_date").set(buffer); + } + } + + uint32_t Theatre::InitCGameStub() + { + if (Dvar::Var("cl_autoRecord").get() && !*Game::demoPlaying) + { + std::vector files; + std::vector demos = FileSystem::GetFileList("demos/", "dm_13"); + + for (auto demo : demos) + { + if (Utils::String::StartsWith(demo, "auto_")) + { + files.push_back(demo); + } + } + + int numDel = files.size() - Dvar::Var("cl_demosKeep").get(); + + for (int i = 0; i < numDel; ++i) + { + Logger::Print("Deleting old demo %s\n", files[i].data()); + FileSystem::DeleteFile("demos", files[i].data()); + FileSystem::DeleteFile("demos", Utils::String::VA("%s.json", files[i].data())); + } + + Command::Execute(Utils::String::VA("record auto_%lld", time(0)), true); + } + + return Utils::Hook::Call(0x42BBB0)(); + } + + void Theatre::MapChangeStub() + { + if (*Game::demoRecording) + { + Command::Execute("stoprecord", true); + } + + Utils::Hook::Call(0x464A60)(); + } + + void Theatre::MapChangeSVStub(char* a1, char* a2) + { + if (*Game::demoRecording) + { + Command::Execute("stoprecord", true); + } + + Utils::Hook::Call(0x487C50)(a1, a2); + } + + Theatre::Theatre() + { + Dvar::Register("cl_autoRecord", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Automatically record games."); + Dvar::Register("cl_demosKeep", 30, 1, 999, Game::dvar_flag::DVAR_FLAG_SAVED, "How many demos to keep with autorecord."); + + Utils::Hook(0x5A8370, Theatre::GamestateWriteStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A85D2, Theatre::RecordGamestateStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5ABE36, Theatre::BaselineStoreStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x5A8630, Theatre::BaselineToFileStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4CB3EF, Theatre::UISetActiveMenuStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x50320E, Theatre::AdjustTimeDeltaStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A8E03, Theatre::ServerTimedOutStub, HOOK_JUMP).install()->quick(); + + // Hook commands to enforce metadata generation + Utils::Hook(0x5A82AE, Theatre::RecordStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5A8156, Theatre::StopRecordStub, HOOK_CALL).install()->quick(); + + // Autorecording + Utils::Hook(0x5A1D6A, Theatre::InitCGameStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x4A712A, Theatre::MapChangeStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x5AA91C, Theatre::MapChangeSVStub, HOOK_CALL).install()->quick(); + + // UIScripts + UIScript::Add("loadDemos", Theatre::LoadDemos); + UIScript::Add("launchDemo", Theatre::PlayDemo); + UIScript::Add("deleteDemo", Theatre::DeleteDemo); + + // Feeder + UIFeeder::Add(10.0f, Theatre::GetDemoCount, Theatre::GetDemoText, Theatre::SelectDemo); + + // set the configstrings stuff to load the default (empty) string table; this should allow demo recording on all gametypes/maps + if(!Dedicated::IsEnabled()) Utils::Hook::Set(0x47440B, "mp/defaultStringTable.csv"); + + // Change font size + Utils::Hook::Set(0x5AC854, 2); + Utils::Hook::Set(0x5AC85A, 2); + +// Command::Add("democycle", [] (Command::Params params) +// { +// // Cmd_FollowCycle_f +// Utils::Hook::Call(0x458ED0)(Game::g_entities, -1); +// }); + } +} diff --git a/src/Components/Modules/Theatre.hpp b/src/Components/Modules/Theatre.hpp index 9539d471..c1473db0 100644 --- a/src/Components/Modules/Theatre.hpp +++ b/src/Components/Modules/Theatre.hpp @@ -1,70 +1,70 @@ -namespace Components -{ - class Theatre : public Component - { - public: - Theatre(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Theatre"; }; -#endif - - private: - class DemoInfo - { - public: - std::string name; - std::string mapname; - std::string gametype; - std::string author; - int length; - std::time_t timeStamp; - - json11::Json to_json() const - { - return json11::Json::object - { - { "mapname", mapname }, - { "gametype", gametype }, - { "author", author }, - { "length", length }, - { "timestamp", Utils::String::VA("%lld", timeStamp) } //Ugly, but prevents information loss - }; - } - }; - - static DemoInfo CurrentInfo; - static unsigned int CurrentSelection; - static std::vector Demos; - - static char BaselineSnapshot[131072]; - static int BaselineSnapshotMsgLen; - static int BaselineSnapshotMsgOff; - - static void WriteBaseline(); - static void StoreBaseline(PBYTE snapshotMsg); - - static void LoadDemos(UIScript::Token); - static void DeleteDemo(UIScript::Token); - static void PlayDemo(UIScript::Token); - - static unsigned int GetDemoCount(); - static const char* GetDemoText(unsigned int item, int column); - static void SelectDemo(unsigned int index); - - static void GamestateWriteStub(Game::msg_t* msg, char byte); - static void RecordGamestateStub(); - static void BaselineStoreStub(); - static void BaselineToFileStub(); - static void AdjustTimeDeltaStub(); - static void ServerTimedOutStub(); - static void UISetActiveMenuStub(); - - static uint32_t InitCGameStub(); - static void MapChangeStub(); - static void MapChangeSVStub(char* a1, char* a2); - - static void RecordStub(int channel, char* message, char* file); - static void StopRecordStub(int channel, char* message); - }; -} +namespace Components +{ + class Theatre : public Component + { + public: + Theatre(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Theatre"; }; +#endif + + private: + class DemoInfo + { + public: + std::string name; + std::string mapname; + std::string gametype; + std::string author; + int length; + std::time_t timeStamp; + + json11::Json to_json() const + { + return json11::Json::object + { + { "mapname", mapname }, + { "gametype", gametype }, + { "author", author }, + { "length", length }, + { "timestamp", Utils::String::VA("%lld", timeStamp) } //Ugly, but prevents information loss + }; + } + }; + + static DemoInfo CurrentInfo; + static unsigned int CurrentSelection; + static std::vector Demos; + + static char BaselineSnapshot[131072]; + static int BaselineSnapshotMsgLen; + static int BaselineSnapshotMsgOff; + + static void WriteBaseline(); + static void StoreBaseline(PBYTE snapshotMsg); + + static void LoadDemos(UIScript::Token); + static void DeleteDemo(UIScript::Token); + static void PlayDemo(UIScript::Token); + + static unsigned int GetDemoCount(); + static const char* GetDemoText(unsigned int item, int column); + static void SelectDemo(unsigned int index); + + static void GamestateWriteStub(Game::msg_t* msg, char byte); + static void RecordGamestateStub(); + static void BaselineStoreStub(); + static void BaselineToFileStub(); + static void AdjustTimeDeltaStub(); + static void ServerTimedOutStub(); + static void UISetActiveMenuStub(); + + static uint32_t InitCGameStub(); + static void MapChangeStub(); + static void MapChangeSVStub(char* a1, char* a2); + + static void RecordStub(int channel, char* message, char* file); + static void StopRecordStub(int channel, char* message); + }; +} diff --git a/src/Components/Modules/Threading.cpp b/src/Components/Modules/Threading.cpp index 5059d096..2288dd90 100644 --- a/src/Components/Modules/Threading.cpp +++ b/src/Components/Modules/Threading.cpp @@ -1,47 +1,47 @@ -#include "STDInclude.hpp" - -namespace Components -{ - __declspec(naked) void Threading::FrameEpilogueStub() - { - __asm - { - pop edi - pop esi - pop ebx - mov esp, ebp - pop ebp - retn - } - } - - __declspec(naked) void Threading::PacketEventStub() - { - __asm - { - mov eax, 49F0B0h - call eax - mov eax, 458160h - jmp eax - } - } - - Threading::Threading() - { - // remove starting of server thread from Com_Init_Try_Block_Function - Utils::Hook::Nop(0x60BEC0, 5); - - // make server thread function jump to per-frame stuff - Utils::Hook(0x627049, 0x6271CE, HOOK_JUMP).install()->quick(); - - // make SV_WaitServer insta-return - Utils::Hook::Set(0x4256F0, 0xC3); - - // dvar setting function, unknown stuff related to server thread sync - Utils::Hook::Set(0x647781, 0xEB); - - Utils::Hook(0x627695, 0x627040, HOOK_CALL).install()->quick(); - Utils::Hook(0x43D1C7, Threading::PacketEventStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x6272E3, Threading::FrameEpilogueStub, HOOK_JUMP).install()->quick(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + __declspec(naked) void Threading::FrameEpilogueStub() + { + __asm + { + pop edi + pop esi + pop ebx + mov esp, ebp + pop ebp + retn + } + } + + __declspec(naked) void Threading::PacketEventStub() + { + __asm + { + mov eax, 49F0B0h + call eax + mov eax, 458160h + jmp eax + } + } + + Threading::Threading() + { + // remove starting of server thread from Com_Init_Try_Block_Function + Utils::Hook::Nop(0x60BEC0, 5); + + // make server thread function jump to per-frame stuff + Utils::Hook(0x627049, 0x6271CE, HOOK_JUMP).install()->quick(); + + // make SV_WaitServer insta-return + Utils::Hook::Set(0x4256F0, 0xC3); + + // dvar setting function, unknown stuff related to server thread sync + Utils::Hook::Set(0x647781, 0xEB); + + Utils::Hook(0x627695, 0x627040, HOOK_CALL).install()->quick(); + Utils::Hook(0x43D1C7, Threading::PacketEventStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x6272E3, Threading::FrameEpilogueStub, HOOK_JUMP).install()->quick(); + } +} diff --git a/src/Components/Modules/Threading.hpp b/src/Components/Modules/Threading.hpp index 286d3d92..a907a460 100644 --- a/src/Components/Modules/Threading.hpp +++ b/src/Components/Modules/Threading.hpp @@ -1,16 +1,16 @@ -namespace Components -{ - class Threading : public Component - { - public: - Threading(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Threading"; }; -#endif - - private: - static void FrameEpilogueStub(); - static void PacketEventStub(); - }; -} +namespace Components +{ + class Threading : public Component + { + public: + Threading(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Threading"; }; +#endif + + private: + static void FrameEpilogueStub(); + static void PacketEventStub(); + }; +} diff --git a/src/Components/Modules/Toast.cpp b/src/Components/Modules/Toast.cpp index fa954650..35863896 100644 --- a/src/Components/Modules/Toast.cpp +++ b/src/Components/Modules/Toast.cpp @@ -1,140 +1,140 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::queue Toast::Queue; - std::mutex Toast::Mutex; - - void Toast::Show(std::string image, std::string title, std::string description, int length) - { - Toast::Mutex.lock(); - Toast::Queue.push({ image, title, description, length, 0 }); - Toast::Mutex.unlock(); - } - - void Toast::Draw(UIToast* toast) - { - if (!toast) return; - - int width = Renderer::Width(); - int height = Renderer::Height(); - int slideTime = 100; - - int duration = toast->length; - int startTime = toast->start; - - int border = 1; - int cornerSize = 15; - int bHeight = 74; - - int imgDim = 60; - - float fontSize = 0.9f; - float descSize = 0.9f; - - Game::Material* white = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "white").material; - Game::Material* image = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, toast->image.data()).material; - Game::Font* font = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/objectiveFont").font; - Game::Font* descfont = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/normalFont").font; - Game::vec4_t wColor = { 1.0f, 1.0f, 1.0f, 1.0f }; - Game::vec4_t bgColor = { 0.0f, 0.0f, 0.0f, 0.5f }; - Game::vec4_t borderColor = { 1.0f, 1.0f, 1.0f, 0.2f }; - - height /= 5; - height *= 4; - - if (Game::Sys_Milliseconds() < startTime || (startTime + duration) < Game::Sys_Milliseconds()) return; - - // Fadein stuff - else if (Game::Sys_Milliseconds() - startTime < slideTime) - { - int diffH = Renderer::Height() / 5; - int diff = Game::Sys_Milliseconds() - startTime; - double scale = 1.0 - ((1.0 * diff) / (1.0 * slideTime)); - diffH = static_cast(diffH * scale); - height += diffH; - } - - // Fadeout stuff - else if (Game::Sys_Milliseconds() - startTime > (duration - slideTime)) - { - int diffH = Renderer::Height() / 5; - int diff = (startTime + duration) - Game::Sys_Milliseconds(); - double scale = 1.0 - ((1.0 * diff) / (1.0 * slideTime)); - diffH = static_cast(diffH * scale); - height += diffH; - } - - height += bHeight / 2 - cornerSize; - - // Calculate width data - int iOffset = (bHeight - imgDim) / 2; - int iOffsetLeft = iOffset * 2; - float titleSize = Game::R_TextWidth(toast->title.data(), 0x7FFFFFFF, font) * fontSize; - float descrSize = Game::R_TextWidth(toast->desc.data(), 0x7FFFFFFF, descfont) * descSize; - float bWidth = iOffsetLeft * 3 + imgDim + std::max(titleSize, descrSize); - - // Make stuff divisible by 2 - // Otherwise there are overlapping images - // and I'm too lazy to figure out the actual problem :P - bWidth = (static_cast(bWidth) + (static_cast(bWidth) % 2)) * 1.0f; - bHeight += (bHeight % 2); - - // Background - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height - bHeight / 2), bWidth * 1.0f, bHeight * 1.0f, 0, 0, 1.0f, 1.0f, bgColor, white); - - // Border - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 - border), static_cast(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, white); // Left - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + bWidth), static_cast(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, white); // Right - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height - bHeight / 2 - border), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Top - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height + bHeight / 2), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Bottom - - // Image - Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + iOffsetLeft), static_cast(height - bHeight / 2 + iOffset), imgDim * 1.0f, imgDim * 1.0f, 0, 0, 1.0f, 1.0f, wColor, image); - - // Text - float leftText = width / 2 - bWidth / 2 - cornerSize + iOffsetLeft * 2 + imgDim; - float rightText = width / 2 + bWidth / 2 - cornerSize - iOffsetLeft; - Game::R_AddCmdDrawText(Utils::String::ToUpper(toast->title).data(), 0x7FFFFFFF, font, static_cast(leftText + (rightText - leftText) / 2 - titleSize / 2 + cornerSize), static_cast(height - bHeight / 2 + cornerSize * 2 + 7), fontSize, fontSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Title - Game::R_AddCmdDrawText(toast->desc.data(), 0x7FFFFFFF, descfont, leftText + (rightText - leftText) / 2 - descrSize / 2 + cornerSize, static_cast(height - bHeight / 2 + cornerSize * 2 + 33), descSize, descSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Description - } - - void Toast::Handler() - { - if (Toast::Queue.empty()) return; - - std::lock_guard _(Toast::Mutex); - - Toast::UIToast* toast = &Toast::Queue.front(); - - // Set start time - if (!toast->start) - { - toast->start = Game::Sys_Milliseconds(); - } - - if ((toast->start + toast->length) < Game::Sys_Milliseconds()) - { - Toast::Queue.pop(); - } - else - { - Toast::Draw(toast); - } - } - - Toast::Toast() - { - Renderer::OnFrame(Toast::Handler); - - Command::Add("testtoast", [] (Command::Params*) - { - Toast::Show("cardicon_prestige10", "Test", "This is a test toast", 3000); - }); - } - - Toast::~Toast() - { - Toast::Queue = std::queue(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::queue Toast::Queue; + std::mutex Toast::Mutex; + + void Toast::Show(std::string image, std::string title, std::string description, int length) + { + Toast::Mutex.lock(); + Toast::Queue.push({ image, title, description, length, 0 }); + Toast::Mutex.unlock(); + } + + void Toast::Draw(UIToast* toast) + { + if (!toast) return; + + int width = Renderer::Width(); + int height = Renderer::Height(); + int slideTime = 100; + + int duration = toast->length; + int startTime = toast->start; + + int border = 1; + int cornerSize = 15; + int bHeight = 74; + + int imgDim = 60; + + float fontSize = 0.9f; + float descSize = 0.9f; + + Game::Material* white = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, "white").material; + Game::Material* image = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_MATERIAL, toast->image.data()).material; + Game::Font* font = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/objectiveFont").font; + Game::Font* descfont = Game::DB_FindXAssetHeader(Game::XAssetType::ASSET_TYPE_FONT, "fonts/normalFont").font; + Game::vec4_t wColor = { 1.0f, 1.0f, 1.0f, 1.0f }; + Game::vec4_t bgColor = { 0.0f, 0.0f, 0.0f, 0.5f }; + Game::vec4_t borderColor = { 1.0f, 1.0f, 1.0f, 0.2f }; + + height /= 5; + height *= 4; + + if (Game::Sys_Milliseconds() < startTime || (startTime + duration) < Game::Sys_Milliseconds()) return; + + // Fadein stuff + else if (Game::Sys_Milliseconds() - startTime < slideTime) + { + int diffH = Renderer::Height() / 5; + int diff = Game::Sys_Milliseconds() - startTime; + double scale = 1.0 - ((1.0 * diff) / (1.0 * slideTime)); + diffH = static_cast(diffH * scale); + height += diffH; + } + + // Fadeout stuff + else if (Game::Sys_Milliseconds() - startTime > (duration - slideTime)) + { + int diffH = Renderer::Height() / 5; + int diff = (startTime + duration) - Game::Sys_Milliseconds(); + double scale = 1.0 - ((1.0 * diff) / (1.0 * slideTime)); + diffH = static_cast(diffH * scale); + height += diffH; + } + + height += bHeight / 2 - cornerSize; + + // Calculate width data + int iOffset = (bHeight - imgDim) / 2; + int iOffsetLeft = iOffset * 2; + float titleSize = Game::R_TextWidth(toast->title.data(), 0x7FFFFFFF, font) * fontSize; + float descrSize = Game::R_TextWidth(toast->desc.data(), 0x7FFFFFFF, descfont) * descSize; + float bWidth = iOffsetLeft * 3 + imgDim + std::max(titleSize, descrSize); + + // Make stuff divisible by 2 + // Otherwise there are overlapping images + // and I'm too lazy to figure out the actual problem :P + bWidth = (static_cast(bWidth) + (static_cast(bWidth) % 2)) * 1.0f; + bHeight += (bHeight % 2); + + // Background + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height - bHeight / 2), bWidth * 1.0f, bHeight * 1.0f, 0, 0, 1.0f, 1.0f, bgColor, white); + + // Border + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 - border), static_cast(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, white); // Left + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + bWidth), static_cast(height - bHeight / 2 - border), border * 1.0f, bHeight + (border * 2.0f), 0, 0, 1.0f, 1.0f, borderColor, white); // Right + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height - bHeight / 2 - border), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Top + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2), static_cast(height + bHeight / 2), bWidth * 1.0f, border * 1.0f, 0, 0, 1.0f, 1.0f, borderColor, white); // Bottom + + // Image + Game::CL_DrawStretchPicPhysical(static_cast(width / 2 - bWidth / 2 + iOffsetLeft), static_cast(height - bHeight / 2 + iOffset), imgDim * 1.0f, imgDim * 1.0f, 0, 0, 1.0f, 1.0f, wColor, image); + + // Text + float leftText = width / 2 - bWidth / 2 - cornerSize + iOffsetLeft * 2 + imgDim; + float rightText = width / 2 + bWidth / 2 - cornerSize - iOffsetLeft; + Game::R_AddCmdDrawText(Utils::String::ToUpper(toast->title).data(), 0x7FFFFFFF, font, static_cast(leftText + (rightText - leftText) / 2 - titleSize / 2 + cornerSize), static_cast(height - bHeight / 2 + cornerSize * 2 + 7), fontSize, fontSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Title + Game::R_AddCmdDrawText(toast->desc.data(), 0x7FFFFFFF, descfont, leftText + (rightText - leftText) / 2 - descrSize / 2 + cornerSize, static_cast(height - bHeight / 2 + cornerSize * 2 + 33), descSize, descSize, 0, wColor, Game::ITEM_TEXTSTYLE_SHADOWED); // Description + } + + void Toast::Handler() + { + if (Toast::Queue.empty()) return; + + std::lock_guard _(Toast::Mutex); + + Toast::UIToast* toast = &Toast::Queue.front(); + + // Set start time + if (!toast->start) + { + toast->start = Game::Sys_Milliseconds(); + } + + if ((toast->start + toast->length) < Game::Sys_Milliseconds()) + { + Toast::Queue.pop(); + } + else + { + Toast::Draw(toast); + } + } + + Toast::Toast() + { + Renderer::OnFrame(Toast::Handler); + + Command::Add("testtoast", [] (Command::Params*) + { + Toast::Show("cardicon_prestige10", "Test", "This is a test toast", 3000); + }); + } + + Toast::~Toast() + { + Toast::Queue = std::queue(); + } +} diff --git a/src/Components/Modules/Toast.hpp b/src/Components/Modules/Toast.hpp index 82f99eb4..e5d45d5a 100644 --- a/src/Components/Modules/Toast.hpp +++ b/src/Components/Modules/Toast.hpp @@ -1,32 +1,32 @@ -namespace Components -{ - class Toast : public Component - { - public: - Toast(); - ~Toast(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Toast"; }; -#endif - - static void Show(std::string image, std::string title, std::string description, int length); - - private: - class UIToast - { - public: - std::string image; - std::string title; - std::string desc; - int length; - int start; - }; - - static void Handler(); - static void Draw(UIToast* toast); - - static std::queue Queue; - static std::mutex Mutex; - }; -} +namespace Components +{ + class Toast : public Component + { + public: + Toast(); + ~Toast(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Toast"; }; +#endif + + static void Show(std::string image, std::string title, std::string description, int length); + + private: + class UIToast + { + public: + std::string image; + std::string title; + std::string desc; + int length; + int start; + }; + + static void Handler(); + static void Draw(UIToast* toast); + + static std::queue Queue; + static std::mutex Mutex; + }; +} diff --git a/src/Components/Modules/UIFeeder.cpp b/src/Components/Modules/UIFeeder.cpp index 16039a09..c4d397cd 100644 --- a/src/Components/Modules/UIFeeder.cpp +++ b/src/Components/Modules/UIFeeder.cpp @@ -1,308 +1,308 @@ -#include "STDInclude.hpp" - -namespace Components -{ - UIFeeder::Container UIFeeder::Current; - std::unordered_map UIFeeder::Feeders; - - void UIFeeder::Add(float feeder, UIFeeder::GetItemCount_t itemCountCb, UIFeeder::GetItemText_t itemTextCb, UIFeeder::Select_t selectCb) - { - UIFeeder::Feeders[feeder] = { itemCountCb, itemTextCb, selectCb }; - } - - const char* UIFeeder::GetItemText() - { - if (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()) - { - return UIFeeder::Feeders[UIFeeder::Current.feeder].getItemText(UIFeeder::Current.index, UIFeeder::Current.column); - } - - return nullptr; - } - - unsigned int UIFeeder::GetItemCount() - { - if (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()) - { - return UIFeeder::Feeders[UIFeeder::Current.feeder].getItemCount(); - } - - return 0; - } - - bool UIFeeder::SetItemSelection() - { - if (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()) - { - UIFeeder::Feeders[UIFeeder::Current.feeder].select(UIFeeder::Current.index); - return true; - } - - return false; - } - - bool UIFeeder::CheckFeeder() - { - if (UIFeeder::Current.feeder == 15.0f) return false; - return (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()); - } - - __declspec(naked) void UIFeeder::SetItemSelectionStub() - { - __asm - { - mov eax, [esp + 08h] - mov UIFeeder::Current.feeder, eax - - mov eax, [esp + 0Ch] - mov UIFeeder::Current.index, eax - - call UIFeeder::SetItemSelection - - test al, al - jz continue - - retn - - continue: - fld ds:739FD0h - - mov eax, 4C25D6h - jmp eax - } - } - - __declspec(naked) void UIFeeder::GetItemTextStub() - { - __asm - { - mov eax, [esp + 0Ch] - mov UIFeeder::Current.feeder, eax - - mov eax, [esp + 10h] - mov UIFeeder::Current.index, eax - - mov eax, [esp + 14h] - mov UIFeeder::Current.column, eax - - call UIFeeder::GetItemText - - test eax, eax - jz continue - - push ebx - mov ebx, [esp + 4 + 28h] - mov dword ptr[ebx], 0 - pop ebx - retn - - continue: - push ecx - fld ds:739FD0h - - mov eax, 4CE9E7h - jmp eax - } - } - - __declspec(naked) void UIFeeder::GetItemCountStub() - { - __asm - { - mov eax, [esp + 8h] - mov UIFeeder::Current.feeder, eax - - call UIFeeder::GetItemCount - - test eax, eax - jz continue - - retn - - continue: - push ecx - fld ds:739FD0h - - mov eax, 41A0D7h - jmp eax; - } - } - - __declspec(naked) void UIFeeder::HandleKeyStub() - { - static int NextClickTime = 0; - - __asm - { - mov ebx, ebp - mov eax, [ebp + 12Ch] - mov UIFeeder::Current.feeder, eax - - push ebx - call UIFeeder::CheckFeeder - pop ebx - - test al, al - jz continueOriginal - - // Get current milliseconds - call Game::Sys_Milliseconds - - // Check if allowed to click - cmp eax, NextClickTime - jl continueOriginal - - // Set next allowed click time to current time + 300ms - add eax, 300 - mov NextClickTime, eax - - // Get item cursor position ptr - mov ecx, ebx - add ecx, Game::itemDef_t::cursorPos - - // Get item listbox ptr - mov edx, ebx - add edx, Game::itemDef_t::typeData - - // Get listbox cursor pos - mov edx, [edx] - add edx, Game::listBoxDef_s::startPos - mov edx, [edx] - - // Resolve item cursor pos pointer - mov ebx, [ecx] - - // Check if item cursor pos equals listbox cursor pos - cmp ebx, edx - je returnSafe - - // Update indices if not - mov [ecx], edx - mov UIFeeder::Current.index, edx - - call UIFeeder::SetItemSelection - - returnSafe: - retn - - continueOriginal: - mov eax, 635570h - jmp eax - } - } - - __declspec(naked) void UIFeeder::MouseEnterStub() - { - __asm - { - mov eax, [edi + 12Ch] - mov UIFeeder::Current.feeder, eax - - call UIFeeder::CheckFeeder - - test al, al - jnz continue - - mov[edi + 130h], esi - - continue: - mov eax, 639D75h - jmp eax - } - } - - __declspec(naked) void UIFeeder::MouseSelectStub() - { - __asm - { - mov eax, [esp + 08h] - mov UIFeeder::Current.feeder, eax - - call UIFeeder::CheckFeeder - - test al, al - jnz continue - - mov eax, 4C25D0h - jmp eax - - continue: - retn - } - } - - __declspec(naked) void UIFeeder::PlaySoundStub() - { - __asm - { - mov eax, [edi + 12Ch] - mov UIFeeder::Current.feeder, eax - - call UIFeeder::CheckFeeder - - test al, al - jnz continue - - mov eax, 685E10h - jmp eax - - continue: - retn - } - } - - void UIFeeder::ApplyMapFeeder(Game::dvar_t* dvar, int num) - { - Dvar::Var(dvar).set(num); - - if (num < 0 || num >= *Game::arenaCount) - { - num = 0; - } - - // UI_SortArenas - Utils::Hook::Call(0x630AE0)(); - - const char* mapname = ArenaLength::NewArenas[reinterpret_cast(0x633E934)[num]].mapName; - - Dvar::Var("ui_mapname").set(mapname); - - // Party_SetDisplayMapName - Utils::Hook::Call(0x503B50)(mapname); - } - - UIFeeder::UIFeeder() - { - // Get feeder item count - Utils::Hook(0x41A0D0, UIFeeder::GetItemCountStub, HOOK_JUMP).install()->quick(); - - // Get feeder item text - Utils::Hook(0x4CE9E0, UIFeeder::GetItemTextStub, HOOK_JUMP).install()->quick(); - - // Select feeder item - Utils::Hook(0x4C25D0, UIFeeder::SetItemSelectionStub, HOOK_JUMP).install()->quick(); - - // Mouse enter check - Utils::Hook(0x639D6E, UIFeeder::MouseEnterStub, HOOK_JUMP).install()->quick(); - - // Handle key event - Utils::Hook(0x63C5BC, UIFeeder::HandleKeyStub, HOOK_CALL).install()->quick(); - - // Mouse select check - Utils::Hook(0x639D31, UIFeeder::MouseSelectStub, HOOK_CALL).install()->quick(); - - // Play mouse over sound check - Utils::Hook(0x639D66, UIFeeder::PlaySoundStub, HOOK_CALL).install()->quick(); - - // some thing overwriting feeder 2's data - Utils::Hook::Set(0x4A06A9, 0xEB); - - // correct feeder 4 - Utils::Hook(0x4C260E, UIFeeder::ApplyMapFeeder, HOOK_CALL).install()->quick(); - } - - UIFeeder::~UIFeeder() - { - UIFeeder::Feeders.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + UIFeeder::Container UIFeeder::Current; + std::unordered_map UIFeeder::Feeders; + + void UIFeeder::Add(float feeder, UIFeeder::GetItemCount_t itemCountCb, UIFeeder::GetItemText_t itemTextCb, UIFeeder::Select_t selectCb) + { + UIFeeder::Feeders[feeder] = { itemCountCb, itemTextCb, selectCb }; + } + + const char* UIFeeder::GetItemText() + { + if (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()) + { + return UIFeeder::Feeders[UIFeeder::Current.feeder].getItemText(UIFeeder::Current.index, UIFeeder::Current.column); + } + + return nullptr; + } + + unsigned int UIFeeder::GetItemCount() + { + if (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()) + { + return UIFeeder::Feeders[UIFeeder::Current.feeder].getItemCount(); + } + + return 0; + } + + bool UIFeeder::SetItemSelection() + { + if (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()) + { + UIFeeder::Feeders[UIFeeder::Current.feeder].select(UIFeeder::Current.index); + return true; + } + + return false; + } + + bool UIFeeder::CheckFeeder() + { + if (UIFeeder::Current.feeder == 15.0f) return false; + return (UIFeeder::Feeders.find(UIFeeder::Current.feeder) != UIFeeder::Feeders.end()); + } + + __declspec(naked) void UIFeeder::SetItemSelectionStub() + { + __asm + { + mov eax, [esp + 08h] + mov UIFeeder::Current.feeder, eax + + mov eax, [esp + 0Ch] + mov UIFeeder::Current.index, eax + + call UIFeeder::SetItemSelection + + test al, al + jz continue + + retn + + continue: + fld ds:739FD0h + + mov eax, 4C25D6h + jmp eax + } + } + + __declspec(naked) void UIFeeder::GetItemTextStub() + { + __asm + { + mov eax, [esp + 0Ch] + mov UIFeeder::Current.feeder, eax + + mov eax, [esp + 10h] + mov UIFeeder::Current.index, eax + + mov eax, [esp + 14h] + mov UIFeeder::Current.column, eax + + call UIFeeder::GetItemText + + test eax, eax + jz continue + + push ebx + mov ebx, [esp + 4 + 28h] + mov dword ptr[ebx], 0 + pop ebx + retn + + continue: + push ecx + fld ds:739FD0h + + mov eax, 4CE9E7h + jmp eax + } + } + + __declspec(naked) void UIFeeder::GetItemCountStub() + { + __asm + { + mov eax, [esp + 8h] + mov UIFeeder::Current.feeder, eax + + call UIFeeder::GetItemCount + + test eax, eax + jz continue + + retn + + continue: + push ecx + fld ds:739FD0h + + mov eax, 41A0D7h + jmp eax; + } + } + + __declspec(naked) void UIFeeder::HandleKeyStub() + { + static int NextClickTime = 0; + + __asm + { + mov ebx, ebp + mov eax, [ebp + 12Ch] + mov UIFeeder::Current.feeder, eax + + push ebx + call UIFeeder::CheckFeeder + pop ebx + + test al, al + jz continueOriginal + + // Get current milliseconds + call Game::Sys_Milliseconds + + // Check if allowed to click + cmp eax, NextClickTime + jl continueOriginal + + // Set next allowed click time to current time + 300ms + add eax, 300 + mov NextClickTime, eax + + // Get item cursor position ptr + mov ecx, ebx + add ecx, Game::itemDef_t::cursorPos + + // Get item listbox ptr + mov edx, ebx + add edx, Game::itemDef_t::typeData + + // Get listbox cursor pos + mov edx, [edx] + add edx, Game::listBoxDef_s::startPos + mov edx, [edx] + + // Resolve item cursor pos pointer + mov ebx, [ecx] + + // Check if item cursor pos equals listbox cursor pos + cmp ebx, edx + je returnSafe + + // Update indices if not + mov [ecx], edx + mov UIFeeder::Current.index, edx + + call UIFeeder::SetItemSelection + + returnSafe: + retn + + continueOriginal: + mov eax, 635570h + jmp eax + } + } + + __declspec(naked) void UIFeeder::MouseEnterStub() + { + __asm + { + mov eax, [edi + 12Ch] + mov UIFeeder::Current.feeder, eax + + call UIFeeder::CheckFeeder + + test al, al + jnz continue + + mov[edi + 130h], esi + + continue: + mov eax, 639D75h + jmp eax + } + } + + __declspec(naked) void UIFeeder::MouseSelectStub() + { + __asm + { + mov eax, [esp + 08h] + mov UIFeeder::Current.feeder, eax + + call UIFeeder::CheckFeeder + + test al, al + jnz continue + + mov eax, 4C25D0h + jmp eax + + continue: + retn + } + } + + __declspec(naked) void UIFeeder::PlaySoundStub() + { + __asm + { + mov eax, [edi + 12Ch] + mov UIFeeder::Current.feeder, eax + + call UIFeeder::CheckFeeder + + test al, al + jnz continue + + mov eax, 685E10h + jmp eax + + continue: + retn + } + } + + void UIFeeder::ApplyMapFeeder(Game::dvar_t* dvar, int num) + { + Dvar::Var(dvar).set(num); + + if (num < 0 || num >= *Game::arenaCount) + { + num = 0; + } + + // UI_SortArenas + Utils::Hook::Call(0x630AE0)(); + + const char* mapname = ArenaLength::NewArenas[reinterpret_cast(0x633E934)[num]].mapName; + + Dvar::Var("ui_mapname").set(mapname); + + // Party_SetDisplayMapName + Utils::Hook::Call(0x503B50)(mapname); + } + + UIFeeder::UIFeeder() + { + // Get feeder item count + Utils::Hook(0x41A0D0, UIFeeder::GetItemCountStub, HOOK_JUMP).install()->quick(); + + // Get feeder item text + Utils::Hook(0x4CE9E0, UIFeeder::GetItemTextStub, HOOK_JUMP).install()->quick(); + + // Select feeder item + Utils::Hook(0x4C25D0, UIFeeder::SetItemSelectionStub, HOOK_JUMP).install()->quick(); + + // Mouse enter check + Utils::Hook(0x639D6E, UIFeeder::MouseEnterStub, HOOK_JUMP).install()->quick(); + + // Handle key event + Utils::Hook(0x63C5BC, UIFeeder::HandleKeyStub, HOOK_CALL).install()->quick(); + + // Mouse select check + Utils::Hook(0x639D31, UIFeeder::MouseSelectStub, HOOK_CALL).install()->quick(); + + // Play mouse over sound check + Utils::Hook(0x639D66, UIFeeder::PlaySoundStub, HOOK_CALL).install()->quick(); + + // some thing overwriting feeder 2's data + Utils::Hook::Set(0x4A06A9, 0xEB); + + // correct feeder 4 + Utils::Hook(0x4C260E, UIFeeder::ApplyMapFeeder, HOOK_CALL).install()->quick(); + } + + UIFeeder::~UIFeeder() + { + UIFeeder::Feeders.clear(); + } +} diff --git a/src/Components/Modules/UIFeeder.hpp b/src/Components/Modules/UIFeeder.hpp index 6a865bba..9d2daca5 100644 --- a/src/Components/Modules/UIFeeder.hpp +++ b/src/Components/Modules/UIFeeder.hpp @@ -1,56 +1,56 @@ -namespace Components -{ - class UIFeeder : public Component - { - public: - typedef unsigned int(__cdecl * GetItemCount_t)(); - typedef const char* (__cdecl * GetItemText_t)(unsigned int index, int column); - typedef void(__cdecl * Select_t)(unsigned int index); - - struct Callbacks - { - GetItemCount_t getItemCount; - GetItemText_t getItemText; - Select_t select; - }; - - UIFeeder(); - ~UIFeeder(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "UIFeeder"; }; -#endif - - static void Add(float feeder, GetItemCount_t itemCountCb, GetItemText_t itemTextCb, Select_t selectCb); - - private: - struct Container - { - float feeder; - int index; - int column; - }; - - static Container Current; - - static void GetItemCountStub(); - static unsigned int GetItemCount(); - - static void GetItemTextStub(); - static const char* GetItemText(); - - static void SetItemSelectionStub(); - static bool SetItemSelection(); - - static bool CheckFeeder(); - - static void MouseEnterStub(); - static void MouseSelectStub(); - static void HandleKeyStub(); - static void PlaySoundStub(); - - static std::unordered_map Feeders; - - static void ApplyMapFeeder(Game::dvar_t* dvar, int num); - }; -} +namespace Components +{ + class UIFeeder : public Component + { + public: + typedef unsigned int(__cdecl * GetItemCount_t)(); + typedef const char* (__cdecl * GetItemText_t)(unsigned int index, int column); + typedef void(__cdecl * Select_t)(unsigned int index); + + struct Callbacks + { + GetItemCount_t getItemCount; + GetItemText_t getItemText; + Select_t select; + }; + + UIFeeder(); + ~UIFeeder(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "UIFeeder"; }; +#endif + + static void Add(float feeder, GetItemCount_t itemCountCb, GetItemText_t itemTextCb, Select_t selectCb); + + private: + struct Container + { + float feeder; + int index; + int column; + }; + + static Container Current; + + static void GetItemCountStub(); + static unsigned int GetItemCount(); + + static void GetItemTextStub(); + static const char* GetItemText(); + + static void SetItemSelectionStub(); + static bool SetItemSelection(); + + static bool CheckFeeder(); + + static void MouseEnterStub(); + static void MouseSelectStub(); + static void HandleKeyStub(); + static void PlaySoundStub(); + + static std::unordered_map Feeders; + + static void ApplyMapFeeder(Game::dvar_t* dvar, int num); + }; +} diff --git a/src/Components/Modules/UIScript.cpp b/src/Components/Modules/UIScript.cpp index db33856f..95f118e8 100644 --- a/src/Components/Modules/UIScript.cpp +++ b/src/Components/Modules/UIScript.cpp @@ -1,131 +1,131 @@ -#include "STDInclude.hpp" - -namespace Components -{ - std::unordered_map> UIScript::UIScripts; - std::unordered_map> UIScript::UIOwnerDraws; - - template<> int UIScript::Token::get() - { - if (this->isValid()) - { - return atoi(this->token); - } - - return 0; - } - - template<> char* UIScript::Token::get() - { - if (this->isValid()) - { - return this->token; - } - - return ""; - } - - template<> const char* UIScript::Token::get() - { - return this->get(); - } - - template<> std::string UIScript::Token::get() - { - return this->get(); - } - - bool UIScript::Token::isValid() - { - return (this->token && this->token[0]); - } - - void UIScript::Token::parse(const char** args) - { - if (args) - { - this->token = Game::Com_Parse(args); - } - } - - void UIScript::Add(std::string name, Utils::Slot callback) - { - UIScript::UIScripts[name] = callback; - } - - void UIScript::AddOwnerDraw(int ownerdraw, Utils::Slot callback) - { - UIScript::UIOwnerDraws[ownerdraw] = callback; - } - - bool UIScript::RunMenuScript(const char* name, const char** args) - { - if (UIScript::UIScripts.find(name) != UIScript::UIScripts.end()) - { - UIScript::UIScripts[name](UIScript::Token(args)); - return true; - } - - return false; - } - - void UIScript::OwnerDrawHandleKeyStub(int ownerDraw, int flags, float *special, int key) - { - if (key == 200 || key == 201) //mouse buttons - { - for (auto i = UIScript::UIOwnerDraws.begin(); i != UIScript::UIOwnerDraws.end(); ++i) - { - if (i->first == ownerDraw) - { - i->second(); - } - } - } - - Utils::Hook::Call(0x4F58A0)(ownerDraw, flags, special, key); - } - - __declspec(naked) void UIScript::RunMenuScriptStub() - { - __asm - { - mov eax, esp - add eax, 8h - mov edx, eax // UIScript name - mov eax, [esp + 0C10h] // UIScript args - - push eax - push edx - call UIScript::RunMenuScript - add esp, 8h - - test al, al - jz continue - - // if returned - pop edi - pop esi - add esp, 0C00h - retn - - continue: - mov eax, 45ED00h - jmp eax - } - } - - UIScript::UIScript() - { - // Install handler - Utils::Hook::RedirectJump(0x45EC59, UIScript::RunMenuScriptStub); - - // Install ownerdraw handler - Utils::Hook(0x63D233, UIScript::OwnerDrawHandleKeyStub, HOOK_CALL).install()->quick(); - } - - UIScript::~UIScript() - { - UIScript::UIScripts.clear(); - UIScript::UIOwnerDraws.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + std::unordered_map> UIScript::UIScripts; + std::unordered_map> UIScript::UIOwnerDraws; + + template<> int UIScript::Token::get() + { + if (this->isValid()) + { + return atoi(this->token); + } + + return 0; + } + + template<> char* UIScript::Token::get() + { + if (this->isValid()) + { + return this->token; + } + + return ""; + } + + template<> const char* UIScript::Token::get() + { + return this->get(); + } + + template<> std::string UIScript::Token::get() + { + return this->get(); + } + + bool UIScript::Token::isValid() + { + return (this->token && this->token[0]); + } + + void UIScript::Token::parse(const char** args) + { + if (args) + { + this->token = Game::Com_Parse(args); + } + } + + void UIScript::Add(std::string name, Utils::Slot callback) + { + UIScript::UIScripts[name] = callback; + } + + void UIScript::AddOwnerDraw(int ownerdraw, Utils::Slot callback) + { + UIScript::UIOwnerDraws[ownerdraw] = callback; + } + + bool UIScript::RunMenuScript(const char* name, const char** args) + { + if (UIScript::UIScripts.find(name) != UIScript::UIScripts.end()) + { + UIScript::UIScripts[name](UIScript::Token(args)); + return true; + } + + return false; + } + + void UIScript::OwnerDrawHandleKeyStub(int ownerDraw, int flags, float *special, int key) + { + if (key == 200 || key == 201) //mouse buttons + { + for (auto i = UIScript::UIOwnerDraws.begin(); i != UIScript::UIOwnerDraws.end(); ++i) + { + if (i->first == ownerDraw) + { + i->second(); + } + } + } + + Utils::Hook::Call(0x4F58A0)(ownerDraw, flags, special, key); + } + + __declspec(naked) void UIScript::RunMenuScriptStub() + { + __asm + { + mov eax, esp + add eax, 8h + mov edx, eax // UIScript name + mov eax, [esp + 0C10h] // UIScript args + + push eax + push edx + call UIScript::RunMenuScript + add esp, 8h + + test al, al + jz continue + + // if returned + pop edi + pop esi + add esp, 0C00h + retn + + continue: + mov eax, 45ED00h + jmp eax + } + } + + UIScript::UIScript() + { + // Install handler + Utils::Hook::RedirectJump(0x45EC59, UIScript::RunMenuScriptStub); + + // Install ownerdraw handler + Utils::Hook(0x63D233, UIScript::OwnerDrawHandleKeyStub, HOOK_CALL).install()->quick(); + } + + UIScript::~UIScript() + { + UIScript::UIScripts.clear(); + UIScript::UIOwnerDraws.clear(); + } +} diff --git a/src/Components/Modules/UIScript.hpp b/src/Components/Modules/UIScript.hpp index b3f676df..2b97b40f 100644 --- a/src/Components/Modules/UIScript.hpp +++ b/src/Components/Modules/UIScript.hpp @@ -1,44 +1,44 @@ -namespace Components -{ - class UIScript : public Component - { - public: - UIScript(); - ~UIScript(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "UIScript"; }; -#endif - - class Token - { - public: - Token() : token(0) {}; - Token(const char** args) : token(0) { this->parse(args); }; - Token(const Token &obj) { this->token = obj.token; }; - - template T get(); - bool isValid(); - - - private: - char* token; - - void parse(const char** args); - }; - - typedef void(Callback)(Token token); - typedef void(CallbackRaw)(); - - static void Add(std::string name, Utils::Slot callback); - static void AddOwnerDraw(int ownerdraw, Utils::Slot callback); - - private: - static void OwnerDrawHandleKeyStub(int ownerDraw, int flags, float *special, int key); - static bool RunMenuScript(const char* name, const char** args); - static void RunMenuScriptStub(); - - static std::unordered_map> UIScripts; - static std::unordered_map> UIOwnerDraws; - }; -} +namespace Components +{ + class UIScript : public Component + { + public: + UIScript(); + ~UIScript(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "UIScript"; }; +#endif + + class Token + { + public: + Token() : token(0) {}; + Token(const char** args) : token(0) { this->parse(args); }; + Token(const Token &obj) { this->token = obj.token; }; + + template T get(); + bool isValid(); + + + private: + char* token; + + void parse(const char** args); + }; + + typedef void(Callback)(Token token); + typedef void(CallbackRaw)(); + + static void Add(std::string name, Utils::Slot callback); + static void AddOwnerDraw(int ownerdraw, Utils::Slot callback); + + private: + static void OwnerDrawHandleKeyStub(int ownerDraw, int flags, float *special, int key); + static bool RunMenuScript(const char* name, const char** args); + static void RunMenuScriptStub(); + + static std::unordered_map> UIScripts; + static std::unordered_map> UIOwnerDraws; + }; +} diff --git a/src/Components/Modules/Weapon.cpp b/src/Components/Modules/Weapon.cpp index 306c2316..fb951c81 100644 --- a/src/Components/Modules/Weapon.cpp +++ b/src/Components/Modules/Weapon.cpp @@ -1,31 +1,31 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Game::XAssetHeader Weapon::WeaponFileLoad(Game::XAssetType /*type*/, std::string filename) - { - Game::XAssetHeader header = { 0 }; - - // Try loading raw weapon - if (FileSystem::File(Utils::String::VA("weapons/mp/%s", filename.data())).exists()) - { - header.data = Game::BG_LoadWeaponDef_LoadObj(filename.data()); - } - - return header; - } - - Weapon::Weapon() - { - // Intercept weapon loading - AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_WEAPON, Weapon::WeaponFileLoad); - - // weapon asset existence check - Utils::Hook::Nop(0x408228, 5); // find asset header - Utils::Hook::Nop(0x408230, 5); // is asset default - Utils::Hook::Nop(0x40823A, 2); // jump - - // Skip double loading for fs_game - Utils::Hook::Set(0x4081FD, 0xEB); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Game::XAssetHeader Weapon::WeaponFileLoad(Game::XAssetType /*type*/, std::string filename) + { + Game::XAssetHeader header = { 0 }; + + // Try loading raw weapon + if (FileSystem::File(Utils::String::VA("weapons/mp/%s", filename.data())).exists()) + { + header.data = Game::BG_LoadWeaponDef_LoadObj(filename.data()); + } + + return header; + } + + Weapon::Weapon() + { + // Intercept weapon loading + AssetHandler::OnFind(Game::XAssetType::ASSET_TYPE_WEAPON, Weapon::WeaponFileLoad); + + // weapon asset existence check + Utils::Hook::Nop(0x408228, 5); // find asset header + Utils::Hook::Nop(0x408230, 5); // is asset default + Utils::Hook::Nop(0x40823A, 2); // jump + + // Skip double loading for fs_game + Utils::Hook::Set(0x4081FD, 0xEB); + } +} diff --git a/src/Components/Modules/Weapon.hpp b/src/Components/Modules/Weapon.hpp index ce53933f..e92e3c27 100644 --- a/src/Components/Modules/Weapon.hpp +++ b/src/Components/Modules/Weapon.hpp @@ -1,15 +1,15 @@ -namespace Components -{ - class Weapon : public Component - { - public: - Weapon(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Weapon"; }; -#endif - - private: - static Game::XAssetHeader WeaponFileLoad(Game::XAssetType type, std::string filename); - }; -} +namespace Components +{ + class Weapon : public Component + { + public: + Weapon(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Weapon"; }; +#endif + + private: + static Game::XAssetHeader WeaponFileLoad(Game::XAssetType type, std::string filename); + }; +} diff --git a/src/Components/Modules/Window.cpp b/src/Components/Modules/Window.cpp index 3dff0b99..fd812df3 100644 --- a/src/Components/Modules/Window.cpp +++ b/src/Components/Modules/Window.cpp @@ -1,164 +1,164 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Dvar::Var Window::NoBorder; - Dvar::Var Window::NativeCursor; - - HWND Window::MainWindow = 0; - BOOL Window::CursorVisible = TRUE; - - int Window::Width() - { - return Window::Width(Window::MainWindow); - } - - int Window::Height() - { - return Window::Height(Window::MainWindow); - } - - int Window::Width(HWND window) - { - RECT rect; - Window::Dimension(window, &rect); - return (rect.right - rect.left); - } - - int Window::Height(HWND window) - { - RECT rect; - Window::Dimension(window, &rect); - return (rect.bottom - rect.top); - } - - void Window::Dimension(RECT* rect) - { - Window::Dimension(Window::MainWindow, rect); - } - - void Window::Dimension(HWND window, RECT* rect) - { - if (rect) - { - ZeroMemory(rect, sizeof(RECT)); - - if (window && IsWindow(window)) - { - GetWindowRect(window, rect); - } - } - } - - bool Window::IsCursorWithin(HWND window) - { - RECT rect; - POINT point; - Window::Dimension(window, &rect); - - GetCursorPos(&point); - - return ((point.x - rect.left) > 0 && (point.y - rect.top) > 0 && (rect.right - point.x) > 0 && (rect.bottom - point.y) > 0); - } - - int Window::IsNoBorder() - { - return Window::NoBorder.get(); - } - - __declspec(naked) void Window::StyleHookStub() - { - __asm - { - call Window::IsNoBorder - test al, al - jz setBorder - - mov ebp, WS_VISIBLE | WS_POPUP - retn - - setBorder: - mov ebp, WS_VISIBLE | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX - retn - } - } - - void Window::DrawCursorStub(void *scrPlace, float x, float y, float w, float h, int horzAlign, int vertAlign, const float *color, Game::Material *material) - { - if (Window::NativeCursor.get()) - { - Window::CursorVisible = TRUE; - } - else - { - Game::UI_DrawHandlePic(scrPlace, x, y, w, h, horzAlign, vertAlign, color, material); - } - } - - int WINAPI Window::ShowCursorHook(BOOL show) - { - if (Window::NativeCursor.get() && IsWindow(Window::MainWindow) && GetForegroundWindow() == Window::MainWindow && Window::IsCursorWithin(Window::MainWindow)) - { - static int count = 0; - (show ? ++count : --count); - - if (count >= 0) - { - Window::CursorVisible = TRUE; - } - - return count; - } - - return ShowCursor(show); - } - - HWND WINAPI Window::CreateMainWindow(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) - { - Window::MainWindow = CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); - return Window::MainWindow; - } - - Window::Window() - { - // Borderless window - Window::NoBorder = Dvar::Register("r_noborder", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Do not use a border in windowed mode"); - Window::NativeCursor = Dvar::Register("ui_nativeCursor", false, Game::dvar_flag::DVAR_FLAG_SAVED, "Display native cursor"); - - Utils::Hook(0x507643, Window::StyleHookStub, HOOK_CALL).install()->quick(); - - // Main window creation - Utils::Hook::Nop(0x5076AA, 1); - Utils::Hook(0x5076AB, Window::CreateMainWindow, HOOK_CALL).install()->quick(); - - // Mark the cursor as visible - Utils::Hook(0x48E5D3, Window::DrawCursorStub, HOOK_CALL).install()->quick(); - - // Draw the cursor if necessary - Renderer::OnFrame([] () - { - if (Window::NativeCursor.get() && IsWindow(Window::MainWindow) && GetForegroundWindow() == Window::MainWindow && Window::IsCursorWithin(Window::MainWindow)) - { - int value = 0; - - if (Window::CursorVisible) - { - SetCursor(LoadCursor(NULL, IDC_ARROW)); - - while ((value = ShowCursor(TRUE)) < 0); - while (value > 0) { value = ShowCursor(FALSE); } // Set display counter to 0 - } - else - { - while ((value = ShowCursor(FALSE)) >= 0); - while (value < -1) { value = ShowCursor(TRUE); } // Set display counter to -1 - } - - Window::CursorVisible = FALSE; - } - }); - - // Don't let the game interact with the native cursor - Utils::Hook::Set(0x6D7348, Window::ShowCursorHook); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Dvar::Var Window::NoBorder; + Dvar::Var Window::NativeCursor; + + HWND Window::MainWindow = 0; + BOOL Window::CursorVisible = TRUE; + + int Window::Width() + { + return Window::Width(Window::MainWindow); + } + + int Window::Height() + { + return Window::Height(Window::MainWindow); + } + + int Window::Width(HWND window) + { + RECT rect; + Window::Dimension(window, &rect); + return (rect.right - rect.left); + } + + int Window::Height(HWND window) + { + RECT rect; + Window::Dimension(window, &rect); + return (rect.bottom - rect.top); + } + + void Window::Dimension(RECT* rect) + { + Window::Dimension(Window::MainWindow, rect); + } + + void Window::Dimension(HWND window, RECT* rect) + { + if (rect) + { + ZeroMemory(rect, sizeof(RECT)); + + if (window && IsWindow(window)) + { + GetWindowRect(window, rect); + } + } + } + + bool Window::IsCursorWithin(HWND window) + { + RECT rect; + POINT point; + Window::Dimension(window, &rect); + + GetCursorPos(&point); + + return ((point.x - rect.left) > 0 && (point.y - rect.top) > 0 && (rect.right - point.x) > 0 && (rect.bottom - point.y) > 0); + } + + int Window::IsNoBorder() + { + return Window::NoBorder.get(); + } + + __declspec(naked) void Window::StyleHookStub() + { + __asm + { + call Window::IsNoBorder + test al, al + jz setBorder + + mov ebp, WS_VISIBLE | WS_POPUP + retn + + setBorder: + mov ebp, WS_VISIBLE | WS_SYSMENU | WS_CAPTION | WS_MINIMIZEBOX + retn + } + } + + void Window::DrawCursorStub(void *scrPlace, float x, float y, float w, float h, int horzAlign, int vertAlign, const float *color, Game::Material *material) + { + if (Window::NativeCursor.get()) + { + Window::CursorVisible = TRUE; + } + else + { + Game::UI_DrawHandlePic(scrPlace, x, y, w, h, horzAlign, vertAlign, color, material); + } + } + + int WINAPI Window::ShowCursorHook(BOOL show) + { + if (Window::NativeCursor.get() && IsWindow(Window::MainWindow) && GetForegroundWindow() == Window::MainWindow && Window::IsCursorWithin(Window::MainWindow)) + { + static int count = 0; + (show ? ++count : --count); + + if (count >= 0) + { + Window::CursorVisible = TRUE; + } + + return count; + } + + return ShowCursor(show); + } + + HWND WINAPI Window::CreateMainWindow(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam) + { + Window::MainWindow = CreateWindowExA(dwExStyle, lpClassName, lpWindowName, dwStyle, X, Y, nWidth, nHeight, hWndParent, hMenu, hInstance, lpParam); + return Window::MainWindow; + } + + Window::Window() + { + // Borderless window + Window::NoBorder = Dvar::Register("r_noborder", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Do not use a border in windowed mode"); + Window::NativeCursor = Dvar::Register("ui_nativeCursor", false, Game::dvar_flag::DVAR_FLAG_SAVED, "Display native cursor"); + + Utils::Hook(0x507643, Window::StyleHookStub, HOOK_CALL).install()->quick(); + + // Main window creation + Utils::Hook::Nop(0x5076AA, 1); + Utils::Hook(0x5076AB, Window::CreateMainWindow, HOOK_CALL).install()->quick(); + + // Mark the cursor as visible + Utils::Hook(0x48E5D3, Window::DrawCursorStub, HOOK_CALL).install()->quick(); + + // Draw the cursor if necessary + Renderer::OnFrame([] () + { + if (Window::NativeCursor.get() && IsWindow(Window::MainWindow) && GetForegroundWindow() == Window::MainWindow && Window::IsCursorWithin(Window::MainWindow)) + { + int value = 0; + + if (Window::CursorVisible) + { + SetCursor(LoadCursor(NULL, IDC_ARROW)); + + while ((value = ShowCursor(TRUE)) < 0); + while (value > 0) { value = ShowCursor(FALSE); } // Set display counter to 0 + } + else + { + while ((value = ShowCursor(FALSE)) >= 0); + while (value < -1) { value = ShowCursor(TRUE); } // Set display counter to -1 + } + + Window::CursorVisible = FALSE; + } + }); + + // Don't let the game interact with the native cursor + Utils::Hook::Set(0x6D7348, Window::ShowCursorHook); + } +} diff --git a/src/Components/Modules/Window.hpp b/src/Components/Modules/Window.hpp index 3ddcb4b3..32c6f273 100644 --- a/src/Components/Modules/Window.hpp +++ b/src/Components/Modules/Window.hpp @@ -1,36 +1,36 @@ -namespace Components -{ - class Window : public Component - { - public: - Window(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Window"; }; -#endif - - static int Width(); - static int Height(); - static int Width(HWND window); - static int Height(HWND window); - static void Dimension(RECT* rect); - static void Dimension(HWND window, RECT* rect); - - static bool IsCursorWithin(HWND window); - - private: - static BOOL CursorVisible; - static Dvar::Var NoBorder; - static Dvar::Var NativeCursor; - - static HWND MainWindow; - - static int IsNoBorder(); - - static int WINAPI ShowCursorHook(BOOL show); - static void DrawCursorStub(void *scrPlace, float x, float y, float w, float h, int horzAlign, int vertAlign, const float *color, Game::Material *material); - - static void StyleHookStub(); - static HWND WINAPI CreateMainWindow(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam); - }; -} +namespace Components +{ + class Window : public Component + { + public: + Window(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Window"; }; +#endif + + static int Width(); + static int Height(); + static int Width(HWND window); + static int Height(HWND window); + static void Dimension(RECT* rect); + static void Dimension(HWND window, RECT* rect); + + static bool IsCursorWithin(HWND window); + + private: + static BOOL CursorVisible; + static Dvar::Var NoBorder; + static Dvar::Var NativeCursor; + + static HWND MainWindow; + + static int IsNoBorder(); + + static int WINAPI ShowCursorHook(BOOL show); + static void DrawCursorStub(void *scrPlace, float x, float y, float w, float h, int horzAlign, int vertAlign, const float *color, Game::Material *material); + + static void StyleHookStub(); + static HWND WINAPI CreateMainWindow(DWORD dwExStyle, LPCSTR lpClassName, LPCSTR lpWindowName, DWORD dwStyle, int X, int Y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam); + }; +} diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 6fc708b4..5d2b9c78 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -1,872 +1,872 @@ -#include "STDInclude.hpp" - -namespace Components -{ - Utils::Memory::Allocator ZoneBuilder::MemAllocator; - - std::string ZoneBuilder::TraceZone; - std::vector> ZoneBuilder::TraceAssets; - - std::vector> ZoneBuilder::CommonAssets; - - ZoneBuilder::Zone::Zone(std::string name) : dataMap("zone_source/" + name + ".csv"), zoneName(name), indexStart(0), externalSize(0), branding { 0 }, - - // Reserve 100MB by default. - // That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory. - // That way we can be sure it won't need to reallocate memory. - // Side note: if you need a fastfile larger than 100MB, you're doing it wrong- - // Well, decompressed maps can get way larger than 100MB, so let's increase that. - buffer(0xC800000) - {} - - ZoneBuilder::Zone::~Zone() - { -#ifdef DEBUG - for (auto& subAsset : this->loadedSubAssets) - { - bool found = false; - std::string name = Game::DB_GetXAssetName(&subAsset); - - for (auto& alias : this->aliasList) - { - if (subAsset.type == alias.first.type && name == Game::DB_GetXAssetName(&alias.first)) - { - found = true; - break; - } - } - - if (!found) - { - Logger::Error("Asset %s of type %s was loaded, but not written!", name.data(), Game::DB_GetXAssetTypeName(subAsset.type)); - } - } - - for (auto& alias : this->aliasList) - { - bool found = false; - std::string name = Game::DB_GetXAssetName(&alias.first); - - for (auto& subAsset : this->loadedSubAssets) - { - if (subAsset.type == alias.first.type && name == Game::DB_GetXAssetName(&subAsset)) - { - found = true; - break; - } - } - - if (!found) - { - Logger::Error("Asset %s of type %s was written, but not loaded!", name.data(), Game::DB_GetXAssetTypeName(alias.first.type)); - } - } -#endif - - // Unload our fastfiles - Game::XZoneInfo info; - info.name = nullptr; - info.allocFlags = 0; - info.freeFlags = 0x20; - - Game::DB_LoadXAssets(&info, 1, true); - - AssetHandler::ClearTemporaryAssets(); - Localization::ClearTemp(); - } - - Utils::Stream* ZoneBuilder::Zone::getBuffer() - { - return &this->buffer; - } - - Utils::Memory::Allocator* ZoneBuilder::Zone::getAllocator() - { - return &this->memAllocator; - } - - void ZoneBuilder::Zone::Zone::build() - { - this->loadFastFiles(); - - Logger::Print("Linking assets...\n"); - if (!this->loadAssets()) return; - - this->addBranding(); - - Logger::Print("Saving...\n"); - this->saveData(); - - Logger::Print("Compressing...\n"); - this->writeZone(); - } - - void ZoneBuilder::Zone::loadFastFiles() - { - Logger::Print("Loading required FastFiles...\n"); - - for (int i = 0; i < this->dataMap.getRows(); ++i) - { - if (this->dataMap.getElementAt(i, 0) == "require") - { - std::string fastfile = this->dataMap.getElementAt(i, 1); - - if (!Game::DB_IsZoneLoaded(fastfile.data())) - { - Game::XZoneInfo info; - info.name = fastfile.data(); - info.allocFlags = 0x20; - info.freeFlags = 0; - - Game::DB_LoadXAssets(&info, 1, true); - } - else - { - Logger::Print("Zone '%s' already loaded\n", fastfile.data()); - } - } - } - } - - bool ZoneBuilder::Zone::loadAssets() - { - for (int i = 0; i < this->dataMap.getRows(); ++i) - { - if (this->dataMap.getElementAt(i, 0) != "require") - { - if (this->dataMap.getColumns(i) > 2) - { - if (this->dataMap.getElementAt(i, 0) == "localize") - { - std::string stringOverride = this->dataMap.getElementAt(i, 2); - Utils::String::Replace(stringOverride, "\\n", "\n"); - - Localization::SetTemp(this->dataMap.getElementAt(i, 1), stringOverride); - } - else - { - std::string oldName = this->dataMap.getElementAt(i, 1); - std::string newName = this->dataMap.getElementAt(i, 2); - std::string typeName = this->dataMap.getElementAt(i, 0).data(); - Game::XAssetType type = Game::DB_GetXAssetNameType(typeName.data()); - - if (type < Game::XAssetType::ASSET_TYPE_COUNT && type >= 0) - { - this->renameAsset(type, oldName, newName); - } - else - { - Logger::Error("Unable to rename '%s' to '%s' as the asset type '%s' is invalid!", oldName.data(), newName.data(), typeName.data()); - } - } - } - - if (!this->loadAsset(this->dataMap.getElementAt(i, 0), this->dataMap.getElementAt(i, 1), false)) - { - return false; - } - } - } - - return true; - } - - bool ZoneBuilder::Zone::loadAsset(Game::XAssetType type, void* data, bool isSubAsset) - { - Game::XAsset asset{ type, { data } }; - - const char* name = Game::DB_GetXAssetName(&asset); - - if (name) return this->loadAsset(type, std::string(name), isSubAsset); - else return false; - } - - bool ZoneBuilder::Zone::loadAsset(Game::XAssetType type, std::string name, bool isSubAsset) - { - return this->loadAsset(Game::DB_GetXAssetTypeName(type), name, isSubAsset); - } - - bool ZoneBuilder::Zone::loadAsset(std::string typeName, std::string name, bool isSubAsset) - { - Game::XAssetType type = Game::DB_GetXAssetNameType(typeName.data()); - - // Sanitize name for empty assets - if (name[0] == ',') name.erase(name.begin()); - - if (this->findAsset(type, name) != -1 || this->findSubAsset(type, name).data) return true; - - if (type == Game::XAssetType::ASSET_TYPE_INVALID || type >= Game::XAssetType::ASSET_TYPE_COUNT) - { - Logger::Error("Error: Invalid asset type '%s'\n", typeName.data()); - return false; - } - - Game::XAssetHeader assetHeader = AssetHandler::FindAssetForZone(type, name, this, isSubAsset); - if (!assetHeader.data) - { - Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type)); - return false; - } - - Game::XAsset asset; - asset.type = type; - asset.header = assetHeader; - - if (isSubAsset) - { - this->loadedSubAssets.push_back(asset); - } - else - { - this->loadedAssets.push_back(asset); - } - - // Handle script strings - AssetHandler::ZoneMark(asset, this); - - return true; - } - - int ZoneBuilder::Zone::findAsset(Game::XAssetType type, std::string name) - { - if (name[0] == ',') name.erase(name.begin()); - - for (unsigned int i = 0; i < this->loadedAssets.size(); ++i) - { - Game::XAsset* asset = &this->loadedAssets[i]; - - if (asset->type != type) continue; - - const char* assetName = Game::DB_GetXAssetName(asset); - if (assetName[0] == ',') ++assetName; - - if (name == assetName) - { - return i; - } - } - - return -1; - } - - Game::XAssetHeader ZoneBuilder::Zone::findSubAsset(Game::XAssetType type, std::string name) - { - if (name[0] == ',') name.erase(name.begin()); - - for (unsigned int i = 0; i < this->loadedSubAssets.size(); ++i) - { - Game::XAsset* asset = &this->loadedSubAssets[i]; - - if (asset->type != type) continue; - - const char* assetName = Game::DB_GetXAssetName(asset); - if (assetName[0] == ',') ++assetName; - - if (name == assetName) - { - return asset->header; - } - } - - return { 0 }; - } - - Game::XAsset* ZoneBuilder::Zone::getAsset(int index) - { - if (static_cast(index) < this->loadedAssets.size()) - { - return &this->loadedAssets[index]; - } - - return nullptr; - } - - uint32_t ZoneBuilder::Zone::getAssetTableOffset(int index) - { - Utils::Stream::Offset offset; - offset.block = Game::XFILE_BLOCK_VIRTUAL; - offset.offset = (this->indexStart + (index * sizeof(Game::XAsset)) + 4); - return offset.getPackedOffset(); - } - - bool ZoneBuilder::Zone::hasAlias(Game::XAsset asset) - { - return this->getAlias(asset) != 0; - } - - Game::XAssetHeader ZoneBuilder::Zone::saveSubAsset(Game::XAssetType type, void* ptr) - { - Game::XAssetHeader header { ptr }; - Game::XAsset asset { type, header }; - std::string name = Game::DB_GetXAssetName(&asset); - - int assetIndex = this->findAsset(type, name); - if (assetIndex == -1) // nested asset - { - // already written. find alias and store in ptr - if(this->hasAlias(asset)) - { - header.data = reinterpret_cast(this->getAlias(asset)); - } - else - { - asset.header = this->findSubAsset(type, name); - if (!asset.header.data) - { - Logger::Error("Missing required asset '%s' (%s). Export failed!", name.data(), Game::DB_GetXAssetTypeName(type)); - } - -#ifdef DEBUG - Components::Logger::Print("Saving require (%s): %s\n", Game::DB_GetXAssetTypeName(type), Game::DB_GetXAssetNameHandlers[type](&header)); -#endif - - // we alias the next 4 (aligned) bytes of the stream b/c DB_InsertPointer gives us a nice pointer to use as the alias - // otherwise it would be a fuckfest trying to figure out where the alias is in the stream - this->buffer.pushBlock(Game::XFILE_BLOCK_VIRTUAL); - this->buffer.align(Utils::Stream::ALIGN_4); - this->storeAlias(asset); - this->buffer.increaseBlockSize(4); - this->buffer.popBlock(); - - this->buffer.pushBlock(Game::XFILE_BLOCK_TEMP); - this->buffer.align(Utils::Stream::ALIGN_4); - AssetHandler::ZoneSave(asset, this); - this->buffer.popBlock(); - - header.data = reinterpret_cast(-2); // DB_InsertPointer marker - } - } - else - { - // asset was written normally. not sure this is even possible but its here - header.data = reinterpret_cast(this->getAssetTableOffset(assetIndex)); - } - - return header; - } - - void ZoneBuilder::Zone::writeZone() - { - FILETIME fileTime; - GetSystemTimeAsFileTime(&fileTime); - - Game::XFileHeader header = - { -#ifdef DEBUG - XFILE_MAGIC_UNSIGNED, -#else - XFILE_HEADER_IW4X | (static_cast(XFILE_VERSION_IW4X) << 32), -#endif - XFILE_VERSION, - Game::XFileLanguage::XLANG_NONE, - fileTime.dwHighDateTime, - fileTime.dwLowDateTime - }; - - std::string outBuffer; - outBuffer.append(reinterpret_cast(&header), sizeof(header)); - - std::string zoneBuffer = this->buffer.toBuffer(); - -#ifndef DEBUG - // Insert a random byte, this will destroy the whole alignment and result in a crash, if not handled - zoneBuffer.insert(zoneBuffer.begin(), static_cast(Utils::Cryptography::Rand::GenerateInt())); -#endif - - zoneBuffer = Utils::Compression::ZLib::Compress(zoneBuffer); - outBuffer.append(zoneBuffer); - - std::string outFile = "zone/" + this->zoneName + ".ff"; - Utils::IO::WriteFile(outFile, outBuffer); - - Logger::Print("done.\n"); - Logger::Print("Zone '%s' written with %d assets and %d script strings\n", outFile.data(), (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size()); - } - - void ZoneBuilder::Zone::saveData() - { - // Add header - Game::ZoneHeader zoneHeader = { 0 }; - zoneHeader.assetList.assetCount = this->loadedAssets.size(); - Utils::Stream::ClearPointer(&zoneHeader.assetList.assets); - - // Increment ScriptStrings count (for empty script string) if available - if (!this->scriptStrings.empty()) - { - zoneHeader.assetList.stringList.count = this->scriptStrings.size() + 1; - Utils::Stream::ClearPointer(&zoneHeader.assetList.stringList.strings); - } - - // Write header - this->buffer.save(&zoneHeader, sizeof(Game::ZoneHeader)); - this->buffer.pushBlock(Game::XFILE_BLOCK_VIRTUAL); // Push main stream onto the stream stack - - // Write ScriptStrings, if available - if (!this->scriptStrings.empty()) - { - this->buffer.saveNull(4); // Empty script string? - // This actually represents a NULL string, but as scriptString. - // So scriptString loading for NULL scriptStrings from fastfile results in a NULL scriptString. - // That's the reason why the count is incremented by 1, if scriptStrings are available. - - // Write ScriptString pointer table - for (size_t i = 0; i < this->scriptStrings.size(); ++i) - { - this->buffer.saveMax(4); - } - - this->buffer.align(Utils::Stream::ALIGN_4); - - // Write ScriptStrings - for (auto ScriptString : this->scriptStrings) - { - this->buffer.saveString(ScriptString.data()); - } - } - - // Align buffer (4 bytes) to get correct offsets for pointers - this->buffer.align(Utils::Stream::ALIGN_4); - this->indexStart = this->buffer.getBlockSize(Game::XFILE_BLOCK_VIRTUAL); // Mark AssetTable offset - - // AssetTable - for (auto asset : this->loadedAssets) - { - Game::XAsset entry = { asset.type, 0 }; - Utils::Stream::ClearPointer(&entry.header.data); - - this->buffer.save(&entry); - } - - // Assets - for (auto asset : this->loadedAssets) - { - this->buffer.pushBlock(Game::XFILE_BLOCK_TEMP); - this->buffer.align(Utils::Stream::ALIGN_4); - -#ifdef DEBUG - Components::Logger::Print("Saving (%s): %s\n", Game::DB_GetXAssetTypeName(asset.type), Game::DB_GetXAssetNameHandlers[asset.type](&asset.header)); -#endif - - this->store(asset.header); - AssetHandler::ZoneSave(asset, this); - - this->buffer.popBlock(); - } - - // Adapt header - this->buffer.enterCriticalSection(); - Game::XFile* header = reinterpret_cast(this->buffer.data()); - header->size = this->buffer.length() - sizeof(Game::XFile); // Write correct data size - header->externalSize = this->externalSize; // This actually stores how much external data has to be loaded. It's used to calculate the loadscreen progress - - // Write stream sizes - for (int i = 0; i < Game::MAX_XFILE_COUNT; ++i) - { - header->blockSize[i] = this->buffer.getBlockSize(static_cast(i)); - } - - this->buffer.leaveCriticalSection(); - this->buffer.popBlock(); - } - - // Add branding asset - void ZoneBuilder::Zone::addBranding() - { - char* data = "FastFile built using IW4x ZoneTool!"; - this->branding = { this->zoneName.data(), (int)strlen(data), 0, data }; - - if (this->findAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, this->branding.name) != -1) - { - Logger::Error("Unable to add branding. Asset '%s' already exists!", this->branding.name); - } - - Game::XAssetHeader header = { &this->branding }; - Game::XAsset brandingAsset = { Game::XAssetType::ASSET_TYPE_RAWFILE, header }; - this->loadedAssets.push_back(brandingAsset); - } - - // Check if the given pointer has already been mapped - bool ZoneBuilder::Zone::hasPointer(const void* pointer) - { - return (this->pointerMap.find(pointer) != this->pointerMap.end()); - } - - // Get stored offset for given file pointer - unsigned int ZoneBuilder::Zone::safeGetPointer(const void* pointer) - { - if (this->hasPointer(pointer)) - { - return this->pointerMap[pointer]; - } - - return NULL; - } - - void ZoneBuilder::Zone::storePointer(const void* pointer) - { - this->pointerMap[pointer] = this->buffer.getPackedOffset(); - } - - void ZoneBuilder::Zone::storeAlias(Game::XAsset asset) - { - if (!this->hasAlias(asset)) - { - this->aliasList.push_back({ asset, this->buffer.getPackedOffset() }); - } - } - - unsigned int ZoneBuilder::Zone::getAlias(Game::XAsset asset) - { - std::string name = Game::DB_GetXAssetName(&asset); - - for (auto& entry : this->aliasList) - { - if (asset.type == entry.first.type && name == Game::DB_GetXAssetName(&entry.first)) - { - return entry.second; - } - } - - return 0; - } - - int ZoneBuilder::Zone::addScriptString(std::string str) - { - return this->addScriptString(Game::SL_GetString(str.data(), 0)); - } - - // Mark a scriptString for writing and map it. - int ZoneBuilder::Zone::addScriptString(unsigned short gameIndex) - { - // Handle NULL scriptStrings - // Might optimize that later - if (!gameIndex) - { - if (this->scriptStrings.empty()) - { - this->scriptStrings.push_back(""); - } - - return 0; - } - - std::string str = Game::SL_ConvertToString(gameIndex); - int prev = this->findScriptString(str); - - if (prev > 0) - { - this->scriptStringMap[gameIndex] = prev; - return prev; - } - - this->scriptStrings.push_back(str); - this->scriptStringMap[gameIndex] = this->scriptStrings.size(); - return this->scriptStrings.size(); - } - - // Find a local scriptString - int ZoneBuilder::Zone::findScriptString(std::string str) - { - for (unsigned int i = 0; i < this->scriptStrings.size(); ++i) - { - if (this->scriptStrings[i] == str) - { - return (i + 1); - } - } - - return -1; - } - - // Remap a scriptString to it's corresponding value in the local scriptString table. - void ZoneBuilder::Zone::mapScriptString(unsigned short* gameIndex) - { - *gameIndex = 0xFFFF & this->scriptStringMap[*gameIndex]; - } - - // Store a new name for a given asset - void ZoneBuilder::Zone::renameAsset(Game::XAssetType type, std::string asset, std::string newName) - { - if (type < Game::XAssetType::ASSET_TYPE_COUNT && type >= 0) - { - this->renameMap[type][asset] = newName; - } - else - { - Logger::Error("Unable to rename '%s' to '%s' as the asset type is invalid!", asset.data(), newName.data()); - } - } - - // Return the new name for a given asset - std::string ZoneBuilder::Zone::getAssetName(Game::XAssetType type, std::string asset) - { - if (type < Game::XAssetType::ASSET_TYPE_COUNT && type >= 0) - { - if (this->renameMap[type].find(asset) != this->renameMap[type].end()) - { - return this->renameMap[type][asset]; - } - } - else - { - Logger::Error("Unable to get name for '%s' as the asset type is invalid!", asset.data()); - } - - return asset; - } - - void ZoneBuilder::Zone::store(Game::XAssetHeader header) - { - if (!this->hasPointer(header.data)) // We should never have to restore a pointer, so this expression should always resolve into false - { - this->storePointer(header.data); - } - } - - void ZoneBuilder::Zone::incrementExternalSize(unsigned int size) - { - this->externalSize += size; - } - - bool ZoneBuilder::IsEnabled() - { - static Utils::Value flag; - - if (!flag.isValid()) - { - flag.set(Flags::HasFlag("zonebuilder")); - } - - return (flag.get() && !Dedicated::IsEnabled()); - } - - void ZoneBuilder::BeginAssetTrace(std::string zone) - { - ZoneBuilder::TraceZone = zone; - } - - std::vector> ZoneBuilder::EndAssetTrace() - { - ZoneBuilder::TraceZone.clear(); - - std::vector> AssetTrace; - Utils::Merge(&AssetTrace, ZoneBuilder::TraceAssets); - - ZoneBuilder::TraceAssets.clear(); - - return AssetTrace; - } - - Game::XAssetHeader ZoneBuilder::GetEmptyAssetIfCommon(Game::XAssetType type, std::string name, ZoneBuilder::Zone* builder) - { - Game::XAssetHeader header = { 0 }; - - if (type >= 0 && type < Game::XAssetType::ASSET_TYPE_COUNT) - { - for (auto& asset : ZoneBuilder::CommonAssets) - { - if (asset.first == type && asset.second == name) - { - // Allocate an empty asset (filled with zeros) - header.data = builder->getAllocator()->allocate(Game::DB_GetXAssetSizeHandlers[type]()); - - // Set the name to the original name, so it can be stored - Game::DB_SetXAssetNameHandlers[type](&header, name.data()); - AssetHandler::StoreTemporaryAsset(type, header); - - // Set the name to the empty name - Game::DB_SetXAssetNameHandlers[type](&header, builder->getAllocator()->duplicateString("," + name)); - break; - } - } - } - - return header; - } - - int ZoneBuilder::StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image) - { - size_t size = 16 + (*loadDef)->resourceSize; - void* data = ZoneBuilder::MemAllocator.allocate(size); - std::memcpy(data, *loadDef, size); - - image->loadDef = reinterpret_cast(data); - - return 0; - } - - void ZoneBuilder::ReleaseTexture(Game::XAssetHeader header) - { - if (header.image && header.image->loadDef) - { - ZoneBuilder::MemAllocator.free(header.image->loadDef); - } - } - - ZoneBuilder::ZoneBuilder() - { - AssertSize(Game::XFileHeader, 21); - AssertSize(Game::XFile, 40); - static_assert(Game::MAX_XFILE_COUNT == 8, "XFile block enum is invalid!"); - - ZoneBuilder::EndAssetTrace(); - - if (ZoneBuilder::IsEnabled()) - { - // Prevent loading textures (preserves loaddef) - //Utils::Hook::Set(Game::Load_Texture, 0xC3); - - // Store the loaddef - Utils::Hook(Game::Load_Texture, StoreTexture, HOOK_JUMP).install()->quick(); - - // Release the loaddef - Game::DB_ReleaseXAssetHandlers[Game::XAssetType::ASSET_TYPE_IMAGE] = ZoneBuilder::ReleaseTexture; - - //r_loadForrenderer = 0 - Utils::Hook::Set(0x519DDF, 0); - - //r_delayloadimage retn - Utils::Hook::Set(0x51F450, 0xC3); - - // r_registerDvars hack - Utils::Hook::Set(0x51B1CD, 0xC3); - - // Prevent destroying textures - Utils::Hook::Set(0x51F03D, 0xEB); - - // Don't create default assets - Utils::Hook::Set(0x407BAA, 0xEB); - - // Don't mark clip maps as 'in use' - Utils::Hook::Nop(0x405E07, 7); - - // Don't mark assets - //Utils::Hook::Nop(0x5BB632, 5); - - // Don't load sounds - //Utils::Hook::Set(0x413430, 0xC3); - - // Don't display errors when assets are missing (we might manually build those) - Utils::Hook::Nop(0x5BB3F2, 5); - Utils::Hook::Nop(0x5BB422, 5); - Utils::Hook::Nop(0x5BB43A, 5); - - // Increase asset pools - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MAP_ENTS, 10); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODELSURFS, 8192); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, 14336); - Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, 1536); - - // hunk size (was 300 MiB) - Utils::Hook::Set(0x64A029, 0x38400000); // 900 MiB - Utils::Hook::Set(0x64A057, 0x38400000); - - AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader /*asset*/, std::string name, bool* /*restrict*/) - { - // This is used to track which assets can be stored as empty assets - if (FastFiles::Current() == "common_mp") - { - ZoneBuilder::CommonAssets.push_back({ type, name }); - } - - if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current()) - { - ZoneBuilder::TraceAssets.push_back({ type, name }); - } - }); - - Command::Add("verifyzone", [](Command::Params* params) - { - if (params->length() < 2) return; - - std::string zone = params->get(1); - - ZoneBuilder::BeginAssetTrace(zone); - - Game::XZoneInfo info; - info.name = zone.data(); - info.allocFlags = 0x20; - info.freeFlags = 0; - - Logger::Print("Loading zone '%s'...\n", zone.data()); - - Game::DB_LoadXAssets(&info, 1, true); - AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, zone.data()); // Lock until zone is loaded - - auto assets = ZoneBuilder::EndAssetTrace(); - - Logger::Print("Unloading zone '%s'...\n", zone.data()); - info.freeFlags = 0x20; - info.allocFlags = 0; - info.name = nullptr; - - Game::DB_LoadXAssets(&info, 1, true); - AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, "default"); // Lock until zone is unloaded - - Logger::Print("Zone '%s' loaded with %d assets:\n", zone.data(), assets.size()); - - int count = 0; - for (auto i = assets.begin(); i != assets.end(); ++i, ++count) - { - Logger::Print(" %d: %s: %s\n", count, Game::DB_GetXAssetTypeName(i->first), i->second.data()); - } - - Logger::Print("\n"); - }); - - Command::Add("buildzone", [](Command::Params* params) - { - if (params->length() < 2) return; - - std::string zoneName = params->get(1); - Logger::Print("Building zone '%s'...\n", zoneName.data()); - - Zone(zoneName).build(); - }); - - Command::Add("buildall", [](Command::Params*) - { - auto zoneSources = FileSystem::GetSysFileList(Dvar::Var("fs_basepath").get() + "\\zone_source", "csv", false); - - for (auto source : zoneSources) - { - if (Utils::String::EndsWith(source, ".csv")) - { - source = source.substr(0, source.find(".csv")); - } - - Command::Execute(Utils::String::VA("buildzone %s", source.data()), true); - } - }); - - Command::Add("listassets", [](Command::Params* params) - { - if (params->length() < 2) return; - Game::XAssetType type = Game::DB_GetXAssetNameType(params->get(1)); - - if (type != Game::XAssetType::ASSET_TYPE_INVALID) - { - Game::DB_EnumXAssets(type, [](Game::XAssetHeader header, void* data) - { - Game::XAsset asset = { *reinterpret_cast(data), header }; - Logger::Print("%s\n", Game::DB_GetXAssetName(&asset)); - }, &type, false); - } - }); - - Command::Add("materialInfoDump", [](Command::Params*) - { - Game::DB_EnumXAssets(Game::ASSET_TYPE_MATERIAL, [](Game::XAssetHeader header, void*) - { - Logger::Print("%s: %X %X %X\n", header.material->name, header.material->sortKey & 0xFF, header.material->gameFlags & 0xFF, header.material->stateFlags & 0xFF); - }, nullptr, false); - }); - } - } - - ZoneBuilder::~ZoneBuilder() - { - assert(ZoneBuilder::MemAllocator.empty()); - ZoneBuilder::CommonAssets.clear(); - } -} +#include "STDInclude.hpp" + +namespace Components +{ + Utils::Memory::Allocator ZoneBuilder::MemAllocator; + + std::string ZoneBuilder::TraceZone; + std::vector> ZoneBuilder::TraceAssets; + + std::vector> ZoneBuilder::CommonAssets; + + ZoneBuilder::Zone::Zone(std::string name) : dataMap("zone_source/" + name + ".csv"), zoneName(name), indexStart(0), externalSize(0), branding { 0 }, + + // Reserve 100MB by default. + // That's totally fine, as the dedi doesn't load images and therefore doesn't need much memory. + // That way we can be sure it won't need to reallocate memory. + // Side note: if you need a fastfile larger than 100MB, you're doing it wrong- + // Well, decompressed maps can get way larger than 100MB, so let's increase that. + buffer(0xC800000) + {} + + ZoneBuilder::Zone::~Zone() + { +#ifdef DEBUG + for (auto& subAsset : this->loadedSubAssets) + { + bool found = false; + std::string name = Game::DB_GetXAssetName(&subAsset); + + for (auto& alias : this->aliasList) + { + if (subAsset.type == alias.first.type && name == Game::DB_GetXAssetName(&alias.first)) + { + found = true; + break; + } + } + + if (!found) + { + Logger::Error("Asset %s of type %s was loaded, but not written!", name.data(), Game::DB_GetXAssetTypeName(subAsset.type)); + } + } + + for (auto& alias : this->aliasList) + { + bool found = false; + std::string name = Game::DB_GetXAssetName(&alias.first); + + for (auto& subAsset : this->loadedSubAssets) + { + if (subAsset.type == alias.first.type && name == Game::DB_GetXAssetName(&subAsset)) + { + found = true; + break; + } + } + + if (!found) + { + Logger::Error("Asset %s of type %s was written, but not loaded!", name.data(), Game::DB_GetXAssetTypeName(alias.first.type)); + } + } +#endif + + // Unload our fastfiles + Game::XZoneInfo info; + info.name = nullptr; + info.allocFlags = 0; + info.freeFlags = 0x20; + + Game::DB_LoadXAssets(&info, 1, true); + + AssetHandler::ClearTemporaryAssets(); + Localization::ClearTemp(); + } + + Utils::Stream* ZoneBuilder::Zone::getBuffer() + { + return &this->buffer; + } + + Utils::Memory::Allocator* ZoneBuilder::Zone::getAllocator() + { + return &this->memAllocator; + } + + void ZoneBuilder::Zone::Zone::build() + { + this->loadFastFiles(); + + Logger::Print("Linking assets...\n"); + if (!this->loadAssets()) return; + + this->addBranding(); + + Logger::Print("Saving...\n"); + this->saveData(); + + Logger::Print("Compressing...\n"); + this->writeZone(); + } + + void ZoneBuilder::Zone::loadFastFiles() + { + Logger::Print("Loading required FastFiles...\n"); + + for (int i = 0; i < this->dataMap.getRows(); ++i) + { + if (this->dataMap.getElementAt(i, 0) == "require") + { + std::string fastfile = this->dataMap.getElementAt(i, 1); + + if (!Game::DB_IsZoneLoaded(fastfile.data())) + { + Game::XZoneInfo info; + info.name = fastfile.data(); + info.allocFlags = 0x20; + info.freeFlags = 0; + + Game::DB_LoadXAssets(&info, 1, true); + } + else + { + Logger::Print("Zone '%s' already loaded\n", fastfile.data()); + } + } + } + } + + bool ZoneBuilder::Zone::loadAssets() + { + for (int i = 0; i < this->dataMap.getRows(); ++i) + { + if (this->dataMap.getElementAt(i, 0) != "require") + { + if (this->dataMap.getColumns(i) > 2) + { + if (this->dataMap.getElementAt(i, 0) == "localize") + { + std::string stringOverride = this->dataMap.getElementAt(i, 2); + Utils::String::Replace(stringOverride, "\\n", "\n"); + + Localization::SetTemp(this->dataMap.getElementAt(i, 1), stringOverride); + } + else + { + std::string oldName = this->dataMap.getElementAt(i, 1); + std::string newName = this->dataMap.getElementAt(i, 2); + std::string typeName = this->dataMap.getElementAt(i, 0).data(); + Game::XAssetType type = Game::DB_GetXAssetNameType(typeName.data()); + + if (type < Game::XAssetType::ASSET_TYPE_COUNT && type >= 0) + { + this->renameAsset(type, oldName, newName); + } + else + { + Logger::Error("Unable to rename '%s' to '%s' as the asset type '%s' is invalid!", oldName.data(), newName.data(), typeName.data()); + } + } + } + + if (!this->loadAsset(this->dataMap.getElementAt(i, 0), this->dataMap.getElementAt(i, 1), false)) + { + return false; + } + } + } + + return true; + } + + bool ZoneBuilder::Zone::loadAsset(Game::XAssetType type, void* data, bool isSubAsset) + { + Game::XAsset asset{ type, { data } }; + + const char* name = Game::DB_GetXAssetName(&asset); + + if (name) return this->loadAsset(type, std::string(name), isSubAsset); + else return false; + } + + bool ZoneBuilder::Zone::loadAsset(Game::XAssetType type, std::string name, bool isSubAsset) + { + return this->loadAsset(Game::DB_GetXAssetTypeName(type), name, isSubAsset); + } + + bool ZoneBuilder::Zone::loadAsset(std::string typeName, std::string name, bool isSubAsset) + { + Game::XAssetType type = Game::DB_GetXAssetNameType(typeName.data()); + + // Sanitize name for empty assets + if (name[0] == ',') name.erase(name.begin()); + + if (this->findAsset(type, name) != -1 || this->findSubAsset(type, name).data) return true; + + if (type == Game::XAssetType::ASSET_TYPE_INVALID || type >= Game::XAssetType::ASSET_TYPE_COUNT) + { + Logger::Error("Error: Invalid asset type '%s'\n", typeName.data()); + return false; + } + + Game::XAssetHeader assetHeader = AssetHandler::FindAssetForZone(type, name, this, isSubAsset); + if (!assetHeader.data) + { + Logger::Error("Error: Missing asset '%s' of type '%s'\n", name.data(), Game::DB_GetXAssetTypeName(type)); + return false; + } + + Game::XAsset asset; + asset.type = type; + asset.header = assetHeader; + + if (isSubAsset) + { + this->loadedSubAssets.push_back(asset); + } + else + { + this->loadedAssets.push_back(asset); + } + + // Handle script strings + AssetHandler::ZoneMark(asset, this); + + return true; + } + + int ZoneBuilder::Zone::findAsset(Game::XAssetType type, std::string name) + { + if (name[0] == ',') name.erase(name.begin()); + + for (unsigned int i = 0; i < this->loadedAssets.size(); ++i) + { + Game::XAsset* asset = &this->loadedAssets[i]; + + if (asset->type != type) continue; + + const char* assetName = Game::DB_GetXAssetName(asset); + if (assetName[0] == ',') ++assetName; + + if (name == assetName) + { + return i; + } + } + + return -1; + } + + Game::XAssetHeader ZoneBuilder::Zone::findSubAsset(Game::XAssetType type, std::string name) + { + if (name[0] == ',') name.erase(name.begin()); + + for (unsigned int i = 0; i < this->loadedSubAssets.size(); ++i) + { + Game::XAsset* asset = &this->loadedSubAssets[i]; + + if (asset->type != type) continue; + + const char* assetName = Game::DB_GetXAssetName(asset); + if (assetName[0] == ',') ++assetName; + + if (name == assetName) + { + return asset->header; + } + } + + return { 0 }; + } + + Game::XAsset* ZoneBuilder::Zone::getAsset(int index) + { + if (static_cast(index) < this->loadedAssets.size()) + { + return &this->loadedAssets[index]; + } + + return nullptr; + } + + uint32_t ZoneBuilder::Zone::getAssetTableOffset(int index) + { + Utils::Stream::Offset offset; + offset.block = Game::XFILE_BLOCK_VIRTUAL; + offset.offset = (this->indexStart + (index * sizeof(Game::XAsset)) + 4); + return offset.getPackedOffset(); + } + + bool ZoneBuilder::Zone::hasAlias(Game::XAsset asset) + { + return this->getAlias(asset) != 0; + } + + Game::XAssetHeader ZoneBuilder::Zone::saveSubAsset(Game::XAssetType type, void* ptr) + { + Game::XAssetHeader header { ptr }; + Game::XAsset asset { type, header }; + std::string name = Game::DB_GetXAssetName(&asset); + + int assetIndex = this->findAsset(type, name); + if (assetIndex == -1) // nested asset + { + // already written. find alias and store in ptr + if(this->hasAlias(asset)) + { + header.data = reinterpret_cast(this->getAlias(asset)); + } + else + { + asset.header = this->findSubAsset(type, name); + if (!asset.header.data) + { + Logger::Error("Missing required asset '%s' (%s). Export failed!", name.data(), Game::DB_GetXAssetTypeName(type)); + } + +#ifdef DEBUG + Components::Logger::Print("Saving require (%s): %s\n", Game::DB_GetXAssetTypeName(type), Game::DB_GetXAssetNameHandlers[type](&header)); +#endif + + // we alias the next 4 (aligned) bytes of the stream b/c DB_InsertPointer gives us a nice pointer to use as the alias + // otherwise it would be a fuckfest trying to figure out where the alias is in the stream + this->buffer.pushBlock(Game::XFILE_BLOCK_VIRTUAL); + this->buffer.align(Utils::Stream::ALIGN_4); + this->storeAlias(asset); + this->buffer.increaseBlockSize(4); + this->buffer.popBlock(); + + this->buffer.pushBlock(Game::XFILE_BLOCK_TEMP); + this->buffer.align(Utils::Stream::ALIGN_4); + AssetHandler::ZoneSave(asset, this); + this->buffer.popBlock(); + + header.data = reinterpret_cast(-2); // DB_InsertPointer marker + } + } + else + { + // asset was written normally. not sure this is even possible but its here + header.data = reinterpret_cast(this->getAssetTableOffset(assetIndex)); + } + + return header; + } + + void ZoneBuilder::Zone::writeZone() + { + FILETIME fileTime; + GetSystemTimeAsFileTime(&fileTime); + + Game::XFileHeader header = + { +#ifdef DEBUG + XFILE_MAGIC_UNSIGNED, +#else + XFILE_HEADER_IW4X | (static_cast(XFILE_VERSION_IW4X) << 32), +#endif + XFILE_VERSION, + Game::XFileLanguage::XLANG_NONE, + fileTime.dwHighDateTime, + fileTime.dwLowDateTime + }; + + std::string outBuffer; + outBuffer.append(reinterpret_cast(&header), sizeof(header)); + + std::string zoneBuffer = this->buffer.toBuffer(); + +#ifndef DEBUG + // Insert a random byte, this will destroy the whole alignment and result in a crash, if not handled + zoneBuffer.insert(zoneBuffer.begin(), static_cast(Utils::Cryptography::Rand::GenerateInt())); +#endif + + zoneBuffer = Utils::Compression::ZLib::Compress(zoneBuffer); + outBuffer.append(zoneBuffer); + + std::string outFile = "zone/" + this->zoneName + ".ff"; + Utils::IO::WriteFile(outFile, outBuffer); + + Logger::Print("done.\n"); + Logger::Print("Zone '%s' written with %d assets and %d script strings\n", outFile.data(), (this->aliasList.size() + this->loadedAssets.size()), this->scriptStrings.size()); + } + + void ZoneBuilder::Zone::saveData() + { + // Add header + Game::ZoneHeader zoneHeader = { 0 }; + zoneHeader.assetList.assetCount = this->loadedAssets.size(); + Utils::Stream::ClearPointer(&zoneHeader.assetList.assets); + + // Increment ScriptStrings count (for empty script string) if available + if (!this->scriptStrings.empty()) + { + zoneHeader.assetList.stringList.count = this->scriptStrings.size() + 1; + Utils::Stream::ClearPointer(&zoneHeader.assetList.stringList.strings); + } + + // Write header + this->buffer.save(&zoneHeader, sizeof(Game::ZoneHeader)); + this->buffer.pushBlock(Game::XFILE_BLOCK_VIRTUAL); // Push main stream onto the stream stack + + // Write ScriptStrings, if available + if (!this->scriptStrings.empty()) + { + this->buffer.saveNull(4); // Empty script string? + // This actually represents a NULL string, but as scriptString. + // So scriptString loading for NULL scriptStrings from fastfile results in a NULL scriptString. + // That's the reason why the count is incremented by 1, if scriptStrings are available. + + // Write ScriptString pointer table + for (size_t i = 0; i < this->scriptStrings.size(); ++i) + { + this->buffer.saveMax(4); + } + + this->buffer.align(Utils::Stream::ALIGN_4); + + // Write ScriptStrings + for (auto ScriptString : this->scriptStrings) + { + this->buffer.saveString(ScriptString.data()); + } + } + + // Align buffer (4 bytes) to get correct offsets for pointers + this->buffer.align(Utils::Stream::ALIGN_4); + this->indexStart = this->buffer.getBlockSize(Game::XFILE_BLOCK_VIRTUAL); // Mark AssetTable offset + + // AssetTable + for (auto asset : this->loadedAssets) + { + Game::XAsset entry = { asset.type, 0 }; + Utils::Stream::ClearPointer(&entry.header.data); + + this->buffer.save(&entry); + } + + // Assets + for (auto asset : this->loadedAssets) + { + this->buffer.pushBlock(Game::XFILE_BLOCK_TEMP); + this->buffer.align(Utils::Stream::ALIGN_4); + +#ifdef DEBUG + Components::Logger::Print("Saving (%s): %s\n", Game::DB_GetXAssetTypeName(asset.type), Game::DB_GetXAssetNameHandlers[asset.type](&asset.header)); +#endif + + this->store(asset.header); + AssetHandler::ZoneSave(asset, this); + + this->buffer.popBlock(); + } + + // Adapt header + this->buffer.enterCriticalSection(); + Game::XFile* header = reinterpret_cast(this->buffer.data()); + header->size = this->buffer.length() - sizeof(Game::XFile); // Write correct data size + header->externalSize = this->externalSize; // This actually stores how much external data has to be loaded. It's used to calculate the loadscreen progress + + // Write stream sizes + for (int i = 0; i < Game::MAX_XFILE_COUNT; ++i) + { + header->blockSize[i] = this->buffer.getBlockSize(static_cast(i)); + } + + this->buffer.leaveCriticalSection(); + this->buffer.popBlock(); + } + + // Add branding asset + void ZoneBuilder::Zone::addBranding() + { + char* data = "FastFile built using IW4x ZoneTool!"; + this->branding = { this->zoneName.data(), (int)strlen(data), 0, data }; + + if (this->findAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, this->branding.name) != -1) + { + Logger::Error("Unable to add branding. Asset '%s' already exists!", this->branding.name); + } + + Game::XAssetHeader header = { &this->branding }; + Game::XAsset brandingAsset = { Game::XAssetType::ASSET_TYPE_RAWFILE, header }; + this->loadedAssets.push_back(brandingAsset); + } + + // Check if the given pointer has already been mapped + bool ZoneBuilder::Zone::hasPointer(const void* pointer) + { + return (this->pointerMap.find(pointer) != this->pointerMap.end()); + } + + // Get stored offset for given file pointer + unsigned int ZoneBuilder::Zone::safeGetPointer(const void* pointer) + { + if (this->hasPointer(pointer)) + { + return this->pointerMap[pointer]; + } + + return NULL; + } + + void ZoneBuilder::Zone::storePointer(const void* pointer) + { + this->pointerMap[pointer] = this->buffer.getPackedOffset(); + } + + void ZoneBuilder::Zone::storeAlias(Game::XAsset asset) + { + if (!this->hasAlias(asset)) + { + this->aliasList.push_back({ asset, this->buffer.getPackedOffset() }); + } + } + + unsigned int ZoneBuilder::Zone::getAlias(Game::XAsset asset) + { + std::string name = Game::DB_GetXAssetName(&asset); + + for (auto& entry : this->aliasList) + { + if (asset.type == entry.first.type && name == Game::DB_GetXAssetName(&entry.first)) + { + return entry.second; + } + } + + return 0; + } + + int ZoneBuilder::Zone::addScriptString(std::string str) + { + return this->addScriptString(Game::SL_GetString(str.data(), 0)); + } + + // Mark a scriptString for writing and map it. + int ZoneBuilder::Zone::addScriptString(unsigned short gameIndex) + { + // Handle NULL scriptStrings + // Might optimize that later + if (!gameIndex) + { + if (this->scriptStrings.empty()) + { + this->scriptStrings.push_back(""); + } + + return 0; + } + + std::string str = Game::SL_ConvertToString(gameIndex); + int prev = this->findScriptString(str); + + if (prev > 0) + { + this->scriptStringMap[gameIndex] = prev; + return prev; + } + + this->scriptStrings.push_back(str); + this->scriptStringMap[gameIndex] = this->scriptStrings.size(); + return this->scriptStrings.size(); + } + + // Find a local scriptString + int ZoneBuilder::Zone::findScriptString(std::string str) + { + for (unsigned int i = 0; i < this->scriptStrings.size(); ++i) + { + if (this->scriptStrings[i] == str) + { + return (i + 1); + } + } + + return -1; + } + + // Remap a scriptString to it's corresponding value in the local scriptString table. + void ZoneBuilder::Zone::mapScriptString(unsigned short* gameIndex) + { + *gameIndex = 0xFFFF & this->scriptStringMap[*gameIndex]; + } + + // Store a new name for a given asset + void ZoneBuilder::Zone::renameAsset(Game::XAssetType type, std::string asset, std::string newName) + { + if (type < Game::XAssetType::ASSET_TYPE_COUNT && type >= 0) + { + this->renameMap[type][asset] = newName; + } + else + { + Logger::Error("Unable to rename '%s' to '%s' as the asset type is invalid!", asset.data(), newName.data()); + } + } + + // Return the new name for a given asset + std::string ZoneBuilder::Zone::getAssetName(Game::XAssetType type, std::string asset) + { + if (type < Game::XAssetType::ASSET_TYPE_COUNT && type >= 0) + { + if (this->renameMap[type].find(asset) != this->renameMap[type].end()) + { + return this->renameMap[type][asset]; + } + } + else + { + Logger::Error("Unable to get name for '%s' as the asset type is invalid!", asset.data()); + } + + return asset; + } + + void ZoneBuilder::Zone::store(Game::XAssetHeader header) + { + if (!this->hasPointer(header.data)) // We should never have to restore a pointer, so this expression should always resolve into false + { + this->storePointer(header.data); + } + } + + void ZoneBuilder::Zone::incrementExternalSize(unsigned int size) + { + this->externalSize += size; + } + + bool ZoneBuilder::IsEnabled() + { + static Utils::Value flag; + + if (!flag.isValid()) + { + flag.set(Flags::HasFlag("zonebuilder")); + } + + return (flag.get() && !Dedicated::IsEnabled()); + } + + void ZoneBuilder::BeginAssetTrace(std::string zone) + { + ZoneBuilder::TraceZone = zone; + } + + std::vector> ZoneBuilder::EndAssetTrace() + { + ZoneBuilder::TraceZone.clear(); + + std::vector> AssetTrace; + Utils::Merge(&AssetTrace, ZoneBuilder::TraceAssets); + + ZoneBuilder::TraceAssets.clear(); + + return AssetTrace; + } + + Game::XAssetHeader ZoneBuilder::GetEmptyAssetIfCommon(Game::XAssetType type, std::string name, ZoneBuilder::Zone* builder) + { + Game::XAssetHeader header = { 0 }; + + if (type >= 0 && type < Game::XAssetType::ASSET_TYPE_COUNT) + { + for (auto& asset : ZoneBuilder::CommonAssets) + { + if (asset.first == type && asset.second == name) + { + // Allocate an empty asset (filled with zeros) + header.data = builder->getAllocator()->allocate(Game::DB_GetXAssetSizeHandlers[type]()); + + // Set the name to the original name, so it can be stored + Game::DB_SetXAssetNameHandlers[type](&header, name.data()); + AssetHandler::StoreTemporaryAsset(type, header); + + // Set the name to the empty name + Game::DB_SetXAssetNameHandlers[type](&header, builder->getAllocator()->duplicateString("," + name)); + break; + } + } + } + + return header; + } + + int ZoneBuilder::StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image) + { + size_t size = 16 + (*loadDef)->resourceSize; + void* data = ZoneBuilder::MemAllocator.allocate(size); + std::memcpy(data, *loadDef, size); + + image->loadDef = reinterpret_cast(data); + + return 0; + } + + void ZoneBuilder::ReleaseTexture(Game::XAssetHeader header) + { + if (header.image && header.image->loadDef) + { + ZoneBuilder::MemAllocator.free(header.image->loadDef); + } + } + + ZoneBuilder::ZoneBuilder() + { + AssertSize(Game::XFileHeader, 21); + AssertSize(Game::XFile, 40); + static_assert(Game::MAX_XFILE_COUNT == 8, "XFile block enum is invalid!"); + + ZoneBuilder::EndAssetTrace(); + + if (ZoneBuilder::IsEnabled()) + { + // Prevent loading textures (preserves loaddef) + //Utils::Hook::Set(Game::Load_Texture, 0xC3); + + // Store the loaddef + Utils::Hook(Game::Load_Texture, StoreTexture, HOOK_JUMP).install()->quick(); + + // Release the loaddef + Game::DB_ReleaseXAssetHandlers[Game::XAssetType::ASSET_TYPE_IMAGE] = ZoneBuilder::ReleaseTexture; + + //r_loadForrenderer = 0 + Utils::Hook::Set(0x519DDF, 0); + + //r_delayloadimage retn + Utils::Hook::Set(0x51F450, 0xC3); + + // r_registerDvars hack + Utils::Hook::Set(0x51B1CD, 0xC3); + + // Prevent destroying textures + Utils::Hook::Set(0x51F03D, 0xEB); + + // Don't create default assets + Utils::Hook::Set(0x407BAA, 0xEB); + + // Don't mark clip maps as 'in use' + Utils::Hook::Nop(0x405E07, 7); + + // Don't mark assets + //Utils::Hook::Nop(0x5BB632, 5); + + // Don't load sounds + //Utils::Hook::Set(0x413430, 0xC3); + + // Don't display errors when assets are missing (we might manually build those) + Utils::Hook::Nop(0x5BB3F2, 5); + Utils::Hook::Nop(0x5BB422, 5); + Utils::Hook::Nop(0x5BB43A, 5); + + // Increase asset pools + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_MAP_ENTS, 10); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_XMODELSURFS, 8192); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_IMAGE, 14336); + Game::ReallocateAssetPool(Game::XAssetType::ASSET_TYPE_TECHNIQUE_SET, 1536); + + // hunk size (was 300 MiB) + Utils::Hook::Set(0x64A029, 0x38400000); // 900 MiB + Utils::Hook::Set(0x64A057, 0x38400000); + + AssetHandler::OnLoad([](Game::XAssetType type, Game::XAssetHeader /*asset*/, std::string name, bool* /*restrict*/) + { + // This is used to track which assets can be stored as empty assets + if (FastFiles::Current() == "common_mp") + { + ZoneBuilder::CommonAssets.push_back({ type, name }); + } + + if (!ZoneBuilder::TraceZone.empty() && ZoneBuilder::TraceZone == FastFiles::Current()) + { + ZoneBuilder::TraceAssets.push_back({ type, name }); + } + }); + + Command::Add("verifyzone", [](Command::Params* params) + { + if (params->length() < 2) return; + + std::string zone = params->get(1); + + ZoneBuilder::BeginAssetTrace(zone); + + Game::XZoneInfo info; + info.name = zone.data(); + info.allocFlags = 0x20; + info.freeFlags = 0; + + Logger::Print("Loading zone '%s'...\n", zone.data()); + + Game::DB_LoadXAssets(&info, 1, true); + AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, zone.data()); // Lock until zone is loaded + + auto assets = ZoneBuilder::EndAssetTrace(); + + Logger::Print("Unloading zone '%s'...\n", zone.data()); + info.freeFlags = 0x20; + info.allocFlags = 0; + info.name = nullptr; + + Game::DB_LoadXAssets(&info, 1, true); + AssetHandler::FindOriginalAsset(Game::XAssetType::ASSET_TYPE_RAWFILE, "default"); // Lock until zone is unloaded + + Logger::Print("Zone '%s' loaded with %d assets:\n", zone.data(), assets.size()); + + int count = 0; + for (auto i = assets.begin(); i != assets.end(); ++i, ++count) + { + Logger::Print(" %d: %s: %s\n", count, Game::DB_GetXAssetTypeName(i->first), i->second.data()); + } + + Logger::Print("\n"); + }); + + Command::Add("buildzone", [](Command::Params* params) + { + if (params->length() < 2) return; + + std::string zoneName = params->get(1); + Logger::Print("Building zone '%s'...\n", zoneName.data()); + + Zone(zoneName).build(); + }); + + Command::Add("buildall", [](Command::Params*) + { + auto zoneSources = FileSystem::GetSysFileList(Dvar::Var("fs_basepath").get() + "\\zone_source", "csv", false); + + for (auto source : zoneSources) + { + if (Utils::String::EndsWith(source, ".csv")) + { + source = source.substr(0, source.find(".csv")); + } + + Command::Execute(Utils::String::VA("buildzone %s", source.data()), true); + } + }); + + Command::Add("listassets", [](Command::Params* params) + { + if (params->length() < 2) return; + Game::XAssetType type = Game::DB_GetXAssetNameType(params->get(1)); + + if (type != Game::XAssetType::ASSET_TYPE_INVALID) + { + Game::DB_EnumXAssets(type, [](Game::XAssetHeader header, void* data) + { + Game::XAsset asset = { *reinterpret_cast(data), header }; + Logger::Print("%s\n", Game::DB_GetXAssetName(&asset)); + }, &type, false); + } + }); + + Command::Add("materialInfoDump", [](Command::Params*) + { + Game::DB_EnumXAssets(Game::ASSET_TYPE_MATERIAL, [](Game::XAssetHeader header, void*) + { + Logger::Print("%s: %X %X %X\n", header.material->name, header.material->sortKey & 0xFF, header.material->gameFlags & 0xFF, header.material->stateFlags & 0xFF); + }, nullptr, false); + }); + } + } + + ZoneBuilder::~ZoneBuilder() + { + assert(ZoneBuilder::MemAllocator.empty()); + ZoneBuilder::CommonAssets.clear(); + } +} diff --git a/src/Components/Modules/ZoneBuilder.hpp b/src/Components/Modules/ZoneBuilder.hpp index 33684481..e923df7e 100644 --- a/src/Components/Modules/ZoneBuilder.hpp +++ b/src/Components/Modules/ZoneBuilder.hpp @@ -1,115 +1,115 @@ -#define XFILE_MAGIC_UNSIGNED 0x3030317566665749 -#define XFILE_VERSION 276 - -#define XFILE_HEADER_IW4X 0x78345749 // 'IW4x' -#define XFILE_VERSION_IW4X 1 - -namespace Components -{ - class ZoneBuilder : public Component - { - public: - class Zone - { - public: - Zone(std::string zoneName); - ~Zone(); - - void build(); - - Utils::Stream* getBuffer(); - Utils::Memory::Allocator* getAllocator(); - - bool hasPointer(const void* pointer); - void storePointer(const void* pointer); - - template - inline T* getPointer(const T* pointer) { return reinterpret_cast(this->safeGetPointer(pointer)); } - - int findAsset(Game::XAssetType type, std::string name); - Game::XAssetHeader findSubAsset(Game::XAssetType type, std::string name); - Game::XAsset* getAsset(int index); - uint32_t getAssetTableOffset(int index); - - bool hasAlias(Game::XAsset asset); - Game::XAssetHeader saveSubAsset(Game::XAssetType type, void* ptr); - bool loadAsset(Game::XAssetType type, std::string name, bool isSubAsset = true); - bool loadAsset(Game::XAssetType type, void* data, bool isSubAsset = true); - - int addScriptString(unsigned short gameIndex); - int addScriptString(std::string str); - int findScriptString(std::string str); - - void mapScriptString(unsigned short* gameIndex); - - void renameAsset(Game::XAssetType type, std::string asset, std::string newName); - std::string getAssetName(Game::XAssetType type, std::string asset); - - void store(Game::XAssetHeader header); - - void incrementExternalSize(unsigned int size); - - private: - void loadFastFiles(); - - bool loadAssets(); - bool loadAsset(std::string type, std::string name, bool isSubAsset = true); - - void saveData(); - void writeZone(); - - unsigned int getAlias(Game::XAsset asset); - void storeAlias(Game::XAsset asset); - - void addBranding(); - - uint32_t safeGetPointer(const void* pointer); - - int indexStart; - unsigned int externalSize; - Utils::Stream buffer; - - std::string zoneName; - Utils::CSV dataMap; - - Utils::Memory::Allocator memAllocator; - - std::vector loadedAssets; - std::vector markedAssets; - std::vector loadedSubAssets; - std::vector scriptStrings; - std::map scriptStringMap; - - std::map renameMap[Game::XAssetType::ASSET_TYPE_COUNT]; - - std::map pointerMap; - std::vector> aliasList; - - Game::RawFile branding; - }; - - ZoneBuilder(); - ~ZoneBuilder(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "ZoneBuilder"; }; -#endif - - static bool IsEnabled(); - - static std::string TraceZone; - static std::vector> TraceAssets; - - static std::vector> CommonAssets; - - static void BeginAssetTrace(std::string zone); - static std::vector> EndAssetTrace(); - - static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, std::string name, Zone* builder); - - private: - static Utils::Memory::Allocator MemAllocator; - static int StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image); - static void ReleaseTexture(Game::XAssetHeader header); - }; -} +#define XFILE_MAGIC_UNSIGNED 0x3030317566665749 +#define XFILE_VERSION 276 + +#define XFILE_HEADER_IW4X 0x78345749 // 'IW4x' +#define XFILE_VERSION_IW4X 1 + +namespace Components +{ + class ZoneBuilder : public Component + { + public: + class Zone + { + public: + Zone(std::string zoneName); + ~Zone(); + + void build(); + + Utils::Stream* getBuffer(); + Utils::Memory::Allocator* getAllocator(); + + bool hasPointer(const void* pointer); + void storePointer(const void* pointer); + + template + inline T* getPointer(const T* pointer) { return reinterpret_cast(this->safeGetPointer(pointer)); } + + int findAsset(Game::XAssetType type, std::string name); + Game::XAssetHeader findSubAsset(Game::XAssetType type, std::string name); + Game::XAsset* getAsset(int index); + uint32_t getAssetTableOffset(int index); + + bool hasAlias(Game::XAsset asset); + Game::XAssetHeader saveSubAsset(Game::XAssetType type, void* ptr); + bool loadAsset(Game::XAssetType type, std::string name, bool isSubAsset = true); + bool loadAsset(Game::XAssetType type, void* data, bool isSubAsset = true); + + int addScriptString(unsigned short gameIndex); + int addScriptString(std::string str); + int findScriptString(std::string str); + + void mapScriptString(unsigned short* gameIndex); + + void renameAsset(Game::XAssetType type, std::string asset, std::string newName); + std::string getAssetName(Game::XAssetType type, std::string asset); + + void store(Game::XAssetHeader header); + + void incrementExternalSize(unsigned int size); + + private: + void loadFastFiles(); + + bool loadAssets(); + bool loadAsset(std::string type, std::string name, bool isSubAsset = true); + + void saveData(); + void writeZone(); + + unsigned int getAlias(Game::XAsset asset); + void storeAlias(Game::XAsset asset); + + void addBranding(); + + uint32_t safeGetPointer(const void* pointer); + + int indexStart; + unsigned int externalSize; + Utils::Stream buffer; + + std::string zoneName; + Utils::CSV dataMap; + + Utils::Memory::Allocator memAllocator; + + std::vector loadedAssets; + std::vector markedAssets; + std::vector loadedSubAssets; + std::vector scriptStrings; + std::map scriptStringMap; + + std::map renameMap[Game::XAssetType::ASSET_TYPE_COUNT]; + + std::map pointerMap; + std::vector> aliasList; + + Game::RawFile branding; + }; + + ZoneBuilder(); + ~ZoneBuilder(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "ZoneBuilder"; }; +#endif + + static bool IsEnabled(); + + static std::string TraceZone; + static std::vector> TraceAssets; + + static std::vector> CommonAssets; + + static void BeginAssetTrace(std::string zone); + static std::vector> EndAssetTrace(); + + static Game::XAssetHeader GetEmptyAssetIfCommon(Game::XAssetType type, std::string name, Zone* builder); + + private: + static Utils::Memory::Allocator MemAllocator; + static int StoreTexture(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image); + static void ReleaseTexture(Game::XAssetHeader header); + }; +} diff --git a/src/Components/Modules/Zones.cpp b/src/Components/Modules/Zones.cpp index 5a9f6235..0b56b233 100644 --- a/src/Components/Modules/Zones.cpp +++ b/src/Components/Modules/Zones.cpp @@ -1,1659 +1,1659 @@ -#include "STDInclude.hpp" - -namespace Components -{ - int Zones::ZoneVersion; - - int Zones::FxEffectIndex; - char* Zones::FxEffectStrings[64]; - - bool Zones::LoadFxEffectDef(bool atStreamStart, char* buffer, int size) - { - int count = 0; - - if (Zones::Version() >= VERSION_ALPHA2) - { - size /= 252; - count = size; - size *= 260; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - Zones::FxEffectIndex = 0; - - if (Zones::Version() >= VERSION_ALPHA2) - { - Utils::Memory::Allocator allocator; - Game::FxElemDef* elems = allocator.allocateArray(count); - - for (int i = 0; i < count; ++i) - { - AssetHandler::Relocate(buffer + (260 * i), buffer + (252 * i), 252); - std::memcpy(&elems[i], buffer + (260 * i), 252); - Zones::FxEffectStrings[i] = *reinterpret_cast(buffer + (260 * i) + 256); - } - - std::memcpy(buffer, elems, sizeof(Game::FxElemDef) * count); - } - - return result; - } - - bool Zones::LoadFxElemDefStub(bool atStreamStart, Game::FxElemDef* fxElem, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - if (fxElem->elemType == 3) - { - fxElem->elemType = 2; - } - else if (fxElem->elemType >= 5) - { - fxElem->elemType -= 2; - } - } - - return Game::Load_Stream(atStreamStart, fxElem, size); - } - - void Zones::LoadFxElemDefArrayStub(bool atStreamStart) - { - Game::Load_FxElemDef(atStreamStart); - - if (Zones::Version() >= VERSION_ALPHA2) - { - *Game::varXString = &Zones::FxEffectStrings[Zones::FxEffectIndex++]; - Game::Load_XString(false); - } - } - - bool Zones::LoadXModel(bool atStreamStart, char* xmodel, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - if (Zones::Version() == VERSION_ALPHA2) - { - size = 0x16C; - } - else - { - size = 0x168; - } - } - - bool result = Game::Load_Stream(atStreamStart, xmodel, size); - - if (Zones::Version() >= VERSION_ALPHA2) - { - Game::XModel model[2]; // Allocate 2 models, as we exceed the buffer - - std::memcpy(model, xmodel, 36); - std::memcpy(&model->boneNames, &xmodel[44], 28); - - for (int i = 0; i < 4; ++i) - { - AssertOffset(Game::XModelLodInfo, partBits, 12); - - std::memcpy(&model->lodInfo[i], &xmodel[72 + (i * 56)], 12); - std::memcpy(&model->lodInfo[i].partBits, &xmodel[72 + (i * 56) + 16], 32); - - std::memcpy(reinterpret_cast(&model) + (size - 4) - (i * 4), &xmodel[72 + (i * 56) + 12], 4); - } - - std::memcpy(&model->lodInfo[3].lod, &xmodel[292], (size - 292 - 4)/*68*/); - std::memcpy(&model->physPreset, &xmodel[(size - 8)], 8); - - model[1].name = reinterpret_cast(0xDEADC0DE); - - std::memcpy(xmodel, &model, size); - } - - return result; - } - - void Zones::LoadXModelLodInfo(int i) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - int elSize = (Zones::ZoneVersion == VERSION_ALPHA2) ? 364 : 360; - *Game::varXString = reinterpret_cast(reinterpret_cast(*Game::varXModel) + (elSize - 4) - (4 * (4 - i))); - Game::Load_XString(false); - } - } - - __declspec(naked) void Zones::LoadXModelLodInfoStub() - { - __asm - { - push edi - call Zones::LoadXModelLodInfo - add esp, 4h - - mov eax, [esp + 8h] - push eax - add eax, 8 - push eax - call Game::Load_XModelSurfsFixup - add esp, 8h - - retn - } - } - - bool Zones::LoadXSurfaceArray(bool atStreamStart, char* buffer, int size) - { - int count = 0; - - if (Zones::Version() >= VERSION_ALPHA2) - { - size >>= 6; - - count = size; - size *= 84; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::Version() >= VERSION_ALPHA2) - { - Utils::Memory::Allocator allocator; - Game::XSurface* tempSurfaces = allocator.allocateArray(count); - - for (int i = 0; i < count; ++i) - { - char* source = &buffer[i * 84]; - - std::memcpy(&tempSurfaces[i], source, 12); - std::memcpy(&tempSurfaces[i].triIndices, source + 16, 20); - std::memcpy(&tempSurfaces[i].vertListCount, source + 40, 8); - std::memcpy(&tempSurfaces[i].partBits, source + 52, 24); - - if (Zones::ZoneVersion >= 332) - { - struct - { - short pad; // +0 - char flag; // +2 - char zoneHandle; // +3 - unsigned short vertCount; // +4 - unsigned short triCount; // +6 - // [...] - } surface332; - - // Copy the data to our new structure - std::memcpy(&surface332, &tempSurfaces[i], sizeof(surface332)); - - // Check if that special flag is set - if (!(surface332.flag & 0x20)) - { - Logger::Error("We're not able to handle XSurface buffer allocation yet!"); - } - - // Copy the correct data back to our surface - tempSurfaces[i].zoneHandle = surface332.zoneHandle; - tempSurfaces[i].vertCount = surface332.vertCount; - tempSurfaces[i].triCount = surface332.triCount; - - //std::memmove(&tempSurfaces[i].numVertices, &tempSurfaces[i].numPrimitives, 6); - } - } - - std::memcpy(buffer, tempSurfaces, sizeof(Game::XSurface) * count); - } - - return result; - } - - void Zones::LoadWeaponCompleteDef() - { - if (Zones::ZoneVersion < VERSION_ALPHA2) - { - return Utils::Hook::Call(0x4AE7B0)(true); - } - - // setup structures we use - char* varWeaponCompleteDef = *reinterpret_cast(0x112A9F4); - - int size = 3112; - - if (Zones::ZoneVersion >= 318) - { - size = 3156; - - if (Zones::ZoneVersion >= 332) - { - size = 3068; // We didn't adapt that, but who the fuck cares! - - if (Zones::ZoneVersion >= 359) - { - size = 3120; - - if (Zones::ZoneVersion >= 365) - { - size = 3124; - } - } - } - } - - // and do the stuff - Game::Load_Stream(true, varWeaponCompleteDef, size); - - Game::DB_PushStreamPos(3); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 0); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 4); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 8); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 12); - Game::Load_XString(false); - - *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + 16); - Game::Load_XModelPtr(false); - - if (Zones::ZoneVersion >= 359) - { - for (int offset = 20; offset <= 56; offset += 4) - { - *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_XModelPtr(false); - } - } - else - { - for (int i = 0, offset = 20; i < 32; ++i, offset += 4) - { - *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_XModelPtr(false); - } - - // 148 - for (int offset = 148; offset <= 168; offset += 4) - { - *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_XModelPtr(false); - } - } - - - // 172 - // 32 scriptstrings, should not need to be loaded - - if (Zones::ZoneVersion >= 359) - { - // 236 - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 124); - Game::Load_XStringArray(false, 52); - - // 428 - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 332); - Game::Load_XStringArray(false, 52); - - // 620 - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 540); - Game::Load_XStringArray(false, 52); - } - else - { - // 236 - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 236); - Game::Load_XStringArray(false, 48); - - // 428 - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 428); - Game::Load_XStringArray(false, 48); - - // 620 - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 620); - Game::Load_XStringArray(false, 48); - } - - // 812 - // 16 * 4 scriptstrings - - if (Zones::ZoneVersion >= 359) - { - // 972 - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 908); - Game::Load_FxEffectDefHandle(false); - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 912); - Game::Load_FxEffectDefHandle(false); - } - else - { - // 972 - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 972); - Game::Load_FxEffectDefHandle(false); - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 976); - Game::Load_FxEffectDefHandle(false); - } - - // 980 - if (Zones::ZoneVersion >= 359) - { - // 53 soundalias name references; up to and including 1124 - for (int i = 0, offset = 916; i < 52; ++i, offset += 4) - { - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - } - } - else - { - // 50 soundalias name references; up to and including 1180 - for (int i = 0, offset = 980; i < 50; ++i, offset += 4) - { - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - } - - if (Zones::ZoneVersion >= 318) - { - for (int i = 0, offset = 1184; i < 2; ++i, offset += 4) - { - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - } - - varWeaponCompleteDef += 8; // to compensate for the 2 in between here - } - } - - if (Zones::ZoneVersion >= 359) - { - if (*reinterpret_cast(varWeaponCompleteDef + 1128)) - { - if (*reinterpret_cast(varWeaponCompleteDef + 1128) == -1) - { - *reinterpret_cast(varWeaponCompleteDef + 1128) = Game::DB_AllocStreamPos(3); - *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1128); - - Game::Load_snd_alias_list_nameArray(true, 31); - } - else - { - // full usability requires ConvertOffsetToPointer here - } - } - - if (*reinterpret_cast(varWeaponCompleteDef + 1132)) - { - if (*reinterpret_cast(varWeaponCompleteDef + 1132) == -1) - { - *reinterpret_cast(varWeaponCompleteDef + 1132) = Game::DB_AllocStreamPos(3); - *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1132); - - Game::Load_snd_alias_list_nameArray(true, 31); - } - else - { - // full usability requires ConvertOffsetToPointer here - } - } - } - else - { - if (*reinterpret_cast(varWeaponCompleteDef + 1184)) - { - if (*reinterpret_cast(varWeaponCompleteDef + 1184) == -1) - { - *reinterpret_cast(varWeaponCompleteDef + 1184) = Game::DB_AllocStreamPos(3); - *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1184); - - Game::Load_snd_alias_list_nameArray(true, 31); - } - else - { - // full usability requires ConvertOffsetToPointer here - } - } - - if (*reinterpret_cast(varWeaponCompleteDef + 1188)) - { - if (*reinterpret_cast(varWeaponCompleteDef + 1188) == -1) - { - *reinterpret_cast(varWeaponCompleteDef + 1188) = Game::DB_AllocStreamPos(3); - *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1188); - - Game::Load_snd_alias_list_nameArray(true, 31); - } - else - { - // full usability requires ConvertOffsetToPointer here - } - } - } - - if (Zones::ZoneVersion >= 359) - { - // 1192 - for (int offset = 1136; offset <= 1148; offset += 4) - { - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_FxEffectDefHandle(false); - } - } - else - { - // 1192 - for (int offset = 1192; offset <= 1204; offset += 4) - { - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_FxEffectDefHandle(false); - } - } - - if (Zones::ZoneVersion >= 359) - { - // 1208 - static int matOffsets1[] = { 1152, 1156, 1372,1376,1380, 1384, 1388, 1392, 1400, 1408 }; - for (int i = 0; i < ARRAYSIZE(matOffsets1); ++i) - { - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + matOffsets1[i]); - Game::Load_MaterialHandle(false); - } - } - else - { // 1208 - static int matOffsets1[] = { 1208, 1212, 1428, 1432, 1436, 1440, 1444, 1448, 1456, 1464 }; - for (int i = 0; i < ARRAYSIZE(matOffsets1); ++i) - { - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + matOffsets1[i]); - Game::Load_MaterialHandle(false); - } - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1428); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1436); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1452); - Game::Load_XString(false); - } - else - { - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1484); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1492); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1508); - Game::Load_XString(false); - } - - if (Zones::ZoneVersion >= 359) - { - for (int offset = 1716; offset <= 1728; offset += 4) - { - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_MaterialHandle(false); - } - } - else - { - for (int offset = 1764; offset <= 1776; offset += 4) - { - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_MaterialHandle(false); - } - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varPhysCollmapPtr = reinterpret_cast(varWeaponCompleteDef + 1928); - Game::Load_PhysCollmapPtr(false); - - *Game::varPhysPresetPtr = reinterpret_cast(varWeaponCompleteDef + 1932); - Game::Load_PhysPresetPtr(false); - } - else - { - *Game::varPhysCollmapPtr = reinterpret_cast(varWeaponCompleteDef + 1964); - Game::Load_PhysCollmapPtr(false); - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + 2020); - Game::Load_XModelPtr(false); - } - else - { - *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + 2052); - Game::Load_XModelPtr(false); - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2028); - Game::Load_FxEffectDefHandle(false); - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2032); - Game::Load_FxEffectDefHandle(false); - } - else - { - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2060); - Game::Load_FxEffectDefHandle(false); - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2064); - Game::Load_FxEffectDefHandle(false); - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2036); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2040); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - } - else - { - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2068); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2072); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2304); - Game::Load_FxEffectDefHandle(false); - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2308); - Game::Load_FxEffectDefHandle(false); - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2336); - Game::Load_FxEffectDefHandle(false); - } - else - { - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2336); - Game::Load_FxEffectDefHandle(false); - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2340); - Game::Load_FxEffectDefHandle(false); - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2368); // 2376 - Game::Load_FxEffectDefHandle(false); - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2340); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2516); - Game::Load_XString(false); - } - else - { - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2372); // 2380 - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2548); // 2556 - Game::Load_XString(false); - } - - if (Zones::ZoneVersion >= 359) - { - if (*reinterpret_cast(varWeaponCompleteDef + 2524) == -1) - { - void* vec2 = Game::DB_AllocStreamPos(3); - *reinterpret_cast(varWeaponCompleteDef + 2524) = vec2; - - Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3044)); - } - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2520); - Game::Load_XString(false); - - if (*reinterpret_cast(varWeaponCompleteDef + 2528) == -1) - { - void* vec2 = Game::DB_AllocStreamPos(3); - *reinterpret_cast(varWeaponCompleteDef + 2528) = vec2; - - Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3046)); - } - } - else - { - if (*reinterpret_cast(varWeaponCompleteDef + 2556) == -1) // 2564 - { - void* vec2 = Game::DB_AllocStreamPos(3); - *reinterpret_cast(varWeaponCompleteDef + 2556) = vec2; - - Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + ((Zones::ZoneVersion >= 318) ? 3076 : 3040))); - } - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2552); - Game::Load_XString(false); - - if (*reinterpret_cast(varWeaponCompleteDef + 2560) == -1) - { - void* vec2 = Game::DB_AllocStreamPos(3); - *reinterpret_cast(varWeaponCompleteDef + 2560) = vec2; - - Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + ((Zones::ZoneVersion >= 318) ? 3078 : 3042))); - } - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2608); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2612); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2644); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2648); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2772); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2776); - Game::Load_XString(false); - } - else - { - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2640); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2644); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2676); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2680); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2804); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2808); - Game::Load_XString(false); - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varTracerDefPtr = reinterpret_cast(varWeaponCompleteDef + 2780); - Game::Load_TracerDefPtr(false); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2808); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); // 2848 - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2812); - Game::Load_FxEffectDefHandle(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2816); - Game::Load_XString(false); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2832); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2836); - Game::Load_snd_alias_list_nameArray(false, 4); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2852); - Game::Load_snd_alias_list_nameArray(false, 4); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2868); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2872); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - } - else - { - *Game::varTracerDefPtr = reinterpret_cast(varWeaponCompleteDef + 2812); - Game::Load_TracerDefPtr(false); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2840); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); // 2848 - - *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2844); - Game::Load_FxEffectDefHandle(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2848); - Game::Load_XString(false); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2864); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2868); - Game::Load_snd_alias_list_nameArray(false, 4); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2884); - Game::Load_snd_alias_list_nameArray(false, 4); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2900); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2904); // 2912 - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - } - - if (Zones::ZoneVersion >= 359) - { - for (int i = 0, offset = 2940; i < 6; ++i, offset += 4) - { - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - } - } - else - { - if (Zones::ZoneVersion >= 318) - { - for (int i = 0, offset = 2972; i < 6; ++i, offset += 4) - { - *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); - Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); - } - - varWeaponCompleteDef += (6 * 4); - varWeaponCompleteDef += 12; - } - else - { - - } - } - - if (Zones::ZoneVersion >= 359) - { - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2988); - Game::Load_XString(false); - - if (Zones::ZoneVersion >= 365) - { - varWeaponCompleteDef += 4; - } - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3000); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3004); - Game::Load_XString(false); - - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3012); - Game::Load_MaterialHandle(false); - - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3016); - Game::Load_MaterialHandle(false); - - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3020); - Game::Load_MaterialHandle(false); - } - else - { - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2984); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2996); - Game::Load_XString(false); - - *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3000); - Game::Load_XString(false); - - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3008); - Game::Load_MaterialHandle(false); - - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3012); - Game::Load_MaterialHandle(false); - - *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3016); - Game::Load_MaterialHandle(false); - } - - if (Zones::ZoneVersion >= 359) - { - if (*reinterpret_cast(varWeaponCompleteDef + 3048) == -1) - { - void* vec2 = Game::DB_AllocStreamPos(3); - *reinterpret_cast(varWeaponCompleteDef + 3048) = vec2; - - Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3044)); - } - - if (*reinterpret_cast(varWeaponCompleteDef + 3052) == -1) - { - void* vec2 = Game::DB_AllocStreamPos(3); - *reinterpret_cast(varWeaponCompleteDef + 3052) = vec2; - - Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3046)); - } - } - else - { - if (*reinterpret_cast(varWeaponCompleteDef + 3044) == -1) - { - void* vec2 = Game::DB_AllocStreamPos(3); - *reinterpret_cast(varWeaponCompleteDef + 3044) = vec2; - - Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3040)); - } - - if (*reinterpret_cast(varWeaponCompleteDef + 3048) == -1) - { - void* vec2 = Game::DB_AllocStreamPos(3); - *reinterpret_cast(varWeaponCompleteDef + 3048) = vec2; - - Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3042)); - } - } - - Game::DB_PopStreamPos(); - } - -// Code-analysis has a bug, the first memcpy makes it believe size of tempVar is 44 instead of 84 -#pragma warning(push) -#pragma warning(disable: 6385) - bool Zones::LoadGameWorldSp(bool atStreamStart, char* buffer, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - size = 84; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::Version() >= VERSION_ALPHA2) - { - char tempVar[84] = { 0 }; - std::memcpy(&tempVar[0], &buffer[0], 44); - std::memcpy(&tempVar[56], &buffer[44], 28); - std::memcpy(&tempVar[44], &buffer[72], 12); - - std::memcpy(buffer, tempVar, sizeof(tempVar)); - } - - return result; - } -#pragma warning(pop) - - void Zones::LoadPathDataTail() - { - if (Zones::ZoneVersion >= VERSION_ALPHA2) - { - char* varPathData = reinterpret_cast(*Game::varPathData); - - if (*reinterpret_cast(varPathData + 56)) - { - *reinterpret_cast(varPathData + 56) = Game::DB_AllocStreamPos(0); - Game::Load_Stream(true, *reinterpret_cast(varPathData + 56), *reinterpret_cast(varPathData + 52)); - } - - if (*reinterpret_cast(varPathData + 64)) - { - *reinterpret_cast(varPathData + 64) = Game::DB_AllocStreamPos(0); - Game::Load_Stream(true, *reinterpret_cast(varPathData + 64), *reinterpret_cast(varPathData + 60)); - } - - if (*reinterpret_cast(varPathData + 76)) - { - *reinterpret_cast(varPathData + 76) = Game::DB_AllocStreamPos(0); - Game::Load_Stream(true, *reinterpret_cast(varPathData + 76), *reinterpret_cast(varPathData + 72)); - } - } - } - - bool Zones::Loadsnd_alias_tArray(bool atStreamStart, char* buffer, int len) - { - int count = 0; - - if (Zones::Version() >= VERSION_ALPHA2) - { - len /= 100; - count = len; - len *= 108; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, len); - - if (Zones::Version() >= VERSION_ALPHA2) - { - Utils::Memory::Allocator allocator; - Game::snd_alias_t* tempSounds = allocator.allocateArray(count); - - for (int i = 0; i < count; ++i) - { - char* src = &buffer[i * 108]; - char* dest = reinterpret_cast(&tempSounds[i]); - - std::memcpy(dest + 0, src + 0, 60); - std::memcpy(dest + 60, src + 68, 20); - std::memcpy(dest + 80, src + 88, 20); - - AssetHandler::Relocate(src + 0, buffer + (i * 100) + 0, 60); - AssetHandler::Relocate(src + 68, buffer + (i * 100) + 60, 20); - AssetHandler::Relocate(src + 88, buffer + (i * 100) + 80, 20); - } - - std::memcpy(buffer, tempSounds, sizeof(Game::snd_alias_t) * count); - } - - return result; - } - - bool Zones::LoadLoadedSound(bool atStreamStart, char* buffer, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - size = 48; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::Version() >= VERSION_ALPHA2) - { - std::memmove(buffer + 28, buffer + 32, 16); - AssetHandler::Relocate(buffer + 32, buffer + 28, 16); - } - - return result; - } - -// Code-analysis has a bug, the first memcpy makes it believe size of tempVar is 400 instead of 788 -#pragma warning(push) -#pragma warning(disable: 6385) - bool Zones::LoadVehicleDef(bool atStreamStart, char* buffer, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - size = 788; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::Version() >= VERSION_ALPHA2) - { - char tempVar[788] = { 0 }; - std::memcpy(&tempVar[0], &buffer[0], 400); - std::memcpy(&tempVar[408], &buffer[400], 380); - - AssetHandler::Relocate(buffer + 400, buffer + 408, 388); - - std::memmove(buffer, tempVar, sizeof(tempVar)); - } - - return result; - } -#pragma warning(pop) - - void Zones::LoadWeaponAttachStuff(DWORD* varWeaponAttachStuff, int count) - { - Game::Load_Stream(true, varWeaponAttachStuff, 12 * count); - - for (int i = 0; i < count; ++i) - { - if (varWeaponAttachStuff[1] < 16 || varWeaponAttachStuff[1] == 39) - { - if (varWeaponAttachStuff[2] == -1) - { - varWeaponAttachStuff[2] = reinterpret_cast(Game::DB_AllocStreamPos(0)); - *Game::varConstChar = reinterpret_cast(varWeaponAttachStuff[2]); - Game::Load_XStringCustom(Game::varConstChar); - } - } - - varWeaponAttachStuff += 3; - } - } - - bool Zones::LoadmenuDef_t(bool atStreamStart, char* buffer, int size) - { - if (Zones::ZoneVersion != 359 && Zones::ZoneVersion >= VERSION_ALPHA2) size += 4; - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::ZoneVersion >= VERSION_ALPHA2) - { - std::memmove(buffer + 168, buffer + 172, (Zones::ZoneVersion != 359 ? 232 : 228)); - AssetHandler::Relocate(buffer + 172, buffer + 168, (Zones::ZoneVersion != 359 ? 232 : 228)); - - reinterpret_cast(buffer)->expressionData = nullptr; - } - - return result; - } - - void Zones::LoadWeaponAttach() - { - if (Zones::ZoneVersion < VERSION_ALPHA2) - { - return Utils::Hook::Call(0x4F4160)(true); - } - - // setup structures we use - char* varWeaponAttach = *reinterpret_cast(0x112ADE0); // varAddonMapEnts - - // and do the stuff - Game::Load_Stream(true, varWeaponAttach, 12); - - Game::DB_PushStreamPos(3); - - *Game::varXString = reinterpret_cast(varWeaponAttach); - Game::Load_XString(false); - - *reinterpret_cast(varWeaponAttach + 8) = Game::DB_AllocStreamPos(3); - Zones::LoadWeaponAttachStuff(*reinterpret_cast(varWeaponAttach + 8), *reinterpret_cast(varWeaponAttach + 4)); - - Game::DB_PopStreamPos(); - } - - bool Zones::LoadMaterialShaderArgumentArray(bool atStreamStart, Game::MaterialShaderArgument* argument, int size) - { - bool result = Game::Load_Stream(atStreamStart, argument, size); - - Game::MaterialPass* curPass = *Game::varMaterialPass; - int count = curPass->argCount1 + curPass->argCount2 + curPass->argCount3; - - for (int i = 0; i < count && (Zones::ZoneVersion >= VERSION_ALPHA2); ++i) - { - Game::MaterialShaderArgument* arg = &argument[i]; - - if (arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_TEXTURE && arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_ATTROUT) - { - continue; - } - - // should be min 68 currently - // >= 58 fixes foliage without bad side effects - // >= 53 still has broken shadow mapping - // >= 23 is still broken somehow - if (arg->paramID >= 58 && arg->paramID <= 135) // >= 34 would be 31 in iw4 terms - { - arg->paramID -= 3; - - if (Zones::Version() >= 359/* && arg->paramID <= 113*/) - { - arg->paramID -= 7; - - if (arg->paramID <= 53) - { - arg->paramID += 1; - } - } - } - // >= 21 works fine for specular, but breaks trees - // >= 4 is too low, breaks specular - else if (arg->paramID >= 11 && arg->paramID < 58) - { - arg->paramID -= 2; - - if (Zones::Version() >= 359) - { - if (arg->paramID > 15 && arg->paramID < 30) - { - arg->paramID -= 1; - - if (arg->paramID == 19) - { - arg->paramID = 21; - } - } - else if (arg->paramID >= 50) - { - arg->paramID += 6; - } - } - } - } - - return result; - } - - bool Zones::LoadStructuredDataStructPropertyArray(bool atStreamStart, char* data, int size) - { - int count = 0; - - if (Zones::ZoneVersion >= VERSION_ALPHA2) - { - size /= 16; - count = size; - size *= 24; - } - - bool result = Game::Load_Stream(atStreamStart, data, size); - - if (Zones::ZoneVersion >= VERSION_ALPHA2) - { - for (int i = 0; i < count; ++i) - { - std::memmove(data + (i * 16), data + (i * 24), 16); - AssetHandler::Relocate(data + (i * 24), data + (i * 16), 16); - } - } - - return result; - } - - bool Zones::LoadGfxImage(bool atStreamStart, char* buffer, int size) - { - if (Zones::ZoneVersion >= 332) - { - size = (Zones::ZoneVersion >= 359) ? 52 : 36; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::ZoneVersion >= 332) - { - AssetHandler::Relocate(buffer + (size - 4), buffer + 28, 4); - - if (Zones::Version() >= 359) - { - struct - { - Game::GfxImageLoadDef* texture; - Game::MapType mapType; - char semantic; - char category; - char flags; - int cardMemory; - char pad[8]; // ? - int dataLen1; - int dataLen2; - char pad2[4]; // ? - short height; - short width; - short depth; - char loaded; - char pad3[5]; - Game::GfxImageLoadDef* storedTexture; - char* name; - } image359; - - AssertSize(image359, 52); - - // Copy to new struct - memcpy(&image359, buffer, sizeof(image359)); - - // Convert to old struct - Game::GfxImage* image = reinterpret_cast(buffer); - image->mapType = image359.mapType; - image->semantic = image359.semantic; - image->category = image359.category; - image->flags = image359.flags; - image->cardMemory = image359.cardMemory; - image->dataLen1 = image359.dataLen1; - image->dataLen2 = image359.dataLen2; - image->height = image359.height; - image->width = image359.width; - image->depth = image359.depth; - image->loaded = image359.loaded; - image->name = image359.name; - - // Used for later stuff - image->pad = image359.pad3[1]; - } - else - { - memcpy(buffer + 28, buffer + (size - 4), 4); - } - } - - return result; - } - - bool Zones::LoadXAsset(bool atStreamStart, char* buffer, int size) - { - int count = 0; - - if (Zones::ZoneVersion >= 359) - { - size /= 8; - count = size; - size *= 16; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::ZoneVersion >= 359) - { - Utils::Memory::Allocator allocator; - Game::XAsset* tempAssets = allocator.allocateArray(count); - - for (int i = 0; i < count; ++i) - { - char* src = &buffer[i * 16]; - - std::memcpy(&tempAssets[i].type, src + 0, 4); - std::memcpy(&tempAssets[i].header, src + 8, 4); - - AssetHandler::Relocate(src + 0, buffer + (i * 8) + 0, 4); - AssetHandler::Relocate(src + 8, buffer + (i * 8) + 4, 4); - } - - std::memcpy(buffer, tempAssets, sizeof(Game::XAsset) * count); - } - - return result; - } - - bool Zones::LoadMaterialTechnique(bool atStreamStart, char* buffer, int size) - { - if (Zones::ZoneVersion >= 359) - { - size += 4; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::ZoneVersion >= 359) - { - // This shouldn't make any difference. - // The new entry is an additional remapped techset which is linked at runtime. - // It's used when the 0x100 gameFlag in a material is set. - // As MW2 flags are only 1 byte large, this won't be possible anyways - int shiftTest = 4; - - std::memmove(buffer + 8 + shiftTest, buffer + 12 + shiftTest, 196 - shiftTest); - AssetHandler::Relocate(buffer + 12 + shiftTest, buffer + 8 + shiftTest, 196 - shiftTest); - } - - return result; - } - - bool Zones::LoadMaterial(bool atStreamStart, char* buffer, int size) - { - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::ZoneVersion >= 359) - { - struct material339_s - { - char drawSurfBegin[8]; // 4 - //int surfaceTypeBits; - const char *name; - char drawSurf[6]; - - union - { - char gameFlags; - short sGameFlags; - }; - char sortKey; - char textureAtlasRowCount; - char textureAtlasColumnCount; - } material359; - - static_assert(offsetof(material339_s, gameFlags) == 18, ""); - static_assert(offsetof(material339_s, sortKey) == 20, ""); - static_assert(offsetof(material339_s, textureAtlasColumnCount) == 22, ""); - - static_assert(offsetof(Game::Material, stateBitsEntry) == 24, ""); - - Game::Material* material = (Game::Material*)buffer; - memcpy(&material359, material, sizeof(material359)); - - material->name = material359.name; - material->sortKey = material359.sortKey; - material->textureAtlasRowCount = material359.textureAtlasRowCount; - material->textureAtlasColumnCount = material359.textureAtlasColumnCount; - material->gameFlags = material359.gameFlags; - - // Probably wrong - material->surfaceTypeBits = 0;//material359.surfaceTypeBits; - - // Pretty sure that's wrong - // Actually, it's not - // yes it was lol - memcpy(&material->drawSurf.packed, material359.drawSurfBegin, 8); - - memcpy(&material->surfaceTypeBits, &material359.drawSurf[0], 6); // copies both surfaceTypeBits and hashIndex - //material->drawSurf[8] = material359.drawSurf[0]; - //material->drawSurf[9] = material359.drawSurf[1]; - //material->drawSurf[10] = material359.drawSurf[2]; - //material->drawSurf[11] = material359.drawSurf[3]; - - if (material359.sGameFlags & 0x100) - { - //OutputDebugStringA(""); - } - } - - return result; - } - - bool Zones::LoadGfxWorld(bool atStreamStart, char* buffer, int size) - { - if (Zones::ZoneVersion >= 359) - { - size += 968; - } - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::ZoneVersion >= 359) - { - int sunDiff = 8; // Stuff that is part of the sunflare we would overwrite - std::memmove(buffer + 348 + sunDiff, buffer + 1316 + sunDiff, 280 - sunDiff); - AssetHandler::Relocate(buffer + 1316, buffer + 348, 280); - } - - return result; - } - - void Zones::Loadsunflare_t(bool atStreamStart) - { - Game::Load_MaterialHandle(atStreamStart); - - if (Zones::ZoneVersion >= 359) - { - char* varsunflare_t = *reinterpret_cast(0x112A848); - - *Game::varMaterialHandle = reinterpret_cast(varsunflare_t + 12); - Game::Load_MaterialHandle(atStreamStart); - - *Game::varMaterialHandle = reinterpret_cast(varsunflare_t + 16); - Game::Load_MaterialHandle(atStreamStart); - - std::memmove(varsunflare_t + 12, varsunflare_t + 20, 84); - - // Copy the remaining struct data we couldn't copy in LoadGfxWorld - char* varGfxWorld = *reinterpret_cast(0x112A7F4); - std::memmove(varGfxWorld + 348, varGfxWorld + 1316, 8); - } - } - - bool Zones::LoadStatement(bool atStreamStart, char* buffer, int size) - { - if (Zones::Version() >= 359) size -= 4; - - bool result = Game::Load_Stream(atStreamStart, buffer, size); - - if (Zones::Version() >= 359) - { - std::memmove(buffer + 12, buffer + 8, 12); - } - - return result; - } - - void Zones::LoadWindowImage(bool atStreamStart) - { - Game::Load_MaterialHandle(atStreamStart); - - if (Zones::Version() >= 360) - { - char** varGfxImagePtr = reinterpret_cast(0x112B4A0); - char** varwindowDef_t = reinterpret_cast(0x112AF94); - - *varGfxImagePtr = *varwindowDef_t + 164; - Game::Load_GfxImagePtr(atStreamStart); - } - } - - void Zones::LoadPhysPreset(bool atStreamStart, char* buffer, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - size = 68; - } - - Game::Load_Stream(atStreamStart, buffer, size); - } - - void Zones::LoadXModelSurfs(bool atStreamStart, char* buffer, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - size = 48; - } - - Game::Load_Stream(atStreamStart, buffer, size); - } - - void Zones::LoadImpactFx(bool atStreamStart, char* buffer, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - size = 0x8C0; - } - - Game::Load_Stream(atStreamStart, buffer, size); - } - - int Zones::ImpactFxArrayCount() - { - if (Zones::Version() >= VERSION_ALPHA2) - { - return 16; - } - - return 15; - } - - __declspec(naked) void Zones::LoadImpactFxArray() - { - __asm - { - push edi - pushad - - push edi - call Zones::ImpactFxArrayCount - pop edi - - mov [esp + 20h], eax - - popad - pop edi - - push 4447E0h - retn - } - } - - void Zones::LoadPathNodeArray(bool atStreamStart, char* buffer, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - size /= 136; - size *= 148; - } - - Game::Load_Stream(atStreamStart, buffer, size); - } - - void Zones::LoadPathnodeConstantTail(bool atStreamStart, char* buffer, int size) - { - if (Zones::Version() >= VERSION_ALPHA2) - { - size /= 12; - size *= 16; - } - - Game::Load_Stream(atStreamStart, buffer, size); - } - - void Zones::LoadExpressionSupportingDataPtr(bool atStreamStart) - { - if (Zones::Version() < 359) - { - Utils::Hook::Call(0x4AF680)(atStreamStart); - } - } - - void Zones::SetVersion(int version) - { - AssetHandler::ClearRelocations(); - Zones::ZoneVersion = version; - } - - __declspec(noinline) bool Zones::CheckGameMapSp(int type) - { - if (type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP) - { - return true; - } - - if (Zones::Version() >= VERSION_ALPHA2 && type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP) - { - Maps::HandleAsSPMap(); - return true; - } - - return false; - } - - __declspec(naked) void Zones::GameMapSpPatchStub() - { - __asm - { - pushad - - push eax - call Zones::CheckGameMapSp - add esp, 4h - - test al, al - jnz returnSafe - - popad - push 4189AEh - retn - - returnSafe: - popad - push 41899Dh - retn - } - } - - int Zones::PathDataSize() - { - if (Zones::Version() >= VERSION_ALPHA2) - { - return 148; - } - - return 136; - } - - __declspec(naked) void Zones::LoadPathDataConstant() - { - __asm - { - push esi - pushad - - call Zones::PathDataSize - - add [esp + 20h], eax - - popad - pop esi - - push 4D6A4Dh - retn - } - } - - Zones::Zones() - { - Zones::ZoneVersion = 0; - - // Ignore missing soundaliases for now - // TODO: Include them in the dependency zone! - Utils::Hook::Nop(0x644207, 5); - - // Block Mark_pathnode_constant_t - Utils::Hook::Set(0x4F74B0, 0xC3); - - // addon_map_ents asset type (we reuse it for weaponattach) - Utils::Hook::Set(0x418B31, 0x72); - - Utils::Hook(0x495938, Zones::LoadFxElemDefArrayStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x45ADA0, Zones::LoadFxElemDefStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x4EA6FE, Zones::LoadXModelLodInfoStub, HOOK_CALL).install()->quick(); - Utils::Hook(0x410D90, Zones::LoadXModel, HOOK_CALL).install()->quick(); - Utils::Hook(0x4925C8, Zones::LoadXSurfaceArray, HOOK_CALL).install()->quick(); - Utils::Hook(0x4F4D0D, Zones::LoadGameWorldSp, HOOK_CALL).install()->quick(); - Utils::Hook(0x47CCD2, Zones::LoadWeaponCompleteDef, HOOK_CALL).install()->quick(); - Utils::Hook(0x483DA0, Zones::LoadVehicleDef, HOOK_CALL).install()->quick(); - Utils::Hook(0x4F0AC8, Zones::Loadsnd_alias_tArray, HOOK_CALL).install()->quick(); - Utils::Hook(0x403A5D, Zones::LoadLoadedSound, HOOK_CALL).install()->quick(); - Utils::Hook(0x463022, Zones::LoadWeaponAttach, HOOK_CALL).install()->quick(); - Utils::Hook(0x41A570, Zones::LoadmenuDef_t, HOOK_CALL).install()->quick(); - Utils::Hook(0x49591B, Zones::LoadFxEffectDef, HOOK_CALL).install()->quick(); - Utils::Hook(0x428F0A, Zones::LoadMaterialShaderArgumentArray, HOOK_CALL).install()->quick(); - Utils::Hook(0x4B1EB8, Zones::LoadStructuredDataStructPropertyArray, HOOK_CALL).install()->quick(); - Utils::Hook(0x49CE0D, Zones::LoadPhysPreset, HOOK_CALL).install()->quick(); - Utils::Hook(0x48E84D, Zones::LoadXModelSurfs, HOOK_CALL).install()->quick(); - Utils::Hook(0x4447C2, Zones::LoadImpactFx, HOOK_CALL).install()->quick(); - Utils::Hook(0x4447D0, Zones::LoadImpactFxArray, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4D6A0B, Zones::LoadPathNodeArray, HOOK_CALL).install()->quick(); - Utils::Hook(0x4D6A47, Zones::LoadPathDataConstant, HOOK_JUMP).install()->quick(); - Utils::Hook(0x463D6E, Zones::LoadPathnodeConstantTail, HOOK_CALL).install()->quick(); - - Utils::Hook(0x4471AD, Zones::LoadGfxImage, HOOK_CALL).install()->quick(); - - Utils::Hook(0x41A590, Zones::LoadExpressionSupportingDataPtr, HOOK_CALL).install()->quick(); - Utils::Hook(0x459833, Zones::LoadExpressionSupportingDataPtr, HOOK_JUMP).install()->quick(); - - Utils::Hook(0x5B9AA5, Zones::LoadXAsset, HOOK_CALL).install()->quick(); - Utils::Hook(0x461710, Zones::LoadMaterialTechnique, HOOK_CALL).install()->quick(); - Utils::Hook(0x40330D, Zones::LoadMaterial, HOOK_CALL).install()->quick(); - Utils::Hook(0x4B8DC0, Zones::LoadGfxWorld, HOOK_CALL).install()->quick(); - Utils::Hook(0x4B8FF5, Zones::Loadsunflare_t, HOOK_CALL).install()->quick(); - - Utils::Hook(0x418998, Zones::GameMapSpPatchStub, HOOK_JUMP).install()->quick(); - Utils::Hook(0x427A1B, Zones::LoadPathDataTail, HOOK_JUMP).install()->quick(); - Utils::Hook(0x4F4D3B, [] () - { - if (Zones::ZoneVersion >= VERSION_ALPHA3) - { - ZeroMemory(*Game::varPathData, sizeof(Game::PathData)); - } - else - { - // Load_PathData - Utils::Hook::Call(0x4278A0)(false); - } - }, HOOK_CALL).install()->quick(); - - // Change stream for images - Utils::Hook(0x4D3225, [] () - { - Game::DB_PushStreamPos((Zones::ZoneVersion >= 332) ? 3 : 0); - }, HOOK_CALL).install()->quick(); - - Utils::Hook(0x4597DD, Zones::LoadStatement, HOOK_CALL).install()->quick(); - Utils::Hook(0x471A39, Zones::LoadWindowImage, HOOK_JUMP).install()->quick(); - -#ifdef DEBUG - // Easy dirty disk debugging - Utils::Hook::Set(0x4CF7F0, 0xC3CC); - // disable _invoke_watson to allow debugging - Utils::Hook::Set(0x6B9602,0xCCCC); -#endif - } - - Zones::~Zones() - { - - } -} +#include "STDInclude.hpp" + +namespace Components +{ + int Zones::ZoneVersion; + + int Zones::FxEffectIndex; + char* Zones::FxEffectStrings[64]; + + bool Zones::LoadFxEffectDef(bool atStreamStart, char* buffer, int size) + { + int count = 0; + + if (Zones::Version() >= VERSION_ALPHA2) + { + size /= 252; + count = size; + size *= 260; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + Zones::FxEffectIndex = 0; + + if (Zones::Version() >= VERSION_ALPHA2) + { + Utils::Memory::Allocator allocator; + Game::FxElemDef* elems = allocator.allocateArray(count); + + for (int i = 0; i < count; ++i) + { + AssetHandler::Relocate(buffer + (260 * i), buffer + (252 * i), 252); + std::memcpy(&elems[i], buffer + (260 * i), 252); + Zones::FxEffectStrings[i] = *reinterpret_cast(buffer + (260 * i) + 256); + } + + std::memcpy(buffer, elems, sizeof(Game::FxElemDef) * count); + } + + return result; + } + + bool Zones::LoadFxElemDefStub(bool atStreamStart, Game::FxElemDef* fxElem, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + if (fxElem->elemType == 3) + { + fxElem->elemType = 2; + } + else if (fxElem->elemType >= 5) + { + fxElem->elemType -= 2; + } + } + + return Game::Load_Stream(atStreamStart, fxElem, size); + } + + void Zones::LoadFxElemDefArrayStub(bool atStreamStart) + { + Game::Load_FxElemDef(atStreamStart); + + if (Zones::Version() >= VERSION_ALPHA2) + { + *Game::varXString = &Zones::FxEffectStrings[Zones::FxEffectIndex++]; + Game::Load_XString(false); + } + } + + bool Zones::LoadXModel(bool atStreamStart, char* xmodel, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + if (Zones::Version() == VERSION_ALPHA2) + { + size = 0x16C; + } + else + { + size = 0x168; + } + } + + bool result = Game::Load_Stream(atStreamStart, xmodel, size); + + if (Zones::Version() >= VERSION_ALPHA2) + { + Game::XModel model[2]; // Allocate 2 models, as we exceed the buffer + + std::memcpy(model, xmodel, 36); + std::memcpy(&model->boneNames, &xmodel[44], 28); + + for (int i = 0; i < 4; ++i) + { + AssertOffset(Game::XModelLodInfo, partBits, 12); + + std::memcpy(&model->lodInfo[i], &xmodel[72 + (i * 56)], 12); + std::memcpy(&model->lodInfo[i].partBits, &xmodel[72 + (i * 56) + 16], 32); + + std::memcpy(reinterpret_cast(&model) + (size - 4) - (i * 4), &xmodel[72 + (i * 56) + 12], 4); + } + + std::memcpy(&model->lodInfo[3].lod, &xmodel[292], (size - 292 - 4)/*68*/); + std::memcpy(&model->physPreset, &xmodel[(size - 8)], 8); + + model[1].name = reinterpret_cast(0xDEADC0DE); + + std::memcpy(xmodel, &model, size); + } + + return result; + } + + void Zones::LoadXModelLodInfo(int i) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + int elSize = (Zones::ZoneVersion == VERSION_ALPHA2) ? 364 : 360; + *Game::varXString = reinterpret_cast(reinterpret_cast(*Game::varXModel) + (elSize - 4) - (4 * (4 - i))); + Game::Load_XString(false); + } + } + + __declspec(naked) void Zones::LoadXModelLodInfoStub() + { + __asm + { + push edi + call Zones::LoadXModelLodInfo + add esp, 4h + + mov eax, [esp + 8h] + push eax + add eax, 8 + push eax + call Game::Load_XModelSurfsFixup + add esp, 8h + + retn + } + } + + bool Zones::LoadXSurfaceArray(bool atStreamStart, char* buffer, int size) + { + int count = 0; + + if (Zones::Version() >= VERSION_ALPHA2) + { + size >>= 6; + + count = size; + size *= 84; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::Version() >= VERSION_ALPHA2) + { + Utils::Memory::Allocator allocator; + Game::XSurface* tempSurfaces = allocator.allocateArray(count); + + for (int i = 0; i < count; ++i) + { + char* source = &buffer[i * 84]; + + std::memcpy(&tempSurfaces[i], source, 12); + std::memcpy(&tempSurfaces[i].triIndices, source + 16, 20); + std::memcpy(&tempSurfaces[i].vertListCount, source + 40, 8); + std::memcpy(&tempSurfaces[i].partBits, source + 52, 24); + + if (Zones::ZoneVersion >= 332) + { + struct + { + short pad; // +0 + char flag; // +2 + char zoneHandle; // +3 + unsigned short vertCount; // +4 + unsigned short triCount; // +6 + // [...] + } surface332; + + // Copy the data to our new structure + std::memcpy(&surface332, &tempSurfaces[i], sizeof(surface332)); + + // Check if that special flag is set + if (!(surface332.flag & 0x20)) + { + Logger::Error("We're not able to handle XSurface buffer allocation yet!"); + } + + // Copy the correct data back to our surface + tempSurfaces[i].zoneHandle = surface332.zoneHandle; + tempSurfaces[i].vertCount = surface332.vertCount; + tempSurfaces[i].triCount = surface332.triCount; + + //std::memmove(&tempSurfaces[i].numVertices, &tempSurfaces[i].numPrimitives, 6); + } + } + + std::memcpy(buffer, tempSurfaces, sizeof(Game::XSurface) * count); + } + + return result; + } + + void Zones::LoadWeaponCompleteDef() + { + if (Zones::ZoneVersion < VERSION_ALPHA2) + { + return Utils::Hook::Call(0x4AE7B0)(true); + } + + // setup structures we use + char* varWeaponCompleteDef = *reinterpret_cast(0x112A9F4); + + int size = 3112; + + if (Zones::ZoneVersion >= 318) + { + size = 3156; + + if (Zones::ZoneVersion >= 332) + { + size = 3068; // We didn't adapt that, but who the fuck cares! + + if (Zones::ZoneVersion >= 359) + { + size = 3120; + + if (Zones::ZoneVersion >= 365) + { + size = 3124; + } + } + } + } + + // and do the stuff + Game::Load_Stream(true, varWeaponCompleteDef, size); + + Game::DB_PushStreamPos(3); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 0); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 4); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 8); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 12); + Game::Load_XString(false); + + *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + 16); + Game::Load_XModelPtr(false); + + if (Zones::ZoneVersion >= 359) + { + for (int offset = 20; offset <= 56; offset += 4) + { + *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_XModelPtr(false); + } + } + else + { + for (int i = 0, offset = 20; i < 32; ++i, offset += 4) + { + *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_XModelPtr(false); + } + + // 148 + for (int offset = 148; offset <= 168; offset += 4) + { + *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_XModelPtr(false); + } + } + + + // 172 + // 32 scriptstrings, should not need to be loaded + + if (Zones::ZoneVersion >= 359) + { + // 236 + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 124); + Game::Load_XStringArray(false, 52); + + // 428 + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 332); + Game::Load_XStringArray(false, 52); + + // 620 + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 540); + Game::Load_XStringArray(false, 52); + } + else + { + // 236 + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 236); + Game::Load_XStringArray(false, 48); + + // 428 + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 428); + Game::Load_XStringArray(false, 48); + + // 620 + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 620); + Game::Load_XStringArray(false, 48); + } + + // 812 + // 16 * 4 scriptstrings + + if (Zones::ZoneVersion >= 359) + { + // 972 + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 908); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 912); + Game::Load_FxEffectDefHandle(false); + } + else + { + // 972 + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 972); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 976); + Game::Load_FxEffectDefHandle(false); + } + + // 980 + if (Zones::ZoneVersion >= 359) + { + // 53 soundalias name references; up to and including 1124 + for (int i = 0, offset = 916; i < 52; ++i, offset += 4) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + } + else + { + // 50 soundalias name references; up to and including 1180 + for (int i = 0, offset = 980; i < 50; ++i, offset += 4) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + + if (Zones::ZoneVersion >= 318) + { + for (int i = 0, offset = 1184; i < 2; ++i, offset += 4) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + + varWeaponCompleteDef += 8; // to compensate for the 2 in between here + } + } + + if (Zones::ZoneVersion >= 359) + { + if (*reinterpret_cast(varWeaponCompleteDef + 1128)) + { + if (*reinterpret_cast(varWeaponCompleteDef + 1128) == -1) + { + *reinterpret_cast(varWeaponCompleteDef + 1128) = Game::DB_AllocStreamPos(3); + *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1128); + + Game::Load_snd_alias_list_nameArray(true, 31); + } + else + { + // full usability requires ConvertOffsetToPointer here + } + } + + if (*reinterpret_cast(varWeaponCompleteDef + 1132)) + { + if (*reinterpret_cast(varWeaponCompleteDef + 1132) == -1) + { + *reinterpret_cast(varWeaponCompleteDef + 1132) = Game::DB_AllocStreamPos(3); + *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1132); + + Game::Load_snd_alias_list_nameArray(true, 31); + } + else + { + // full usability requires ConvertOffsetToPointer here + } + } + } + else + { + if (*reinterpret_cast(varWeaponCompleteDef + 1184)) + { + if (*reinterpret_cast(varWeaponCompleteDef + 1184) == -1) + { + *reinterpret_cast(varWeaponCompleteDef + 1184) = Game::DB_AllocStreamPos(3); + *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1184); + + Game::Load_snd_alias_list_nameArray(true, 31); + } + else + { + // full usability requires ConvertOffsetToPointer here + } + } + + if (*reinterpret_cast(varWeaponCompleteDef + 1188)) + { + if (*reinterpret_cast(varWeaponCompleteDef + 1188) == -1) + { + *reinterpret_cast(varWeaponCompleteDef + 1188) = Game::DB_AllocStreamPos(3); + *Game::varsnd_alias_list_name = *reinterpret_cast(varWeaponCompleteDef + 1188); + + Game::Load_snd_alias_list_nameArray(true, 31); + } + else + { + // full usability requires ConvertOffsetToPointer here + } + } + } + + if (Zones::ZoneVersion >= 359) + { + // 1192 + for (int offset = 1136; offset <= 1148; offset += 4) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_FxEffectDefHandle(false); + } + } + else + { + // 1192 + for (int offset = 1192; offset <= 1204; offset += 4) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_FxEffectDefHandle(false); + } + } + + if (Zones::ZoneVersion >= 359) + { + // 1208 + static int matOffsets1[] = { 1152, 1156, 1372,1376,1380, 1384, 1388, 1392, 1400, 1408 }; + for (int i = 0; i < ARRAYSIZE(matOffsets1); ++i) + { + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + matOffsets1[i]); + Game::Load_MaterialHandle(false); + } + } + else + { // 1208 + static int matOffsets1[] = { 1208, 1212, 1428, 1432, 1436, 1440, 1444, 1448, 1456, 1464 }; + for (int i = 0; i < ARRAYSIZE(matOffsets1); ++i) + { + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + matOffsets1[i]); + Game::Load_MaterialHandle(false); + } + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1428); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1436); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1452); + Game::Load_XString(false); + } + else + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1484); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1492); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 1508); + Game::Load_XString(false); + } + + if (Zones::ZoneVersion >= 359) + { + for (int offset = 1716; offset <= 1728; offset += 4) + { + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_MaterialHandle(false); + } + } + else + { + for (int offset = 1764; offset <= 1776; offset += 4) + { + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_MaterialHandle(false); + } + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varPhysCollmapPtr = reinterpret_cast(varWeaponCompleteDef + 1928); + Game::Load_PhysCollmapPtr(false); + + *Game::varPhysPresetPtr = reinterpret_cast(varWeaponCompleteDef + 1932); + Game::Load_PhysPresetPtr(false); + } + else + { + *Game::varPhysCollmapPtr = reinterpret_cast(varWeaponCompleteDef + 1964); + Game::Load_PhysCollmapPtr(false); + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + 2020); + Game::Load_XModelPtr(false); + } + else + { + *Game::varXModelPtr = reinterpret_cast(varWeaponCompleteDef + 2052); + Game::Load_XModelPtr(false); + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2028); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2032); + Game::Load_FxEffectDefHandle(false); + } + else + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2060); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2064); + Game::Load_FxEffectDefHandle(false); + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2036); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2040); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + else + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2068); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2072); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2304); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2308); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2336); + Game::Load_FxEffectDefHandle(false); + } + else + { + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2336); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2340); + Game::Load_FxEffectDefHandle(false); + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2368); // 2376 + Game::Load_FxEffectDefHandle(false); + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2340); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2516); + Game::Load_XString(false); + } + else + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2372); // 2380 + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2548); // 2556 + Game::Load_XString(false); + } + + if (Zones::ZoneVersion >= 359) + { + if (*reinterpret_cast(varWeaponCompleteDef + 2524) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 2524) = vec2; + + Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3044)); + } + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2520); + Game::Load_XString(false); + + if (*reinterpret_cast(varWeaponCompleteDef + 2528) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 2528) = vec2; + + Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3046)); + } + } + else + { + if (*reinterpret_cast(varWeaponCompleteDef + 2556) == -1) // 2564 + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 2556) = vec2; + + Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + ((Zones::ZoneVersion >= 318) ? 3076 : 3040))); + } + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2552); + Game::Load_XString(false); + + if (*reinterpret_cast(varWeaponCompleteDef + 2560) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 2560) = vec2; + + Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + ((Zones::ZoneVersion >= 318) ? 3078 : 3042))); + } + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2608); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2612); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2644); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2648); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2772); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2776); + Game::Load_XString(false); + } + else + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2640); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2644); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2676); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2680); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2804); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2808); + Game::Load_XString(false); + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varTracerDefPtr = reinterpret_cast(varWeaponCompleteDef + 2780); + Game::Load_TracerDefPtr(false); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2808); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); // 2848 + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2812); + Game::Load_FxEffectDefHandle(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2816); + Game::Load_XString(false); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2832); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2836); + Game::Load_snd_alias_list_nameArray(false, 4); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2852); + Game::Load_snd_alias_list_nameArray(false, 4); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2868); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2872); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + else + { + *Game::varTracerDefPtr = reinterpret_cast(varWeaponCompleteDef + 2812); + Game::Load_TracerDefPtr(false); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2840); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); // 2848 + + *Game::varFxEffectDefHandle = reinterpret_cast(varWeaponCompleteDef + 2844); + Game::Load_FxEffectDefHandle(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2848); + Game::Load_XString(false); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2864); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2868); + Game::Load_snd_alias_list_nameArray(false, 4); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2884); + Game::Load_snd_alias_list_nameArray(false, 4); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2900); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + 2904); // 2912 + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + + if (Zones::ZoneVersion >= 359) + { + for (int i = 0, offset = 2940; i < 6; ++i, offset += 4) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + } + else + { + if (Zones::ZoneVersion >= 318) + { + for (int i = 0, offset = 2972; i < 6; ++i, offset += 4) + { + *Game::varsnd_alias_list_name = reinterpret_cast(varWeaponCompleteDef + offset); + Game::Load_SndAliasCustom(*Game::varsnd_alias_list_name); + } + + varWeaponCompleteDef += (6 * 4); + varWeaponCompleteDef += 12; + } + else + { + + } + } + + if (Zones::ZoneVersion >= 359) + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2988); + Game::Load_XString(false); + + if (Zones::ZoneVersion >= 365) + { + varWeaponCompleteDef += 4; + } + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3000); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3004); + Game::Load_XString(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3012); + Game::Load_MaterialHandle(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3016); + Game::Load_MaterialHandle(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3020); + Game::Load_MaterialHandle(false); + } + else + { + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2984); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 2996); + Game::Load_XString(false); + + *Game::varXString = reinterpret_cast(varWeaponCompleteDef + 3000); + Game::Load_XString(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3008); + Game::Load_MaterialHandle(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3012); + Game::Load_MaterialHandle(false); + + *Game::varMaterialHandle = reinterpret_cast(varWeaponCompleteDef + 3016); + Game::Load_MaterialHandle(false); + } + + if (Zones::ZoneVersion >= 359) + { + if (*reinterpret_cast(varWeaponCompleteDef + 3048) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 3048) = vec2; + + Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3044)); + } + + if (*reinterpret_cast(varWeaponCompleteDef + 3052) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 3052) = vec2; + + Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3046)); + } + } + else + { + if (*reinterpret_cast(varWeaponCompleteDef + 3044) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 3044) = vec2; + + Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3040)); + } + + if (*reinterpret_cast(varWeaponCompleteDef + 3048) == -1) + { + void* vec2 = Game::DB_AllocStreamPos(3); + *reinterpret_cast(varWeaponCompleteDef + 3048) = vec2; + + Game::Load_Stream(true, (void*)vec2, 8 * *reinterpret_cast(varWeaponCompleteDef + 3042)); + } + } + + Game::DB_PopStreamPos(); + } + +// Code-analysis has a bug, the first memcpy makes it believe size of tempVar is 44 instead of 84 +#pragma warning(push) +#pragma warning(disable: 6385) + bool Zones::LoadGameWorldSp(bool atStreamStart, char* buffer, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + size = 84; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::Version() >= VERSION_ALPHA2) + { + char tempVar[84] = { 0 }; + std::memcpy(&tempVar[0], &buffer[0], 44); + std::memcpy(&tempVar[56], &buffer[44], 28); + std::memcpy(&tempVar[44], &buffer[72], 12); + + std::memcpy(buffer, tempVar, sizeof(tempVar)); + } + + return result; + } +#pragma warning(pop) + + void Zones::LoadPathDataTail() + { + if (Zones::ZoneVersion >= VERSION_ALPHA2) + { + char* varPathData = reinterpret_cast(*Game::varPathData); + + if (*reinterpret_cast(varPathData + 56)) + { + *reinterpret_cast(varPathData + 56) = Game::DB_AllocStreamPos(0); + Game::Load_Stream(true, *reinterpret_cast(varPathData + 56), *reinterpret_cast(varPathData + 52)); + } + + if (*reinterpret_cast(varPathData + 64)) + { + *reinterpret_cast(varPathData + 64) = Game::DB_AllocStreamPos(0); + Game::Load_Stream(true, *reinterpret_cast(varPathData + 64), *reinterpret_cast(varPathData + 60)); + } + + if (*reinterpret_cast(varPathData + 76)) + { + *reinterpret_cast(varPathData + 76) = Game::DB_AllocStreamPos(0); + Game::Load_Stream(true, *reinterpret_cast(varPathData + 76), *reinterpret_cast(varPathData + 72)); + } + } + } + + bool Zones::Loadsnd_alias_tArray(bool atStreamStart, char* buffer, int len) + { + int count = 0; + + if (Zones::Version() >= VERSION_ALPHA2) + { + len /= 100; + count = len; + len *= 108; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, len); + + if (Zones::Version() >= VERSION_ALPHA2) + { + Utils::Memory::Allocator allocator; + Game::snd_alias_t* tempSounds = allocator.allocateArray(count); + + for (int i = 0; i < count; ++i) + { + char* src = &buffer[i * 108]; + char* dest = reinterpret_cast(&tempSounds[i]); + + std::memcpy(dest + 0, src + 0, 60); + std::memcpy(dest + 60, src + 68, 20); + std::memcpy(dest + 80, src + 88, 20); + + AssetHandler::Relocate(src + 0, buffer + (i * 100) + 0, 60); + AssetHandler::Relocate(src + 68, buffer + (i * 100) + 60, 20); + AssetHandler::Relocate(src + 88, buffer + (i * 100) + 80, 20); + } + + std::memcpy(buffer, tempSounds, sizeof(Game::snd_alias_t) * count); + } + + return result; + } + + bool Zones::LoadLoadedSound(bool atStreamStart, char* buffer, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + size = 48; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::Version() >= VERSION_ALPHA2) + { + std::memmove(buffer + 28, buffer + 32, 16); + AssetHandler::Relocate(buffer + 32, buffer + 28, 16); + } + + return result; + } + +// Code-analysis has a bug, the first memcpy makes it believe size of tempVar is 400 instead of 788 +#pragma warning(push) +#pragma warning(disable: 6385) + bool Zones::LoadVehicleDef(bool atStreamStart, char* buffer, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + size = 788; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::Version() >= VERSION_ALPHA2) + { + char tempVar[788] = { 0 }; + std::memcpy(&tempVar[0], &buffer[0], 400); + std::memcpy(&tempVar[408], &buffer[400], 380); + + AssetHandler::Relocate(buffer + 400, buffer + 408, 388); + + std::memmove(buffer, tempVar, sizeof(tempVar)); + } + + return result; + } +#pragma warning(pop) + + void Zones::LoadWeaponAttachStuff(DWORD* varWeaponAttachStuff, int count) + { + Game::Load_Stream(true, varWeaponAttachStuff, 12 * count); + + for (int i = 0; i < count; ++i) + { + if (varWeaponAttachStuff[1] < 16 || varWeaponAttachStuff[1] == 39) + { + if (varWeaponAttachStuff[2] == -1) + { + varWeaponAttachStuff[2] = reinterpret_cast(Game::DB_AllocStreamPos(0)); + *Game::varConstChar = reinterpret_cast(varWeaponAttachStuff[2]); + Game::Load_XStringCustom(Game::varConstChar); + } + } + + varWeaponAttachStuff += 3; + } + } + + bool Zones::LoadmenuDef_t(bool atStreamStart, char* buffer, int size) + { + if (Zones::ZoneVersion != 359 && Zones::ZoneVersion >= VERSION_ALPHA2) size += 4; + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::ZoneVersion >= VERSION_ALPHA2) + { + std::memmove(buffer + 168, buffer + 172, (Zones::ZoneVersion != 359 ? 232 : 228)); + AssetHandler::Relocate(buffer + 172, buffer + 168, (Zones::ZoneVersion != 359 ? 232 : 228)); + + reinterpret_cast(buffer)->expressionData = nullptr; + } + + return result; + } + + void Zones::LoadWeaponAttach() + { + if (Zones::ZoneVersion < VERSION_ALPHA2) + { + return Utils::Hook::Call(0x4F4160)(true); + } + + // setup structures we use + char* varWeaponAttach = *reinterpret_cast(0x112ADE0); // varAddonMapEnts + + // and do the stuff + Game::Load_Stream(true, varWeaponAttach, 12); + + Game::DB_PushStreamPos(3); + + *Game::varXString = reinterpret_cast(varWeaponAttach); + Game::Load_XString(false); + + *reinterpret_cast(varWeaponAttach + 8) = Game::DB_AllocStreamPos(3); + Zones::LoadWeaponAttachStuff(*reinterpret_cast(varWeaponAttach + 8), *reinterpret_cast(varWeaponAttach + 4)); + + Game::DB_PopStreamPos(); + } + + bool Zones::LoadMaterialShaderArgumentArray(bool atStreamStart, Game::MaterialShaderArgument* argument, int size) + { + bool result = Game::Load_Stream(atStreamStart, argument, size); + + Game::MaterialPass* curPass = *Game::varMaterialPass; + int count = curPass->argCount1 + curPass->argCount2 + curPass->argCount3; + + for (int i = 0; i < count && (Zones::ZoneVersion >= VERSION_ALPHA2); ++i) + { + Game::MaterialShaderArgument* arg = &argument[i]; + + if (arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_TEXTURE && arg->type != D3DSHADER_PARAM_REGISTER_TYPE::D3DSPR_ATTROUT) + { + continue; + } + + // should be min 68 currently + // >= 58 fixes foliage without bad side effects + // >= 53 still has broken shadow mapping + // >= 23 is still broken somehow + if (arg->paramID >= 58 && arg->paramID <= 135) // >= 34 would be 31 in iw4 terms + { + arg->paramID -= 3; + + if (Zones::Version() >= 359/* && arg->paramID <= 113*/) + { + arg->paramID -= 7; + + if (arg->paramID <= 53) + { + arg->paramID += 1; + } + } + } + // >= 21 works fine for specular, but breaks trees + // >= 4 is too low, breaks specular + else if (arg->paramID >= 11 && arg->paramID < 58) + { + arg->paramID -= 2; + + if (Zones::Version() >= 359) + { + if (arg->paramID > 15 && arg->paramID < 30) + { + arg->paramID -= 1; + + if (arg->paramID == 19) + { + arg->paramID = 21; + } + } + else if (arg->paramID >= 50) + { + arg->paramID += 6; + } + } + } + } + + return result; + } + + bool Zones::LoadStructuredDataStructPropertyArray(bool atStreamStart, char* data, int size) + { + int count = 0; + + if (Zones::ZoneVersion >= VERSION_ALPHA2) + { + size /= 16; + count = size; + size *= 24; + } + + bool result = Game::Load_Stream(atStreamStart, data, size); + + if (Zones::ZoneVersion >= VERSION_ALPHA2) + { + for (int i = 0; i < count; ++i) + { + std::memmove(data + (i * 16), data + (i * 24), 16); + AssetHandler::Relocate(data + (i * 24), data + (i * 16), 16); + } + } + + return result; + } + + bool Zones::LoadGfxImage(bool atStreamStart, char* buffer, int size) + { + if (Zones::ZoneVersion >= 332) + { + size = (Zones::ZoneVersion >= 359) ? 52 : 36; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::ZoneVersion >= 332) + { + AssetHandler::Relocate(buffer + (size - 4), buffer + 28, 4); + + if (Zones::Version() >= 359) + { + struct + { + Game::GfxImageLoadDef* texture; + Game::MapType mapType; + char semantic; + char category; + char flags; + int cardMemory; + char pad[8]; // ? + int dataLen1; + int dataLen2; + char pad2[4]; // ? + short height; + short width; + short depth; + char loaded; + char pad3[5]; + Game::GfxImageLoadDef* storedTexture; + char* name; + } image359; + + AssertSize(image359, 52); + + // Copy to new struct + memcpy(&image359, buffer, sizeof(image359)); + + // Convert to old struct + Game::GfxImage* image = reinterpret_cast(buffer); + image->mapType = image359.mapType; + image->semantic = image359.semantic; + image->category = image359.category; + image->flags = image359.flags; + image->cardMemory = image359.cardMemory; + image->dataLen1 = image359.dataLen1; + image->dataLen2 = image359.dataLen2; + image->height = image359.height; + image->width = image359.width; + image->depth = image359.depth; + image->loaded = image359.loaded; + image->name = image359.name; + + // Used for later stuff + image->pad = image359.pad3[1]; + } + else + { + memcpy(buffer + 28, buffer + (size - 4), 4); + } + } + + return result; + } + + bool Zones::LoadXAsset(bool atStreamStart, char* buffer, int size) + { + int count = 0; + + if (Zones::ZoneVersion >= 359) + { + size /= 8; + count = size; + size *= 16; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::ZoneVersion >= 359) + { + Utils::Memory::Allocator allocator; + Game::XAsset* tempAssets = allocator.allocateArray(count); + + for (int i = 0; i < count; ++i) + { + char* src = &buffer[i * 16]; + + std::memcpy(&tempAssets[i].type, src + 0, 4); + std::memcpy(&tempAssets[i].header, src + 8, 4); + + AssetHandler::Relocate(src + 0, buffer + (i * 8) + 0, 4); + AssetHandler::Relocate(src + 8, buffer + (i * 8) + 4, 4); + } + + std::memcpy(buffer, tempAssets, sizeof(Game::XAsset) * count); + } + + return result; + } + + bool Zones::LoadMaterialTechnique(bool atStreamStart, char* buffer, int size) + { + if (Zones::ZoneVersion >= 359) + { + size += 4; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::ZoneVersion >= 359) + { + // This shouldn't make any difference. + // The new entry is an additional remapped techset which is linked at runtime. + // It's used when the 0x100 gameFlag in a material is set. + // As MW2 flags are only 1 byte large, this won't be possible anyways + int shiftTest = 4; + + std::memmove(buffer + 8 + shiftTest, buffer + 12 + shiftTest, 196 - shiftTest); + AssetHandler::Relocate(buffer + 12 + shiftTest, buffer + 8 + shiftTest, 196 - shiftTest); + } + + return result; + } + + bool Zones::LoadMaterial(bool atStreamStart, char* buffer, int size) + { + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::ZoneVersion >= 359) + { + struct material339_s + { + char drawSurfBegin[8]; // 4 + //int surfaceTypeBits; + const char *name; + char drawSurf[6]; + + union + { + char gameFlags; + short sGameFlags; + }; + char sortKey; + char textureAtlasRowCount; + char textureAtlasColumnCount; + } material359; + + static_assert(offsetof(material339_s, gameFlags) == 18, ""); + static_assert(offsetof(material339_s, sortKey) == 20, ""); + static_assert(offsetof(material339_s, textureAtlasColumnCount) == 22, ""); + + static_assert(offsetof(Game::Material, stateBitsEntry) == 24, ""); + + Game::Material* material = (Game::Material*)buffer; + memcpy(&material359, material, sizeof(material359)); + + material->name = material359.name; + material->sortKey = material359.sortKey; + material->textureAtlasRowCount = material359.textureAtlasRowCount; + material->textureAtlasColumnCount = material359.textureAtlasColumnCount; + material->gameFlags = material359.gameFlags; + + // Probably wrong + material->surfaceTypeBits = 0;//material359.surfaceTypeBits; + + // Pretty sure that's wrong + // Actually, it's not + // yes it was lol + memcpy(&material->drawSurf.packed, material359.drawSurfBegin, 8); + + memcpy(&material->surfaceTypeBits, &material359.drawSurf[0], 6); // copies both surfaceTypeBits and hashIndex + //material->drawSurf[8] = material359.drawSurf[0]; + //material->drawSurf[9] = material359.drawSurf[1]; + //material->drawSurf[10] = material359.drawSurf[2]; + //material->drawSurf[11] = material359.drawSurf[3]; + + if (material359.sGameFlags & 0x100) + { + //OutputDebugStringA(""); + } + } + + return result; + } + + bool Zones::LoadGfxWorld(bool atStreamStart, char* buffer, int size) + { + if (Zones::ZoneVersion >= 359) + { + size += 968; + } + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::ZoneVersion >= 359) + { + int sunDiff = 8; // Stuff that is part of the sunflare we would overwrite + std::memmove(buffer + 348 + sunDiff, buffer + 1316 + sunDiff, 280 - sunDiff); + AssetHandler::Relocate(buffer + 1316, buffer + 348, 280); + } + + return result; + } + + void Zones::Loadsunflare_t(bool atStreamStart) + { + Game::Load_MaterialHandle(atStreamStart); + + if (Zones::ZoneVersion >= 359) + { + char* varsunflare_t = *reinterpret_cast(0x112A848); + + *Game::varMaterialHandle = reinterpret_cast(varsunflare_t + 12); + Game::Load_MaterialHandle(atStreamStart); + + *Game::varMaterialHandle = reinterpret_cast(varsunflare_t + 16); + Game::Load_MaterialHandle(atStreamStart); + + std::memmove(varsunflare_t + 12, varsunflare_t + 20, 84); + + // Copy the remaining struct data we couldn't copy in LoadGfxWorld + char* varGfxWorld = *reinterpret_cast(0x112A7F4); + std::memmove(varGfxWorld + 348, varGfxWorld + 1316, 8); + } + } + + bool Zones::LoadStatement(bool atStreamStart, char* buffer, int size) + { + if (Zones::Version() >= 359) size -= 4; + + bool result = Game::Load_Stream(atStreamStart, buffer, size); + + if (Zones::Version() >= 359) + { + std::memmove(buffer + 12, buffer + 8, 12); + } + + return result; + } + + void Zones::LoadWindowImage(bool atStreamStart) + { + Game::Load_MaterialHandle(atStreamStart); + + if (Zones::Version() >= 360) + { + char** varGfxImagePtr = reinterpret_cast(0x112B4A0); + char** varwindowDef_t = reinterpret_cast(0x112AF94); + + *varGfxImagePtr = *varwindowDef_t + 164; + Game::Load_GfxImagePtr(atStreamStart); + } + } + + void Zones::LoadPhysPreset(bool atStreamStart, char* buffer, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + size = 68; + } + + Game::Load_Stream(atStreamStart, buffer, size); + } + + void Zones::LoadXModelSurfs(bool atStreamStart, char* buffer, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + size = 48; + } + + Game::Load_Stream(atStreamStart, buffer, size); + } + + void Zones::LoadImpactFx(bool atStreamStart, char* buffer, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + size = 0x8C0; + } + + Game::Load_Stream(atStreamStart, buffer, size); + } + + int Zones::ImpactFxArrayCount() + { + if (Zones::Version() >= VERSION_ALPHA2) + { + return 16; + } + + return 15; + } + + __declspec(naked) void Zones::LoadImpactFxArray() + { + __asm + { + push edi + pushad + + push edi + call Zones::ImpactFxArrayCount + pop edi + + mov [esp + 20h], eax + + popad + pop edi + + push 4447E0h + retn + } + } + + void Zones::LoadPathNodeArray(bool atStreamStart, char* buffer, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + size /= 136; + size *= 148; + } + + Game::Load_Stream(atStreamStart, buffer, size); + } + + void Zones::LoadPathnodeConstantTail(bool atStreamStart, char* buffer, int size) + { + if (Zones::Version() >= VERSION_ALPHA2) + { + size /= 12; + size *= 16; + } + + Game::Load_Stream(atStreamStart, buffer, size); + } + + void Zones::LoadExpressionSupportingDataPtr(bool atStreamStart) + { + if (Zones::Version() < 359) + { + Utils::Hook::Call(0x4AF680)(atStreamStart); + } + } + + void Zones::SetVersion(int version) + { + AssetHandler::ClearRelocations(); + Zones::ZoneVersion = version; + } + + __declspec(noinline) bool Zones::CheckGameMapSp(int type) + { + if (type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_SP) + { + return true; + } + + if (Zones::Version() >= VERSION_ALPHA2 && type == Game::XAssetType::ASSET_TYPE_GAMEWORLD_MP) + { + Maps::HandleAsSPMap(); + return true; + } + + return false; + } + + __declspec(naked) void Zones::GameMapSpPatchStub() + { + __asm + { + pushad + + push eax + call Zones::CheckGameMapSp + add esp, 4h + + test al, al + jnz returnSafe + + popad + push 4189AEh + retn + + returnSafe: + popad + push 41899Dh + retn + } + } + + int Zones::PathDataSize() + { + if (Zones::Version() >= VERSION_ALPHA2) + { + return 148; + } + + return 136; + } + + __declspec(naked) void Zones::LoadPathDataConstant() + { + __asm + { + push esi + pushad + + call Zones::PathDataSize + + add [esp + 20h], eax + + popad + pop esi + + push 4D6A4Dh + retn + } + } + + Zones::Zones() + { + Zones::ZoneVersion = 0; + + // Ignore missing soundaliases for now + // TODO: Include them in the dependency zone! + Utils::Hook::Nop(0x644207, 5); + + // Block Mark_pathnode_constant_t + Utils::Hook::Set(0x4F74B0, 0xC3); + + // addon_map_ents asset type (we reuse it for weaponattach) + Utils::Hook::Set(0x418B31, 0x72); + + Utils::Hook(0x495938, Zones::LoadFxElemDefArrayStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x45ADA0, Zones::LoadFxElemDefStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x4EA6FE, Zones::LoadXModelLodInfoStub, HOOK_CALL).install()->quick(); + Utils::Hook(0x410D90, Zones::LoadXModel, HOOK_CALL).install()->quick(); + Utils::Hook(0x4925C8, Zones::LoadXSurfaceArray, HOOK_CALL).install()->quick(); + Utils::Hook(0x4F4D0D, Zones::LoadGameWorldSp, HOOK_CALL).install()->quick(); + Utils::Hook(0x47CCD2, Zones::LoadWeaponCompleteDef, HOOK_CALL).install()->quick(); + Utils::Hook(0x483DA0, Zones::LoadVehicleDef, HOOK_CALL).install()->quick(); + Utils::Hook(0x4F0AC8, Zones::Loadsnd_alias_tArray, HOOK_CALL).install()->quick(); + Utils::Hook(0x403A5D, Zones::LoadLoadedSound, HOOK_CALL).install()->quick(); + Utils::Hook(0x463022, Zones::LoadWeaponAttach, HOOK_CALL).install()->quick(); + Utils::Hook(0x41A570, Zones::LoadmenuDef_t, HOOK_CALL).install()->quick(); + Utils::Hook(0x49591B, Zones::LoadFxEffectDef, HOOK_CALL).install()->quick(); + Utils::Hook(0x428F0A, Zones::LoadMaterialShaderArgumentArray, HOOK_CALL).install()->quick(); + Utils::Hook(0x4B1EB8, Zones::LoadStructuredDataStructPropertyArray, HOOK_CALL).install()->quick(); + Utils::Hook(0x49CE0D, Zones::LoadPhysPreset, HOOK_CALL).install()->quick(); + Utils::Hook(0x48E84D, Zones::LoadXModelSurfs, HOOK_CALL).install()->quick(); + Utils::Hook(0x4447C2, Zones::LoadImpactFx, HOOK_CALL).install()->quick(); + Utils::Hook(0x4447D0, Zones::LoadImpactFxArray, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4D6A0B, Zones::LoadPathNodeArray, HOOK_CALL).install()->quick(); + Utils::Hook(0x4D6A47, Zones::LoadPathDataConstant, HOOK_JUMP).install()->quick(); + Utils::Hook(0x463D6E, Zones::LoadPathnodeConstantTail, HOOK_CALL).install()->quick(); + + Utils::Hook(0x4471AD, Zones::LoadGfxImage, HOOK_CALL).install()->quick(); + + Utils::Hook(0x41A590, Zones::LoadExpressionSupportingDataPtr, HOOK_CALL).install()->quick(); + Utils::Hook(0x459833, Zones::LoadExpressionSupportingDataPtr, HOOK_JUMP).install()->quick(); + + Utils::Hook(0x5B9AA5, Zones::LoadXAsset, HOOK_CALL).install()->quick(); + Utils::Hook(0x461710, Zones::LoadMaterialTechnique, HOOK_CALL).install()->quick(); + Utils::Hook(0x40330D, Zones::LoadMaterial, HOOK_CALL).install()->quick(); + Utils::Hook(0x4B8DC0, Zones::LoadGfxWorld, HOOK_CALL).install()->quick(); + Utils::Hook(0x4B8FF5, Zones::Loadsunflare_t, HOOK_CALL).install()->quick(); + + Utils::Hook(0x418998, Zones::GameMapSpPatchStub, HOOK_JUMP).install()->quick(); + Utils::Hook(0x427A1B, Zones::LoadPathDataTail, HOOK_JUMP).install()->quick(); + Utils::Hook(0x4F4D3B, [] () + { + if (Zones::ZoneVersion >= VERSION_ALPHA3) + { + ZeroMemory(*Game::varPathData, sizeof(Game::PathData)); + } + else + { + // Load_PathData + Utils::Hook::Call(0x4278A0)(false); + } + }, HOOK_CALL).install()->quick(); + + // Change stream for images + Utils::Hook(0x4D3225, [] () + { + Game::DB_PushStreamPos((Zones::ZoneVersion >= 332) ? 3 : 0); + }, HOOK_CALL).install()->quick(); + + Utils::Hook(0x4597DD, Zones::LoadStatement, HOOK_CALL).install()->quick(); + Utils::Hook(0x471A39, Zones::LoadWindowImage, HOOK_JUMP).install()->quick(); + +#ifdef DEBUG + // Easy dirty disk debugging + Utils::Hook::Set(0x4CF7F0, 0xC3CC); + // disable _invoke_watson to allow debugging + Utils::Hook::Set(0x6B9602,0xCCCC); +#endif + } + + Zones::~Zones() + { + + } +} diff --git a/src/Components/Modules/Zones.hpp b/src/Components/Modules/Zones.hpp index c7bb32aa..c58f9d16 100644 --- a/src/Components/Modules/Zones.hpp +++ b/src/Components/Modules/Zones.hpp @@ -1,68 +1,68 @@ -#define VERSION_ALPHA2 316 -#define VERSION_ALPHA3 318//319 -#define VERSION_ALPHA3_DEC 319 - -namespace Components -{ - class Zones : public Component - { - public: - Zones(); - ~Zones(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - const char* getName() { return "Zones"; }; -#endif - - static void SetVersion(int version); - - static int Version() { return Zones::ZoneVersion; }; - - private: - static int ZoneVersion; - - static int FxEffectIndex; - static char* FxEffectStrings[64]; - - static bool CheckGameMapSp(int type); - static void GameMapSpPatchStub(); - - static void LoadFxElemDefArrayStub(bool atStreamStart); - static bool LoadFxElemDefStub(bool atStreamStart, Game::FxElemDef* fxElem, int size); - - static void LoadXModelLodInfo(int i); - static void LoadXModelLodInfoStub(); - static bool LoadXModel(bool atStreamStart, char* xmodel, int size); - static bool LoadXSurfaceArray(bool atStreamStart, char* buffer, int size); - static bool LoadGameWorldSp(bool atStreamStart, char* buffer, int size); - static bool LoadVehicleDef(bool atStreamStart, char* buffer, int size); - static bool Loadsnd_alias_tArray(bool atStreamStart, char* buffer, int len); - static bool LoadLoadedSound(bool atStreamStart, char* buffer, int size); - static bool LoadmenuDef_t(bool atStreamStart, char* buffer, int size); - static bool LoadFxEffectDef(bool atStreamStart, char* buffer, int size); - static bool LoadMaterialShaderArgumentArray(bool atStreamStart, Game::MaterialShaderArgument* argument, int size); - static bool LoadStructuredDataStructPropertyArray(bool atStreamStart, char* data, int size); - static void LoadPathDataTail(); - static void LoadWeaponAttach(); - static void LoadWeaponAttachStuff(DWORD* varWeaponAttachStuff, int count); - static void LoadWeaponCompleteDef(); - static bool LoadGfxImage(bool atStreamStart, char* buffer, int size); - static bool LoadXAsset(bool atStreamStart, char* buffer, int size); - static bool LoadMaterialTechnique(bool atStreamStart, char* buffer, int size); - static bool LoadMaterial(bool atStreamStart, char* buffer, int size); - static bool LoadGfxWorld(bool atStreamStart, char* buffer, int size); - static void Loadsunflare_t(bool atStreamStart); - static bool LoadStatement(bool atStreamStart, char* buffer, int size); - static void LoadWindowImage(bool atStreamStart); - static void LoadPhysPreset(bool atStreamStart, char* buffer, int size); - static void LoadXModelSurfs(bool atStreamStart, char* buffer, int size); - static void LoadImpactFx(bool atStreamStart, char* buffer, int size); - static void LoadPathNodeArray(bool atStreamStart, char* buffer, int size); - static void LoadPathnodeConstantTail(bool atStreamStart, char* buffer, int size); - static void LoadExpressionSupportingDataPtr(bool atStreamStart); - static void LoadImpactFxArray(); - static int ImpactFxArrayCount(); - static void LoadPathDataConstant(); - static int PathDataSize(); - }; -} +#define VERSION_ALPHA2 316 +#define VERSION_ALPHA3 318//319 +#define VERSION_ALPHA3_DEC 319 + +namespace Components +{ + class Zones : public Component + { + public: + Zones(); + ~Zones(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + const char* getName() { return "Zones"; }; +#endif + + static void SetVersion(int version); + + static int Version() { return Zones::ZoneVersion; }; + + private: + static int ZoneVersion; + + static int FxEffectIndex; + static char* FxEffectStrings[64]; + + static bool CheckGameMapSp(int type); + static void GameMapSpPatchStub(); + + static void LoadFxElemDefArrayStub(bool atStreamStart); + static bool LoadFxElemDefStub(bool atStreamStart, Game::FxElemDef* fxElem, int size); + + static void LoadXModelLodInfo(int i); + static void LoadXModelLodInfoStub(); + static bool LoadXModel(bool atStreamStart, char* xmodel, int size); + static bool LoadXSurfaceArray(bool atStreamStart, char* buffer, int size); + static bool LoadGameWorldSp(bool atStreamStart, char* buffer, int size); + static bool LoadVehicleDef(bool atStreamStart, char* buffer, int size); + static bool Loadsnd_alias_tArray(bool atStreamStart, char* buffer, int len); + static bool LoadLoadedSound(bool atStreamStart, char* buffer, int size); + static bool LoadmenuDef_t(bool atStreamStart, char* buffer, int size); + static bool LoadFxEffectDef(bool atStreamStart, char* buffer, int size); + static bool LoadMaterialShaderArgumentArray(bool atStreamStart, Game::MaterialShaderArgument* argument, int size); + static bool LoadStructuredDataStructPropertyArray(bool atStreamStart, char* data, int size); + static void LoadPathDataTail(); + static void LoadWeaponAttach(); + static void LoadWeaponAttachStuff(DWORD* varWeaponAttachStuff, int count); + static void LoadWeaponCompleteDef(); + static bool LoadGfxImage(bool atStreamStart, char* buffer, int size); + static bool LoadXAsset(bool atStreamStart, char* buffer, int size); + static bool LoadMaterialTechnique(bool atStreamStart, char* buffer, int size); + static bool LoadMaterial(bool atStreamStart, char* buffer, int size); + static bool LoadGfxWorld(bool atStreamStart, char* buffer, int size); + static void Loadsunflare_t(bool atStreamStart); + static bool LoadStatement(bool atStreamStart, char* buffer, int size); + static void LoadWindowImage(bool atStreamStart); + static void LoadPhysPreset(bool atStreamStart, char* buffer, int size); + static void LoadXModelSurfs(bool atStreamStart, char* buffer, int size); + static void LoadImpactFx(bool atStreamStart, char* buffer, int size); + static void LoadPathNodeArray(bool atStreamStart, char* buffer, int size); + static void LoadPathnodeConstantTail(bool atStreamStart, char* buffer, int size); + static void LoadExpressionSupportingDataPtr(bool atStreamStart); + static void LoadImpactFxArray(); + static int ImpactFxArrayCount(); + static void LoadPathDataConstant(); + static int PathDataSize(); + }; +} diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 5e253ff8..f35b3dfd 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -1,669 +1,669 @@ -#include "STDInclude.hpp" - -namespace Game -{ - // C-Style casts are fine here, that's where we're doing our dirty stuff anyways... - BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj = (BG_LoadWeaponDef_LoadObj_t)0x57B5F0; - - Cbuf_AddServerText_t Cbuf_AddServerText = (Cbuf_AddServerText_t)0x4BB9B0; - Cbuf_AddText_t Cbuf_AddText = (Cbuf_AddText_t)0x404B20; - - CG_GetClientNum_t CG_GetClientNum = (CG_GetClientNum_t)0x433700; - - CL_GetClientName_t CL_GetClientName = (CL_GetClientName_t)0x4563D0; - CL_IsCgameInitialized_t CL_IsCgameInitialized = (CL_IsCgameInitialized_t)0x43EB20; - CL_ConnectFromParty_t CL_ConnectFromParty = (CL_ConnectFromParty_t)0x433D30; - CL_DownloadsComplete_t CL_DownloadsComplete = (CL_DownloadsComplete_t)0x42CE90; - CL_DrawStretchPicPhysical_t CL_DrawStretchPicPhysical = (CL_DrawStretchPicPhysical_t)0x4FC120; - CL_HandleRelayPacket_t CL_HandleRelayPacket = (CL_HandleRelayPacket_t)0x5A8C70; - CL_ResetViewport_t CL_ResetViewport = (CL_ResetViewport_t)0x4A8830; - CL_SelectStringTableEntryInDvar_f_t CL_SelectStringTableEntryInDvar_f = (CL_SelectStringTableEntryInDvar_f_t)0x4A4560; - - Cmd_AddCommand_t Cmd_AddCommand = (Cmd_AddCommand_t)0x470090; - Cmd_AddServerCommand_t Cmd_AddServerCommand = (Cmd_AddServerCommand_t)0x4DCE00; - Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand = (Cmd_ExecuteSingleCommand_t)0x609540; - Com_ClientPacketEvent_t Com_ClientPacketEvent = (Com_ClientPacketEvent_t)0x49F0B0; - - Com_Error_t Com_Error = (Com_Error_t)0x4B22D0; - Com_Printf_t Com_Printf = (Com_Printf_t)0x402500; - Com_PrintMessage_t Com_PrintMessage = (Com_PrintMessage_t)0x4AA830; - Com_EndParseSession_t Com_EndParseSession = (Com_EndParseSession_t)0x4B80B0; - Com_BeginParseSession_t Com_BeginParseSession = (Com_BeginParseSession_t)0x4AAB80; - Com_SetSpaceDelimited_t Com_SetSpaceDelimited = (Com_SetSpaceDelimited_t)0x4FC710; - Com_Parse_t Com_Parse = (Com_Parse_t)0x474D60; - Com_SetSlowMotion_t Com_SetSlowMotion = (Com_SetSlowMotion_t)0x446E20; - - Con_DrawMiniConsole_t Con_DrawMiniConsole = (Con_DrawMiniConsole_t)0x464F30; - Con_DrawSolidConsole_t Con_DrawSolidConsole = (Con_DrawSolidConsole_t)0x5A5040; - - DB_AllocStreamPos_t DB_AllocStreamPos = (DB_AllocStreamPos_t)0x418380; - DB_PushStreamPos_t DB_PushStreamPos = (DB_PushStreamPos_t)0x458A20; - DB_PopStreamPos_t DB_PopStreamPos = (DB_PopStreamPos_t)0x4D1D60; - - DB_BeginRecoverLostDevice_t DB_BeginRecoverLostDevice = (DB_BeginRecoverLostDevice_t)0x4BFF90; - DB_EndRecoverLostDevice_t DB_EndRecoverLostDevice = (DB_EndRecoverLostDevice_t)0x46B660; - DB_EnumXAssets_t DB_EnumXAssets = (DB_EnumXAssets_t)0x4B76D0; - DB_EnumXAssets_Internal_t DB_EnumXAssets_Internal = (DB_EnumXAssets_Internal_t)0x5BB0A0; - DB_FindXAssetHeader_t DB_FindXAssetHeader = (DB_FindXAssetHeader_t)0x407930; - DB_GetRawBuffer_t DB_GetRawBuffer = (DB_GetRawBuffer_t)0x4CDC50; - DB_GetRawFileLen_t DB_GetRawFileLen = (DB_GetRawFileLen_t)0x4DAA80; - DB_GetLoadedFraction_t DB_GetLoadedFraction = (DB_GetLoadedFraction_t)0x468380; - DB_GetXAssetNameHandler_t* DB_GetXAssetNameHandlers = (DB_GetXAssetNameHandler_t*)0x799328; - DB_GetXAssetSizeHandler_t* DB_GetXAssetSizeHandlers = (DB_GetXAssetSizeHandler_t*)0x799488; - DB_GetXAssetTypeName_t DB_GetXAssetTypeName = (DB_GetXAssetTypeName_t)0x4CFCF0; - DB_IsXAssetDefault_t DB_IsXAssetDefault = (DB_IsXAssetDefault_t)0x48E6A0; - DB_LoadXAssets_t DB_LoadXAssets = (DB_LoadXAssets_t)0x4E5930; - DB_LoadXFileData_t DB_LoadXFileData = (DB_LoadXFileData_t)0x445460; - DB_ReadXFile_t DB_ReadXFile = (DB_ReadXFile_t)0x445460; - DB_ReadXFileUncompressed_t DB_ReadXFileUncompressed = (DB_ReadXFileUncompressed_t)0x4705E0; - DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers = (DB_ReleaseXAssetHandler_t*)0x799AB8; - DB_SetXAssetName_t DB_SetXAssetName = (DB_SetXAssetName_t)0x453580; - DB_SetXAssetNameHandler_t* DB_SetXAssetNameHandlers = (DB_SetXAssetNameHandler_t*)0x7993D8; - DB_XModelSurfsFixup_t DB_XModelSurfsFixup = (DB_XModelSurfsFixup_t)0x5BAC50; - - Dvar_RegisterBool_t Dvar_RegisterBool = (Dvar_RegisterBool_t)0x4CE1A0; - Dvar_RegisterFloat_t Dvar_RegisterFloat = (Dvar_RegisterFloat_t)0x648440; - Dvar_RegisterVec2_t Dvar_RegisterVec2 = (Dvar_RegisterVec2_t)0x4F6070; - Dvar_RegisterVec3_t Dvar_RegisterVec3 = (Dvar_RegisterVec3_t)0x4EF8E0; - Dvar_RegisterVec4_t Dvar_RegisterVec4 = (Dvar_RegisterVec4_t)0x4F28E0; - Dvar_RegisterInt_t Dvar_RegisterInt = (Dvar_RegisterInt_t)0x479830; - Dvar_RegisterEnum_t Dvar_RegisterEnum = (Dvar_RegisterEnum_t)0x412E40; - Dvar_RegisterString_t Dvar_RegisterString = (Dvar_RegisterString_t)0x4FC7E0; - Dvar_RegisterColor_t Dvar_RegisterColor = (Dvar_RegisterColor_t)0x4F28E0;//0x471500; - - Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName = (Dvar_GetUnpackedColorByName_t)0x406530; - Dvar_FindVar_t Dvar_FindVar = (Dvar_FindVar_t)0x4D5390; - Dvar_InfoString_Big_t Dvar_InfoString_Big = (Dvar_InfoString_Big_t)0x4D98A0; - Dvar_SetCommand_t Dvar_SetCommand = (Dvar_SetCommand_t)0x4EE430; - - Encode_Init_t Encode_Init = (Encode_Init_t)0x462AB0; - - Field_Clear_t Field_Clear = (Field_Clear_t)0x437EB0; - - FreeMemory_t FreeMemory = (FreeMemory_t)0x4D6640; - - FS_FileExists_t FS_FileExists = (FS_FileExists_t)0x4DEFA0; - FS_FreeFile_t FS_FreeFile = (FS_FreeFile_t)0x4416B0; - FS_ReadFile_t FS_ReadFile = (FS_ReadFile_t)0x4F4B90; - FS_GetFileList_t FS_GetFileList = (FS_GetFileList_t)0x441BB0; - FS_FreeFileList_t FS_FreeFileList = (FS_FreeFileList_t)0x4A5DE0; - FS_FOpenFileAppend_t FS_FOpenFileAppend = (FS_FOpenFileAppend_t)0x410BB0; - FS_FOpenFileAppend_t FS_FOpenFileWrite = (FS_FOpenFileAppend_t)0x4BA530; - FS_FOpenFileRead_t FS_FOpenFileRead = (FS_FOpenFileRead_t)0x46CBF0; - FS_FOpenFileRead_t FS_FOpenFileReadDatabase = (FS_FOpenFileRead_t)0x42ECA0; - FS_FOpenFileReadForThread_t FS_FOpenFileReadForThread = (FS_FOpenFileReadForThread_t)0x643270; - FS_FCloseFile_t FS_FCloseFile = (FS_FCloseFile_t)0x462000; - FS_WriteFile_t FS_WriteFile = (FS_WriteFile_t)0x426450; - FS_Write_t FS_Write = (FS_Write_t)0x4C06E0; - FS_Read_t FS_Read = (FS_Read_t)0x4A04C0; - FS_Seek_t FS_Seek = (FS_Seek_t)0x4A63D0; - FS_FTell_t FS_FTell = (FS_FTell_t)0x4E6760; - FS_Remove_t FS_Remove = (FS_Remove_t)0x4660F0; - FS_Restart_t FS_Restart = (FS_Restart_t)0x461A50; - FS_BuildPathToFile_t FS_BuildPathToFile = (FS_BuildPathToFile_t)0x4702C0; - - G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = (G_SpawnEntitiesFromString_t)0x4D8840; - - GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript = (GScr_LoadGameTypeScript_t)0x4ED9A0; - - Image_LoadFromFileWithReader_t Image_LoadFromFileWithReader = (Image_LoadFromFileWithReader_t)0x53ABF0; - Image_Release_t Image_Release = (Image_Release_t)0x51F010; - - Key_SetCatcher_t Key_SetCatcher = (Key_SetCatcher_t)0x43BD00; - - LargeLocalInit_t LargeLocalInit = (LargeLocalInit_t)0x4A62A0; - - Load_Stream_t Load_Stream = (Load_Stream_t)0x470E30; - Load_XString_t Load_XString = (Load_XString_t)0x47FDA0; - Load_XModelPtr_t Load_XModelPtr = (Load_XModelPtr_t)0x4FCA70; - Load_XModelSurfsFixup_t Load_XModelSurfsFixup = (Load_XModelSurfsFixup_t)0x40D7A0; - Load_XStringArray_t Load_XStringArray = (Load_XStringArray_t)0x4977F0; - Load_XStringCustom_t Load_XStringCustom = (Load_XStringCustom_t)0x4E0DD0; - Load_FxEffectDefHandle_t Load_FxEffectDefHandle = (Load_FxEffectDefHandle_t)0x4D9B90; - Load_FxElemDef_t Load_FxElemDef = (Load_FxElemDef_t)0x45AD90; - Load_GfxImagePtr_t Load_GfxImagePtr = (Load_GfxImagePtr_t)0x4C13D0; - Load_Texture_t Load_Texture = (Load_Texture_t)0x51F4E0; - Load_GfxTextureLoad_t Load_GfxTextureLoad = (Load_GfxTextureLoad_t)0x4D3210; - Load_SndAliasCustom_t Load_SndAliasCustom = (Load_SndAliasCustom_t)0x49B6B0; - Load_MaterialHandle_t Load_MaterialHandle = (Load_MaterialHandle_t)0x403960; - Load_PhysCollmapPtr_t Load_PhysCollmapPtr = (Load_PhysCollmapPtr_t)0x47E990; - Load_PhysPresetPtr_t Load_PhysPresetPtr = (Load_PhysPresetPtr_t)0x4FAD30; - Load_TracerDefPtr_t Load_TracerDefPtr = (Load_TracerDefPtr_t)0x493090; - Load_snd_alias_list_nameArray_t Load_snd_alias_list_nameArray = (Load_snd_alias_list_nameArray_t)0x4499F0; - - Menus_CloseAll_t Menus_CloseAll = (Menus_CloseAll_t)0x4BA5B0; - Menus_OpenByName_t Menus_OpenByName = (Menus_OpenByName_t)0x4CCE60; - Menus_FindByName_t Menus_FindByName = (Menus_FindByName_t)0x487240; - Menu_IsVisible_t Menu_IsVisible = (Menu_IsVisible_t)0x4D77D0; - Menus_MenuIsInStack_t Menus_MenuIsInStack = (Menus_MenuIsInStack_t)0x47ACB0; - - MSG_Init_t MSG_Init = (MSG_Init_t)0x45FCA0; - MSG_ReadData_t MSG_ReadData = (MSG_ReadData_t)0x4527C0; - MSG_ReadLong_t MSG_ReadLong = (MSG_ReadLong_t)0x4C9550; - MSG_ReadShort_t MSG_ReadShort = (MSG_ReadShort_t)0x40BDD0; - MSG_ReadInt64_t MSG_ReadInt64 = (MSG_ReadInt64_t)0x4F1850; - MSG_ReadString_t MSG_ReadString = (MSG_ReadString_t)0x60E2B0; - MSG_WriteByte_t MSG_WriteByte = (MSG_WriteByte_t)0x48C520; - MSG_WriteData_t MSG_WriteData = (MSG_WriteData_t)0x4F4120; - MSG_WriteLong_t MSG_WriteLong = (MSG_WriteLong_t)0x41CA20; - MSG_WriteBitsCompress_t MSG_WriteBitsCompress = (MSG_WriteBitsCompress_t)0x4319D0; - MSG_ReadByte_t MSG_ReadByte = (MSG_ReadByte_t)0x4C1C20; - MSG_ReadBitsCompress_t MSG_ReadBitsCompress = (MSG_ReadBitsCompress_t)0x4DCC30; - - NetadrToSockadr_t NetadrToSockadr = (NetadrToSockadr_t)0x4B4B40; - - NET_AdrToString_t NET_AdrToString = (NET_AdrToString_t)0x469880; - NET_CompareAdr_t NET_CompareAdr = (NET_CompareAdr_t)0x4D0AA0; - NET_ErrorString_t NET_ErrorString = (NET_ErrorString_t)0x4E7720; - NET_Init_t NET_Init = (NET_Init_t)0x491860; - NET_IsLocalAddress_t NET_IsLocalAddress = (NET_IsLocalAddress_t)0x402BD0; - NET_StringToAdr_t NET_StringToAdr = (NET_StringToAdr_t)0x409010; - NET_OutOfBandPrint_t NET_OutOfBandPrint = (NET_OutOfBandPrint_t)0x4AEF00; - NET_OutOfBandData_t NET_OutOfBandData = (NET_OutOfBandData_t)0x49C7E0; - - Live_MPAcceptInvite_t Live_MPAcceptInvite = (Live_MPAcceptInvite_t)0x420A6D; - Live_GetMapIndex_t Live_GetMapIndex = (Live_GetMapIndex_t)0x4F6440; - - LoadModdableRawfile_t LoadModdableRawfile = (LoadModdableRawfile_t)0x61ABC0; - - PC_ReadToken_t PC_ReadToken = (PC_ReadToken_t)0x4ACCD0; - PC_ReadTokenHandle_t PC_ReadTokenHandle = (PC_ReadTokenHandle_t)0x4D2060; - PC_SourceError_t PC_SourceError = (PC_SourceError_t)0x467A00; - - Party_GetMaxPlayers_t Party_GetMaxPlayers = (Party_GetMaxPlayers_t)0x4F5D60; - PartyHost_CountMembers_t PartyHost_CountMembers = (PartyHost_CountMembers_t)0x497330; - PartyHost_GetMemberAddressBySlot_t PartyHost_GetMemberAddressBySlot = (PartyHost_GetMemberAddressBySlot_t)0x44E100; - PartyHost_GetMemberName_t PartyHost_GetMemberName = (PartyHost_GetMemberName_t)0x44BE90; - - Playlist_ParsePlaylists_t Playlist_ParsePlaylists = (Playlist_ParsePlaylists_t)0x4295A0; - - R_AddCmdDrawStretchPic_t R_AddCmdDrawStretchPic = (R_AddCmdDrawStretchPic_t)0x509770; - R_AllocStaticIndexBuffer_t R_AllocStaticIndexBuffer = (R_AllocStaticIndexBuffer_t)0x51E7A0; - R_Cinematic_StartPlayback_Now_t R_Cinematic_StartPlayback_Now = (R_Cinematic_StartPlayback_Now_t)0x51C5B0; - R_RegisterFont_t R_RegisterFont = (R_RegisterFont_t)0x505670; - R_AddCmdDrawText_t R_AddCmdDrawText = (R_AddCmdDrawText_t)0x509D80; - R_LoadGraphicsAssets_t R_LoadGraphicsAssets = (R_LoadGraphicsAssets_t)0x506AC0; - R_TextWidth_t R_TextWidth = (R_TextWidth_t)0x5056C0; - R_TextHeight_t R_TextHeight = (R_TextHeight_t)0x505770; - R_FlushSun_t R_FlushSun = (R_FlushSun_t)0x53FB50; - - Scr_LoadGameType_t Scr_LoadGameType = (Scr_LoadGameType_t)0x4D9520; - - Scr_LoadScript_t Scr_LoadScript = (Scr_LoadScript_t)0x45D940; - Scr_GetFunctionHandle_t Scr_GetFunctionHandle = (Scr_GetFunctionHandle_t)0x4234F0; - - Scr_GetFloat_t Scr_GetFloat = (Scr_GetFloat_t)0x443140; - Scr_GetNumParam_t Scr_GetNumParam = (Scr_GetNumParam_t)0x4B0E90; - - Scr_ExecThread_t Scr_ExecThread = (Scr_ExecThread_t)0x4AD0B0; - Scr_FreeThread_t Scr_FreeThread = (Scr_FreeThread_t)0x4BD320; - - Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode = (Scr_ShutdownAllocNode_t)0x441650; - - Script_Alloc_t Script_Alloc = (Script_Alloc_t)0x422E70; - Script_SetupTokens_t Script_SetupTokens = (Script_SetupTokens_t)0x4E6950; - Script_CleanString_t Script_CleanString = (Script_CleanString_t)0x498220; - - SE_Load_t SE_Load = (SE_Load_t)0x502A30; - - SEH_StringEd_GetString_t SEH_StringEd_GetString = (SEH_StringEd_GetString_t)0x44BB30; - - Dvar_SetStringByName_t Dvar_SetStringByName = (Dvar_SetStringByName_t)0x44F060; - - SL_ConvertToString_t SL_ConvertToString = (SL_ConvertToString_t)0x4EC1D0; - SL_GetString_t SL_GetString = (SL_GetString_t)0x4CDC10; - - SND_Init_t SND_Init = (SND_Init_t)0x46A630; - SND_InitDriver_t SND_InitDriver = (SND_InitDriver_t)0x4F5090; - - SockadrToNetadr_t SockadrToNetadr = (SockadrToNetadr_t)0x4F8460; - - Steam_JoinLobby_t Steam_JoinLobby = (Steam_JoinLobby_t)0x49CF70; - - SV_GameClientNum_Score_t SV_GameClientNum_Score = (SV_GameClientNum_Score_t)0x469AC0; - SV_GameSendServerCommand_t SV_GameSendServerCommand = (SV_GameSendServerCommand_t)0x4BC3A0; - SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString = (SV_Cmd_TokenizeString_t)0x4B5780; - SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString = (SV_Cmd_EndTokenizedString_t)0x464750; - SV_DirectConnect_t SV_DirectConnect = (SV_DirectConnect_t)0x460480; - - Sys_Error_t Sys_Error = (Sys_Error_t)0x4E0200; - Sys_FreeFileList_t Sys_FreeFileList = (Sys_FreeFileList_t)0x4D8580; - Sys_IsDatabaseReady_t Sys_IsDatabaseReady = (Sys_IsDatabaseReady_t)0x4CA4A0; - Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2 = (Sys_IsDatabaseReady2_t)0x441280; - Sys_IsMainThread_t Sys_IsMainThread = (Sys_IsMainThread_t)0x4C37D0; - Sys_IsRenderThread_t Sys_IsRenderThread = (Sys_IsRenderThread_t)0x4B20E0; - Sys_IsServerThread_t Sys_IsServerThread = (Sys_IsServerThread_t)0x4B0270; - Sys_IsDatabaseThread_t Sys_IsDatabaseThread = (Sys_IsDatabaseThread_t)0x4C6020; - Sys_SendPacket_t Sys_SendPacket = (Sys_SendPacket_t)0x60FDC0; - Sys_ShowConsole_t Sys_ShowConsole = (Sys_ShowConsole_t)0x4305E0; - Sys_ListFiles_t Sys_ListFiles = (Sys_ListFiles_t)0x45A660; - Sys_Milliseconds_t Sys_Milliseconds = (Sys_Milliseconds_t)0x42A660; - - TeleportPlayer_t TeleportPlayer = (TeleportPlayer_t)0x496850; - - UI_AddMenuList_t UI_AddMenuList = (UI_AddMenuList_t)0x4533C0; - UI_CheckStringTranslation_t UI_CheckStringTranslation = (UI_CheckStringTranslation_t)0x4FB010; - UI_LoadMenus_t UI_LoadMenus = (UI_LoadMenus_t)0x641460; - UI_UpdateArenas_t UI_UpdateArenas = (UI_UpdateArenas_t)0x4A95B0; - UI_DrawHandlePic_t UI_DrawHandlePic = (UI_DrawHandlePic_t)0x4D0EA0; - UI_GetContext_t UI_GetContext = (UI_GetContext_t)0x4F8940; - UI_TextWidth_t UI_TextWidth = (UI_TextWidth_t)0x6315C0; - UI_DrawText_t UI_DrawText = (UI_DrawText_t)0x49C0D0; - - Win_GetLanguage_t Win_GetLanguage = (Win_GetLanguage_t)0x45CBA0; - - Vec3UnpackUnitVec_t Vec3UnpackUnitVec = (Vec3UnpackUnitVec_t)0x45CA90; - - XAssetHeader* DB_XAssetPool = (XAssetHeader*)0x7998A8; - unsigned int* g_poolSize = (unsigned int*)0x7995E8; - - DWORD* cmd_id = (DWORD*)0x1AAC5D0; - DWORD* cmd_argc = (DWORD*)0x1AAC614; - char*** cmd_argv = (char***)0x1AAC634; - - DWORD* cmd_id_sv = (DWORD*)0x1ACF8A0; - DWORD* cmd_argc_sv = (DWORD*)0x1ACF8E4; - char*** cmd_argv_sv = (char***)0x1ACF904; - - cmd_function_t** cmd_functions = (cmd_function_t**)0x1AAC658; - - source_t **sourceFiles = (source_t **)0x7C4A98; - keywordHash_t **menuParseKeywordHash = (keywordHash_t **)0x63AE928; - - int* svs_numclients = (int*)0x31D938C; - client_t* svs_clients = (client_t*)0x31D9390; - - UiContext *uiContext = (UiContext *)0x62E2858; - - int* arenaCount = (int*)0x62E6930; - mapArena_t* arenas = (mapArena_t*)0x62E6934; - - int* gameTypeCount = (int*)0x62E50A0; - gameTypeName_t* gameTypes = (gameTypeName_t*)0x62E50A4; - - searchpath_t* fs_searchpaths = (searchpath_t*)0x63D96E0; - - XBlock** g_streamBlocks = (XBlock**)0x16E554C; - int* g_streamPos = (int*)0x16E5554; - int* g_streamPosIndex = (int*)0x16E5578; - - bool* g_lobbyCreateInProgress = (bool*)0x66C9BC2; - party_t** partyIngame = (party_t**)0x1081C00; - PartyData_s** partyData = (PartyData_s**)0x107E500; - - int* numIP = (int*)0x64A1E68; - netIP_t* localIP = (netIP_t*)0x64A1E28; - - int* demoFile = (int*)0xA5EA1C; - int* demoPlaying = (int*)0xA5EA0C; - int* demoRecording = (int*)0xA5EA08; - int* serverMessageSequence = (int*)0xA3E9B4; - - gentity_t* g_entities = (gentity_t*)0x18835D8; - - netadr_t* connectedHost = (netadr_t*)0xA1E888; - - SOCKET* ip_socket = (SOCKET*)0x64A3008; - - uint32_t* com_frameTime = (uint32_t*)0x1AD8F3C; - - SafeArea* safeArea = (SafeArea*)0xA15F3C; - - SpawnVar* spawnVars = (SpawnVar*)0x1A83DE8; - MapEnts** marMapEntsPtr = (MapEnts**)0x112AD34; - - IDirect3D9** d3d9 = (IDirect3D9**)0x66DEF84; - IDirect3DDevice9** dx_ptr = (IDirect3DDevice9**)0x66DEF88; - - mapname_t* mapnames = (mapname_t*)0x7471D0; - - char*** varXString = (char***)0x112B340; - TracerDef*** varTracerDefPtr = (TracerDef***)0x112B3BC; - XModel*** varXModelPtr = (XModel***)0x112A934; - XModel** varXModel = (XModel**)0x112AE14; - PathData** varPathData = (PathData**)0x112AD7C; - const char** varConstChar = (const char**)0x112A774; - Material*** varMaterialHandle = (Material***)0x112A878; - FxEffectDef*** varFxEffectDefHandle = (FxEffectDef***)0x112ACC0; - PhysCollmap*** varPhysCollmapPtr = (PhysCollmap***)0x112B440; - PhysPreset*** varPhysPresetPtr = (PhysPreset***)0x112B378; - Game::MaterialPass** varMaterialPass = (Game::MaterialPass**)0x112A960; - snd_alias_list_t*** varsnd_alias_list_name = (snd_alias_list_t***)0x112AF38; - - FxElemField* s_elemFields = (FxElemField*)0x73B848; - - XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize) - { - int elSize = DB_GetXAssetSizeHandlers[type](); - XAssetHeader poolEntry = { Utils::Memory::Allocate(newSize * elSize) }; - DB_XAssetPool[type] = poolEntry; - g_poolSize[type] = newSize; - return poolEntry; - } - - void Menu_FreeItemMemory(Game::itemDef_t* item) - { - __asm - { - mov edi, item - mov eax, 63D880h - call eax - } - } - - const char* TableLookup(StringTable* stringtable, int row, int column) - { - if (!stringtable || !stringtable->values || row >= stringtable->rowCount || column >= stringtable->columnCount) return ""; - - const char* value = stringtable->values[row * stringtable->columnCount + column].string; - if (!value) value = ""; - - return value; - } - - const char* UI_LocalizeMapName(const char* mapName) - { - for (int i = 0; i < *arenaCount; ++i) - { - if (!_stricmp(Components::ArenaLength::NewArenas[i].mapName, mapName)) - { - char* uiName = &Components::ArenaLength::NewArenas[i].uiName[0]; - if ((uiName[0] == 'M' && uiName[1] == 'P') || (uiName[0] == 'P' && uiName[1] == 'A')) // MPUI/PATCH - { - return SEH_StringEd_GetString(uiName); - } - - return uiName; - } - } - - return mapName; - } - - const char* UI_LocalizeGameType(const char* gameType) - { - if (!gameType || !*gameType) - { - return ""; - } - - for (int i = 0; i < *gameTypeCount; ++i) - { - if (!_stricmp(gameTypes[i].gameType, gameType)) - { - return SEH_StringEd_GetString(gameTypes[i].uiName); - } - } - - return gameType; - } - - float UI_GetScoreboardLeft(void* a1) - { - static int func = 0x590390; - float result = 0; - - __asm - { - mov eax, a1 - call func - mov result, eax - } - - return result; - } - - const char *DB_GetXAssetName(XAsset *asset) - { - if (!asset) return ""; - return DB_GetXAssetNameHandlers[asset->type](&asset->header); - } - - XAssetType DB_GetXAssetNameType(const char* name) - { - for (int i = 0; i < ASSET_TYPE_COUNT; ++i) - { - XAssetType type = static_cast(i); - if (!_stricmp(DB_GetXAssetTypeName(type), name)) - { - // Col map workaround! - if (type == Game::XAssetType::ASSET_TYPE_CLIPMAP) - { - return Game::XAssetType::ASSET_TYPE_CLIPMAP_PVS; - } - - return type; - } - } - - return ASSET_TYPE_INVALID; - } - - bool DB_IsZoneLoaded(const char* zone) - { - int zoneCount = Utils::Hook::Get(0x1261BCC); - char* zoneIndices = reinterpret_cast(0x16B8A34); - char* zoneData = reinterpret_cast(0x14C0F80); - - for (int i = 0; i < zoneCount; ++i) - { - std::string name = zoneData + 4 + 0xA4 * (zoneIndices[i] & 0xFF); - - if (name == zone) - { - return true; - } - } - - return false; - } - - void FS_AddLocalizedGameDirectory(const char *path, const char *dir) - { - __asm - { - mov ebx, path - mov eax, dir - mov ecx, 642EF0h - call ecx - } - } - - void MessageBox(std::string message, std::string title) - { - Dvar_SetStringByName("com_errorMessage", message.data()); - Dvar_SetStringByName("com_errorTitle", title.data()); - Cbuf_AddText(0, "openmenu error_popmenu_lobby"); - } - - unsigned int R_HashString(const char* string) - { - unsigned int hash = 0; - - while (*string) - { - hash = (*string | 0x20) ^ (33 * hash); - ++string; - } - - return hash; - } - - void R_LoadSunThroughDvars(const char* mapname, sunflare_t* sun) - { - __asm - { - push ecx - push sun - mov eax, mapname - - mov ecx, 53F990h - call ecx - - add esp, 4h - pop ecx - } - } - - void R_SetSunFromDvars(sunflare_t* sun) - { - __asm - { - push esi - mov esi, sun - - mov eax, 53F6D0h - call ecx - - pop esi - } - } - - void SV_KickClient(client_t* client, const char* reason) - { - __asm - { - push edi - push esi - mov edi, 0 - mov esi, client - push reason - push 0 - push 0 - mov eax, 6249A0h - call eax - add esp, 0Ch - pop esi - pop edi - } - } - - void SV_KickClientError(client_t* client, std::string reason) - { - if (client->state < 5) - { - Components::Network::Send(client->addr, Utils::String::VA("error\n%s", reason.data())); - } - - SV_KickClient(client, reason.data()); - } - - void Scr_iPrintLn(int clientNum, std::string message) - { - Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x66, message.data())); - } - - void Scr_iPrintLnBold(int clientNum, std::string message) - { - Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x67, message.data())); - } - - void IN_KeyUp(kbutton_t* button) - { - __asm - { - push esi - mov esi, button - mov eax, 5A5580h - call eax - pop esi - } - } - - void IN_KeyDown(kbutton_t* button) - { - __asm - { - push esi - mov esi, button - mov eax, 5A54E0h - call eax - pop esi - } - } - - int FS_FOpenFileReadCurrentThread(const char* file, int* fh) - { - if (GetCurrentThreadId() == *reinterpret_cast(0x1CDE7FC)) - { - return FS_FOpenFileRead(file, fh); - } - else if (GetCurrentThreadId() == *reinterpret_cast(0x1CDE814)) - { - return FS_FOpenFileReadDatabase(file, fh); - } - else - { - *fh = NULL; - return -1; - } - } - - void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count) - { - if (Components::Dvar::Var("r_loadForRenderer").get()) - { - void* buffer = R_AllocStaticIndexBuffer(storeHere, 2 * count); - std::memcpy(buffer, data, 2 * count); - - if (storeHere && *storeHere) - { - (*storeHere)->Unlock(); - } - } - } - - void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len) - { - __asm - { - push edi - push ebx - - mov eax, len - mov edi, where - push data - - mov ebx, 5112C0h - call ebx - add esp, 4 - - pop ebx - pop edi - } - } - - char* Com_GetParseThreadInfo() - { - if (Game::Sys_IsMainThread()) - { - return reinterpret_cast(0x6466628); - } - else if (Game::Sys_IsRenderThread()) - { - return reinterpret_cast(0x646AC34); - } - else if (Game::Sys_IsServerThread()) - { - return reinterpret_cast(0x646F240); - } - else if(Game::Sys_IsDatabaseThread()) - { - return reinterpret_cast(0x647384C); - } - else - { - return nullptr; - } - } - - void Com_SetParseNegativeNumbers(int parse) - { - char* g_parse = Com_GetParseThreadInfo(); - - if (g_parse) - { - g_parse[1056 * *(reinterpret_cast(g_parse) + 4224) + 1032] = parse != 0; - } - } -} +#include "STDInclude.hpp" + +namespace Game +{ + // C-Style casts are fine here, that's where we're doing our dirty stuff anyways... + BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj = (BG_LoadWeaponDef_LoadObj_t)0x57B5F0; + + Cbuf_AddServerText_t Cbuf_AddServerText = (Cbuf_AddServerText_t)0x4BB9B0; + Cbuf_AddText_t Cbuf_AddText = (Cbuf_AddText_t)0x404B20; + + CG_GetClientNum_t CG_GetClientNum = (CG_GetClientNum_t)0x433700; + + CL_GetClientName_t CL_GetClientName = (CL_GetClientName_t)0x4563D0; + CL_IsCgameInitialized_t CL_IsCgameInitialized = (CL_IsCgameInitialized_t)0x43EB20; + CL_ConnectFromParty_t CL_ConnectFromParty = (CL_ConnectFromParty_t)0x433D30; + CL_DownloadsComplete_t CL_DownloadsComplete = (CL_DownloadsComplete_t)0x42CE90; + CL_DrawStretchPicPhysical_t CL_DrawStretchPicPhysical = (CL_DrawStretchPicPhysical_t)0x4FC120; + CL_HandleRelayPacket_t CL_HandleRelayPacket = (CL_HandleRelayPacket_t)0x5A8C70; + CL_ResetViewport_t CL_ResetViewport = (CL_ResetViewport_t)0x4A8830; + CL_SelectStringTableEntryInDvar_f_t CL_SelectStringTableEntryInDvar_f = (CL_SelectStringTableEntryInDvar_f_t)0x4A4560; + + Cmd_AddCommand_t Cmd_AddCommand = (Cmd_AddCommand_t)0x470090; + Cmd_AddServerCommand_t Cmd_AddServerCommand = (Cmd_AddServerCommand_t)0x4DCE00; + Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand = (Cmd_ExecuteSingleCommand_t)0x609540; + Com_ClientPacketEvent_t Com_ClientPacketEvent = (Com_ClientPacketEvent_t)0x49F0B0; + + Com_Error_t Com_Error = (Com_Error_t)0x4B22D0; + Com_Printf_t Com_Printf = (Com_Printf_t)0x402500; + Com_PrintMessage_t Com_PrintMessage = (Com_PrintMessage_t)0x4AA830; + Com_EndParseSession_t Com_EndParseSession = (Com_EndParseSession_t)0x4B80B0; + Com_BeginParseSession_t Com_BeginParseSession = (Com_BeginParseSession_t)0x4AAB80; + Com_SetSpaceDelimited_t Com_SetSpaceDelimited = (Com_SetSpaceDelimited_t)0x4FC710; + Com_Parse_t Com_Parse = (Com_Parse_t)0x474D60; + Com_SetSlowMotion_t Com_SetSlowMotion = (Com_SetSlowMotion_t)0x446E20; + + Con_DrawMiniConsole_t Con_DrawMiniConsole = (Con_DrawMiniConsole_t)0x464F30; + Con_DrawSolidConsole_t Con_DrawSolidConsole = (Con_DrawSolidConsole_t)0x5A5040; + + DB_AllocStreamPos_t DB_AllocStreamPos = (DB_AllocStreamPos_t)0x418380; + DB_PushStreamPos_t DB_PushStreamPos = (DB_PushStreamPos_t)0x458A20; + DB_PopStreamPos_t DB_PopStreamPos = (DB_PopStreamPos_t)0x4D1D60; + + DB_BeginRecoverLostDevice_t DB_BeginRecoverLostDevice = (DB_BeginRecoverLostDevice_t)0x4BFF90; + DB_EndRecoverLostDevice_t DB_EndRecoverLostDevice = (DB_EndRecoverLostDevice_t)0x46B660; + DB_EnumXAssets_t DB_EnumXAssets = (DB_EnumXAssets_t)0x4B76D0; + DB_EnumXAssets_Internal_t DB_EnumXAssets_Internal = (DB_EnumXAssets_Internal_t)0x5BB0A0; + DB_FindXAssetHeader_t DB_FindXAssetHeader = (DB_FindXAssetHeader_t)0x407930; + DB_GetRawBuffer_t DB_GetRawBuffer = (DB_GetRawBuffer_t)0x4CDC50; + DB_GetRawFileLen_t DB_GetRawFileLen = (DB_GetRawFileLen_t)0x4DAA80; + DB_GetLoadedFraction_t DB_GetLoadedFraction = (DB_GetLoadedFraction_t)0x468380; + DB_GetXAssetNameHandler_t* DB_GetXAssetNameHandlers = (DB_GetXAssetNameHandler_t*)0x799328; + DB_GetXAssetSizeHandler_t* DB_GetXAssetSizeHandlers = (DB_GetXAssetSizeHandler_t*)0x799488; + DB_GetXAssetTypeName_t DB_GetXAssetTypeName = (DB_GetXAssetTypeName_t)0x4CFCF0; + DB_IsXAssetDefault_t DB_IsXAssetDefault = (DB_IsXAssetDefault_t)0x48E6A0; + DB_LoadXAssets_t DB_LoadXAssets = (DB_LoadXAssets_t)0x4E5930; + DB_LoadXFileData_t DB_LoadXFileData = (DB_LoadXFileData_t)0x445460; + DB_ReadXFile_t DB_ReadXFile = (DB_ReadXFile_t)0x445460; + DB_ReadXFileUncompressed_t DB_ReadXFileUncompressed = (DB_ReadXFileUncompressed_t)0x4705E0; + DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers = (DB_ReleaseXAssetHandler_t*)0x799AB8; + DB_SetXAssetName_t DB_SetXAssetName = (DB_SetXAssetName_t)0x453580; + DB_SetXAssetNameHandler_t* DB_SetXAssetNameHandlers = (DB_SetXAssetNameHandler_t*)0x7993D8; + DB_XModelSurfsFixup_t DB_XModelSurfsFixup = (DB_XModelSurfsFixup_t)0x5BAC50; + + Dvar_RegisterBool_t Dvar_RegisterBool = (Dvar_RegisterBool_t)0x4CE1A0; + Dvar_RegisterFloat_t Dvar_RegisterFloat = (Dvar_RegisterFloat_t)0x648440; + Dvar_RegisterVec2_t Dvar_RegisterVec2 = (Dvar_RegisterVec2_t)0x4F6070; + Dvar_RegisterVec3_t Dvar_RegisterVec3 = (Dvar_RegisterVec3_t)0x4EF8E0; + Dvar_RegisterVec4_t Dvar_RegisterVec4 = (Dvar_RegisterVec4_t)0x4F28E0; + Dvar_RegisterInt_t Dvar_RegisterInt = (Dvar_RegisterInt_t)0x479830; + Dvar_RegisterEnum_t Dvar_RegisterEnum = (Dvar_RegisterEnum_t)0x412E40; + Dvar_RegisterString_t Dvar_RegisterString = (Dvar_RegisterString_t)0x4FC7E0; + Dvar_RegisterColor_t Dvar_RegisterColor = (Dvar_RegisterColor_t)0x4F28E0;//0x471500; + + Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName = (Dvar_GetUnpackedColorByName_t)0x406530; + Dvar_FindVar_t Dvar_FindVar = (Dvar_FindVar_t)0x4D5390; + Dvar_InfoString_Big_t Dvar_InfoString_Big = (Dvar_InfoString_Big_t)0x4D98A0; + Dvar_SetCommand_t Dvar_SetCommand = (Dvar_SetCommand_t)0x4EE430; + + Encode_Init_t Encode_Init = (Encode_Init_t)0x462AB0; + + Field_Clear_t Field_Clear = (Field_Clear_t)0x437EB0; + + FreeMemory_t FreeMemory = (FreeMemory_t)0x4D6640; + + FS_FileExists_t FS_FileExists = (FS_FileExists_t)0x4DEFA0; + FS_FreeFile_t FS_FreeFile = (FS_FreeFile_t)0x4416B0; + FS_ReadFile_t FS_ReadFile = (FS_ReadFile_t)0x4F4B90; + FS_GetFileList_t FS_GetFileList = (FS_GetFileList_t)0x441BB0; + FS_FreeFileList_t FS_FreeFileList = (FS_FreeFileList_t)0x4A5DE0; + FS_FOpenFileAppend_t FS_FOpenFileAppend = (FS_FOpenFileAppend_t)0x410BB0; + FS_FOpenFileAppend_t FS_FOpenFileWrite = (FS_FOpenFileAppend_t)0x4BA530; + FS_FOpenFileRead_t FS_FOpenFileRead = (FS_FOpenFileRead_t)0x46CBF0; + FS_FOpenFileRead_t FS_FOpenFileReadDatabase = (FS_FOpenFileRead_t)0x42ECA0; + FS_FOpenFileReadForThread_t FS_FOpenFileReadForThread = (FS_FOpenFileReadForThread_t)0x643270; + FS_FCloseFile_t FS_FCloseFile = (FS_FCloseFile_t)0x462000; + FS_WriteFile_t FS_WriteFile = (FS_WriteFile_t)0x426450; + FS_Write_t FS_Write = (FS_Write_t)0x4C06E0; + FS_Read_t FS_Read = (FS_Read_t)0x4A04C0; + FS_Seek_t FS_Seek = (FS_Seek_t)0x4A63D0; + FS_FTell_t FS_FTell = (FS_FTell_t)0x4E6760; + FS_Remove_t FS_Remove = (FS_Remove_t)0x4660F0; + FS_Restart_t FS_Restart = (FS_Restart_t)0x461A50; + FS_BuildPathToFile_t FS_BuildPathToFile = (FS_BuildPathToFile_t)0x4702C0; + + G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString = (G_SpawnEntitiesFromString_t)0x4D8840; + + GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript = (GScr_LoadGameTypeScript_t)0x4ED9A0; + + Image_LoadFromFileWithReader_t Image_LoadFromFileWithReader = (Image_LoadFromFileWithReader_t)0x53ABF0; + Image_Release_t Image_Release = (Image_Release_t)0x51F010; + + Key_SetCatcher_t Key_SetCatcher = (Key_SetCatcher_t)0x43BD00; + + LargeLocalInit_t LargeLocalInit = (LargeLocalInit_t)0x4A62A0; + + Load_Stream_t Load_Stream = (Load_Stream_t)0x470E30; + Load_XString_t Load_XString = (Load_XString_t)0x47FDA0; + Load_XModelPtr_t Load_XModelPtr = (Load_XModelPtr_t)0x4FCA70; + Load_XModelSurfsFixup_t Load_XModelSurfsFixup = (Load_XModelSurfsFixup_t)0x40D7A0; + Load_XStringArray_t Load_XStringArray = (Load_XStringArray_t)0x4977F0; + Load_XStringCustom_t Load_XStringCustom = (Load_XStringCustom_t)0x4E0DD0; + Load_FxEffectDefHandle_t Load_FxEffectDefHandle = (Load_FxEffectDefHandle_t)0x4D9B90; + Load_FxElemDef_t Load_FxElemDef = (Load_FxElemDef_t)0x45AD90; + Load_GfxImagePtr_t Load_GfxImagePtr = (Load_GfxImagePtr_t)0x4C13D0; + Load_Texture_t Load_Texture = (Load_Texture_t)0x51F4E0; + Load_GfxTextureLoad_t Load_GfxTextureLoad = (Load_GfxTextureLoad_t)0x4D3210; + Load_SndAliasCustom_t Load_SndAliasCustom = (Load_SndAliasCustom_t)0x49B6B0; + Load_MaterialHandle_t Load_MaterialHandle = (Load_MaterialHandle_t)0x403960; + Load_PhysCollmapPtr_t Load_PhysCollmapPtr = (Load_PhysCollmapPtr_t)0x47E990; + Load_PhysPresetPtr_t Load_PhysPresetPtr = (Load_PhysPresetPtr_t)0x4FAD30; + Load_TracerDefPtr_t Load_TracerDefPtr = (Load_TracerDefPtr_t)0x493090; + Load_snd_alias_list_nameArray_t Load_snd_alias_list_nameArray = (Load_snd_alias_list_nameArray_t)0x4499F0; + + Menus_CloseAll_t Menus_CloseAll = (Menus_CloseAll_t)0x4BA5B0; + Menus_OpenByName_t Menus_OpenByName = (Menus_OpenByName_t)0x4CCE60; + Menus_FindByName_t Menus_FindByName = (Menus_FindByName_t)0x487240; + Menu_IsVisible_t Menu_IsVisible = (Menu_IsVisible_t)0x4D77D0; + Menus_MenuIsInStack_t Menus_MenuIsInStack = (Menus_MenuIsInStack_t)0x47ACB0; + + MSG_Init_t MSG_Init = (MSG_Init_t)0x45FCA0; + MSG_ReadData_t MSG_ReadData = (MSG_ReadData_t)0x4527C0; + MSG_ReadLong_t MSG_ReadLong = (MSG_ReadLong_t)0x4C9550; + MSG_ReadShort_t MSG_ReadShort = (MSG_ReadShort_t)0x40BDD0; + MSG_ReadInt64_t MSG_ReadInt64 = (MSG_ReadInt64_t)0x4F1850; + MSG_ReadString_t MSG_ReadString = (MSG_ReadString_t)0x60E2B0; + MSG_WriteByte_t MSG_WriteByte = (MSG_WriteByte_t)0x48C520; + MSG_WriteData_t MSG_WriteData = (MSG_WriteData_t)0x4F4120; + MSG_WriteLong_t MSG_WriteLong = (MSG_WriteLong_t)0x41CA20; + MSG_WriteBitsCompress_t MSG_WriteBitsCompress = (MSG_WriteBitsCompress_t)0x4319D0; + MSG_ReadByte_t MSG_ReadByte = (MSG_ReadByte_t)0x4C1C20; + MSG_ReadBitsCompress_t MSG_ReadBitsCompress = (MSG_ReadBitsCompress_t)0x4DCC30; + + NetadrToSockadr_t NetadrToSockadr = (NetadrToSockadr_t)0x4B4B40; + + NET_AdrToString_t NET_AdrToString = (NET_AdrToString_t)0x469880; + NET_CompareAdr_t NET_CompareAdr = (NET_CompareAdr_t)0x4D0AA0; + NET_ErrorString_t NET_ErrorString = (NET_ErrorString_t)0x4E7720; + NET_Init_t NET_Init = (NET_Init_t)0x491860; + NET_IsLocalAddress_t NET_IsLocalAddress = (NET_IsLocalAddress_t)0x402BD0; + NET_StringToAdr_t NET_StringToAdr = (NET_StringToAdr_t)0x409010; + NET_OutOfBandPrint_t NET_OutOfBandPrint = (NET_OutOfBandPrint_t)0x4AEF00; + NET_OutOfBandData_t NET_OutOfBandData = (NET_OutOfBandData_t)0x49C7E0; + + Live_MPAcceptInvite_t Live_MPAcceptInvite = (Live_MPAcceptInvite_t)0x420A6D; + Live_GetMapIndex_t Live_GetMapIndex = (Live_GetMapIndex_t)0x4F6440; + + LoadModdableRawfile_t LoadModdableRawfile = (LoadModdableRawfile_t)0x61ABC0; + + PC_ReadToken_t PC_ReadToken = (PC_ReadToken_t)0x4ACCD0; + PC_ReadTokenHandle_t PC_ReadTokenHandle = (PC_ReadTokenHandle_t)0x4D2060; + PC_SourceError_t PC_SourceError = (PC_SourceError_t)0x467A00; + + Party_GetMaxPlayers_t Party_GetMaxPlayers = (Party_GetMaxPlayers_t)0x4F5D60; + PartyHost_CountMembers_t PartyHost_CountMembers = (PartyHost_CountMembers_t)0x497330; + PartyHost_GetMemberAddressBySlot_t PartyHost_GetMemberAddressBySlot = (PartyHost_GetMemberAddressBySlot_t)0x44E100; + PartyHost_GetMemberName_t PartyHost_GetMemberName = (PartyHost_GetMemberName_t)0x44BE90; + + Playlist_ParsePlaylists_t Playlist_ParsePlaylists = (Playlist_ParsePlaylists_t)0x4295A0; + + R_AddCmdDrawStretchPic_t R_AddCmdDrawStretchPic = (R_AddCmdDrawStretchPic_t)0x509770; + R_AllocStaticIndexBuffer_t R_AllocStaticIndexBuffer = (R_AllocStaticIndexBuffer_t)0x51E7A0; + R_Cinematic_StartPlayback_Now_t R_Cinematic_StartPlayback_Now = (R_Cinematic_StartPlayback_Now_t)0x51C5B0; + R_RegisterFont_t R_RegisterFont = (R_RegisterFont_t)0x505670; + R_AddCmdDrawText_t R_AddCmdDrawText = (R_AddCmdDrawText_t)0x509D80; + R_LoadGraphicsAssets_t R_LoadGraphicsAssets = (R_LoadGraphicsAssets_t)0x506AC0; + R_TextWidth_t R_TextWidth = (R_TextWidth_t)0x5056C0; + R_TextHeight_t R_TextHeight = (R_TextHeight_t)0x505770; + R_FlushSun_t R_FlushSun = (R_FlushSun_t)0x53FB50; + + Scr_LoadGameType_t Scr_LoadGameType = (Scr_LoadGameType_t)0x4D9520; + + Scr_LoadScript_t Scr_LoadScript = (Scr_LoadScript_t)0x45D940; + Scr_GetFunctionHandle_t Scr_GetFunctionHandle = (Scr_GetFunctionHandle_t)0x4234F0; + + Scr_GetFloat_t Scr_GetFloat = (Scr_GetFloat_t)0x443140; + Scr_GetNumParam_t Scr_GetNumParam = (Scr_GetNumParam_t)0x4B0E90; + + Scr_ExecThread_t Scr_ExecThread = (Scr_ExecThread_t)0x4AD0B0; + Scr_FreeThread_t Scr_FreeThread = (Scr_FreeThread_t)0x4BD320; + + Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode = (Scr_ShutdownAllocNode_t)0x441650; + + Script_Alloc_t Script_Alloc = (Script_Alloc_t)0x422E70; + Script_SetupTokens_t Script_SetupTokens = (Script_SetupTokens_t)0x4E6950; + Script_CleanString_t Script_CleanString = (Script_CleanString_t)0x498220; + + SE_Load_t SE_Load = (SE_Load_t)0x502A30; + + SEH_StringEd_GetString_t SEH_StringEd_GetString = (SEH_StringEd_GetString_t)0x44BB30; + + Dvar_SetStringByName_t Dvar_SetStringByName = (Dvar_SetStringByName_t)0x44F060; + + SL_ConvertToString_t SL_ConvertToString = (SL_ConvertToString_t)0x4EC1D0; + SL_GetString_t SL_GetString = (SL_GetString_t)0x4CDC10; + + SND_Init_t SND_Init = (SND_Init_t)0x46A630; + SND_InitDriver_t SND_InitDriver = (SND_InitDriver_t)0x4F5090; + + SockadrToNetadr_t SockadrToNetadr = (SockadrToNetadr_t)0x4F8460; + + Steam_JoinLobby_t Steam_JoinLobby = (Steam_JoinLobby_t)0x49CF70; + + SV_GameClientNum_Score_t SV_GameClientNum_Score = (SV_GameClientNum_Score_t)0x469AC0; + SV_GameSendServerCommand_t SV_GameSendServerCommand = (SV_GameSendServerCommand_t)0x4BC3A0; + SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString = (SV_Cmd_TokenizeString_t)0x4B5780; + SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString = (SV_Cmd_EndTokenizedString_t)0x464750; + SV_DirectConnect_t SV_DirectConnect = (SV_DirectConnect_t)0x460480; + + Sys_Error_t Sys_Error = (Sys_Error_t)0x4E0200; + Sys_FreeFileList_t Sys_FreeFileList = (Sys_FreeFileList_t)0x4D8580; + Sys_IsDatabaseReady_t Sys_IsDatabaseReady = (Sys_IsDatabaseReady_t)0x4CA4A0; + Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2 = (Sys_IsDatabaseReady2_t)0x441280; + Sys_IsMainThread_t Sys_IsMainThread = (Sys_IsMainThread_t)0x4C37D0; + Sys_IsRenderThread_t Sys_IsRenderThread = (Sys_IsRenderThread_t)0x4B20E0; + Sys_IsServerThread_t Sys_IsServerThread = (Sys_IsServerThread_t)0x4B0270; + Sys_IsDatabaseThread_t Sys_IsDatabaseThread = (Sys_IsDatabaseThread_t)0x4C6020; + Sys_SendPacket_t Sys_SendPacket = (Sys_SendPacket_t)0x60FDC0; + Sys_ShowConsole_t Sys_ShowConsole = (Sys_ShowConsole_t)0x4305E0; + Sys_ListFiles_t Sys_ListFiles = (Sys_ListFiles_t)0x45A660; + Sys_Milliseconds_t Sys_Milliseconds = (Sys_Milliseconds_t)0x42A660; + + TeleportPlayer_t TeleportPlayer = (TeleportPlayer_t)0x496850; + + UI_AddMenuList_t UI_AddMenuList = (UI_AddMenuList_t)0x4533C0; + UI_CheckStringTranslation_t UI_CheckStringTranslation = (UI_CheckStringTranslation_t)0x4FB010; + UI_LoadMenus_t UI_LoadMenus = (UI_LoadMenus_t)0x641460; + UI_UpdateArenas_t UI_UpdateArenas = (UI_UpdateArenas_t)0x4A95B0; + UI_DrawHandlePic_t UI_DrawHandlePic = (UI_DrawHandlePic_t)0x4D0EA0; + UI_GetContext_t UI_GetContext = (UI_GetContext_t)0x4F8940; + UI_TextWidth_t UI_TextWidth = (UI_TextWidth_t)0x6315C0; + UI_DrawText_t UI_DrawText = (UI_DrawText_t)0x49C0D0; + + Win_GetLanguage_t Win_GetLanguage = (Win_GetLanguage_t)0x45CBA0; + + Vec3UnpackUnitVec_t Vec3UnpackUnitVec = (Vec3UnpackUnitVec_t)0x45CA90; + + XAssetHeader* DB_XAssetPool = (XAssetHeader*)0x7998A8; + unsigned int* g_poolSize = (unsigned int*)0x7995E8; + + DWORD* cmd_id = (DWORD*)0x1AAC5D0; + DWORD* cmd_argc = (DWORD*)0x1AAC614; + char*** cmd_argv = (char***)0x1AAC634; + + DWORD* cmd_id_sv = (DWORD*)0x1ACF8A0; + DWORD* cmd_argc_sv = (DWORD*)0x1ACF8E4; + char*** cmd_argv_sv = (char***)0x1ACF904; + + cmd_function_t** cmd_functions = (cmd_function_t**)0x1AAC658; + + source_t **sourceFiles = (source_t **)0x7C4A98; + keywordHash_t **menuParseKeywordHash = (keywordHash_t **)0x63AE928; + + int* svs_numclients = (int*)0x31D938C; + client_t* svs_clients = (client_t*)0x31D9390; + + UiContext *uiContext = (UiContext *)0x62E2858; + + int* arenaCount = (int*)0x62E6930; + mapArena_t* arenas = (mapArena_t*)0x62E6934; + + int* gameTypeCount = (int*)0x62E50A0; + gameTypeName_t* gameTypes = (gameTypeName_t*)0x62E50A4; + + searchpath_t* fs_searchpaths = (searchpath_t*)0x63D96E0; + + XBlock** g_streamBlocks = (XBlock**)0x16E554C; + int* g_streamPos = (int*)0x16E5554; + int* g_streamPosIndex = (int*)0x16E5578; + + bool* g_lobbyCreateInProgress = (bool*)0x66C9BC2; + party_t** partyIngame = (party_t**)0x1081C00; + PartyData_s** partyData = (PartyData_s**)0x107E500; + + int* numIP = (int*)0x64A1E68; + netIP_t* localIP = (netIP_t*)0x64A1E28; + + int* demoFile = (int*)0xA5EA1C; + int* demoPlaying = (int*)0xA5EA0C; + int* demoRecording = (int*)0xA5EA08; + int* serverMessageSequence = (int*)0xA3E9B4; + + gentity_t* g_entities = (gentity_t*)0x18835D8; + + netadr_t* connectedHost = (netadr_t*)0xA1E888; + + SOCKET* ip_socket = (SOCKET*)0x64A3008; + + uint32_t* com_frameTime = (uint32_t*)0x1AD8F3C; + + SafeArea* safeArea = (SafeArea*)0xA15F3C; + + SpawnVar* spawnVars = (SpawnVar*)0x1A83DE8; + MapEnts** marMapEntsPtr = (MapEnts**)0x112AD34; + + IDirect3D9** d3d9 = (IDirect3D9**)0x66DEF84; + IDirect3DDevice9** dx_ptr = (IDirect3DDevice9**)0x66DEF88; + + mapname_t* mapnames = (mapname_t*)0x7471D0; + + char*** varXString = (char***)0x112B340; + TracerDef*** varTracerDefPtr = (TracerDef***)0x112B3BC; + XModel*** varXModelPtr = (XModel***)0x112A934; + XModel** varXModel = (XModel**)0x112AE14; + PathData** varPathData = (PathData**)0x112AD7C; + const char** varConstChar = (const char**)0x112A774; + Material*** varMaterialHandle = (Material***)0x112A878; + FxEffectDef*** varFxEffectDefHandle = (FxEffectDef***)0x112ACC0; + PhysCollmap*** varPhysCollmapPtr = (PhysCollmap***)0x112B440; + PhysPreset*** varPhysPresetPtr = (PhysPreset***)0x112B378; + Game::MaterialPass** varMaterialPass = (Game::MaterialPass**)0x112A960; + snd_alias_list_t*** varsnd_alias_list_name = (snd_alias_list_t***)0x112AF38; + + FxElemField* s_elemFields = (FxElemField*)0x73B848; + + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize) + { + int elSize = DB_GetXAssetSizeHandlers[type](); + XAssetHeader poolEntry = { Utils::Memory::Allocate(newSize * elSize) }; + DB_XAssetPool[type] = poolEntry; + g_poolSize[type] = newSize; + return poolEntry; + } + + void Menu_FreeItemMemory(Game::itemDef_t* item) + { + __asm + { + mov edi, item + mov eax, 63D880h + call eax + } + } + + const char* TableLookup(StringTable* stringtable, int row, int column) + { + if (!stringtable || !stringtable->values || row >= stringtable->rowCount || column >= stringtable->columnCount) return ""; + + const char* value = stringtable->values[row * stringtable->columnCount + column].string; + if (!value) value = ""; + + return value; + } + + const char* UI_LocalizeMapName(const char* mapName) + { + for (int i = 0; i < *arenaCount; ++i) + { + if (!_stricmp(Components::ArenaLength::NewArenas[i].mapName, mapName)) + { + char* uiName = &Components::ArenaLength::NewArenas[i].uiName[0]; + if ((uiName[0] == 'M' && uiName[1] == 'P') || (uiName[0] == 'P' && uiName[1] == 'A')) // MPUI/PATCH + { + return SEH_StringEd_GetString(uiName); + } + + return uiName; + } + } + + return mapName; + } + + const char* UI_LocalizeGameType(const char* gameType) + { + if (!gameType || !*gameType) + { + return ""; + } + + for (int i = 0; i < *gameTypeCount; ++i) + { + if (!_stricmp(gameTypes[i].gameType, gameType)) + { + return SEH_StringEd_GetString(gameTypes[i].uiName); + } + } + + return gameType; + } + + float UI_GetScoreboardLeft(void* a1) + { + static int func = 0x590390; + float result = 0; + + __asm + { + mov eax, a1 + call func + mov result, eax + } + + return result; + } + + const char *DB_GetXAssetName(XAsset *asset) + { + if (!asset) return ""; + return DB_GetXAssetNameHandlers[asset->type](&asset->header); + } + + XAssetType DB_GetXAssetNameType(const char* name) + { + for (int i = 0; i < ASSET_TYPE_COUNT; ++i) + { + XAssetType type = static_cast(i); + if (!_stricmp(DB_GetXAssetTypeName(type), name)) + { + // Col map workaround! + if (type == Game::XAssetType::ASSET_TYPE_CLIPMAP) + { + return Game::XAssetType::ASSET_TYPE_CLIPMAP_PVS; + } + + return type; + } + } + + return ASSET_TYPE_INVALID; + } + + bool DB_IsZoneLoaded(const char* zone) + { + int zoneCount = Utils::Hook::Get(0x1261BCC); + char* zoneIndices = reinterpret_cast(0x16B8A34); + char* zoneData = reinterpret_cast(0x14C0F80); + + for (int i = 0; i < zoneCount; ++i) + { + std::string name = zoneData + 4 + 0xA4 * (zoneIndices[i] & 0xFF); + + if (name == zone) + { + return true; + } + } + + return false; + } + + void FS_AddLocalizedGameDirectory(const char *path, const char *dir) + { + __asm + { + mov ebx, path + mov eax, dir + mov ecx, 642EF0h + call ecx + } + } + + void MessageBox(std::string message, std::string title) + { + Dvar_SetStringByName("com_errorMessage", message.data()); + Dvar_SetStringByName("com_errorTitle", title.data()); + Cbuf_AddText(0, "openmenu error_popmenu_lobby"); + } + + unsigned int R_HashString(const char* string) + { + unsigned int hash = 0; + + while (*string) + { + hash = (*string | 0x20) ^ (33 * hash); + ++string; + } + + return hash; + } + + void R_LoadSunThroughDvars(const char* mapname, sunflare_t* sun) + { + __asm + { + push ecx + push sun + mov eax, mapname + + mov ecx, 53F990h + call ecx + + add esp, 4h + pop ecx + } + } + + void R_SetSunFromDvars(sunflare_t* sun) + { + __asm + { + push esi + mov esi, sun + + mov eax, 53F6D0h + call ecx + + pop esi + } + } + + void SV_KickClient(client_t* client, const char* reason) + { + __asm + { + push edi + push esi + mov edi, 0 + mov esi, client + push reason + push 0 + push 0 + mov eax, 6249A0h + call eax + add esp, 0Ch + pop esi + pop edi + } + } + + void SV_KickClientError(client_t* client, std::string reason) + { + if (client->state < 5) + { + Components::Network::Send(client->addr, Utils::String::VA("error\n%s", reason.data())); + } + + SV_KickClient(client, reason.data()); + } + + void Scr_iPrintLn(int clientNum, std::string message) + { + Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x66, message.data())); + } + + void Scr_iPrintLnBold(int clientNum, std::string message) + { + Game::SV_GameSendServerCommand(clientNum, 0, Utils::String::VA("%c \"%s\"", 0x67, message.data())); + } + + void IN_KeyUp(kbutton_t* button) + { + __asm + { + push esi + mov esi, button + mov eax, 5A5580h + call eax + pop esi + } + } + + void IN_KeyDown(kbutton_t* button) + { + __asm + { + push esi + mov esi, button + mov eax, 5A54E0h + call eax + pop esi + } + } + + int FS_FOpenFileReadCurrentThread(const char* file, int* fh) + { + if (GetCurrentThreadId() == *reinterpret_cast(0x1CDE7FC)) + { + return FS_FOpenFileRead(file, fh); + } + else if (GetCurrentThreadId() == *reinterpret_cast(0x1CDE814)) + { + return FS_FOpenFileReadDatabase(file, fh); + } + else + { + *fh = NULL; + return -1; + } + } + + void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count) + { + if (Components::Dvar::Var("r_loadForRenderer").get()) + { + void* buffer = R_AllocStaticIndexBuffer(storeHere, 2 * count); + std::memcpy(buffer, data, 2 * count); + + if (storeHere && *storeHere) + { + (*storeHere)->Unlock(); + } + } + } + + void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len) + { + __asm + { + push edi + push ebx + + mov eax, len + mov edi, where + push data + + mov ebx, 5112C0h + call ebx + add esp, 4 + + pop ebx + pop edi + } + } + + char* Com_GetParseThreadInfo() + { + if (Game::Sys_IsMainThread()) + { + return reinterpret_cast(0x6466628); + } + else if (Game::Sys_IsRenderThread()) + { + return reinterpret_cast(0x646AC34); + } + else if (Game::Sys_IsServerThread()) + { + return reinterpret_cast(0x646F240); + } + else if(Game::Sys_IsDatabaseThread()) + { + return reinterpret_cast(0x647384C); + } + else + { + return nullptr; + } + } + + void Com_SetParseNegativeNumbers(int parse) + { + char* g_parse = Com_GetParseThreadInfo(); + + if (g_parse) + { + g_parse[1056 * *(reinterpret_cast(g_parse) + 4224) + 1032] = parse != 0; + } + } +} diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index fbdfc485..2baf16ce 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -1,714 +1,714 @@ -namespace Game -{ - typedef void*(__cdecl * BG_LoadWeaponDef_LoadObj_t)(const char* filename); - extern BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj; - - typedef void(__cdecl * Cbuf_AddServerText_t)(); - extern Cbuf_AddServerText_t Cbuf_AddServerText; - - typedef void(__cdecl * Cbuf_AddText_t)(int localClientNum, const char *text); - extern Cbuf_AddText_t Cbuf_AddText; - - typedef int(__cdecl * CG_GetClientNum_t)(); - extern CG_GetClientNum_t CG_GetClientNum; - - typedef char*(__cdecl * CL_GetClientName_t)(int localClientNum, int index, char *buf, size_t size); - extern CL_GetClientName_t CL_GetClientName; - - typedef int(__cdecl * CL_IsCgameInitialized_t)(); - extern CL_IsCgameInitialized_t CL_IsCgameInitialized; - - typedef void(__cdecl * CL_ConnectFromParty_t)(int controllerIndex, _XSESSION_INFO *hostInfo, netadr_t addr, int numPublicSlots, int numPrivateSlots, const char *mapname, const char *gametype); - extern CL_ConnectFromParty_t CL_ConnectFromParty; - - typedef void(__cdecl * CL_DownloadsComplete_t)(int controller); - extern CL_DownloadsComplete_t CL_DownloadsComplete; - - typedef void(_cdecl * CL_DrawStretchPicPhysical_t)(float x, float y, float w, float h, float xScale, float yScale, float xay, float yay, const float *color, Game::Material* material); - extern CL_DrawStretchPicPhysical_t CL_DrawStretchPicPhysical; - - typedef void(__cdecl * CL_HandleRelayPacket_t)(Game::msg_t* msg, int client); - extern CL_HandleRelayPacket_t CL_HandleRelayPacket; - - typedef void(__cdecl * CL_ResetViewport_t)(); - extern CL_ResetViewport_t CL_ResetViewport; - - typedef void(__cdecl * CL_SelectStringTableEntryInDvar_f_t)(); - extern CL_SelectStringTableEntryInDvar_f_t CL_SelectStringTableEntryInDvar_f; - - typedef void(__cdecl * Cmd_AddCommand_t)(const char* cmdName, void(*function), cmd_function_t* allocedCmd, bool isKey); - extern Cmd_AddCommand_t Cmd_AddCommand; - - typedef void(__cdecl * Cmd_AddServerCommand_t)(const char* name, void(*callback), cmd_function_t* data); - extern Cmd_AddServerCommand_t Cmd_AddServerCommand; - - typedef void(__cdecl * Cmd_ExecuteSingleCommand_t)(int localClientNum, int controllerIndex, const char* cmd); - extern Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand; - - typedef void(__cdecl * Com_ClientPacketEvent_t)(); - extern Com_ClientPacketEvent_t Com_ClientPacketEvent; - - typedef void(__cdecl * Com_Error_t)(int type, const char* message, ...); - extern Com_Error_t Com_Error; - - typedef void(__cdecl * Com_Printf_t)(int channel, const char *fmt, ...); - extern Com_Printf_t Com_Printf; - - typedef void(__cdecl * Com_PrintMessage_t)(int channel, const char *msg, int error); - extern Com_PrintMessage_t Com_PrintMessage; - - typedef void(__cdecl * Com_EndParseSession_t)(); - extern Com_EndParseSession_t Com_EndParseSession; - - typedef void(__cdecl * Com_BeginParseSession_t)(const char* why); - extern Com_BeginParseSession_t Com_BeginParseSession; - - typedef void(__cdecl * Com_SetSpaceDelimited_t)(int); - extern Com_SetSpaceDelimited_t Com_SetSpaceDelimited; - - typedef char* (__cdecl * Com_Parse_t)(const char **data_p); - extern Com_Parse_t Com_Parse; - - typedef void(__cdecl * Com_SetSlowMotion_t)(float start, float end, int duration); - extern Com_SetSlowMotion_t Com_SetSlowMotion; - - typedef char* (__cdecl * Con_DrawMiniConsole_t)(int localClientNum, int xPos, int yPos, float alpha); - extern Con_DrawMiniConsole_t Con_DrawMiniConsole; - - typedef void (__cdecl * Con_DrawSolidConsole_t)(); - extern Con_DrawSolidConsole_t Con_DrawSolidConsole; - - typedef char *(__cdecl *DB_AllocStreamPos_t)(int alignment); - extern DB_AllocStreamPos_t DB_AllocStreamPos; - - typedef void(__cdecl * DB_PushStreamPos_t)(unsigned int index); - extern DB_PushStreamPos_t DB_PushStreamPos; - - typedef void(__cdecl * DB_PopStreamPos_t)(); - extern DB_PopStreamPos_t DB_PopStreamPos; - - typedef void(__cdecl * DB_BeginRecoverLostDevice_t)(); - extern DB_BeginRecoverLostDevice_t DB_BeginRecoverLostDevice; - - typedef void(__cdecl * DB_EndRecoverLostDevice_t)(); - extern DB_EndRecoverLostDevice_t DB_EndRecoverLostDevice; - - typedef void(__cdecl * DB_EnumXAssets_t)(XAssetType type, void(*)(XAssetHeader, void *), void* userdata, bool overrides); - extern DB_EnumXAssets_t DB_EnumXAssets; - - typedef void(__cdecl * DB_EnumXAssets_Internal_t)(XAssetType type, void(*)(XAssetHeader, void *), void* userdata, bool overrides); - extern DB_EnumXAssets_Internal_t DB_EnumXAssets_Internal; - - typedef XAssetHeader (__cdecl * DB_FindXAssetHeader_t)(XAssetType type, const char* name); - extern DB_FindXAssetHeader_t DB_FindXAssetHeader; - - typedef float(__cdecl * DB_GetLoadedFraction_t)(); - extern DB_GetLoadedFraction_t DB_GetLoadedFraction; - - typedef const char* (__cdecl * DB_GetXAssetNameHandler_t)(XAssetHeader* asset); - extern DB_GetXAssetNameHandler_t* DB_GetXAssetNameHandlers; - - typedef int(__cdecl * DB_GetXAssetSizeHandler_t)(); - extern DB_GetXAssetSizeHandler_t* DB_GetXAssetSizeHandlers; - - typedef const char *(__cdecl * DB_GetXAssetTypeName_t)(XAssetType type); - extern DB_GetXAssetTypeName_t DB_GetXAssetTypeName; - - typedef const char *(__cdecl * DB_IsXAssetDefault_t)(XAssetType type, const char* name); - extern DB_IsXAssetDefault_t DB_IsXAssetDefault; - - typedef void(__cdecl * DB_GetRawBuffer_t)(RawFile* rawfile, char* buffer, int size); - extern DB_GetRawBuffer_t DB_GetRawBuffer; - - typedef int(__cdecl * DB_GetRawFileLen_t)(RawFile* rawfile); - extern DB_GetRawFileLen_t DB_GetRawFileLen; - - typedef void(*DB_LoadXAssets_t)(XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); - extern DB_LoadXAssets_t DB_LoadXAssets; - - typedef void(*DB_LoadXFileData_t)(char *pos, int size); - extern DB_LoadXFileData_t DB_LoadXFileData; - - typedef void(__cdecl * DB_ReadXFileUncompressed_t)(void* buffer, int size); - extern DB_ReadXFileUncompressed_t DB_ReadXFileUncompressed; - - typedef void(__cdecl * DB_ReadXFile_t)(void* buffer, int size); - extern DB_ReadXFile_t DB_ReadXFile; - - typedef void(__cdecl * DB_ReleaseXAssetHandler_t)(XAssetHeader header); - extern DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers; - - typedef void(__cdecl * DB_SetXAssetName_t)(XAsset* asset, const char* name); - extern DB_SetXAssetName_t DB_SetXAssetName; - - typedef void(__cdecl * DB_SetXAssetNameHandler_t)(XAssetHeader* header, const char* name); - extern DB_SetXAssetNameHandler_t* DB_SetXAssetNameHandlers; - - typedef void(__cdecl * DB_XModelSurfsFixup_t)(XModel* model); - extern DB_XModelSurfsFixup_t DB_XModelSurfsFixup; - - typedef dvar_t* (__cdecl * Dvar_RegisterBool_t)(const char* name, bool default, int flags, const char* description); - extern Dvar_RegisterBool_t Dvar_RegisterBool; - - typedef dvar_t* (__cdecl * Dvar_RegisterFloat_t)(const char* name, float default, float min, float max, int flags, const char* description); - extern Dvar_RegisterFloat_t Dvar_RegisterFloat; - - typedef dvar_t* (__cdecl * Dvar_RegisterVec2_t)(const char* name, float defx, float defy, float min, float max, int flags, const char* description); - extern Dvar_RegisterVec2_t Dvar_RegisterVec2; - - typedef dvar_t* (__cdecl * Dvar_RegisterVec3_t)(const char* name, float defx, float defy, float defz, float min, float max, int flags, const char* description); - extern Dvar_RegisterVec3_t Dvar_RegisterVec3; - - typedef dvar_t* (__cdecl * Dvar_RegisterVec4_t)(const char* name, float defx, float defy, float defz, float defw, float min, float max, int flags, const char* description); - extern Dvar_RegisterVec4_t Dvar_RegisterVec4; - - typedef dvar_t* (__cdecl * Dvar_RegisterInt_t)(const char* name, int default, int min, int max, int flags, const char* description); - extern Dvar_RegisterInt_t Dvar_RegisterInt; - - typedef dvar_t* (__cdecl * Dvar_RegisterEnum_t)(const char* name, char** enumValues, int default, int flags, const char* description); - extern Dvar_RegisterEnum_t Dvar_RegisterEnum; - - typedef dvar_t* (__cdecl * Dvar_RegisterString_t)(const char* name, const char* default, int, const char*); - extern Dvar_RegisterString_t Dvar_RegisterString; - - typedef dvar_t* (__cdecl * Dvar_RegisterColor_t)(const char* name, float r, float g, float b, float a, int flags, const char* description); - extern Dvar_RegisterColor_t Dvar_RegisterColor; - - typedef void(__cdecl * Dvar_SetStringByName_t)(const char* cvar, const char* value); - extern Dvar_SetStringByName_t Dvar_SetStringByName; - - typedef void(__cdecl * Dvar_GetUnpackedColorByName_t)(const char* name, float* color); - extern Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName; - - typedef dvar_t* (__cdecl * Dvar_FindVar_t)(const char *dvarName); - extern Dvar_FindVar_t Dvar_FindVar; - - typedef char* (__cdecl* Dvar_InfoString_Big_t)(int typeMask); - extern Dvar_InfoString_Big_t Dvar_InfoString_Big; - - typedef dvar_t* (__cdecl * Dvar_SetCommand_t)(const char* name, const char* value); - extern Dvar_SetCommand_t Dvar_SetCommand; - - typedef bool(__cdecl * Encode_Init_t)(const char* ); - extern Encode_Init_t Encode_Init; - - typedef void(__cdecl * Field_Clear_t)(void* field); - extern Field_Clear_t Field_Clear; - - typedef void(__cdecl * FreeMemory_t)(void* buffer); - extern FreeMemory_t FreeMemory; - - typedef void(__cdecl * FS_FreeFile_t)(void* buffer); - extern FS_FreeFile_t FS_FreeFile; - - typedef int(__cdecl * FS_ReadFile_t)(const char* path, char** buffer); - extern FS_ReadFile_t FS_ReadFile; - - typedef char** (__cdecl * FS_GetFileList_t)(const char *path, const char *extension, FsListBehavior_e behavior, int *numfiles, int allocTrackType); - extern FS_GetFileList_t FS_GetFileList; - - typedef void(__cdecl * FS_FreeFileList_t)(char** list); - extern FS_FreeFileList_t FS_FreeFileList; - - typedef int(__cdecl * FS_FOpenFileAppend_t)(const char* file); - extern FS_FOpenFileAppend_t FS_FOpenFileAppend; - extern FS_FOpenFileAppend_t FS_FOpenFileWrite; - - typedef int(__cdecl * FS_FOpenFileRead_t)(const char* file, int* fh/*, int uniqueFile*/); - extern FS_FOpenFileRead_t FS_FOpenFileRead; - - typedef int(__cdecl * FS_FOpenFileReadForThread_t)(const char *filename, int *file, int thread); - extern FS_FOpenFileReadForThread_t FS_FOpenFileReadForThread; - - typedef int(__cdecl * FS_FCloseFile_t)(int fh); - extern FS_FCloseFile_t FS_FCloseFile; - - typedef bool(__cdecl * FS_FileExists_t)(const char* file); - extern FS_FileExists_t FS_FileExists; - - typedef bool(__cdecl * FS_WriteFile_t)(char* filename, char* folder, void* buffer, int size); - extern FS_WriteFile_t FS_WriteFile; - - typedef int(__cdecl * FS_Write_t)(const void* buffer, size_t size, int file); - extern FS_Write_t FS_Write; - - typedef int(__cdecl * FS_Read_t)(void* buffer, size_t size, int file); - extern FS_Read_t FS_Read; - - typedef int(__cdecl * FS_Seek_t)(int fileHandle, int seekPosition, int seekOrigin); - extern FS_Seek_t FS_Seek; - - typedef int(__cdecl * FS_FTell_t)(int fileHandle); - extern FS_FTell_t FS_FTell; - - typedef int(__cdecl * FS_Remove_t)(char *); - extern FS_Remove_t FS_Remove; - - typedef int(__cdecl * FS_Restart_t)(int localClientNum, int checksumFeed); - extern FS_Restart_t FS_Restart; - - typedef int(__cdecl * FS_BuildPathToFile_t)(const char*, const char*, const char*, char**); - extern FS_BuildPathToFile_t FS_BuildPathToFile; - - typedef void(__cdecl* G_SpawnEntitiesFromString_t)(); - extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString; - - typedef void(__cdecl * GScr_LoadGameTypeScript_t)(); - extern GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript; - - typedef int(__cdecl * Reader_t)(char const*, int *); - typedef bool(__cdecl * Image_LoadFromFileWithReader_t)(GfxImage* image, Reader_t reader); - extern Image_LoadFromFileWithReader_t Image_LoadFromFileWithReader; - - typedef void(__cdecl * Image_Release_t)(GfxImage* image); - extern Image_Release_t Image_Release; - - typedef void(__cdecl * Key_SetCatcher_t)(int localClientNum, int catcher); - extern Key_SetCatcher_t Key_SetCatcher; - - typedef void(__cdecl * LargeLocalInit_t)(); - extern LargeLocalInit_t LargeLocalInit; - - typedef bool(__cdecl * Load_Stream_t)(bool atStreamStart, const void *ptr, int size); - extern Load_Stream_t Load_Stream; - - typedef void(__cdecl * Load_XString_t)(bool atStreamStart); - extern Load_XString_t Load_XString; - - typedef void(__cdecl * Load_XModelPtr_t)(bool atStreamStart); - extern Load_XModelPtr_t Load_XModelPtr; - - typedef void(__cdecl * Load_XModelSurfsFixup_t)(XModelSurfs **, XModelLodInfo *); - extern Load_XModelSurfsFixup_t Load_XModelSurfsFixup; - - typedef void(__cdecl * Load_XStringArray_t)(bool atStreamStart, int count); - extern Load_XStringArray_t Load_XStringArray; - - typedef void(__cdecl * Load_XStringCustom_t)(const char **str); - extern Load_XStringCustom_t Load_XStringCustom; - - typedef void(__cdecl *Load_FxEffectDefHandle_t)(bool atStreamStart); - extern Load_FxEffectDefHandle_t Load_FxEffectDefHandle; - - typedef void(__cdecl *Load_FxElemDef_t)(bool atStreamStart); - extern Load_FxElemDef_t Load_FxElemDef; - - typedef void(__cdecl *Load_GfxImagePtr_t)(bool atStreamStart); - extern Load_GfxImagePtr_t Load_GfxImagePtr; - - typedef void(__cdecl *Load_GfxTextureLoad_t)(bool atStreamStart); - extern Load_GfxTextureLoad_t Load_GfxTextureLoad; - - typedef int(__cdecl *Load_Texture_t)(GfxImageLoadDef **loadDef, GfxImage *image); - extern Load_Texture_t Load_Texture; - - typedef void(__cdecl * Load_SndAliasCustom_t)(snd_alias_list_t** var); - extern Load_SndAliasCustom_t Load_SndAliasCustom; - - typedef void(__cdecl *Load_MaterialHandle_t)(bool atStreamStart); - extern Load_MaterialHandle_t Load_MaterialHandle; - - typedef void(__cdecl *Load_PhysCollmapPtr_t)(bool atStreamStart); - extern Load_PhysCollmapPtr_t Load_PhysCollmapPtr; - - typedef void(__cdecl *Load_PhysPresetPtr_t)(bool atStreamStart); - extern Load_PhysPresetPtr_t Load_PhysPresetPtr; - - typedef void(__cdecl *Load_TracerDefPtr_t)(bool atStreamStart); - extern Load_TracerDefPtr_t Load_TracerDefPtr; - - typedef void(__cdecl *Load_snd_alias_list_nameArray_t)(bool atStreamStart, int count); - extern Load_snd_alias_list_nameArray_t Load_snd_alias_list_nameArray; - - typedef void(__cdecl * Menus_CloseAll_t)(UiContext *dc); - extern Menus_CloseAll_t Menus_CloseAll; - - typedef int(__cdecl * Menus_OpenByName_t)(UiContext *dc, const char *p); - extern Menus_OpenByName_t Menus_OpenByName; - - typedef menuDef_t *(__cdecl * Menus_FindByName_t)(UiContext *dc, const char *name); - extern Menus_FindByName_t Menus_FindByName; - - typedef bool(__cdecl * Menu_IsVisible_t)(UiContext *dc, menuDef_t *menu); - extern Menu_IsVisible_t Menu_IsVisible; - - typedef bool(__cdecl * Menus_MenuIsInStack_t)(UiContext *dc, menuDef_t *menu); - extern Menus_MenuIsInStack_t Menus_MenuIsInStack; - - typedef void(__cdecl * MSG_Init_t)(msg_t *buf, char *data, int length); - extern MSG_Init_t MSG_Init; - - typedef void(__cdecl * MSG_ReadData_t)(msg_t *msg, void *data, int len); - extern MSG_ReadData_t MSG_ReadData; - - typedef int(__cdecl * MSG_ReadLong_t)(msg_t* msg); - extern MSG_ReadLong_t MSG_ReadLong; - - typedef short(__cdecl * MSG_ReadShort_t)(msg_t* msg); - extern MSG_ReadShort_t MSG_ReadShort; - - typedef __int64(__cdecl * MSG_ReadInt64_t)(msg_t* msg); - extern MSG_ReadInt64_t MSG_ReadInt64; - - typedef char* (__cdecl * MSG_ReadString_t)(msg_t* msg); - extern MSG_ReadString_t MSG_ReadString; - - typedef int(__cdecl * MSG_ReadByte_t)(msg_t* msg); - extern MSG_ReadByte_t MSG_ReadByte; - - typedef int(__cdecl * MSG_ReadBitsCompress_t)(const char *from, char *to, int size); - extern MSG_ReadBitsCompress_t MSG_ReadBitsCompress; - - typedef void(__cdecl * MSG_WriteByte_t)(msg_t* msg, unsigned char c); - extern MSG_WriteByte_t MSG_WriteByte; - - typedef void(__cdecl * MSG_WriteData_t)(msg_t *buf, const void *data, int length); - extern MSG_WriteData_t MSG_WriteData; - - typedef void(__cdecl * MSG_WriteLong_t)(msg_t *msg, int c); - extern MSG_WriteLong_t MSG_WriteLong; - - typedef int(__cdecl * MSG_WriteBitsCompress_t)(bool trainHuffman, const char *from, char *to, int size); - extern MSG_WriteBitsCompress_t MSG_WriteBitsCompress; - - typedef void(__cdecl * NetadrToSockadr_t)(netadr_t *a, sockaddr *s); - extern NetadrToSockadr_t NetadrToSockadr; - - typedef const char* (__cdecl * NET_AdrToString_t)(netadr_t adr); - extern NET_AdrToString_t NET_AdrToString; - - typedef bool(__cdecl * NET_CompareAdr_t)(netadr_t a, netadr_t b); - extern NET_CompareAdr_t NET_CompareAdr; - - typedef const char* (__cdecl * NET_ErrorString_t)(); - extern NET_ErrorString_t NET_ErrorString; - - typedef void(__cdecl * NET_Init_t)(); - extern NET_Init_t NET_Init; - - typedef bool(__cdecl * NET_IsLocalAddress_t)(netadr_t adr); - extern NET_IsLocalAddress_t NET_IsLocalAddress; - - typedef bool(__cdecl * NET_StringToAdr_t)(const char *s, netadr_t *a); - extern NET_StringToAdr_t NET_StringToAdr; - - typedef void(__cdecl* NET_OutOfBandPrint_t)(netsrc_t sock, netadr_t adr, const char *data); - extern NET_OutOfBandPrint_t NET_OutOfBandPrint; - - typedef void(__cdecl* NET_OutOfBandData_t)(netsrc_t sock, netadr_t adr, const char *format, int len); - extern NET_OutOfBandData_t NET_OutOfBandData; - - typedef void(__cdecl * Live_MPAcceptInvite_t)(_XSESSION_INFO *hostInfo, const int controllerIndex, bool fromGameInvite); - extern Live_MPAcceptInvite_t Live_MPAcceptInvite; - - typedef int(__cdecl * Live_GetMapIndex_t)(const char* mapname); - extern Live_GetMapIndex_t Live_GetMapIndex; - - typedef char* (__cdecl * LoadModdableRawfile_t)(int a1, const char* filename); - extern LoadModdableRawfile_t LoadModdableRawfile; - - typedef int(__cdecl * PC_ReadToken_t)(source_t*, token_t*); - extern PC_ReadToken_t PC_ReadToken; - - typedef int(__cdecl * PC_ReadTokenHandle_t)(int handle, pc_token_s *pc_token); - extern PC_ReadTokenHandle_t PC_ReadTokenHandle; - - typedef void(__cdecl * PC_SourceError_t)(int, const char*, ...); - extern PC_SourceError_t PC_SourceError; - - typedef int(__cdecl * Party_GetMaxPlayers_t)(party_s* party); - extern Party_GetMaxPlayers_t Party_GetMaxPlayers; - - typedef int(__cdecl * PartyHost_CountMembers_t)(PartyData_s* party); - extern PartyHost_CountMembers_t PartyHost_CountMembers; - - typedef netadr_t *(__cdecl * PartyHost_GetMemberAddressBySlot_t)(int unk, void *party, const int slot); - extern PartyHost_GetMemberAddressBySlot_t PartyHost_GetMemberAddressBySlot; - - typedef const char *(__cdecl * PartyHost_GetMemberName_t)(PartyData_s* party, const int clientNum); - extern PartyHost_GetMemberName_t PartyHost_GetMemberName; - - typedef void(__cdecl * Playlist_ParsePlaylists_t)(const char* data); - extern Playlist_ParsePlaylists_t Playlist_ParsePlaylists; - - typedef Font* (__cdecl * R_RegisterFont_t)(const char* asset); - extern R_RegisterFont_t R_RegisterFont; - - typedef void(__cdecl * R_AddCmdDrawText_t)(const char *text, int maxChars, Font *font, float x, float y, float xScale, float yScale, float rotation, const float *color, int style); - extern R_AddCmdDrawText_t R_AddCmdDrawText; - - typedef void(_cdecl * R_AddCmdDrawStretchPic_t)(float x, float y, float w, float h, float xScale, float yScale, float xay, float yay, const float *color, Game::Material* material); - extern R_AddCmdDrawStretchPic_t R_AddCmdDrawStretchPic; - - typedef void* (__cdecl * R_AllocStaticIndexBuffer_t)(IDirect3DIndexBuffer9** store, int length); - extern R_AllocStaticIndexBuffer_t R_AllocStaticIndexBuffer; - - typedef bool(__cdecl * R_Cinematic_StartPlayback_Now_t)(); - extern R_Cinematic_StartPlayback_Now_t R_Cinematic_StartPlayback_Now; - - typedef void(__cdecl * R_LoadGraphicsAssets_t)(); - extern R_LoadGraphicsAssets_t R_LoadGraphicsAssets; - - typedef int(__cdecl * R_TextWidth_t)(const char* text, int maxlength, Font* font); - extern R_TextWidth_t R_TextWidth; - - typedef int(__cdecl * R_TextHeight_t)(Font* font); - extern R_TextHeight_t R_TextHeight; - - typedef void(__cdecl * R_FlushSun_t)(); - extern R_FlushSun_t R_FlushSun; - - typedef void(__cdecl * Scr_ShutdownAllocNode_t)(); - extern Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode; - - typedef int(__cdecl * Scr_LoadGameType_t)(); - extern Scr_LoadGameType_t Scr_LoadGameType; - - typedef int(__cdecl * Scr_LoadScript_t)(const char*); - extern Scr_LoadScript_t Scr_LoadScript; - - typedef float(__cdecl * Scr_GetFloat_t)(int); - extern Scr_GetFloat_t Scr_GetFloat; - - typedef int(__cdecl * Scr_GetNumParam_t)(void); - extern Scr_GetNumParam_t Scr_GetNumParam; - - typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char*, const char*); - extern Scr_GetFunctionHandle_t Scr_GetFunctionHandle; - - typedef int(__cdecl * Scr_ExecThread_t)(int, int); - extern Scr_ExecThread_t Scr_ExecThread; - - typedef int(__cdecl * Scr_FreeThread_t)(int); - extern Scr_FreeThread_t Scr_FreeThread; - - typedef script_t* (__cdecl * Script_Alloc_t)(int length); - extern Script_Alloc_t Script_Alloc; - - typedef void(__cdecl * Script_SetupTokens_t)(script_t* script, void* tokens); - extern Script_SetupTokens_t Script_SetupTokens; - - typedef int(__cdecl * Script_CleanString_t)(char* buffer); - extern Script_CleanString_t Script_CleanString; - - typedef char* (__cdecl * SE_Load_t)(const char* file, int Unk); - extern SE_Load_t SE_Load; - - typedef char* (__cdecl * SEH_StringEd_GetString_t)(const char* string); - extern SEH_StringEd_GetString_t SEH_StringEd_GetString; - - typedef char* (__cdecl * SL_ConvertToString_t)(unsigned short stringValue); - extern SL_ConvertToString_t SL_ConvertToString; - - typedef short(__cdecl * SL_GetString_t)(const char *str, unsigned int user); - extern SL_GetString_t SL_GetString; - - typedef void(__cdecl * SND_Init_t)(int a1, int a2, int a3); - extern SND_Init_t SND_Init; - - typedef void(__cdecl * SND_InitDriver_t)(); - extern SND_InitDriver_t SND_InitDriver; - - typedef void(__cdecl * SockadrToNetadr_t)(sockaddr *s, netadr_t *a); - extern SockadrToNetadr_t SockadrToNetadr; - - typedef void(__cdecl * Steam_JoinLobby_t)(SteamID, char); - extern Steam_JoinLobby_t Steam_JoinLobby; - - typedef int(__cdecl* SV_GameClientNum_Score_t)(int clientID); - extern SV_GameClientNum_Score_t SV_GameClientNum_Score; - - typedef void(__cdecl * SV_GameSendServerCommand_t)(int clientNum, /*svscmd_type*/int type, const char* text); - extern SV_GameSendServerCommand_t SV_GameSendServerCommand; - - typedef void(__cdecl * SV_Cmd_TokenizeString_t)(const char* string); - extern SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString; - - typedef void(__cdecl * SV_Cmd_EndTokenizedString_t)(); - extern SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString; - - typedef void(__cdecl * SV_DirectConnect_t)(netadr_t adr); - extern SV_DirectConnect_t SV_DirectConnect; - - typedef int(__cdecl * Sys_Error_t)(int, char *, ...); - extern Sys_Error_t Sys_Error; - - typedef void(__cdecl * Sys_FreeFileList_t)(char** list); - extern Sys_FreeFileList_t Sys_FreeFileList; - - typedef bool(__cdecl * Sys_IsDatabaseReady_t)(); - extern Sys_IsDatabaseReady_t Sys_IsDatabaseReady; - - typedef bool(__cdecl * Sys_IsDatabaseReady2_t)(); - extern Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2; - - typedef bool(__cdecl * Sys_IsMainThread_t)(); - extern Sys_IsMainThread_t Sys_IsMainThread; - - typedef bool(__cdecl * Sys_IsRenderThread_t)(); - extern Sys_IsRenderThread_t Sys_IsRenderThread; - - typedef bool(__cdecl * Sys_IsServerThread_t)(); - extern Sys_IsServerThread_t Sys_IsServerThread; - - typedef bool(__cdecl * Sys_IsDatabaseThread_t)(); - extern Sys_IsDatabaseThread_t Sys_IsDatabaseThread; - - typedef char** (__cdecl * Sys_ListFiles_t)(const char *directory, const char *extension, const char *filter, int *numfiles, int wantsubs); - extern Sys_ListFiles_t Sys_ListFiles; - - typedef int(__cdecl * Sys_Milliseconds_t)(); - extern Sys_Milliseconds_t Sys_Milliseconds; - - typedef void(__cdecl * TeleportPlayer_t)(gentity_t* entity, float* pos, float* orientation); - extern TeleportPlayer_t TeleportPlayer; - - typedef bool(__cdecl * Sys_SendPacket_t)(netsrc_t sock, size_t len, const char *format, netadr_t adr); - extern Sys_SendPacket_t Sys_SendPacket; - - typedef void(__cdecl * Sys_ShowConsole_t)(); - extern Sys_ShowConsole_t Sys_ShowConsole; - - typedef void(__cdecl * UI_AddMenuList_t)(UiContext *dc, MenuList *menuList, int close); - extern UI_AddMenuList_t UI_AddMenuList; - - typedef char* (__cdecl * UI_CheckStringTranslation_t)(char*, char*); - extern UI_CheckStringTranslation_t UI_CheckStringTranslation; - - typedef MenuList *(__cdecl * UI_LoadMenus_t)(const char *menuFile, int imageTrack); - extern UI_LoadMenus_t UI_LoadMenus; - - typedef void(__cdecl * UI_UpdateArenas_t)(); - extern UI_UpdateArenas_t UI_UpdateArenas; - - typedef void(__cdecl * UI_DrawHandlePic_t)(/*ScreenPlacement*/void *scrPlace, float x, float y, float w, float h, int horzAlign, int vertAlign, const float *color, Material *material); - extern UI_DrawHandlePic_t UI_DrawHandlePic; - - typedef void* (__cdecl * UI_GetContext_t)(void*); - extern UI_GetContext_t UI_GetContext; - - typedef int(__cdecl * UI_TextWidth_t)(const char *text, int maxChars, Font *font, float scale); - extern UI_TextWidth_t UI_TextWidth; - - typedef void(__cdecl * UI_DrawText_t)(void* scrPlace, const char *text, int maxChars, Font *font, float x, float y, int horzAlign, int vertAlign, float scale, const float *color, int style); - extern UI_DrawText_t UI_DrawText; - - typedef const char * (__cdecl * Win_GetLanguage_t)(); - extern Win_GetLanguage_t Win_GetLanguage; - - typedef void (__cdecl * Vec3UnpackUnitVec_t)(PackedUnitVec, vec3_t *); - extern Vec3UnpackUnitVec_t Vec3UnpackUnitVec; - - extern XAssetHeader* DB_XAssetPool; - extern unsigned int* g_poolSize; - - extern DWORD* cmd_id; - extern DWORD* cmd_argc; - extern char*** cmd_argv; - - extern DWORD* cmd_id_sv; - extern DWORD* cmd_argc_sv; - extern char*** cmd_argv_sv; - - extern cmd_function_t** cmd_functions; - - extern int* svs_numclients; - extern client_t* svs_clients; - - extern source_t **sourceFiles; - extern keywordHash_t **menuParseKeywordHash; - - extern UiContext *uiContext; - - extern int* arenaCount; - extern mapArena_t* arenas; - - extern int* gameTypeCount; - extern gameTypeName_t* gameTypes; - - extern searchpath_t* fs_searchpaths; - - extern XBlock** g_streamBlocks; - extern int* g_streamPos; - extern int* g_streamPosIndex; - - extern bool* g_lobbyCreateInProgress; - extern party_t** partyIngame; - extern PartyData_s** partyData; - - extern int* numIP; - extern netIP_t* localIP; - - extern int* demoFile; - extern int* demoPlaying; - extern int* demoRecording; - extern int* serverMessageSequence; - - extern gentity_t* g_entities; - - extern netadr_t* connectedHost; - extern SOCKET* ip_socket; - - extern uint32_t* com_frameTime; - - extern SafeArea* safeArea; - - extern SpawnVar* spawnVars; - extern MapEnts** marMapEntsPtr; - - extern IDirect3D9** d3d9; - extern IDirect3DDevice9** dx_ptr; - - extern mapname_t* mapnames; - - extern char*** varXString; - extern TracerDef*** varTracerDefPtr; - extern XModel*** varXModelPtr; - extern XModel** varXModel; - extern PathData** varPathData; - extern const char** varConstChar; - extern Material*** varMaterialHandle; - extern FxEffectDef*** varFxEffectDefHandle; - extern PhysCollmap*** varPhysCollmapPtr; - extern PhysPreset*** varPhysPresetPtr; - extern Game::MaterialPass** varMaterialPass; - extern snd_alias_list_t*** varsnd_alias_list_name; - - extern FxElemField* s_elemFields; - - XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize); - void Menu_FreeItemMemory(Game::itemDef_t* item); - const char* TableLookup(StringTable* stringtable, int row, int column); - const char* UI_LocalizeMapName(const char* mapName); - const char* UI_LocalizeGameType(const char* gameType); - float UI_GetScoreboardLeft(void*); - - const char *DB_GetXAssetName(XAsset *asset); - XAssetType DB_GetXAssetNameType(const char* name); - bool DB_IsZoneLoaded(const char* zone); - - void FS_AddLocalizedGameDirectory(const char *path, const char *dir); - - void MessageBox(std::string message, std::string title); - - unsigned int R_HashString(const char* string); - void R_LoadSunThroughDvars(const char* mapname, sunflare_t* sun); - void R_SetSunFromDvars(sunflare_t* sun); - - void SV_KickClient(client_t* client, const char* reason); - void SV_KickClientError(client_t* client, std::string reason); - - void Scr_iPrintLn(int clientNum, std::string message); - void Scr_iPrintLnBold(int clientNum, std::string message); - - void IN_KeyUp(kbutton_t* button); - void IN_KeyDown(kbutton_t* button); - - int FS_FOpenFileReadCurrentThread(const char* file, int* fh); - - void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count); - void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len); - - char* Com_GetParseThreadInfo(); - void Com_SetParseNegativeNumbers(int parse); -} +namespace Game +{ + typedef void*(__cdecl * BG_LoadWeaponDef_LoadObj_t)(const char* filename); + extern BG_LoadWeaponDef_LoadObj_t BG_LoadWeaponDef_LoadObj; + + typedef void(__cdecl * Cbuf_AddServerText_t)(); + extern Cbuf_AddServerText_t Cbuf_AddServerText; + + typedef void(__cdecl * Cbuf_AddText_t)(int localClientNum, const char *text); + extern Cbuf_AddText_t Cbuf_AddText; + + typedef int(__cdecl * CG_GetClientNum_t)(); + extern CG_GetClientNum_t CG_GetClientNum; + + typedef char*(__cdecl * CL_GetClientName_t)(int localClientNum, int index, char *buf, size_t size); + extern CL_GetClientName_t CL_GetClientName; + + typedef int(__cdecl * CL_IsCgameInitialized_t)(); + extern CL_IsCgameInitialized_t CL_IsCgameInitialized; + + typedef void(__cdecl * CL_ConnectFromParty_t)(int controllerIndex, _XSESSION_INFO *hostInfo, netadr_t addr, int numPublicSlots, int numPrivateSlots, const char *mapname, const char *gametype); + extern CL_ConnectFromParty_t CL_ConnectFromParty; + + typedef void(__cdecl * CL_DownloadsComplete_t)(int controller); + extern CL_DownloadsComplete_t CL_DownloadsComplete; + + typedef void(_cdecl * CL_DrawStretchPicPhysical_t)(float x, float y, float w, float h, float xScale, float yScale, float xay, float yay, const float *color, Game::Material* material); + extern CL_DrawStretchPicPhysical_t CL_DrawStretchPicPhysical; + + typedef void(__cdecl * CL_HandleRelayPacket_t)(Game::msg_t* msg, int client); + extern CL_HandleRelayPacket_t CL_HandleRelayPacket; + + typedef void(__cdecl * CL_ResetViewport_t)(); + extern CL_ResetViewport_t CL_ResetViewport; + + typedef void(__cdecl * CL_SelectStringTableEntryInDvar_f_t)(); + extern CL_SelectStringTableEntryInDvar_f_t CL_SelectStringTableEntryInDvar_f; + + typedef void(__cdecl * Cmd_AddCommand_t)(const char* cmdName, void(*function), cmd_function_t* allocedCmd, bool isKey); + extern Cmd_AddCommand_t Cmd_AddCommand; + + typedef void(__cdecl * Cmd_AddServerCommand_t)(const char* name, void(*callback), cmd_function_t* data); + extern Cmd_AddServerCommand_t Cmd_AddServerCommand; + + typedef void(__cdecl * Cmd_ExecuteSingleCommand_t)(int localClientNum, int controllerIndex, const char* cmd); + extern Cmd_ExecuteSingleCommand_t Cmd_ExecuteSingleCommand; + + typedef void(__cdecl * Com_ClientPacketEvent_t)(); + extern Com_ClientPacketEvent_t Com_ClientPacketEvent; + + typedef void(__cdecl * Com_Error_t)(int type, const char* message, ...); + extern Com_Error_t Com_Error; + + typedef void(__cdecl * Com_Printf_t)(int channel, const char *fmt, ...); + extern Com_Printf_t Com_Printf; + + typedef void(__cdecl * Com_PrintMessage_t)(int channel, const char *msg, int error); + extern Com_PrintMessage_t Com_PrintMessage; + + typedef void(__cdecl * Com_EndParseSession_t)(); + extern Com_EndParseSession_t Com_EndParseSession; + + typedef void(__cdecl * Com_BeginParseSession_t)(const char* why); + extern Com_BeginParseSession_t Com_BeginParseSession; + + typedef void(__cdecl * Com_SetSpaceDelimited_t)(int); + extern Com_SetSpaceDelimited_t Com_SetSpaceDelimited; + + typedef char* (__cdecl * Com_Parse_t)(const char **data_p); + extern Com_Parse_t Com_Parse; + + typedef void(__cdecl * Com_SetSlowMotion_t)(float start, float end, int duration); + extern Com_SetSlowMotion_t Com_SetSlowMotion; + + typedef char* (__cdecl * Con_DrawMiniConsole_t)(int localClientNum, int xPos, int yPos, float alpha); + extern Con_DrawMiniConsole_t Con_DrawMiniConsole; + + typedef void (__cdecl * Con_DrawSolidConsole_t)(); + extern Con_DrawSolidConsole_t Con_DrawSolidConsole; + + typedef char *(__cdecl *DB_AllocStreamPos_t)(int alignment); + extern DB_AllocStreamPos_t DB_AllocStreamPos; + + typedef void(__cdecl * DB_PushStreamPos_t)(unsigned int index); + extern DB_PushStreamPos_t DB_PushStreamPos; + + typedef void(__cdecl * DB_PopStreamPos_t)(); + extern DB_PopStreamPos_t DB_PopStreamPos; + + typedef void(__cdecl * DB_BeginRecoverLostDevice_t)(); + extern DB_BeginRecoverLostDevice_t DB_BeginRecoverLostDevice; + + typedef void(__cdecl * DB_EndRecoverLostDevice_t)(); + extern DB_EndRecoverLostDevice_t DB_EndRecoverLostDevice; + + typedef void(__cdecl * DB_EnumXAssets_t)(XAssetType type, void(*)(XAssetHeader, void *), void* userdata, bool overrides); + extern DB_EnumXAssets_t DB_EnumXAssets; + + typedef void(__cdecl * DB_EnumXAssets_Internal_t)(XAssetType type, void(*)(XAssetHeader, void *), void* userdata, bool overrides); + extern DB_EnumXAssets_Internal_t DB_EnumXAssets_Internal; + + typedef XAssetHeader (__cdecl * DB_FindXAssetHeader_t)(XAssetType type, const char* name); + extern DB_FindXAssetHeader_t DB_FindXAssetHeader; + + typedef float(__cdecl * DB_GetLoadedFraction_t)(); + extern DB_GetLoadedFraction_t DB_GetLoadedFraction; + + typedef const char* (__cdecl * DB_GetXAssetNameHandler_t)(XAssetHeader* asset); + extern DB_GetXAssetNameHandler_t* DB_GetXAssetNameHandlers; + + typedef int(__cdecl * DB_GetXAssetSizeHandler_t)(); + extern DB_GetXAssetSizeHandler_t* DB_GetXAssetSizeHandlers; + + typedef const char *(__cdecl * DB_GetXAssetTypeName_t)(XAssetType type); + extern DB_GetXAssetTypeName_t DB_GetXAssetTypeName; + + typedef const char *(__cdecl * DB_IsXAssetDefault_t)(XAssetType type, const char* name); + extern DB_IsXAssetDefault_t DB_IsXAssetDefault; + + typedef void(__cdecl * DB_GetRawBuffer_t)(RawFile* rawfile, char* buffer, int size); + extern DB_GetRawBuffer_t DB_GetRawBuffer; + + typedef int(__cdecl * DB_GetRawFileLen_t)(RawFile* rawfile); + extern DB_GetRawFileLen_t DB_GetRawFileLen; + + typedef void(*DB_LoadXAssets_t)(XZoneInfo *zoneInfo, unsigned int zoneCount, int sync); + extern DB_LoadXAssets_t DB_LoadXAssets; + + typedef void(*DB_LoadXFileData_t)(char *pos, int size); + extern DB_LoadXFileData_t DB_LoadXFileData; + + typedef void(__cdecl * DB_ReadXFileUncompressed_t)(void* buffer, int size); + extern DB_ReadXFileUncompressed_t DB_ReadXFileUncompressed; + + typedef void(__cdecl * DB_ReadXFile_t)(void* buffer, int size); + extern DB_ReadXFile_t DB_ReadXFile; + + typedef void(__cdecl * DB_ReleaseXAssetHandler_t)(XAssetHeader header); + extern DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers; + + typedef void(__cdecl * DB_SetXAssetName_t)(XAsset* asset, const char* name); + extern DB_SetXAssetName_t DB_SetXAssetName; + + typedef void(__cdecl * DB_SetXAssetNameHandler_t)(XAssetHeader* header, const char* name); + extern DB_SetXAssetNameHandler_t* DB_SetXAssetNameHandlers; + + typedef void(__cdecl * DB_XModelSurfsFixup_t)(XModel* model); + extern DB_XModelSurfsFixup_t DB_XModelSurfsFixup; + + typedef dvar_t* (__cdecl * Dvar_RegisterBool_t)(const char* name, bool default, int flags, const char* description); + extern Dvar_RegisterBool_t Dvar_RegisterBool; + + typedef dvar_t* (__cdecl * Dvar_RegisterFloat_t)(const char* name, float default, float min, float max, int flags, const char* description); + extern Dvar_RegisterFloat_t Dvar_RegisterFloat; + + typedef dvar_t* (__cdecl * Dvar_RegisterVec2_t)(const char* name, float defx, float defy, float min, float max, int flags, const char* description); + extern Dvar_RegisterVec2_t Dvar_RegisterVec2; + + typedef dvar_t* (__cdecl * Dvar_RegisterVec3_t)(const char* name, float defx, float defy, float defz, float min, float max, int flags, const char* description); + extern Dvar_RegisterVec3_t Dvar_RegisterVec3; + + typedef dvar_t* (__cdecl * Dvar_RegisterVec4_t)(const char* name, float defx, float defy, float defz, float defw, float min, float max, int flags, const char* description); + extern Dvar_RegisterVec4_t Dvar_RegisterVec4; + + typedef dvar_t* (__cdecl * Dvar_RegisterInt_t)(const char* name, int default, int min, int max, int flags, const char* description); + extern Dvar_RegisterInt_t Dvar_RegisterInt; + + typedef dvar_t* (__cdecl * Dvar_RegisterEnum_t)(const char* name, char** enumValues, int default, int flags, const char* description); + extern Dvar_RegisterEnum_t Dvar_RegisterEnum; + + typedef dvar_t* (__cdecl * Dvar_RegisterString_t)(const char* name, const char* default, int, const char*); + extern Dvar_RegisterString_t Dvar_RegisterString; + + typedef dvar_t* (__cdecl * Dvar_RegisterColor_t)(const char* name, float r, float g, float b, float a, int flags, const char* description); + extern Dvar_RegisterColor_t Dvar_RegisterColor; + + typedef void(__cdecl * Dvar_SetStringByName_t)(const char* cvar, const char* value); + extern Dvar_SetStringByName_t Dvar_SetStringByName; + + typedef void(__cdecl * Dvar_GetUnpackedColorByName_t)(const char* name, float* color); + extern Dvar_GetUnpackedColorByName_t Dvar_GetUnpackedColorByName; + + typedef dvar_t* (__cdecl * Dvar_FindVar_t)(const char *dvarName); + extern Dvar_FindVar_t Dvar_FindVar; + + typedef char* (__cdecl* Dvar_InfoString_Big_t)(int typeMask); + extern Dvar_InfoString_Big_t Dvar_InfoString_Big; + + typedef dvar_t* (__cdecl * Dvar_SetCommand_t)(const char* name, const char* value); + extern Dvar_SetCommand_t Dvar_SetCommand; + + typedef bool(__cdecl * Encode_Init_t)(const char* ); + extern Encode_Init_t Encode_Init; + + typedef void(__cdecl * Field_Clear_t)(void* field); + extern Field_Clear_t Field_Clear; + + typedef void(__cdecl * FreeMemory_t)(void* buffer); + extern FreeMemory_t FreeMemory; + + typedef void(__cdecl * FS_FreeFile_t)(void* buffer); + extern FS_FreeFile_t FS_FreeFile; + + typedef int(__cdecl * FS_ReadFile_t)(const char* path, char** buffer); + extern FS_ReadFile_t FS_ReadFile; + + typedef char** (__cdecl * FS_GetFileList_t)(const char *path, const char *extension, FsListBehavior_e behavior, int *numfiles, int allocTrackType); + extern FS_GetFileList_t FS_GetFileList; + + typedef void(__cdecl * FS_FreeFileList_t)(char** list); + extern FS_FreeFileList_t FS_FreeFileList; + + typedef int(__cdecl * FS_FOpenFileAppend_t)(const char* file); + extern FS_FOpenFileAppend_t FS_FOpenFileAppend; + extern FS_FOpenFileAppend_t FS_FOpenFileWrite; + + typedef int(__cdecl * FS_FOpenFileRead_t)(const char* file, int* fh/*, int uniqueFile*/); + extern FS_FOpenFileRead_t FS_FOpenFileRead; + + typedef int(__cdecl * FS_FOpenFileReadForThread_t)(const char *filename, int *file, int thread); + extern FS_FOpenFileReadForThread_t FS_FOpenFileReadForThread; + + typedef int(__cdecl * FS_FCloseFile_t)(int fh); + extern FS_FCloseFile_t FS_FCloseFile; + + typedef bool(__cdecl * FS_FileExists_t)(const char* file); + extern FS_FileExists_t FS_FileExists; + + typedef bool(__cdecl * FS_WriteFile_t)(char* filename, char* folder, void* buffer, int size); + extern FS_WriteFile_t FS_WriteFile; + + typedef int(__cdecl * FS_Write_t)(const void* buffer, size_t size, int file); + extern FS_Write_t FS_Write; + + typedef int(__cdecl * FS_Read_t)(void* buffer, size_t size, int file); + extern FS_Read_t FS_Read; + + typedef int(__cdecl * FS_Seek_t)(int fileHandle, int seekPosition, int seekOrigin); + extern FS_Seek_t FS_Seek; + + typedef int(__cdecl * FS_FTell_t)(int fileHandle); + extern FS_FTell_t FS_FTell; + + typedef int(__cdecl * FS_Remove_t)(char *); + extern FS_Remove_t FS_Remove; + + typedef int(__cdecl * FS_Restart_t)(int localClientNum, int checksumFeed); + extern FS_Restart_t FS_Restart; + + typedef int(__cdecl * FS_BuildPathToFile_t)(const char*, const char*, const char*, char**); + extern FS_BuildPathToFile_t FS_BuildPathToFile; + + typedef void(__cdecl* G_SpawnEntitiesFromString_t)(); + extern G_SpawnEntitiesFromString_t G_SpawnEntitiesFromString; + + typedef void(__cdecl * GScr_LoadGameTypeScript_t)(); + extern GScr_LoadGameTypeScript_t GScr_LoadGameTypeScript; + + typedef int(__cdecl * Reader_t)(char const*, int *); + typedef bool(__cdecl * Image_LoadFromFileWithReader_t)(GfxImage* image, Reader_t reader); + extern Image_LoadFromFileWithReader_t Image_LoadFromFileWithReader; + + typedef void(__cdecl * Image_Release_t)(GfxImage* image); + extern Image_Release_t Image_Release; + + typedef void(__cdecl * Key_SetCatcher_t)(int localClientNum, int catcher); + extern Key_SetCatcher_t Key_SetCatcher; + + typedef void(__cdecl * LargeLocalInit_t)(); + extern LargeLocalInit_t LargeLocalInit; + + typedef bool(__cdecl * Load_Stream_t)(bool atStreamStart, const void *ptr, int size); + extern Load_Stream_t Load_Stream; + + typedef void(__cdecl * Load_XString_t)(bool atStreamStart); + extern Load_XString_t Load_XString; + + typedef void(__cdecl * Load_XModelPtr_t)(bool atStreamStart); + extern Load_XModelPtr_t Load_XModelPtr; + + typedef void(__cdecl * Load_XModelSurfsFixup_t)(XModelSurfs **, XModelLodInfo *); + extern Load_XModelSurfsFixup_t Load_XModelSurfsFixup; + + typedef void(__cdecl * Load_XStringArray_t)(bool atStreamStart, int count); + extern Load_XStringArray_t Load_XStringArray; + + typedef void(__cdecl * Load_XStringCustom_t)(const char **str); + extern Load_XStringCustom_t Load_XStringCustom; + + typedef void(__cdecl *Load_FxEffectDefHandle_t)(bool atStreamStart); + extern Load_FxEffectDefHandle_t Load_FxEffectDefHandle; + + typedef void(__cdecl *Load_FxElemDef_t)(bool atStreamStart); + extern Load_FxElemDef_t Load_FxElemDef; + + typedef void(__cdecl *Load_GfxImagePtr_t)(bool atStreamStart); + extern Load_GfxImagePtr_t Load_GfxImagePtr; + + typedef void(__cdecl *Load_GfxTextureLoad_t)(bool atStreamStart); + extern Load_GfxTextureLoad_t Load_GfxTextureLoad; + + typedef int(__cdecl *Load_Texture_t)(GfxImageLoadDef **loadDef, GfxImage *image); + extern Load_Texture_t Load_Texture; + + typedef void(__cdecl * Load_SndAliasCustom_t)(snd_alias_list_t** var); + extern Load_SndAliasCustom_t Load_SndAliasCustom; + + typedef void(__cdecl *Load_MaterialHandle_t)(bool atStreamStart); + extern Load_MaterialHandle_t Load_MaterialHandle; + + typedef void(__cdecl *Load_PhysCollmapPtr_t)(bool atStreamStart); + extern Load_PhysCollmapPtr_t Load_PhysCollmapPtr; + + typedef void(__cdecl *Load_PhysPresetPtr_t)(bool atStreamStart); + extern Load_PhysPresetPtr_t Load_PhysPresetPtr; + + typedef void(__cdecl *Load_TracerDefPtr_t)(bool atStreamStart); + extern Load_TracerDefPtr_t Load_TracerDefPtr; + + typedef void(__cdecl *Load_snd_alias_list_nameArray_t)(bool atStreamStart, int count); + extern Load_snd_alias_list_nameArray_t Load_snd_alias_list_nameArray; + + typedef void(__cdecl * Menus_CloseAll_t)(UiContext *dc); + extern Menus_CloseAll_t Menus_CloseAll; + + typedef int(__cdecl * Menus_OpenByName_t)(UiContext *dc, const char *p); + extern Menus_OpenByName_t Menus_OpenByName; + + typedef menuDef_t *(__cdecl * Menus_FindByName_t)(UiContext *dc, const char *name); + extern Menus_FindByName_t Menus_FindByName; + + typedef bool(__cdecl * Menu_IsVisible_t)(UiContext *dc, menuDef_t *menu); + extern Menu_IsVisible_t Menu_IsVisible; + + typedef bool(__cdecl * Menus_MenuIsInStack_t)(UiContext *dc, menuDef_t *menu); + extern Menus_MenuIsInStack_t Menus_MenuIsInStack; + + typedef void(__cdecl * MSG_Init_t)(msg_t *buf, char *data, int length); + extern MSG_Init_t MSG_Init; + + typedef void(__cdecl * MSG_ReadData_t)(msg_t *msg, void *data, int len); + extern MSG_ReadData_t MSG_ReadData; + + typedef int(__cdecl * MSG_ReadLong_t)(msg_t* msg); + extern MSG_ReadLong_t MSG_ReadLong; + + typedef short(__cdecl * MSG_ReadShort_t)(msg_t* msg); + extern MSG_ReadShort_t MSG_ReadShort; + + typedef __int64(__cdecl * MSG_ReadInt64_t)(msg_t* msg); + extern MSG_ReadInt64_t MSG_ReadInt64; + + typedef char* (__cdecl * MSG_ReadString_t)(msg_t* msg); + extern MSG_ReadString_t MSG_ReadString; + + typedef int(__cdecl * MSG_ReadByte_t)(msg_t* msg); + extern MSG_ReadByte_t MSG_ReadByte; + + typedef int(__cdecl * MSG_ReadBitsCompress_t)(const char *from, char *to, int size); + extern MSG_ReadBitsCompress_t MSG_ReadBitsCompress; + + typedef void(__cdecl * MSG_WriteByte_t)(msg_t* msg, unsigned char c); + extern MSG_WriteByte_t MSG_WriteByte; + + typedef void(__cdecl * MSG_WriteData_t)(msg_t *buf, const void *data, int length); + extern MSG_WriteData_t MSG_WriteData; + + typedef void(__cdecl * MSG_WriteLong_t)(msg_t *msg, int c); + extern MSG_WriteLong_t MSG_WriteLong; + + typedef int(__cdecl * MSG_WriteBitsCompress_t)(bool trainHuffman, const char *from, char *to, int size); + extern MSG_WriteBitsCompress_t MSG_WriteBitsCompress; + + typedef void(__cdecl * NetadrToSockadr_t)(netadr_t *a, sockaddr *s); + extern NetadrToSockadr_t NetadrToSockadr; + + typedef const char* (__cdecl * NET_AdrToString_t)(netadr_t adr); + extern NET_AdrToString_t NET_AdrToString; + + typedef bool(__cdecl * NET_CompareAdr_t)(netadr_t a, netadr_t b); + extern NET_CompareAdr_t NET_CompareAdr; + + typedef const char* (__cdecl * NET_ErrorString_t)(); + extern NET_ErrorString_t NET_ErrorString; + + typedef void(__cdecl * NET_Init_t)(); + extern NET_Init_t NET_Init; + + typedef bool(__cdecl * NET_IsLocalAddress_t)(netadr_t adr); + extern NET_IsLocalAddress_t NET_IsLocalAddress; + + typedef bool(__cdecl * NET_StringToAdr_t)(const char *s, netadr_t *a); + extern NET_StringToAdr_t NET_StringToAdr; + + typedef void(__cdecl* NET_OutOfBandPrint_t)(netsrc_t sock, netadr_t adr, const char *data); + extern NET_OutOfBandPrint_t NET_OutOfBandPrint; + + typedef void(__cdecl* NET_OutOfBandData_t)(netsrc_t sock, netadr_t adr, const char *format, int len); + extern NET_OutOfBandData_t NET_OutOfBandData; + + typedef void(__cdecl * Live_MPAcceptInvite_t)(_XSESSION_INFO *hostInfo, const int controllerIndex, bool fromGameInvite); + extern Live_MPAcceptInvite_t Live_MPAcceptInvite; + + typedef int(__cdecl * Live_GetMapIndex_t)(const char* mapname); + extern Live_GetMapIndex_t Live_GetMapIndex; + + typedef char* (__cdecl * LoadModdableRawfile_t)(int a1, const char* filename); + extern LoadModdableRawfile_t LoadModdableRawfile; + + typedef int(__cdecl * PC_ReadToken_t)(source_t*, token_t*); + extern PC_ReadToken_t PC_ReadToken; + + typedef int(__cdecl * PC_ReadTokenHandle_t)(int handle, pc_token_s *pc_token); + extern PC_ReadTokenHandle_t PC_ReadTokenHandle; + + typedef void(__cdecl * PC_SourceError_t)(int, const char*, ...); + extern PC_SourceError_t PC_SourceError; + + typedef int(__cdecl * Party_GetMaxPlayers_t)(party_s* party); + extern Party_GetMaxPlayers_t Party_GetMaxPlayers; + + typedef int(__cdecl * PartyHost_CountMembers_t)(PartyData_s* party); + extern PartyHost_CountMembers_t PartyHost_CountMembers; + + typedef netadr_t *(__cdecl * PartyHost_GetMemberAddressBySlot_t)(int unk, void *party, const int slot); + extern PartyHost_GetMemberAddressBySlot_t PartyHost_GetMemberAddressBySlot; + + typedef const char *(__cdecl * PartyHost_GetMemberName_t)(PartyData_s* party, const int clientNum); + extern PartyHost_GetMemberName_t PartyHost_GetMemberName; + + typedef void(__cdecl * Playlist_ParsePlaylists_t)(const char* data); + extern Playlist_ParsePlaylists_t Playlist_ParsePlaylists; + + typedef Font* (__cdecl * R_RegisterFont_t)(const char* asset); + extern R_RegisterFont_t R_RegisterFont; + + typedef void(__cdecl * R_AddCmdDrawText_t)(const char *text, int maxChars, Font *font, float x, float y, float xScale, float yScale, float rotation, const float *color, int style); + extern R_AddCmdDrawText_t R_AddCmdDrawText; + + typedef void(_cdecl * R_AddCmdDrawStretchPic_t)(float x, float y, float w, float h, float xScale, float yScale, float xay, float yay, const float *color, Game::Material* material); + extern R_AddCmdDrawStretchPic_t R_AddCmdDrawStretchPic; + + typedef void* (__cdecl * R_AllocStaticIndexBuffer_t)(IDirect3DIndexBuffer9** store, int length); + extern R_AllocStaticIndexBuffer_t R_AllocStaticIndexBuffer; + + typedef bool(__cdecl * R_Cinematic_StartPlayback_Now_t)(); + extern R_Cinematic_StartPlayback_Now_t R_Cinematic_StartPlayback_Now; + + typedef void(__cdecl * R_LoadGraphicsAssets_t)(); + extern R_LoadGraphicsAssets_t R_LoadGraphicsAssets; + + typedef int(__cdecl * R_TextWidth_t)(const char* text, int maxlength, Font* font); + extern R_TextWidth_t R_TextWidth; + + typedef int(__cdecl * R_TextHeight_t)(Font* font); + extern R_TextHeight_t R_TextHeight; + + typedef void(__cdecl * R_FlushSun_t)(); + extern R_FlushSun_t R_FlushSun; + + typedef void(__cdecl * Scr_ShutdownAllocNode_t)(); + extern Scr_ShutdownAllocNode_t Scr_ShutdownAllocNode; + + typedef int(__cdecl * Scr_LoadGameType_t)(); + extern Scr_LoadGameType_t Scr_LoadGameType; + + typedef int(__cdecl * Scr_LoadScript_t)(const char*); + extern Scr_LoadScript_t Scr_LoadScript; + + typedef float(__cdecl * Scr_GetFloat_t)(int); + extern Scr_GetFloat_t Scr_GetFloat; + + typedef int(__cdecl * Scr_GetNumParam_t)(void); + extern Scr_GetNumParam_t Scr_GetNumParam; + + typedef int(__cdecl * Scr_GetFunctionHandle_t)(const char*, const char*); + extern Scr_GetFunctionHandle_t Scr_GetFunctionHandle; + + typedef int(__cdecl * Scr_ExecThread_t)(int, int); + extern Scr_ExecThread_t Scr_ExecThread; + + typedef int(__cdecl * Scr_FreeThread_t)(int); + extern Scr_FreeThread_t Scr_FreeThread; + + typedef script_t* (__cdecl * Script_Alloc_t)(int length); + extern Script_Alloc_t Script_Alloc; + + typedef void(__cdecl * Script_SetupTokens_t)(script_t* script, void* tokens); + extern Script_SetupTokens_t Script_SetupTokens; + + typedef int(__cdecl * Script_CleanString_t)(char* buffer); + extern Script_CleanString_t Script_CleanString; + + typedef char* (__cdecl * SE_Load_t)(const char* file, int Unk); + extern SE_Load_t SE_Load; + + typedef char* (__cdecl * SEH_StringEd_GetString_t)(const char* string); + extern SEH_StringEd_GetString_t SEH_StringEd_GetString; + + typedef char* (__cdecl * SL_ConvertToString_t)(unsigned short stringValue); + extern SL_ConvertToString_t SL_ConvertToString; + + typedef short(__cdecl * SL_GetString_t)(const char *str, unsigned int user); + extern SL_GetString_t SL_GetString; + + typedef void(__cdecl * SND_Init_t)(int a1, int a2, int a3); + extern SND_Init_t SND_Init; + + typedef void(__cdecl * SND_InitDriver_t)(); + extern SND_InitDriver_t SND_InitDriver; + + typedef void(__cdecl * SockadrToNetadr_t)(sockaddr *s, netadr_t *a); + extern SockadrToNetadr_t SockadrToNetadr; + + typedef void(__cdecl * Steam_JoinLobby_t)(SteamID, char); + extern Steam_JoinLobby_t Steam_JoinLobby; + + typedef int(__cdecl* SV_GameClientNum_Score_t)(int clientID); + extern SV_GameClientNum_Score_t SV_GameClientNum_Score; + + typedef void(__cdecl * SV_GameSendServerCommand_t)(int clientNum, /*svscmd_type*/int type, const char* text); + extern SV_GameSendServerCommand_t SV_GameSendServerCommand; + + typedef void(__cdecl * SV_Cmd_TokenizeString_t)(const char* string); + extern SV_Cmd_TokenizeString_t SV_Cmd_TokenizeString; + + typedef void(__cdecl * SV_Cmd_EndTokenizedString_t)(); + extern SV_Cmd_EndTokenizedString_t SV_Cmd_EndTokenizedString; + + typedef void(__cdecl * SV_DirectConnect_t)(netadr_t adr); + extern SV_DirectConnect_t SV_DirectConnect; + + typedef int(__cdecl * Sys_Error_t)(int, char *, ...); + extern Sys_Error_t Sys_Error; + + typedef void(__cdecl * Sys_FreeFileList_t)(char** list); + extern Sys_FreeFileList_t Sys_FreeFileList; + + typedef bool(__cdecl * Sys_IsDatabaseReady_t)(); + extern Sys_IsDatabaseReady_t Sys_IsDatabaseReady; + + typedef bool(__cdecl * Sys_IsDatabaseReady2_t)(); + extern Sys_IsDatabaseReady2_t Sys_IsDatabaseReady2; + + typedef bool(__cdecl * Sys_IsMainThread_t)(); + extern Sys_IsMainThread_t Sys_IsMainThread; + + typedef bool(__cdecl * Sys_IsRenderThread_t)(); + extern Sys_IsRenderThread_t Sys_IsRenderThread; + + typedef bool(__cdecl * Sys_IsServerThread_t)(); + extern Sys_IsServerThread_t Sys_IsServerThread; + + typedef bool(__cdecl * Sys_IsDatabaseThread_t)(); + extern Sys_IsDatabaseThread_t Sys_IsDatabaseThread; + + typedef char** (__cdecl * Sys_ListFiles_t)(const char *directory, const char *extension, const char *filter, int *numfiles, int wantsubs); + extern Sys_ListFiles_t Sys_ListFiles; + + typedef int(__cdecl * Sys_Milliseconds_t)(); + extern Sys_Milliseconds_t Sys_Milliseconds; + + typedef void(__cdecl * TeleportPlayer_t)(gentity_t* entity, float* pos, float* orientation); + extern TeleportPlayer_t TeleportPlayer; + + typedef bool(__cdecl * Sys_SendPacket_t)(netsrc_t sock, size_t len, const char *format, netadr_t adr); + extern Sys_SendPacket_t Sys_SendPacket; + + typedef void(__cdecl * Sys_ShowConsole_t)(); + extern Sys_ShowConsole_t Sys_ShowConsole; + + typedef void(__cdecl * UI_AddMenuList_t)(UiContext *dc, MenuList *menuList, int close); + extern UI_AddMenuList_t UI_AddMenuList; + + typedef char* (__cdecl * UI_CheckStringTranslation_t)(char*, char*); + extern UI_CheckStringTranslation_t UI_CheckStringTranslation; + + typedef MenuList *(__cdecl * UI_LoadMenus_t)(const char *menuFile, int imageTrack); + extern UI_LoadMenus_t UI_LoadMenus; + + typedef void(__cdecl * UI_UpdateArenas_t)(); + extern UI_UpdateArenas_t UI_UpdateArenas; + + typedef void(__cdecl * UI_DrawHandlePic_t)(/*ScreenPlacement*/void *scrPlace, float x, float y, float w, float h, int horzAlign, int vertAlign, const float *color, Material *material); + extern UI_DrawHandlePic_t UI_DrawHandlePic; + + typedef void* (__cdecl * UI_GetContext_t)(void*); + extern UI_GetContext_t UI_GetContext; + + typedef int(__cdecl * UI_TextWidth_t)(const char *text, int maxChars, Font *font, float scale); + extern UI_TextWidth_t UI_TextWidth; + + typedef void(__cdecl * UI_DrawText_t)(void* scrPlace, const char *text, int maxChars, Font *font, float x, float y, int horzAlign, int vertAlign, float scale, const float *color, int style); + extern UI_DrawText_t UI_DrawText; + + typedef const char * (__cdecl * Win_GetLanguage_t)(); + extern Win_GetLanguage_t Win_GetLanguage; + + typedef void (__cdecl * Vec3UnpackUnitVec_t)(PackedUnitVec, vec3_t *); + extern Vec3UnpackUnitVec_t Vec3UnpackUnitVec; + + extern XAssetHeader* DB_XAssetPool; + extern unsigned int* g_poolSize; + + extern DWORD* cmd_id; + extern DWORD* cmd_argc; + extern char*** cmd_argv; + + extern DWORD* cmd_id_sv; + extern DWORD* cmd_argc_sv; + extern char*** cmd_argv_sv; + + extern cmd_function_t** cmd_functions; + + extern int* svs_numclients; + extern client_t* svs_clients; + + extern source_t **sourceFiles; + extern keywordHash_t **menuParseKeywordHash; + + extern UiContext *uiContext; + + extern int* arenaCount; + extern mapArena_t* arenas; + + extern int* gameTypeCount; + extern gameTypeName_t* gameTypes; + + extern searchpath_t* fs_searchpaths; + + extern XBlock** g_streamBlocks; + extern int* g_streamPos; + extern int* g_streamPosIndex; + + extern bool* g_lobbyCreateInProgress; + extern party_t** partyIngame; + extern PartyData_s** partyData; + + extern int* numIP; + extern netIP_t* localIP; + + extern int* demoFile; + extern int* demoPlaying; + extern int* demoRecording; + extern int* serverMessageSequence; + + extern gentity_t* g_entities; + + extern netadr_t* connectedHost; + extern SOCKET* ip_socket; + + extern uint32_t* com_frameTime; + + extern SafeArea* safeArea; + + extern SpawnVar* spawnVars; + extern MapEnts** marMapEntsPtr; + + extern IDirect3D9** d3d9; + extern IDirect3DDevice9** dx_ptr; + + extern mapname_t* mapnames; + + extern char*** varXString; + extern TracerDef*** varTracerDefPtr; + extern XModel*** varXModelPtr; + extern XModel** varXModel; + extern PathData** varPathData; + extern const char** varConstChar; + extern Material*** varMaterialHandle; + extern FxEffectDef*** varFxEffectDefHandle; + extern PhysCollmap*** varPhysCollmapPtr; + extern PhysPreset*** varPhysPresetPtr; + extern Game::MaterialPass** varMaterialPass; + extern snd_alias_list_t*** varsnd_alias_list_name; + + extern FxElemField* s_elemFields; + + XAssetHeader ReallocateAssetPool(XAssetType type, unsigned int newSize); + void Menu_FreeItemMemory(Game::itemDef_t* item); + const char* TableLookup(StringTable* stringtable, int row, int column); + const char* UI_LocalizeMapName(const char* mapName); + const char* UI_LocalizeGameType(const char* gameType); + float UI_GetScoreboardLeft(void*); + + const char *DB_GetXAssetName(XAsset *asset); + XAssetType DB_GetXAssetNameType(const char* name); + bool DB_IsZoneLoaded(const char* zone); + + void FS_AddLocalizedGameDirectory(const char *path, const char *dir); + + void MessageBox(std::string message, std::string title); + + unsigned int R_HashString(const char* string); + void R_LoadSunThroughDvars(const char* mapname, sunflare_t* sun); + void R_SetSunFromDvars(sunflare_t* sun); + + void SV_KickClient(client_t* client, const char* reason); + void SV_KickClientError(client_t* client, std::string reason); + + void Scr_iPrintLn(int clientNum, std::string message); + void Scr_iPrintLnBold(int clientNum, std::string message); + + void IN_KeyUp(kbutton_t* button); + void IN_KeyDown(kbutton_t* button); + + int FS_FOpenFileReadCurrentThread(const char* file, int* fh); + + void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count); + void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len); + + char* Com_GetParseThreadInfo(); + void Com_SetParseNegativeNumbers(int parse); +} diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index e86b2b0c..e1cba0e8 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -1,3802 +1,3802 @@ -#define PROTOCOL 0x92 - -// This allows us to compile our structures in IDA, for easier reversing :3 -#ifdef __cplusplus -namespace Game -{ -#endif - - typedef float vec_t; - typedef vec_t vec2_t[2]; - typedef vec_t vec3_t[3]; - typedef vec_t vec4_t[4]; - - typedef enum - { - ASSET_TYPE_PHYSPRESET = 0, - ASSET_TYPE_PHYS_COLLMAP = 1, - ASSET_TYPE_XANIMPARTS = 2, - ASSET_TYPE_XMODELSURFS = 3, - ASSET_TYPE_XMODEL = 4, - ASSET_TYPE_MATERIAL = 5, - ASSET_TYPE_PIXELSHADER = 6, - ASSET_TYPE_VERTEXSHADER = 7, - ASSET_TYPE_VERTEXDECL = 8, - ASSET_TYPE_TECHNIQUE_SET = 9, - ASSET_TYPE_IMAGE = 10, - ASSET_TYPE_SOUND = 11, - ASSET_TYPE_SOUND_CURVE = 12, - ASSET_TYPE_LOADED_SOUND = 13, - ASSET_TYPE_CLIPMAP = 14, - ASSET_TYPE_CLIPMAP_PVS = 15, - ASSET_TYPE_COMWORLD = 16, - ASSET_TYPE_GAMEWORLD_SP = 17, - ASSET_TYPE_GAMEWORLD_MP = 18, - ASSET_TYPE_MAP_ENTS = 19, - ASSET_TYPE_FX_MAP = 20, - ASSET_TYPE_GFXWORLD = 21, - ASSET_TYPE_LIGHT_DEF = 22, - ASSET_TYPE_UI_MAP = 23, - ASSET_TYPE_FONT = 24, - ASSET_TYPE_MENULIST = 25, - ASSET_TYPE_MENU = 26, - ASSET_TYPE_LOCALIZE_ENTRY = 27, - ASSET_TYPE_WEAPON = 28, - ASSET_TYPE_SNDDRIVER_GLOBALS = 29, - ASSET_TYPE_FX = 30, - ASSET_TYPE_IMPACT_FX = 31, - ASSET_TYPE_AITYPE = 32, - ASSET_TYPE_MPTYPE = 33, - ASSET_TYPE_CHARACTER = 34, - ASSET_TYPE_XMODELALIAS = 35, - ASSET_TYPE_RAWFILE = 36, - ASSET_TYPE_STRINGTABLE = 37, - ASSET_TYPE_LEADERBOARDDEF = 38, - ASSET_TYPE_STRUCTUREDDATADEF = 39, - ASSET_TYPE_TRACER = 40, - ASSET_TYPE_VEHICLE = 41, - ASSET_TYPE_ADDON_MAP_ENTS = 42, - - ASSET_TYPE_COUNT, - ASSET_TYPE_INVALID = -1, - } XAssetType; - - typedef enum - { - DVAR_FLAG_NONE = 0x0, //no flags - DVAR_FLAG_SAVED = 0x1, //saves in config_mp.cfg for clients - DVAR_FLAG_LATCHED = 0x2, //no changing apart from initial value (although it might apply on a map reload, I think) - DVAR_FLAG_CHEAT = 0x4, //cheat - DVAR_FLAG_REPLICATED = 0x8, //on change, this is sent to all clients (if you are host) - DVAR_FLAG_UNKNOWN10 = 0x10, //unknown - DVAR_FLAG_UNKNOWN20 = 0x20, //unknown - DVAR_FLAG_UNKNOWN40 = 0x40, //unknown - DVAR_FLAG_UNKNOWN80 = 0x80, //unknown - DVAR_FLAG_USERCREATED = 0x100, //a 'set' type command created it - DVAR_FLAG_USERINFO = 0x200, //userinfo? - DVAR_FLAG_SERVERINFO = 0x400, //in the getstatus oob - DVAR_FLAG_WRITEPROTECTED = 0x800, //write protected - DVAR_FLAG_UNKNOWN1000 = 0x1000, //unknown - DVAR_FLAG_READONLY = 0x2000, //read only (same as 0x800?) - DVAR_FLAG_UNKNOWN4000 = 0x4000, //unknown - DVAR_FLAG_UNKNOWN8000 = 0x8000, //unknown - DVAR_FLAG_UNKNOWN10000 = 0x10000, //unknown - DVAR_FLAG_DEDISAVED = 0x1000000, //unknown - DVAR_FLAG_NONEXISTENT = 0xFFFFFFFF //no such dvar - } dvar_flag; - - typedef enum - { - DVAR_TYPE_BOOL = 0, - DVAR_TYPE_FLOAT = 1, - DVAR_TYPE_FLOAT_2 = 2, - DVAR_TYPE_FLOAT_3 = 3, - DVAR_TYPE_FLOAT_4 = 4, - DVAR_TYPE_INT = 5, - DVAR_TYPE_ENUM = 6, - DVAR_TYPE_STRING = 7, - DVAR_TYPE_COLOR = 8, - //DVAR_TYPE_INT64 = 9 only in Tx - } dvar_type; - // 67/72 bytes figured out - union dvar_value_t { - char* string; - int integer; - float value; - bool boolean; - float vec2[2]; - float vec3[3]; - float vec4[4]; - unsigned char color[4]; //to get float: multiply by 0.003921568859368563 - BaberZz - //__int64 integer64; only in Tx - }; - union dvar_maxmin_t { - int i; - float f; - }; - typedef struct dvar_t - { - //startbyte:endbyte - const char* name; //0:3 - const char* description; //4:7 - unsigned int flags; //8:11 - char type; //12:12 - bool modified; //13:15 - dvar_value_t current; //16:31 - dvar_value_t latched; //32:47 - dvar_value_t _default; //48:64 - dvar_maxmin_t min; //65:67 - dvar_maxmin_t max; //68:72 woooo - } dvar_t; - - typedef struct cmd_function_s - { - cmd_function_s *next; - const char *name; - const char *autoCompleteDir; - const char *autoCompleteExt; - void(__cdecl *function)(); - bool isKey; // Looks like this is true when the command is a key/button - } cmd_function_t; - -#pragma pack(push, 4) - struct kbutton_t - { - int down[2]; - unsigned int downtime; - unsigned int msec; - bool active; - bool wasPressed; - }; -#pragma pack(pop) - - struct SpeakerLevels - { - int speaker; - int numLevels; - float levels[2]; - }; - - struct ChannelMap - { - int entryCount; // how many entries are used - SpeakerLevels speakers[6]; - }; - - struct SpeakerMap - { - bool isDefault; - const char *name; - ChannelMap channelMaps[2][2]; - }; - - enum snd_alias_type_t : char - { - SAT_UNKNOWN = 0x0, - SAT_LOADED = 0x1, - SAT_STREAMED = 0x2 - }; - - struct StreamedSound - { - const char *dir; - const char *name; - }; - - struct SndCurve - { - const char *filename; - unsigned __int16 knotCount; - vec2_t knots[16]; - }; - - struct MssSound - { - char unknown1[8]; - int size; - char unknown2[22]; - char *data; // size = soundSize - }; - - struct LoadedSound - { - const char *name; - MssSound mssSound; - }; - - union SoundData - { - LoadedSound* loaded; - StreamedSound stream; - }; - - struct SoundFile // 0xC - { - snd_alias_type_t type; - bool exists; - SoundData data; - }; - - struct snd_alias_t - { - const char *name; - const char *subtitle; - const char *secondaryAliasName; - const char *chainAliasName; - const char *string4; - SoundFile *soundFile; - int sequence; - float volMin; - float volMax; - float pitchMin; - float pitchMax; - float distMin; - float distMax; - int flags; - float slavePercentage; - float probability; - float lfePercentage; - float centerPercentage; - int startDelay; - int pad; - SndCurve *volumeFalloffCurve; - float envelopMin; - float envelopMax; - float envelopPercentage; - SpeakerMap *speakerMap; - }; - - struct snd_alias_list_t - { - const char *name; - snd_alias_t *head; - int count; - }; - - typedef struct - { - const char *name; - int allocFlags; - int freeFlags; - } XZoneInfo; - - struct expression_s; - struct statement_s; - struct menuDef_t; - enum operationEnum; - - enum IWI_COMPRESSION - { - IWI_INVALID = 0x0, - IWI_ARGB = 0x1, - IWI_RGB8 = 0x2, - IWI_DXT1 = 0xB, - IWI_DXT3 = 0xC, - IWI_DXT5 = 0xD, - }; - - struct GfxImageFileHeader - { - char tag[3]; - char version; - int flags; - char format; - short dimensions[3]; - int fileSizeForPicmip[4]; - }; - - struct __declspec(align(4)) GfxImageLoadDef - { - char levelCount; - char flags; - __int16 dimensions[3]; - int format; - int resourceSize; - char data[1]; - }; - - enum MapType : char - { - MAPTYPE_NONE = 0x0, - MAPTYPE_INVALID1 = 0x1, - MAPTYPE_INVALID2 = 0x2, - MAPTYPE_2D = 0x3, - MAPTYPE_3D = 0x4, - MAPTYPE_CUBE = 0x5, - MAPTYPE_COUNT = 0x6, - }; - - struct GfxImage - { - union - { - GfxImageLoadDef* loadDef; -#ifdef __cplusplus - IDirect3DBaseTexture9 *basemap; - IDirect3DTexture9 *map; - IDirect3DVolumeTexture9 *volmap; - IDirect3DCubeTexture9 *cubemap; -#endif - }; - - MapType mapType; - char semantic; - char category; - char flags; - int cardMemory; - int dataLen1; - int dataLen2; - short height; - short width; - short depth; - char loaded; - char pad; - char* name; - }; - - struct WaterWritable - { - float floatTime; - }; - - struct complex_s - { - float real; - float imag; - }; - - struct water_t - { - WaterWritable writable; - complex_s *H0; - float *wTerm; - int M; - int N; - float Lx; - float Lz; - float gravity; - float windvel; - float winddir[2]; - float amplitude; - float codeConstant[4]; - GfxImage *image; - }; - -#define SEMANTIC_WATER_MAP 11 - - union MaterialTextureDefInfo - { - GfxImage *image; // MaterialTextureDef->semantic != SEMANTIC_WATER_MAP - water_t *water; // MaterialTextureDef->semantic == SEMANTIC_WATER_MAP - }; - - struct MaterialTextureDef - { - unsigned int nameHash; - char nameStart; - char nameEnd; - char sampleState; - char semantic; - MaterialTextureDefInfo info; - }; - - struct MaterialShaderArgument - { - short type; - short dest; - short paramID; - short more; - }; - - struct MaterialVertexDeclaration - { - const char* name; - int unknown; - char pad[28]; - /*IDirect3DVertexDeclaration9**/void* declarations[16]; - }; - - struct GfxPixelShaderLoadDef - { - char *cachedPart; - char *physicalPart; - unsigned __int16 cachedPartSize; - unsigned __int16 physicalPartSize; - }; - - struct MaterialPixelShader - { - const char* name; - GfxPixelShaderLoadDef loadDef; - }; - - struct GfxVertexShaderLoadDef - { - char *cachedPart; - char *physicalPart; - unsigned __int16 cachedPartSize; - unsigned __int16 physicalPartSize; - }; - - struct MaterialVertexShader - { - const char* name; - GfxVertexShaderLoadDef loadDef; - }; - - struct MaterialPass - { - MaterialVertexDeclaration* vertexDecl; - MaterialVertexShader* vertexShader; - MaterialPixelShader* pixelShader; - char argCount1; - char argCount2; - char argCount3; - char unk; - MaterialShaderArgument* argumentDef; - }; - - struct MaterialTechnique - { - char* name; - short pad2; - short numPasses; - MaterialPass passes[1]; - }; - - enum MaterialTechniqueType - { - TECHNIQUE_DEPTH_PREPASS = 0x0, - TECHNIQUE_BUILD_FLOAT_Z = 0x1, - TECHNIQUE_BUILD_SHADOWMAP_DEPTH = 0x2, - TECHNIQUE_BUILD_SHADOWMAP_COLOR = 0x3, - TECHNIQUE_UNLIT = 0x4, - TECHNIQUE_EMISSIVE = 0x5, - TECHNIQUE_EMISSIVE_DFOG = 0x6, - TECHNIQUE_EMISSIVE_SHADOW = 0x7, - TECHNIQUE_EMISSIVE_SHADOW_DFOG = 0x8, - TECHNIQUE_LIT_BEGIN = 0x9, - TECHNIQUE_LIT = 0x9, - TECHNIQUE_LIT_DFOG = 0xA, - TECHNIQUE_LIT_SUN = 0xB, - TECHNIQUE_LIT_SUN_DFOG = 0xC, - TECHNIQUE_LIT_SUN_SHADOW = 0xD, - TECHNIQUE_LIT_SUN_SHADOW_DFOG = 0xE, - TECHNIQUE_LIT_SPOT = 0xF, - TECHNIQUE_LIT_SPOT_DFOG = 0x10, - TECHNIQUE_LIT_SPOT_SHADOW = 0x11, - TECHNIQUE_LIT_SPOT_SHADOW_DFOG = 0x12, - TECHNIQUE_LIT_OMNI = 0x13, - TECHNIQUE_LIT_OMNI_DFOG = 0x14, - TECHNIQUE_LIT_OMNI_SHADOW = 0x15, - TECHNIQUE_LIT_OMNI_SHADOW_DFOG = 0x16, - TECHNIQUE_LIT_INSTANCED = 0x17, - TECHNIQUE_LIT_INSTANCED_DFOG = 0x18, - TECHNIQUE_LIT_INSTANCED_SUN = 0x19, - TECHNIQUE_LIT_INSTANCED_SUN_DFOG = 0x1A, - TECHNIQUE_LIT_INSTANCED_SUN_SHADOW = 0x1B, - TECHNIQUE_LIT_INSTANCED_SUN_SHADOW_DFOG = 0x1C, - TECHNIQUE_LIT_INSTANCED_SPOT = 0x1D, - TECHNIQUE_LIT_INSTANCED_SPOT_DFOG = 0x1E, - TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW = 0x1F, - TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW_DFOG = 0x20, - TECHNIQUE_LIT_INSTANCED_OMNI = 0x21, - TECHNIQUE_LIT_INSTANCED_OMNI_DFOG = 0x22, - TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW = 0x23, - TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW_DFOG = 0x24, - TECHNIQUE_LIT_END = 0x25, - TECHNIQUE_LIGHT_SPOT = 0x25, - TECHNIQUE_LIGHT_OMNI = 0x26, - TECHNIQUE_LIGHT_SPOT_SHADOW = 0x27, - TECHNIQUE_FAKELIGHT_NORMAL = 0x28, - TECHNIQUE_FAKELIGHT_VIEW = 0x29, - TECHNIQUE_SUNLIGHT_PREVIEW = 0x2A, - TECHNIQUE_CASE_TEXTURE = 0x2B, - TECHNIQUE_WIREFRAME_SOLID = 0x2C, - TECHNIQUE_WIREFRAME_SHADED = 0x2D, - TECHNIQUE_DEBUG_BUMPMAP = 0x2E, - TECHNIQUE_DEBUG_BUMPMAP_INSTANCED = 0x2F, - TECHNIQUE_COUNT = 0x30, - TECHNIQUE_TOTAL_COUNT = 0x31, - TECHNIQUE_NONE = 0x32, - }; - - struct MaterialTechniqueSet - { - const char* name; - char worldVertFormat; - bool hasBeenUploaded; - char unused[1]; - MaterialTechniqueSet* remappedTechniques; - MaterialTechnique* techniques[48]; - }; - - struct MaterialConstantDef - { - int nameHash; - char name[12]; - vec4_t literal; - }; - - struct GfxDrawSurfFields - { - __int64 _bf0; - }; - - union GfxDrawSurf - { - GfxDrawSurfFields fields; - unsigned __int64 packed; - }; - - struct GfxStateBits - { - unsigned int loadBits[2]; - }; - -#pragma pack(push, 4) - struct Material - { - const char *name; - char gameFlags; - char sortKey; - char textureAtlasRowCount; - char textureAtlasColumnCount; - GfxDrawSurf drawSurf; - int surfaceTypeBits; - unsigned __int16 hashIndex; - unsigned __int16 pad; - char stateBitsEntry[48]; - char textureCount; - char constantCount; - char stateBitsCount; - char stateFlags; - char cameraRegion; - MaterialTechniqueSet *techniqueSet; - MaterialTextureDef *textureTable; - MaterialConstantDef *constantTable; - GfxStateBits *stateBitTable; - }; -#pragma pack(pop) - - struct TracerDef - { - const char * name; - Material * material; - unsigned int drawInterval; - float speed; - float beamLength; - float beamWidth; - float screwRadius; - float screwDist; - float colors[5][4]; - }; - - struct keyname_t - { - const char *name; - int keynum; - }; - - struct ItemFloatExpressionEntry - { - int target; - const char *s1; - const char *s2; - }; - - // Taken from menudefinition.h - enum itemTextStyle - { - ITEM_TEXTSTYLE_NORMAL = 0, // normal text - ITEM_TEXTSTYLE_SHADOWED = 3, // drop shadow ( need a color for this ) - ITEM_TEXTSTYLE_SHADOWEDMORE = 6, // drop shadow ( need a color for this ) - ITEM_TEXTSTYLE_BORDERED = 7, // border (stroke) - ITEM_TEXTSTYLE_BORDEREDMORE = 8, // more border :P - ITEM_TEXTSTYLE_MONOSPACE = 128, - ITEM_TEXTSTYLE_MONOSPACESHADOWED = 132, - }; - -#define ITEM_TYPE_TEXT 0 // simple text -#define ITEM_TYPE_BUTTON 1 // button, basically text with a border -#define ITEM_TYPE_RADIOBUTTON 2 // toggle button, may be grouped -#define ITEM_TYPE_CHECKBOX 3 // check box -#define ITEM_TYPE_EDITFIELD 4 // editable text, associated with a dvar -#define ITEM_TYPE_COMBO 5 // drop down list -#define ITEM_TYPE_LISTBOX 6 // scrollable list -#define ITEM_TYPE_MODEL 7 // model -#define ITEM_TYPE_OWNERDRAW 8 // owner draw, name specs what it is -#define ITEM_TYPE_NUMERICFIELD 9 // editable text, associated with a dvar -#define ITEM_TYPE_SLIDER 10 // mouse speed, volume, etc. -#define ITEM_TYPE_YESNO 11 // yes no dvar setting -#define ITEM_TYPE_MULTI 12 // multiple list setting, enumerated -#define ITEM_TYPE_DVARENUM 13 // multiple list setting, enumerated from a dvar -#define ITEM_TYPE_BIND 14 // bind -#define ITEM_TYPE_MENUMODEL 15 // special menu model -#define ITEM_TYPE_VALIDFILEFIELD 16 // text must be valid for use in a dos filename -#define ITEM_TYPE_DECIMALFIELD 17 // editable text, associated with a dvar, which allows decimal input -#define ITEM_TYPE_UPREDITFIELD 18 // editable text, associated with a dvar -#define ITEM_TYPE_GAME_MESSAGE_WINDOW 19 // game message window -#define ITEM_TYPE_NEWSTICKER 20 // horizontal scrollbox -#define ITEM_TYPE_TEXTSCROLL 21 // vertical scrollbox -#define ITEM_TYPE_EMAILFIELD 22 -#define ITEM_TYPE_PASSWORDFIELD 23 - - struct MenuEventHandlerSet; - struct Statement_s; - - struct UIFunctionList - { - int totalFunctions; - Statement_s **functions; - }; - - struct StaticDvar - { - /*dvar_t*/ - void *dvar; - char *dvarName; - }; - - struct StaticDvarList - { - int numStaticDvars; - StaticDvar **staticDvars; - }; - - struct StringList - { - int totalStrings; - const char **strings; - }; - - struct ExpressionSupportingData - { - UIFunctionList uifunctions; - StaticDvarList staticDvarList; - StringList uiStrings; - }; - - enum expDataType : int - { - VAL_INT = 0x0, - VAL_FLOAT = 0x1, - VAL_STRING = 0x2, - VAL_FUNCTION = 0x3, - }; - - struct ExpressionString - { - const char *string; - }; - - union operandInternalDataUnion - { - int intVal; - float floatVal; - ExpressionString stringVal; - Statement_s *function; - }; - - struct Operand - { - expDataType dataType; - operandInternalDataUnion internals; - }; - - union entryInternalData - { - //operationEnum op; - Operand operand; - }; - - /* expressionEntry->type */ -#define OPERATOR 0 -#define OPERAND 1 - - struct expressionEntry // 0xC - { - int type; - entryInternalData data; - }; - - struct Statement_s // 0x18 - { - int numEntries; - expressionEntry *entries; - ExpressionSupportingData *supportingData; - char unknown[0xC]; // ? - }; - - struct SetLocalVarData - { - const char *localVarName; - Statement_s *expression; - }; - - struct ConditionalScript - { - MenuEventHandlerSet *eventHandlerSet; - Statement_s *eventExpression; // loads this first - }; - - union EventData - { - const char *unconditionalScript; - ConditionalScript *conditionalScript; - MenuEventHandlerSet *elseScript; - SetLocalVarData *setLocalVarData; - }; - - enum EventType - { - EVENT_UNCONDITIONAL = 0x0, - EVENT_IF = 0x1, - EVENT_ELSE = 0x2, - EVENT_SET_LOCAL_VAR_BOOL = 0x3, - EVENT_SET_LOCAL_VAR_INT = 0x4, - EVENT_SET_LOCAL_VAR_FLOAT = 0x5, - EVENT_SET_LOCAL_VAR_STRING = 0x6, - EVENT_COUNT = 0x7, - }; - - struct MenuEventHandler - { - EventData eventData; - EventType eventType; - }; - - struct MenuEventHandlerSet - { - int eventHandlerCount; - MenuEventHandler **eventHandlers; - }; - - struct ItemKeyHandler - { - int key; - MenuEventHandlerSet *action; - ItemKeyHandler *next; - }; - -#pragma pack(push, 4) - struct rectDef_s - { - float x; - float y; - float w; - float h; - char horzAlign; - char vertAlign; - }; -#pragma pack(pop) - - /* windowDef_t->dynamicFlags */ - // 0x1 -#define WINDOWDYNAMIC_HASFOCUS 0x00000002 -#define WINDOWDYNAMIC_VISIBLE 0x00000004 -#define WINDOWDYNAMIC_FADEOUT 0x00000010 -#define WINDOWDYNAMIC_FADEIN 0x00000020 - // 0x40 - // 0x80 -#define WINDOWDYNAMIC_CLOSED 0x00000800 - // 0x2000 -#define WINDOWDYNAMIC_BACKCOLOR 0x00008000 -#define WINDOWDYNAMIC_FORECOLOR 0x00010000 - - /* windowDef_t->staticFlags */ -#define WINDOWSTATIC_DECORATION 0x00100000 -#define WINDOWSTATIC_HORIZONTALSCROLL 0x00200000 -#define WINDOWSTATIC_SCREENSPACE 0x00400000 -#define WINDOWSTATIC_AUTOWRAPPED 0x00800000 -#define WINDOWSTATIC_POPUP 0x01000000 -#define WINDOWSTATIC_OUTOFBOUNDSCLICK 0x02000000 -#define WINDOWSTATIC_LEGACYSPLITSCREENSCALE 0x04000000 -#define WINDOWSTATIC_HIDDENDURINGFLASH 0x10000000 -#define WINDOWSTATIC_HIDDENDURINGSCOPE 0x20000000 -#define WINDOWSTATIC_HIDDENDURINGUI 0x40000000 -#define WINDOWSTATIC_TEXTONLYFOCUS 0x80000000 - - struct windowDef_t // 0xA4 - { - const char *name; // 0x00 - rectDef_s rect; - rectDef_s rectClient; - char *group; // 0x2C - int style; // 0x30 - int border; // 0x34 - int ownerDraw; // 0x38 - int ownerDrawFlags; // 0x3C - float borderSize; // 0x40 - int staticFlags; // 0x44 - int dynamicFlags; // 0x48 - int nextTime; // 0x4C - float foreColor[4]; // 0x50 - float backColor[4]; // 0x60 - float borderColor[4];// 0x70 - float outlineColor[4];// 0x80 - float disableColor[4];// 0x90 - Material *background; // 0xA0 - }; - - enum ItemFloatExpressionTarget - { - ITEM_FLOATEXP_TGT_RECT_X = 0x0, - ITEM_FLOATEXP_TGT_RECT_Y = 0x1, - ITEM_FLOATEXP_TGT_RECT_W = 0x2, - ITEM_FLOATEXP_TGT_RECT_H = 0x3, - ITEM_FLOATEXP_TGT_FORECOLOR_R = 0x4, - ITEM_FLOATEXP_TGT_FORECOLOR_G = 0x5, - ITEM_FLOATEXP_TGT_FORECOLOR_B = 0x6, - ITEM_FLOATEXP_TGT_FORECOLOR_RGB = 0x7, - ITEM_FLOATEXP_TGT_FORECOLOR_A = 0x8, - ITEM_FLOATEXP_TGT_GLOWCOLOR_R = 0x9, - ITEM_FLOATEXP_TGT_GLOWCOLOR_G = 0xA, - ITEM_FLOATEXP_TGT_GLOWCOLOR_B = 0xB, - ITEM_FLOATEXP_TGT_GLOWCOLOR_RGB = 0xC, - ITEM_FLOATEXP_TGT_GLOWCOLOR_A = 0xD, - ITEM_FLOATEXP_TGT_BACKCOLOR_R = 0xE, - ITEM_FLOATEXP_TGT_BACKCOLOR_G = 0xF, - ITEM_FLOATEXP_TGT_BACKCOLOR_B = 0x10, - ITEM_FLOATEXP_TGT_BACKCOLOR_RGB = 0x11, - ITEM_FLOATEXP_TGT_BACKCOLOR_A = 0x12, - ITEM_FLOATEXP_TGT__COUNT = 0x13, - }; - - struct ItemFloatExpression - { - ItemFloatExpressionTarget target; - Statement_s *expression; - }; - - struct editFieldDef_s - { - float minVal; - float maxVal; - float defVal; - float range; - int maxChars; - int maxCharsGotoNext; - int maxPaintChars; - int paintOffset; - }; - - struct multiDef_s // 0x188 - { - const char *dvarList[32]; - const char *dvarStr[32]; - float dvarValue[32]; - int count; - int strDef; - }; - - struct columnInfo_s - { - int xpos; - int width; - int maxChars; - int alignment; - }; - - struct listBoxDef_s // 0x144 - { - // somethings not right here - int startPos[2]; - int endPos[2]; - float elementWidth; - float elementHeight; - int elementStyle; - int numColumns; - columnInfo_s columnInfo[16]; - MenuEventHandlerSet *doubleClick; // 0xC8 - int notselectable; - int noscrollbars; - int usepaging; - float selectBorder[4]; - Material *selectIcon; - }; - - struct newsTickerDef_s - { - int feedId; - int speed; - int spacing; - }; - - struct textScrollDef_s - { - int startTime; - }; - - union itemDefData_t - { - listBoxDef_s *listBox; - editFieldDef_s *editField; - newsTickerDef_s *ticker; - multiDef_s *multiDef; - const char *enumDvarName; - textScrollDef_s *scroll; - void *data; - }; - - struct itemDef_t - { - windowDef_t window; - rectDef_s textRect; - int type; - int dataType; - int alignment; - int fontEnum; - int textAlignMode; - float textAlignX; - float textAlignY; - float textScale; - int textStyle; - int gameMsgWindowIndex; - int gameMsgWindowMode; - const char *text; - int textSaveGameInfo; - int parent; - MenuEventHandlerSet *mouseEnterText; - MenuEventHandlerSet *mouseExitText; - MenuEventHandlerSet *mouseEnter; - MenuEventHandlerSet *mouseExit; - MenuEventHandlerSet *action; - MenuEventHandlerSet *accept; - MenuEventHandlerSet *onFocus; - MenuEventHandlerSet *leaveFocus; - const char *dvar; - const char *dvarTest; - ItemKeyHandler *onKey; - const char *enableDvar; - const char *localVar; - int dvarFlags; - const char *focusSound; - float special; - int cursorPos; - itemDefData_t typeData; - int imageTrack; - int floatExpressionCount; - ItemFloatExpression *floatExpressions; - Statement_s *visibleExp; - Statement_s *disabledExp; - Statement_s *textExp; - Statement_s *materialExp; - float glowColor[4]; - bool decayActive; - int fxBirthTime; - int fxLetterTime; - int fxDecayStartTime; - int fxDecayDuration; - int lastSoundPlayedTime; - }; - - struct menuTransition // 0x18 - { - int transitionType; - int startTime; - float startVal; - float endVal; - float time; - int endTriggerType; - }; - - struct menuDef_t - { - windowDef_t window; - int font; - int fullscreen; - int itemCount; - int fontIndex; - int cursorItems; - int fadeCycle; - float fadeClamp; - float fadeAmount; - float fadeInAmount; - float blurRadius; - MenuEventHandlerSet *onOpen; - MenuEventHandlerSet *onRequestClose; - MenuEventHandlerSet *onClose; - MenuEventHandlerSet *onEsc; - ItemKeyHandler *onKey; - Statement_s *visibleExp; - const char *allowedBinding; - const char *soundLoop; - int imageTrack; - float focusColor[4]; - Statement_s *rectXExp; - Statement_s *rectYExp; - Statement_s *rectHExp; - Statement_s *rectWExp; - Statement_s *openSoundExp; - Statement_s *closeSoundExp; - itemDef_t **items; - char unknown[112]; - ExpressionSupportingData *expressionData; - }; - - struct MenuList - { - char *name; - int menuCount; - menuDef_t **menus; - }; - -#define FS_SEEK_CUR 0 -#define FS_SEEK_END 1 -#define FS_SEEK_SET 2 - - enum FsListBehavior_e - { - FS_LIST_PURE_ONLY = 0x0, - FS_LIST_ALL = 0x1, - }; - - typedef enum - { - NA_BOT, - NA_BAD, // an address lookup failed - NA_LOOPBACK, - NA_BROADCAST, - NA_IP, - NA_IP6, // custom type - } netadrtype_t; - - typedef enum - { - NS_CLIENT, - NS_SERVER - } netsrc_t; - - typedef union - { - unsigned char bytes[4]; - DWORD full; - } netIP_t; - - typedef struct - { - netadrtype_t type; - netIP_t ip; - unsigned short port; - unsigned char ipx[8]; - } netadr_t; - - typedef struct - { - int overflowed; - int readOnly; - char *data; - char *splitData; - int maxsize; - int cursize; - int splitSize; - int readcount; - int bit; - int lastEntityRef; - } msg_t; - - enum playerFlag - { - PLAYER_FLAG_NOCLIP = 1 << 0, - PLAYER_FLAG_UFO = 1 << 1, - PLAYER_FLAG_FROZEN = 1 << 2, - }; - - typedef struct gclient_s - { - unsigned char pad[12764]; - unsigned int team; - char pad2[436]; - int flags; - char pad3[724]; - } gclient_t; - - typedef struct gentity_s - { - unsigned char pad[312]; // 0 - float origin[3]; // 312 - float angles[3]; // 324 - char pad2[8]; - gclient_t* client; // 344 - unsigned char pad3[28]; - short classname; - short pad4; - unsigned char pad5[248]; - } gentity_t; - -#pragma pack(push, 1) - typedef struct client_s - { - // 0 - int state; - // 4 - char pad[36]; - // 40 - netadr_t addr; - // 60 - char pad1[1568]; - // 1628 - char connectInfoString[1024]; - // 2652 - char pad2[133192]; - // 135844 - char name[16]; - // 135860 - char pad3[12]; - // 135872 - int snapNum; - // 135876 - int pad4; - // 135880 - short ping; - // 135882 - //char pad5[142390]; - char pad5[133158]; - // 269040 - int isBot; - // 269044 - char pad6[9228]; - // 278272 - unsigned __int64 steamid; - // 278280 - char pad7[403592]; - } client_t; -#pragma pack(pop) - - // Q3TA precompiler code - - //undef if binary numbers of the form 0b... or 0B... are not allowed -#define BINARYNUMBERS - //undef if not using the token.intvalue and token.floatvalue -#define NUMBERVALUE - //use dollar sign also as punctuation -#define DOLLAR - - //maximum token length -#define MAX_TOKEN 1024 - - //punctuation - typedef struct punctuation_s - { - char *p; //punctuation character(s) - int n; //punctuation indication - struct punctuation_s *next; //next punctuation - } punctuation_t; - - //token - typedef struct token_s - { - char string[MAX_TOKEN]; //available token - int type; //last read token type - int subtype; //last read token sub type -#ifdef NUMBERVALUE - unsigned long int intvalue; //integer value - long double floatvalue; //floating point value -#endif //NUMBERVALUE - char *whitespace_p; //start of white space before token - char *endwhitespace_p; //start of white space before token - int line; //line the token was on - int linescrossed; //lines crossed in white space - struct token_s *next; //next token in chain - } token_t; - - //script file - typedef struct script_s - { - char filename[64]; //file name of the script - char *buffer; //buffer containing the script - char *script_p; //current pointer in the script - char *end_p; //pointer to the end of the script - char *lastscript_p; //script pointer before reading token - char *whitespace_p; //begin of the white space - char *endwhitespace_p; //end of the white space - int length; //length of the script in bytes - int line; //current line in script - int lastline; //line before reading token - int tokenavailable; //set by UnreadLastToken - int flags; //several script flags - punctuation_t *punctuations; //the punctuations used in the script - punctuation_t **punctuationtable; - token_t token; //available token - struct script_s *next; //next script in a chain - } script_t; - - //macro definitions - typedef struct define_s - { - char *name; //define name - int flags; //define flags - int builtin; // > 0 if builtin define - int numparms; //number of define parameters - token_t *parms; //define parameters - token_t *tokens; //macro tokens (possibly containing parm tokens) - struct define_s *next; //next defined macro in a list - struct define_s *hashnext; //next define in the hash chain - } define_t; - - //indents - //used for conditional compilation directives: - //#if, #else, #elif, #ifdef, #ifndef - typedef struct indent_s - { - int type; //indent type - int skip; //true if skipping current indent - script_t *script; //script the indent was in - struct indent_s *next; //next indent on the indent stack - } indent_t; - - //source file - typedef struct source_s - { - char filename[64]; //file name of the script - char includepath[64]; //path to include files - punctuation_t *punctuations; //punctuations to use - script_t *scriptstack; //stack with scripts of the source - token_t *tokens; //tokens to read first - define_t *defines; //list with macro definitions - define_t **definehash; //hash chain with defines - indent_t *indentstack; //stack with indents - int skip; // > 0 if skipping conditional code - token_t token; //last read token - } source_t; - -#define MAX_TOKENLENGTH 1024 - - typedef struct pc_token_s - { - int type; - int subtype; - int intvalue; - float floatvalue; - char string[MAX_TOKENLENGTH]; - } pc_token_t; - - //token types -#define TT_STRING 1 // string -#define TT_LITERAL 2 // literal -#define TT_NUMBER 3 // number -#define TT_NAME 4 // name -#define TT_PUNCTUATION 5 // punctuation - -#define KEYWORDHASH_SIZE 512 - - typedef struct keywordHash_s - { - char *keyword; - bool(*func)(menuDef_t *item, int handle); - //struct keywordHash_s *next; - } keywordHash_t; - - enum UILocalVarType - { - UILOCALVAR_INT = 0x0, - UILOCALVAR_FLOAT = 0x1, - UILOCALVAR_STRING = 0x2, - }; - - struct UILocalVar - { - UILocalVarType type; - const char *name; - union - { - int integer; - float value; - const char *string; - }; - }; - - struct UILocalVarContext - { - UILocalVar table[256]; - }; - - struct UiContext - { -// int localClientNum; -// float bias; -// int realTime; -// int frameTime; -// int cursorx; -// int cursory; -// int debug; -// int screenWidth; -// int screenHeight; -// float screenAspect; -// float FPS; -// float blurRadiusOut; - char pad[56]; - menuDef_t *menus[512]; - char pad2[512]; - int menuCount; - // Unsure if below is correct - menuDef_t *menuStack[16]; - int openMenuCount; - UILocalVarContext localVars; - }; - - struct LocalizeEntry - { - const char* value; - const char* name; - }; - - struct Bounds - { - vec3_t midPoint; - vec3_t halfSize; - }; - - struct TriggerModel - { - int contents; - unsigned short hullCount; - unsigned short firstHull; - }; - - struct TriggerHull - { - Bounds bounds; - int contents; - unsigned short slabCount; - unsigned short firstSlab; - }; - - struct TriggerSlab - { - vec3_t dir; - float midPoint; - float halfSize; - }; - - struct MapTriggers - { - int modelCount; - TriggerModel* models; // sizeof 8 - int hullCount; - TriggerHull* hulls; // sizeof 32 - int slabCount; - TriggerSlab* slabs; // sizeof 20 - }; - - struct Stage - { - char * stageName; - float offset[3]; - int flags; - }; - - struct MapEnts - { - const char *name; - char *entityString; - int numEntityChars; - MapTriggers trigger; - Stage * stages; - char stageCount; - }; - - struct StringTableCell - { - const char *string; - int hash; - }; - - struct StringTable - { - const char *name; - int columnCount; - int rowCount; - StringTableCell *values; - }; - - struct RawFile - { - const char* name; - int sizeCompressed; - int sizeUnCompressed; - char * compressedData; - }; - - struct FontEntry - { - unsigned short character; - unsigned char padLeft; - unsigned char padTop; - unsigned char padRight; - unsigned char width; - unsigned char height; - unsigned char const0; - float uvLeft; - float uvTop; - float uvRight; - float uvBottom; - }; - - typedef struct Font_s - { - char* name; - int size; - int entries; - Material* image; - Material* glowImage; - FontEntry* characters; - } Font; - - typedef enum - { - STRUCTURED_DATA_INT = 0, - STRUCTURED_DATA_BYTE = 1, - STRUCTURED_DATA_BOOL = 2, - STRUCTURED_DATA_STRING = 3, - STRUCTURED_DATA_ENUM = 4, - STRUCTURED_DATA_STRUCT = 5, - STRUCTURED_DATA_INDEXEDARR = 6, - STRUCTURED_DATA_ENUMARR = 7, - STRUCTURED_DATA_FLOAT = 8, - STRUCTURED_DATA_SHORT = 9 - } StructuredDataType; - - typedef struct - { - StructuredDataType type; - union - { - int index; - }; - int offset; - } StructuredDataItem; - - typedef struct - { - const char* name; - StructuredDataItem item; - } StructuredDataStructProperty; - - typedef struct - { - int numProperties; - StructuredDataStructProperty* property; - int unknown1; - int unknown2; - } StructuredDataStruct; - - typedef struct - { - int enumIndex; - StructuredDataItem item; - } StructuredDataEnumedArray; - - typedef struct - { - const char* key; - int index; - } StructuredDataEnumEntry; - - typedef struct - { - int numIndices; - int unknown; - StructuredDataEnumEntry* indices; - } StructuredDataEnum; - - typedef struct - { - int numItems; - StructuredDataItem item; - } StructuredDataIndexedArray; - - typedef struct - { - int version; - unsigned int hash; - int numEnums; - StructuredDataEnum* enums; - int numStructs; - StructuredDataStruct* structs; - int numIndexedArrays; - StructuredDataIndexedArray* indexedArrays; - int numEnumArrays; - StructuredDataEnumedArray* enumArrays; - StructuredDataItem rootItem; - } StructuredDataDef; - - typedef struct - { - const char* name; - int count; - StructuredDataDef* data; - } StructuredDataDefSet; - - typedef struct - { - StructuredDataDef* data; - StructuredDataItem* item; - int offset; - int error; - } structuredDataFindState_t; - - struct XSurfaceCollisionLeaf - { - unsigned __int16 triangleBeginIndex; - }; - - struct XSurfaceCollisionAabb - { - unsigned __int16 mins[3]; - unsigned __int16 maxs[3]; - }; - - struct XSurfaceCollisionNode - { - XSurfaceCollisionAabb aabb; - unsigned __int16 childBeginIndex; - unsigned __int16 childCount; - }; - - struct XSurfaceCollisionTree - { - float trans[3]; - float scale[3]; - unsigned int nodeCount; - XSurfaceCollisionNode *nodes; - unsigned int leafCount; - XSurfaceCollisionLeaf *leafs; - }; - - struct XRigidVertList - { - unsigned __int16 boneOffset; - unsigned __int16 vertCount; - unsigned __int16 triOffset; - unsigned __int16 triCount; - XSurfaceCollisionTree *collisionTree; - }; - - union PackedTexCoords - { - unsigned int packed; - }; - - union GfxColor - { - unsigned int packed; - char array[4]; - unsigned char uArray[4]; - }; - - union PackedUnitVec - { - unsigned int packed; - }; - - struct GfxPackedVertex - { - float xyz[3]; - float binormalSign; - GfxColor color; - PackedTexCoords texCoord; - PackedUnitVec normal; - PackedUnitVec tangent; - }; - - struct XSurfaceVertexInfo - { - __int16 vertCount[4]; - unsigned __int16 *vertsBlend; - }; - - struct XSurface - { - char tileMode; - bool deformed; - unsigned __int16 vertCount; - unsigned __int16 triCount; - char zoneHandle; - unsigned __int16 baseTriIndex; - unsigned __int16 baseVertIndex; - unsigned __int16 *triIndices; - XSurfaceVertexInfo vertInfo; - GfxPackedVertex *verts0; - unsigned int vertListCount; - XRigidVertList *vertList; - int partBits[6]; - }; - - struct XModelSurfs - { - const char* name; - XSurface* surfaces; - int numSurfaces; - int partBits[6]; - }; - - struct XModelLodInfo - { - float dist; - unsigned __int16 numsurfs; - unsigned __int16 surfIndex; - XModelSurfs *modelSurfs; - int partBits[6]; - XSurface *surfs; - char lod; - char smcBaseIndexPlusOne; - char smcSubIndexMask; - char smcBucket; - }; - - struct cplane_t - { - float normal[3]; - float dist; - char type; - char signbits; - char pad[2]; - }; - - struct cbrushside_t - { - cplane_t *plane; - unsigned __int16 materialNum; - char firstAdjacentSideOffset; - char edgeCount; - }; - - struct cbrushWrapper_t - { - short count; - cbrushside_t* brushSide; - char * brushEdge; - char pad[24]; - }; - -#pragma pack(push, 4) - struct BrushWrapper - { - float mins[3]; - float maxs[3]; - cbrushWrapper_t brush; - int totalEdgeCount; - cplane_t *planes; - }; -#pragma pack(pop) - - struct PhysGeomInfo - { - BrushWrapper *brush; - int type; - float orientation[3][3]; - float offset[3]; - float halfLengths[3]; - }; - - struct PhysMass - { - float centerOfMass[3]; - float momentsOfInertia[3]; - float productsOfInertia[3]; - }; - - struct PhysCollmap - { - const char *name; - unsigned int count; - PhysGeomInfo *geoms; - char unknown[0x18]; - PhysMass mass; - }; - - struct DObjAnimMat - { - float quat[4]; - float trans[3]; - float transWeight; - }; - - struct XModelCollTri_s - { - float plane[4]; - float svec[4]; - float tvec[4]; - }; - - struct XModelCollSurf_s - { - XModelCollTri_s *collTris; - int numCollTris; - Bounds bounds; - int boneIdx; - int contents; - int surfFlags; - }; - - struct PhysPreset - { - const char *name; - int type; - float mass; - float bounce; - float friction; - float bulletForceScale; - float explosiveForceScale; - const char *sndAliasPrefix; - float piecesSpreadFraction; - float piecesUpwardVelocity; - bool tempDefaultToCylinder; - }; - - struct XBoneInfo - { - float offset[3]; - float bounds[3]; - float radiusSquared; - }; - - struct XModel - { - const char *name; - char numBones; - char numRootBones; - char numsurfs; - char lodRampType; - float scale; - unsigned int noScalePartBits[6]; - unsigned __int16 *boneNames; - char *parentList; - __int16 *quats; - float *trans; - char *partClassification; - DObjAnimMat *baseMat; - Material **materialHandles; - XModelLodInfo lodInfo[4]; - char maxLoadedLod; - char numLods; - char collLod; - char flags; - XModelCollSurf_s *collSurfs; - int numCollSurfs; - int contents; - XBoneInfo *boneInfo; - float radius; - Bounds bounds; - int memUsage; - bool bad; - PhysPreset *physPreset; - PhysCollmap *physCollmap; - }; - - struct CModelAllocData - { - void* mainArray; - void* vertexBuffer; - void* indexBuffer; - }; - - struct CModelSectionHeader - { - int size; - int offset; - int fixupStart; - int fixupCount; - void* buffer; - }; - - enum CModelSection - { - SECTION_MAIN = 0, - SECTION_INDEX = 1, - SECTION_VERTEX = 2, - SECTION_FIXUP = 3, - }; - - struct CModelHeader - { - int version; - unsigned int signature; - CModelSectionHeader sectionHeader[4]; - }; - - struct DSkelPartBits - { - int anim[4]; - int control[4]; - int skel[4]; - }; - - struct DSkel - { - DSkelPartBits partBits; - int timeStamp; - DObjAnimMat *mat; - }; - -#pragma pack(push, 2) - struct DObj - { - /*XAnimTree_s*/void *tree; - unsigned __int16 duplicateParts; - unsigned __int16 entnum; - char duplicatePartsSize; - char numModels; - char numBones; - char pad; - unsigned int ignoreCollision; - volatile int locked; - DSkel skel; - float radius; - int hidePartBits[4]; - char pad2[56]; - XModel **models; - }; -#pragma pack(pop) - - union XAnimDynamicIndices - { - char _1[1]; - unsigned __int16 _2[1]; - }; - - union XAnimDynamicFrames - { - char(*_1)[3]; - unsigned __int16(*_2)[3]; - }; - - struct XAnimNotifyInfo - { - unsigned __int16 name; - float time; - }; - - union XAnimIndices - { - char* _1; - unsigned short *_2; - void* data; - }; - - struct XAnimPartTransFrames - { - float mins[3]; - float size[3]; - XAnimDynamicFrames frames; - XAnimDynamicIndices indices; - }; - - union XAnimPartTransData - { - XAnimPartTransFrames frames; - float frame0[3]; - }; - - struct XAnimPartTrans - { - unsigned __int16 size; - char smallTrans; - XAnimPartTransData u; - }; - - struct XAnimDeltaPartQuatDataFrames2 - { - __int16 *frames; - char indices[1]; - }; - - union XAnimDeltaPartQuatData2 - { - XAnimDeltaPartQuatDataFrames2 frames; - __int16 frame0[2]; - }; - - struct XAnimDeltaPartQuat2 - { - unsigned __int16 size; - XAnimDeltaPartQuatData2 u; - }; - - struct XAnimDeltaPartQuatDataFrames - { - __int16 *frames; - char indices[1]; - }; - - union XAnimDeltaPartQuatData - { - XAnimDeltaPartQuatDataFrames frames; - __int16 frame0[4]; - }; - - struct XAnimDeltaPartQuat - { - unsigned __int16 size; - XAnimDeltaPartQuatData u; - }; - - struct XAnimDeltaPart - { - XAnimPartTrans *trans; - XAnimDeltaPartQuat2 *quat2; - XAnimDeltaPartQuat *quat; - }; - - enum XAnimPartType - { - PART_TYPE_NO_QUAT = 0x0, - PART_TYPE_HALF_QUAT = 0x1, - PART_TYPE_FULL_QUAT = 0x2, - PART_TYPE_HALF_QUAT_NO_SIZE = 0x3, - PART_TYPE_FULL_QUAT_NO_SIZE = 0x4, - PART_TYPE_SMALL_TRANS = 0x5, - PART_TYPE_TRANS = 0x6, - PART_TYPE_TRANS_NO_SIZE = 0x7, - PART_TYPE_NO_TRANS = 0x8, - PART_TYPE_ALL = 0x9, - }; - - enum XAnimFlags - { - XANIM_LOOP_SYNC_TIME = 0x1, - XANIM_NONLOOP_SYNC_TIME = 0x2, - XANIM_SYNC_ROOT = 0x4, - XANIM_COMPLETE = 0x8, - XANIM_ADDITIVE = 0x10, - XANIM_CLIENT = 0x20, - XANIM_SEPARATE = 0x40, - XANIM_FORCELOAD = 0x80, - XANIM_PROPOGATE_FLAGS = 0x63, - }; - - struct XAnimParts - { - const char * name; // 0 - unsigned short dataByteCount; // 4 - unsigned short dataShortCount; // 6 - unsigned short dataIntCount; // 8 - unsigned short randomDataByteCount; // 10 - 0xA - unsigned short randomDataIntCount;// 12 - 0xC - unsigned short framecount; // 14 - 0xE - char bLoop; // 16 - char boneCount[10]; // 17 - char notetrackCount; // 27 - bool pad1; // 28 - bool bDelta; // 29 - char assetType; // 30 - char pad2; // 31 - int randomDataShortCount; // 32 - 0x20 - int indexcount; // 36 - 0x24 - float framerate; // 40 - 0x28 - float frequency; // 44 - 0x2C - short * tagnames; // 48 - 0x30 - char *dataByte;// 52 - 0x34 - short *dataShort; // 56 - 0x38 - int *dataInt; // 60 - 0x3C - short *randomDataShort; // 64 - 0x40 - char *randomDataByte; // 68 - 0x44 - int *randomDataInt; // 72 - 0x48 - XAnimIndices indices; // 76 - 0x4C - XAnimNotifyInfo* notetracks; // 80 - 0x50 - XAnimDeltaPart * delta; // 84 - 0x54 - // 88 - 0x58 - }; - -/* FxEffectDef::flags */ -#define FX_ELEM_LOOPING 0x1 -#define FX_ELEM_USE_RAND_COLOR 0x2 -#define FX_ELEM_USE_RAND_ALPHA 0x4 -#define FX_ELEM_USE_RAND_SIZE0 0x8 -#define FX_ELEM_USE_RAND_SIZE1 0x10 -#define FX_ELEM_USE_RAND_SCALE 0x20 -#define FX_ELEM_USE_RAND_ROT_DELTA 0x40 -#define FX_ELEM_MOD_COLOR_BY_ALPHA 0x80 -#define FX_ELEM_USE_RAND_VEL0 0x100 -#define FX_ELEM_USE_RAND_VEL1 0x200 -#define FX_ELEM_USE_BACK_COMPAT_VEL 0x400 -#define FX_ELEM_ABS_VEL0 0x800 -#define FX_ELEM_ABS_VEL1 0x1000 -#define FX_ELEM_PLAY_ON_TOUCH 0x2000 -#define FX_ELEM_PLAY_ON_DEATH 0x4000 -#define FX_ELEM_PLAY_ON_RUN 0x8000 -#define FX_ELEM_BOUNDING_SPHERE 0x10000 -#define FX_ELEM_USE_ITEM_CLIP 0x20000 -#define FX_ELEM_DECAL_FADE_IN 0x40000 -#define FX_ELEM_DISABLED 0x80000000 - -/* FxElemDef::flags */ -#define FX_ELEM_SPAWN_RELATIVE_TO_EFFECT 0x2 -#define FX_ELEM_SPAWN_FRUSTUM_CULL 0x4 -#define FX_ELEM_RUNNER_USES_RAND_ROT 0x8 -#define FX_ELEM_SPAWN_OFFSET_NONE 0x0 -#define FX_ELEM_SPAWN_OFFSET_SPHERE 0x10 -#define FX_ELEM_SPAWN_OFFSET_CYLINDER 0x20 -#define FX_ELEM_SPAWN_OFFSET_MASK 0x30 -#define FX_ELEM_RUN_RELATIVE_TO_WORLD 0x0 -#define FX_ELEM_RUN_RELATIVE_TO_SPAWN 0x40 -#define FX_ELEM_RUN_RELATIVE_TO_EFFECT 0x80 -#define FX_ELEM_RUN_RELATIVE_TO_OFFSET 0xC0 -#define FX_ELEM_RUN_MASK 0xC0 -#define FX_ELEM_USE_COLLISION 0x100 -#define FX_ELEM_DIE_ON_TOUCH 0x200 -#define FX_ELEM_DRAW_PAST_FOG 0x400 -#define FX_ELEM_DRAW_WITH_VIEWMODEL 0x800 -#define FX_ELEM_BLOCK_SIGHT 0x1000 -#define FX_ELEM_HAS_VELOCITY_GRAPH_LOCAL 0x1000000 -#define FX_ELEM_HAS_VELOCITY_GRAPH_WORLD 0x2000000 -#define FX_ELEM_HAS_GRAVITY 0x4000000 -#define FX_ELEM_USE_MODEL_PHYSICS 0x8000000 -#define FX_ELEM_NONUNIFORM_SCALE 0x10000000 -#define FX_ELEM_CLOUD_SHAPE_CUBE 0x0 -#define FX_ELEM_CLOUD_SHAPE_SPHERE_LARGE 0x20000000 -#define FX_ELEM_CLOUD_SHAPE_SPHERE_MEDIUM 0x40000000 -#define FX_ELEM_CLOUD_SHAPE_SPHERE_SMALL 0x60000000 -#define FX_ELEM_CLOUD_MASK 0x60000000 -#define FX_ELEM_DISABLE_FOUNTAIN_COLLISION 0x80000000 -#define FX_ELEM_DRAW_IN_THERMAL_ONLY 0x2000 -#define FX_ELEM_TRAIL_ORIENT_BY_VELOCITY 0x4000 -#define FX_ELEM_EMIT_ORIENT_BY_ELEM 0x8000 - -/* FxElemAtlas::behavior */ -#define FX_ATLAS_START_MASK 0x3 -#define FX_ATLAS_START_FIXED 0x0 -#define FX_ATLAS_START_RANDOM 0x1 -#define FX_ATLAS_START_INDEXED 0x2 -#define FX_ATLAS_PLAY_OVER_LIFE 0x4 -#define FX_ATLAS_LOOP_ONLY_N_TIMES 0x8 - - enum FxElemType : char - { - FX_ELEM_TYPE_SPRITE_BILLBOARD = 0x0, - FX_ELEM_TYPE_SPRITE_ORIENTED = 0x1, - FX_ELEM_TYPE_TAIL = 0x2, - FX_ELEM_TYPE_TRAIL = 0x3, - FX_ELEM_TYPE_CLOUD = 0x4, - FX_ELEM_TYPE_SPARKCLOUD = 0x5, - FX_ELEM_TYPE_SPARKFOUNTAIN = 0x6, - FX_ELEM_TYPE_MODEL = 0x7, - FX_ELEM_TYPE_OMNI_LIGHT = 0x8, - FX_ELEM_TYPE_SPOT_LIGHT = 0x9, - FX_ELEM_TYPE_SOUND = 0xA, - FX_ELEM_TYPE_DECAL = 0xB, - FX_ELEM_TYPE_RUNNER = 0xC, - FX_ELEM_TYPE_COUNT = 0xD, - FX_ELEM_TYPE_LAST_SPRITE = 0x3, - FX_ELEM_TYPE_LAST_DRAWN = 0x9, - }; - - struct FxElemVec3Range - { - float base[3]; - float amplitude[3]; - }; - - struct FxIntRange - { - int base; - int amplitude; - }; - - struct FxFloatRange - { - float base; - float amplitude; - }; - - struct FxSpawnDefLooping - { - int intervalMsec; - int count; - }; - - struct FxSpawnDefOneShot - { - FxIntRange count; - }; - - union FxSpawnDef - { - FxSpawnDefLooping looping; - FxSpawnDefOneShot oneShot; - }; - - struct FxEffectDef; - - union FxEffectDefRef - { - FxEffectDef *handle; - const char *name; - }; - - union FxElemVisuals - { - const void *anonymous; - Material *material; - XModel *xmodel; - FxEffectDefRef *effectDef; - const char *soundName; - }; - - struct FxElemMarkVisuals - { - Material* data[2]; - }; - - union FxElemDefVisuals - { - FxElemVisuals instance; - //If parent FxElemDef::elemType == 0x7, use xmodel - //If parent FxElemDef::elemType == 0xC, use effectDef - //If parent FxElemDef::elemType == 0xA, use soundName - //If parent FxElemDef::elemType != 0x9 || 0x8, use material - - FxElemVisuals *array; //Total count = parent FxElemDef::visualCount - FxElemMarkVisuals *markArray; //Total count = parent FxElemDef::visualCount - }; - - struct FxTrailVertex - { - /* - float pos[2]; - float normal[2]; - float texCoord[2]; - */ - char pad[20]; - }; - - struct FxTrailDef - { - int scrollTimeMsec; - int repeatDist; - float splitArcDist; - int splitDist; - int splitTime; - int vertCount; - FxTrailVertex *verts; - int indCount; - unsigned __int16 *inds; - }; - - struct FxSparkFountain - { - float sparkFountainGravity; - float sparkFountainBounceFrac; - float sparkFountainBounceRand; - float sparkFountainSparkSpacing; - float sparkFountainSparkLength; - int sparkFountainSparkCount; - float sparkFountainLoopTime; - float sparkFountainVelMin; - float sparkFountainVelMax; - float sparkFountainVelConeAngle; - float sparkFountainRestSpeed; - float sparkFountainBoostTime; - float sparkFountainBoostFactor; - }; - - union FxElemExtendedDef - { - char *unknownBytes; - FxSparkFountain *sparkFountain; - FxTrailDef *trailDef; - }; - - struct FxElemAtlas - { - char behavior; - char index; - char fps; - char loopCount; - char colIndexBits; - char rowIndexBits; - __int16 entryCount; - }; - - struct FxElemVelStateInFrame - { - FxElemVec3Range velocity; - FxElemVec3Range totalDelta; - }; - - struct FxElemVelStateSample - { - FxElemVelStateInFrame local; - FxElemVelStateInFrame world; - }; - - struct FxElemVisualState - { - char color[4]; - float rotationDelta; - float rotationTotal; - float size[2]; - float scale; - }; - - struct FxElemVisStateSample - { - FxElemVisualState base; - FxElemVisualState amplitude; - }; - - struct FxElemDef // 0xFC - { - int flags; - FxSpawnDef spawn; - FxFloatRange spawnRange; - FxFloatRange fadeInRange; - FxFloatRange fadeOutRange; - float spawnFrustumCullRadius; - FxIntRange spawnDelayMsec; - FxIntRange lifeSpanMsec; - FxFloatRange spawnOrigin[3]; - FxFloatRange spawnOffsetRadius; - FxFloatRange spawnOffsetHeight; - FxFloatRange spawnAngles[3]; - FxFloatRange angularVelocity[3]; - FxFloatRange initialRotation; - FxFloatRange gravity; - FxFloatRange reflectionFactor; - FxElemAtlas atlas; - char elemType; - char visualCount; - char velIntervalCount; - char visStateIntervalCount; - FxElemVelStateSample *velSamples; // count = velIntervalCount - FxElemVisStateSample *visSamples; // count = visStateIntervalCount - FxElemDefVisuals visuals; - //If elemType is 0xB, then use markVisuals - //If elemType is not 0xB and visualCount == 1, then use visual - //If elemType is not 0xB and visualCount != 1, then use visualsArray - vec3_t collMins; - vec3_t collMaxs; - FxEffectDefRef *effectOnImpact; - FxEffectDefRef *effectOnDeath; - FxEffectDefRef *effectEmitted; - FxFloatRange emitDist; - FxFloatRange emitDistVariance; - FxElemExtendedDef extendedDef; - //If elemType == 3, then use trailDef - //If elemType == 6, then use sparkFountain - //If elemType != 3 && elemType != 6 use unknownBytes (size = 1) - char sortOrder; - char lightingFrac; - char unused[2]; - }; - - struct FxEffectDef - { - const char *name; - int flags; - int totalSize; - int msecLoopingLife; - int elemDefCountLooping; - int elemDefCountOneShot; - int elemDefCountEmission; - FxElemDef *elemDefs; //Count = elemDefCountOneShot + elemDefCountEmission + elemDefCountLooping - }; - -#define FX_ELEM_FIELD_COUNT 90 - - struct FxEditorElemAtlas - { - int behavior; - int index; - int fps; - int loopCount; - int indexRange; - int colIndexBits; - int rowIndexBits; - int entryCount; - }; - - struct FxCurve - { - int dimensionCount; - int keyCount; - float keys[1]; - }; - - struct FxEditorTrailDef - { - FxTrailVertex verts[64]; - int vertCount; - unsigned __int16 inds[128]; - int indCount; - }; - - struct FxEditorElemDef - { - char name[48]; - int editorFlags; - int flags; - FxFloatRange spawnRange; - FxFloatRange fadeInRange; - FxFloatRange fadeOutRange; - float spawnFrustumCullRadius; - FxSpawnDefLooping spawnLooping; - FxSpawnDefOneShot spawnOneShot; - FxIntRange spawnDelayMsec; - FxIntRange lifeSpanMsec; - FxFloatRange spawnOrigin[3]; - FxFloatRange spawnOffsetRadius; - FxFloatRange spawnOffsetHeight; - FxFloatRange spawnAngles[3]; - FxFloatRange angularVelocity[3]; - FxFloatRange initialRotation; - FxFloatRange gravity; - FxFloatRange elasticity; - FxEditorElemAtlas atlas; - float velScale[2][3]; - FxCurve *velShape[2][3][2]; - float rotationScale; - FxCurve *rotationShape[2]; - float sizeScale[2]; - FxCurve *sizeShape[2][2]; - float scaleScale; - FxCurve *scaleShape[2]; - FxCurve *color[2]; - FxCurve *alpha[2]; - float lightingFrac; - float decalFadeInTime; - float collOffset[3]; - float collRadius; - FxEffectDef *effectOnImpact; - FxEffectDef *effectOnDeath; - int sortOrder; - FxEffectDef *emission; - FxFloatRange emitDist; - FxFloatRange emitDistVariance; - char elemType; - __declspec(align(4)) int visualCount; - union - { - FxElemVisuals visuals[32]; - FxElemMarkVisuals markVisuals[16]; - } visuals; - int trailSplitDist; - int trailSplitArcDist; - int trailSplitTime; - int trailRepeatDist; - float trailScrollTime; - FxEditorTrailDef trailDef; - int sparkFountainGravity; - int sparkFountainBounceFrac; - int sparkFountainBounceRand; - int sparkFountainSparkSpacing; - int sparkFountainSparkLength; - int sparkFountainSparkCount; - int sparkFountainLoopTime; - int sparkFountainVelMin; - int sparkFountainVelMax; - int sparkFountainVelConeAngle; - int sparkFountainRestSpeed; - int sparkFountainBoostTime; - int sparkFountainBoostFactor; - }; - - struct FxEditorEffectDef - { - char name[64]; - int elemCount; - FxEditorElemDef elems[32]; - }; - - struct FxElemField - { - const char *keyName; - bool(__cdecl *handler)(const char**, FxEditorElemDef*); - }; - - struct cStaticModel_t - { - XModel *xmodel; - float origin[3]; - float invScaledAxis[3][3]; - float absmin[3]; - float absmax[3]; - }; - - struct ClipMaterial - { - char* name; - int unk; - int unk2; - }; - - struct cNode_t - { - cplane_t* plane; - short children[2]; - }; - - struct cbrush_t - { - unsigned __int16 numsides; - unsigned __int16 glassPieceIndex; - cbrushside_t *sides; - char *baseAdjacentSide; - __int16 axialMaterialNum[2][3]; - char firstAdjacentSideOffsets[2][3]; - char edgeCount[2][3]; - }; - - - struct cLeaf_t - { - unsigned __int16 firstCollAabbIndex; - unsigned __int16 collAabbCount; - int brushContents; - int terrainContents; - float mins[3]; - float maxs[3]; - int leafBrushNode; - }; - - struct cLeafBrushNodeLeaf_t - { - unsigned __int16 *brushes; - }; - - struct cLeafBrushNode_t - { - char axis; - __int16 leafBrushCount; - int contents; - cLeafBrushNodeLeaf_t data; - char pad[8]; - }; - - struct cmodel_t - { - float mins[3]; - float maxs[3]; - float radius; - cLeaf_t leaf; - }; - - enum DynEntityType - { - DYNENT_TYPE_INVALID = 0x0, - DYNENT_TYPE_CLUTTER = 0x1, - DYNENT_TYPE_DESTRUCT = 0x2, - DYNENT_TYPE_COUNT = 0x3, - }; - - struct GfxPlacement - { - float quat[4]; - float origin[3]; - }; - - struct DynEntityDef - { - DynEntityType type; - GfxPlacement pose; - XModel *xModel; - unsigned __int16 brushModel; - unsigned __int16 physicsBrushModel; - FxEffectDef *destroyFx; - PhysPreset *physPreset; - int health; - PhysMass mass; - int contents; - }; - - struct DynEntityPose - { - GfxPlacement pose; - float radius; - }; - - struct DynEntityClient - { - int physObjId; - unsigned __int16 flags; - unsigned __int16 lightingHandle; - int health; - }; - - struct DynEntityColl - { - unsigned __int16 sector; - unsigned __int16 nextEntInSector; - float linkMins[2]; - float linkMaxs[2]; - }; - - struct CollisionBorder - { - float distEq[3]; - float zBase; - float zSlope; - float start; - float length; - }; - - struct CollisionPartition - { - char triCount; - char borderCount; - int firstTri; - CollisionBorder *borders; - }; - - union CollisionAabbTreeIndex - { - int firstChildIndex; - int partitionIndex; - }; - - struct CollisionAabbTree - { - float midPoint[3]; - unsigned __int16 materialIndex; - unsigned __int16 childCount; - float halfSize[3]; - CollisionAabbTreeIndex u; - }; - - struct SModelAabbNode - { - Bounds bounds; - unsigned __int16 firstChild; - unsigned __int16 childCount; - }; - - - struct clipMap_t - { - const char* name; - int isInUse; // +8 - int numCPlanes; // +8 - cplane_t* cPlanes; // sizeof 20, +12 - int numStaticModels; // +16 - cStaticModel_t* staticModelList; // sizeof 76, +20 - int numMaterials; // +24 - ClipMaterial* materials; // sizeof 12 with a string (possibly name?), +28 - int numCBrushSides; // +32 - cbrushside_t* cBrushSides; // sizeof 8, +36 - int numCBrushEdges; // +40 - char* cBrushEdges; // +44 - int numCNodes; // +48 - cNode_t* cNodes; // sizeof 8, +52 - int numCLeaf; // +56 - cLeaf_t* cLeaf; // +60 - int numCLeafBrushNodes; // +64 - cLeafBrushNode_t* cLeafBrushNodes; // +68 - int numLeafBrushes; // +72 - short* leafBrushes; // +76 - int numLeafSurfaces; // +80 - int* leafSurfaces; // +84 - int numVerts; // +88 - vec3_t* verts; // +92 - int numTriIndices; // +96 - short* triIndices; // +100 - bool* triEdgeIsWalkable; // +104 - int numCollisionBorders; // +108 - CollisionBorder* collisionBorders;// sizeof 28, +112 - int numCollisionPartitions; // +116 - CollisionPartition* collisionPartitions; // sizeof 12, +120 - int numCollisionAABBTrees; // +124 - CollisionAabbTree* collisionAABBTrees;// sizeof 32, +128 - int numCModels; // +132 - cmodel_t* cModels; // sizeof 68, +136 - short numCBrushes; // +140 - short pad2; // +142 - cbrush_t * cBrushes; // sizeof 36, +144 - Bounds* cBrushBounds; // same count as cBrushes, +148 - int * cBrushContents; // same count as cBrushes, +152 - MapEnts * mapEnts; // +156 - unsigned __int16 smodelNodeCount; - short pad; // +160 - SModelAabbNode *smodelNodes; - unsigned __int16 dynEntCount[2]; - DynEntityDef *dynEntDefList[2]; - DynEntityPose *dynEntPoseList[2]; - DynEntityClient *dynEntClientList[2]; - DynEntityColl *dynEntCollList[2]; - unsigned int checksum; - char unknown5[0x30]; - }; // +256 - - struct G_GlassPiece - { - char pad[12]; - }; - - struct G_GlassName - { - char *nameStr; - __int16 name; - unsigned __int16 pieceCount; - unsigned __int16 *pieceIndices; - }; - - struct G_GlassData - { - G_GlassPiece *glassPieces; - unsigned int pieceCount; - unsigned __int16 damageToWeaken; - unsigned __int16 damageToDestroy; - unsigned int glassNameCount; - G_GlassName *glassNames; - char pad[108]; - }; - - struct pathbasenode_t - { - float vOrigin[3]; - unsigned int type; - }; - - struct pathnode_tree_nodes_t - { - int nodeCount; - unsigned __int16 *nodes; - }; - - struct pathnode_tree_t; - - union pathnode_tree_info_t - { - pathnode_tree_t *child[2]; - pathnode_tree_nodes_t s; - }; - - struct pathnode_tree_t - { - int axis; - float dist; - pathnode_tree_info_t u; - }; - - enum nodeType - { - NODE_BADNODE = 0x0, - NODE_PATHNODE = 0x1, - NODE_COVER_STAND = 0x2, - NODE_COVER_CROUCH = 0x3, - NODE_COVER_CROUCH_WINDOW = 0x4, - NODE_COVER_PRONE = 0x5, - NODE_COVER_RIGHT = 0x6, - NODE_COVER_LEFT = 0x7, - NODE_COVER_WIDE_RIGHT = 0x8, - NODE_COVER_WIDE_LEFT = 0x9, - NODE_CONCEALMENT_STAND = 0xA, - NODE_CONCEALMENT_CROUCH = 0xB, - NODE_CONCEALMENT_PRONE = 0xC, - NODE_REACQUIRE = 0xD, - NODE_BALCONY = 0xE, - NODE_SCRIPTED = 0xF, - NODE_NEGOTIATION_BEGIN = 0x10, - NODE_NEGOTIATION_END = 0x11, - NODE_TURRET = 0x12, - NODE_GUARD = 0x13, - NODE_NUMTYPES = 0x14, - NODE_DONTLINK = 0x14, - }; - - struct pathlink_s - { - float fDist; - unsigned __int16 nodeNum; - char disconnectCount; - char negotiationLink; - char ubBadPlaceCount[4]; - }; - - struct pathnode_constant_t - { - nodeType type; - unsigned __int16 spawnflags; - unsigned __int16 targetname; - unsigned __int16 script_linkName; - unsigned __int16 script_noteworthy; - unsigned __int16 target; - unsigned __int16 animscript; - int animscriptfunc; - float vOrigin[3]; - float fAngle; - float forward[2]; - float fRadius; - float minUseDistSq; - __int16 wOverlapNode[2]; -// __int16 wChainId; -// __int16 wChainDepth; -// __int16 wChainParent; - unsigned __int16 totalLinkCount; - pathlink_s *Links; - }; - - struct pathnode_dynamic_t - { - void *pOwner; - int iFreeTime; - int iValidTime[3]; - int inPlayerLOSTime; - __int16 wLinkCount; - __int16 wOverlapCount; - __int16 turretEntNumber; - __int16 userCount; - }; - - struct pathnode_t; - - struct pathnode_transient_t - { - int iSearchFrame; - pathnode_t *pNextOpen; - pathnode_t *pPrevOpen; - pathnode_t *pParent; - float fCost; - float fHeuristic; - float costFactor; - }; - - struct pathnode_t - { - pathnode_constant_t constant; - pathnode_dynamic_t dynamic; - pathnode_transient_t transient; - char pad[12]; - }; - - struct PathData - { - unsigned int nodeCount; - pathnode_t *nodes; - pathbasenode_t *basenodes; - unsigned int chainNodeCount; - unsigned __int16 *chainNodeForNode; - unsigned __int16 *nodeForChainNode; - int visBytes; - char *pathVis; - int nodeTreeCount; - pathnode_tree_t *nodeTree; - }; - - struct VehicleTrackObstacle - { - char pad[12]; - }; - - struct VehicleTrackSector - { - char pad[52]; - VehicleTrackObstacle* trackObstacles; - int trackObstacleCount; - }; - - struct VehicleTrackSegment - { - const char* name; - VehicleTrackSector* trackSectors; - int trackSectorCount; - VehicleTrackSegment** trackSegments1; - int trackSegmentCount1; - VehicleTrackSegment** trackSegments2; - int trackSegmentCount2; - int pad[4]; - }; - - struct VehicleTrack - { - VehicleTrackSegment* trackSegments; - int trackSegmentCount; - }; - - struct GameWorldSp - { - const char* name; - PathData pathData; - VehicleTrack vehicleTrack; - G_GlassData* data; - }; - - struct GameWorldMp - { - const char* name; - G_GlassData* data; - }; - - struct VehicleDef - { - const char* name; - char pad[408]; - void* weaponDef; - char pad2[304]; - }; - - struct XModelDrawInfo - { - unsigned __int16 lod; - unsigned __int16 surfId; - }; - - struct GfxSceneDynModel - { - XModelDrawInfo info; - unsigned __int16 dynEntId; - }; - - struct BModelDrawInfo - { - unsigned __int16 surfId; - }; - - struct GfxSceneDynBrush - { - BModelDrawInfo info; - unsigned __int16 dynEntId; - }; - - struct GfxStreamingAabbTree - { - unsigned __int16 firstItem; - unsigned __int16 itemCount; - unsigned __int16 firstChild; - unsigned __int16 childCount; - float mins[3]; - float maxs[3]; - }; - - struct GfxWorldStreamInfo - { - int aabbTreeCount; - GfxStreamingAabbTree *aabbTrees; - int leafRefCount; - int *leafRefs; - }; - - struct GfxWorldVertex - { - float xyz[3]; - float binormalSign; - GfxColor color; - float texCoord[2]; - float lmapCoord[2]; - PackedUnitVec normal; - PackedUnitVec tangent; - }; - - struct GfxWorldVertexData - { - GfxWorldVertex *vertices; - void/*IDirect3DVertexBuffer9*/* worldVb; - }; - -#pragma pack(push, 4) - struct GfxLightImage - { - GfxImage *image; - char samplerState; - }; -#pragma pack(pop) - - struct GfxLightDef - { - const char *name; - GfxLightImage attenuation; - int lmapLookupStart; - }; - - struct GfxLight - { - char type; - char canUseShadowMap; - char unused[2]; - float color[3]; - float dir[3]; - float origin[3]; - float radius; - float cosHalfFovOuter; - float cosHalfFovInner; - int exponent; - unsigned int spotShadowIndex; - GfxLightDef *def; - }; - - struct GfxReflectionProbe - { - float origin[3]; - }; - - struct GfxWorldDpvsPlanes - { - int cellCount; - cplane_t *planes; - unsigned __int16 *nodes; - unsigned int *sceneEntCellBits; //Size = cellCount << 11 - }; - - struct GfxAabbTree - { - float mins[3]; - float maxs[3]; - int pad; - unsigned __int16 childCount; - unsigned __int16 surfaceCount; - unsigned __int16 startSurfIndex; - unsigned __int16 smodelIndexCount; - unsigned __int16 *smodelIndexes; - int childrenOffset; - }; - - struct GfxCellTree - { - GfxAabbTree *aabbTree; - }; - - struct GfxCellTreeCount - { - int aabbTreeCount; - }; - - struct GfxLightGridEntry - { - unsigned __int16 colorsIndex; - char primaryLightIndex; - char needsTrace; - }; - - struct GfxLightGridColors - { - char rgb[56][3]; - }; - - struct GfxStaticModelInst - { - float mins[3]; - float maxs[3]; - float lightingOrigin[3]; - }; - - enum surfaceType_t - { - SF_TRIANGLES = 0x0, - SF_TRIANGLES_NO_SUN_SHADOW = 0x1, - SF_BEGIN_STATICMODEL = 0x2, - SF_STATICMODEL_RIGID = 0x2, - SF_STATICMODEL_INSTANCED = 0x3, - SF_STATICMODEL_RIGID_NO_SUN_SHADOW = 0x4, - SF_STATICMODEL_INSTANCED_NO_SUN_SHADOW = 0x5, - SF_END_STATICMODEL = 0x6, - SF_BMODEL = 0x6, - SF_BEGIN_XMODEL = 0x7, - SF_XMODEL_RIGID = 0x7, - SF_XMODEL_SKINNED = 0x8, - SF_END_XMODEL = 0x9, - SF_BEGIN_FX = 0x9, - SF_CODE = 0x9, - SF_GLASS = 0xA, - SF_MARK = 0xB, - SF_SPARK = 0xC, - SF_PARTICLE_CLOUD = 0xD, - SF_PARTICLE_SPARK_CLOUD = 0xE, - SF_END_FX = 0xF, - SF_NUM_SURFACE_TYPES = 0xF, - SF_FORCE_32_BITS = 0xFFFFFFFF, - }; - - struct srfTriangles_t - { - int vertexLayerData; - int firstVertex; - unsigned __int16 vertexCount; - unsigned __int16 triCount; - int baseIndex; - }; - - struct GfxSurface - { - srfTriangles_t tris; - Material *material; - char lightmapIndex; - char reflectionProbeIndex; - char primaryLightIndex; - char castsSunShadow; - }; - - struct GfxSurfaceBounds - { - Bounds bounds; - //char flags; - }; - - struct GfxPackedPlacement - { - float origin[3]; - vec3_t axis[3]; - float scale; - }; - - struct GfxStaticModelDrawInst - { - GfxPackedPlacement placement; - XModel *model; - unsigned __int16 cullDist; - unsigned __int16 lightingHandle; - char reflectionProbeIndex; - char primaryLightIndex; - char flags; - char firstMtlSkinIndex; - GfxColor groundLighting; - unsigned __int16 cacheId[4]; - }; - - struct GfxWorldDpvsStatic - { - unsigned int smodelCount; - unsigned int staticSurfaceCount; - unsigned int staticSurfaceCountNoDecal; - unsigned int litOpaqueSurfsBegin; - unsigned int litOpaqueSurfsEnd; - unsigned int litTransSurfsBegin; - unsigned int litTransSurfsEnd; - unsigned int shadowCasterSurfsBegin; - unsigned int shadowCasterSurfsEnd; - unsigned int emissiveSurfsBegin; - unsigned int emissiveSurfsEnd; - unsigned int smodelVisDataCount; - unsigned int surfaceVisDataCount; - char *smodelVisData[3]; - char *surfaceVisData[3]; - unsigned __int16 *sortedSurfIndex; - GfxStaticModelInst *smodelInsts; - GfxSurface *surfaces; - GfxSurfaceBounds *surfacesBounds; - GfxStaticModelDrawInst *smodelDrawInsts; - GfxDrawSurf *surfaceMaterials; - unsigned int *surfaceCastsSunShadow; - volatile int usageCount; - }; - -#pragma pack(push, 4) - - struct GfxPortalWritable - { - char isQueued; - char isAncestor; - char recursionDepth; - char hullPointCount; - float(*hullPoints)[2]; - }; - - struct DpvsPlane - { - float coeffs[4]; - char side[3]; - char pad; - }; - - struct GfxPortal - { - GfxPortalWritable writable; - DpvsPlane plane; - vec3_t* vertices; - unsigned __int16 cellIndex; - char vertexCount; - float hullAxis[2][3]; - }; - - struct GfxCell - { - float mins[3]; - float maxs[3]; - int portalCount; - GfxPortal *portals; - char reflectionProbeCount; - char *reflectionProbes; - }; - - struct GfxLightmapArray - { - GfxImage *primary; - GfxImage *secondary; - }; - - struct GfxLightGrid - { - bool hasLightRegions; - unsigned int sunPrimaryLightIndex; - unsigned __int16 mins[3]; - unsigned __int16 maxs[3]; - unsigned int rowAxis; - unsigned int colAxis; - unsigned __int16 *rowDataStart; - unsigned int rawRowDataSize; - char *rawRowData; - unsigned int entryCount; - GfxLightGridEntry *entries; - unsigned int colorCount; - GfxLightGridColors *colors; - }; - - struct GfxBrushModelWritable - { - float mins[3]; - float maxs[3]; - }; - - struct GfxBrushModel - { - GfxBrushModelWritable writable; - float bounds[2][3]; - unsigned int surfaceCount; - unsigned int startSurfIndex; - int pad; - }; - - struct MaterialMemory - { - Material *material; - int memory; - }; - - struct sunflare_t - { - bool hasValidData; - Material *spriteMaterial; - Material *flareMaterial; - float spriteSize; - float flareMinSize; - float flareMinDot; - float flareMaxSize; - float flareMaxDot; - float flareMaxAlpha; - int flareFadeInTime; - int flareFadeOutTime; - float blindMinDot; - float blindMaxDot; - float blindMaxDarken; - int blindFadeInTime; - int blindFadeOutTime; - float glareMinDot; - float glareMaxDot; - float glareMaxLighten; - int glareFadeInTime; - int glareFadeOutTime; - float sunFxPosition[3]; - }; - - struct GfxShadowGeometry - { - unsigned __int16 surfaceCount; - unsigned __int16 smodelCount; - unsigned __int16 *sortedSurfIndex; - unsigned __int16 *smodelIndex; - }; - - struct GfxLightRegionAxis - { - float dir[3]; - float midPoint; - float halfSize; - }; - - struct GfxLightRegionHull - { - float kdopMidPoint[9]; - float kdopHalfSize[9]; - unsigned int axisCount; - GfxLightRegionAxis *axis; - }; - - struct GfxLightRegion - { - unsigned int hullCount; - GfxLightRegionHull *hulls; - }; - - struct GfxWorldDpvsDynamic - { - unsigned int dynEntClientWordCount[2]; - unsigned int dynEntClientCount[2]; - unsigned int *dynEntCellBits[2]; - char *dynEntVisData[2][3]; - }; - - struct SunLightParseParams - { - char name[64]; - float ambientScale; - float ambientColor[3]; - float diffuseFraction; - float sunLight; - float sunColor[3]; - float diffuseColor[3]; - bool diffuseColorHasBeenSet; - float angles[3]; - }; - - struct GfxWorldVertexLayerData - { - char *data; - void/*IDirect3DVertexBuffer9*/* layerVb; - }; - - typedef unsigned int GfxRawTexture; - - struct GfxWorldDraw - { - unsigned int reflectionProbeCount; - GfxImage * * reflectionImages; - GfxReflectionProbe *reflectionProbes; - GfxRawTexture * reflectionProbeTextures; //Count = refelctionProbeCount - int lightmapCount; - GfxLightmapArray *lightmaps; - GfxRawTexture * lightmapPrimaryTextures; //Count = lightmapCount - GfxRawTexture * lightmapSecondaryTextures; //Count = lightmapCount - GfxImage *skyImage; - GfxImage *outdoorImage; - unsigned int vertexCount; - GfxWorldVertexData vd; - unsigned int vertexLayerDataSize; - GfxWorldVertexLayerData vld; - int indexCount; - unsigned __int16 *indices; - }; - - struct GfxSky - { - int skySurfCount; - int * skyStartSurfs; - GfxImage * skyImage; - int skySamplerState; - }; - - struct GfxHeroOnlyLight - { - char pad[56]; - }; - - struct GfxWorld - { - const char *name; - const char *baseName; - int planeCount; - int nodeCount; - unsigned int surfaceCount; - int skyCount; - GfxSky *skies; - unsigned int lastSunPrimaryLightIndex; - unsigned int primaryLightCount; - unsigned int sortKeyLitDecal; - unsigned int sortKeyEffectDecal; - unsigned int sortKeyEffectAuto; - unsigned int sortKeyDistortion; - GfxWorldDpvsPlanes dpvsPlanes; - GfxCellTreeCount *aabbTreeCounts; - GfxCellTree *aabbTrees; - GfxCell *cells; - GfxWorldDraw draw; - GfxLightGrid lightGrid; - int modelCount; - GfxBrushModel *models; - Bounds bounds; - unsigned int checksum; - int materialMemoryCount; - MaterialMemory *materialMemory; - sunflare_t sun; - float outdoorLookupMatrix[4][4]; - GfxImage *outdoorImage; - unsigned int *cellCasterBits; - unsigned int *cellHasSunLitSurfsBits; - GfxSceneDynModel *sceneDynModel; - GfxSceneDynBrush *sceneDynBrush; - unsigned int *primaryLightEntityShadowVis; - unsigned int *primaryLightDynEntShadowVis[2]; - char *nonSunPrimaryLightForModelDynEnt; - GfxShadowGeometry *shadowGeom; - GfxLightRegion *lightRegion; - GfxWorldDpvsStatic dpvs; - GfxWorldDpvsDynamic dpvsDyn; - unsigned int mapVtxChecksum; - unsigned int heroOnlyLightCount; - GfxHeroOnlyLight *heroOnlyLights; - char fogTypesAllowed; - }; -#pragma pack(pop) - - struct rgpStruct - { - int pad[2117]; - GfxWorld* world; - }; - - struct ComPrimaryLight - { - char type; - char canUseShadowMap; - char exponent; - char unused; - float color[3]; - float dir[3]; - float origin[3]; - float radius; - float cosHalfFovOuter; - float cosHalfFovInner; - float cosHalfFovExpanded; - float rotationLimit; - float translationLimit; - char* name; - }; - - struct ComWorld - { - char *name; - int isInUse; - int lightCount; - ComPrimaryLight* lights; - }; - -#pragma pack(push, 4) - struct FxGlassDef - { - float halfThickness; - float texVecs[2][2]; - GfxColor color; - Material *material; - Material *materialShattered; - PhysPreset *physPreset; - }; -#pragma pack(pop) - - struct FxSpatialFrame - { - float quat[4]; - float origin[3]; - }; - - union FxGlassPiecePlace - { - struct - { - FxSpatialFrame frame; - float radius; - }; - unsigned int nextFree; - }; - - struct FxGlassPieceState - { - float texCoordOrigin[2]; - unsigned int supportMask; - unsigned __int16 initIndex; - unsigned __int16 geoDataStart; - unsigned __int16 lightingIndex; - char defIndex; - char pad[3]; - char vertCount; - char holeDataCount; - char crackDataCount; - char fanDataCount; - unsigned __int16 flags; - float areaX2; - }; - - struct FxGlassPieceDynamics - { - char pad[36]; - }; - - struct FxGlassVertex - { - __int16 x; - __int16 y; - }; - - struct FxGlassHoleHeader - { - unsigned __int16 uniqueVertCount; - char touchVert; - char pad[1]; - }; - - struct FxGlassCrackHeader - { - unsigned __int16 uniqueVertCount; - char beginVertIndex; - char endVertIndex; - }; - - union FxGlassGeometryData - { - FxGlassVertex vert; - FxGlassHoleHeader hole; - FxGlassCrackHeader crack; - char asBytes[4]; - __int16 anonymous[2]; - }; - -#pragma pack(push, 4) - struct FxGlassInitPieceState //Note, on MW3 this is missing 4 bytes, just not sure whats missing yet - { - /* - FxSpatialFrame frame; - float radius; - float texCoordOrigin[2]; - unsigned int supportMask; - float areaX2; - unsigned __int16 lightingIndex; - char defIndex; - char vertCount; - char fanDataCount; - */ - char pad[52]; - }; -#pragma pack(pop) - -#pragma pack(push, 8) - struct FxGlassSystem - { - int time; - int prevTime; - unsigned int defCount; - unsigned int pieceLimit; - unsigned int pieceWordCount; - unsigned int initPieceCount; - unsigned int cellCount; - unsigned int activePieceCount; - unsigned int firstFreePiece; - unsigned int geoDataLimit; - unsigned int geoDataCount; - unsigned int initGeoDataCount; - FxGlassDef *defs; - FxGlassPiecePlace *piecePlaces; - FxGlassPieceState *pieceStates; - FxGlassPieceDynamics *pieceDynamics; - FxGlassGeometryData *geoData; - unsigned int *isInUse; - unsigned int *cellBits; - char *visData; - float(*linkOrg)[3]; - float *halfThickness; - unsigned __int16 *lightingHandles; - FxGlassInitPieceState *initPieceStates; - FxGlassGeometryData *initGeoData; - bool needToCompactData; - char initCount; - float effectChanceAccum; - int lastPieceDeletionTime; - }; -#pragma pack(pop) - - struct FxWorld - { - const char * name; - FxGlassSystem glassSys; - }; - - union XAssetHeader - { - void *data; - - MenuList *menuList; - menuDef_t *menu; - Material *material; - snd_alias_list_t* sound; - LocalizeEntry* localize; - StringTable* stringTable; - MapEnts* mapEnts; - RawFile* rawfile; - GfxImage* image; - Font* font; - MaterialTechniqueSet* techniqueSet; - MaterialVertexDeclaration* vertexDecl; - MaterialVertexShader* vertexShader; - MaterialPixelShader* pixelShader; - StructuredDataDefSet* structuredData; - XModel* model; - PhysPreset* physPreset; - PhysCollmap* physCollmap; - XModelSurfs* surfaces; - XAnimParts* parts; - clipMap_t* clipMap; - FxEffectDef* fx; - GameWorldMp* gameWorldMp; - GameWorldSp* gameWorldSp; - TracerDef* tracer; - VehicleDef* vehicle; - FxWorld* fxWorld; - GfxWorld* gfxWorld; - GfxLightDef* lightDef; - SndCurve* sndCurve; - LoadedSound* loadSnd; - ComWorld* comWorld; - }; - - struct XAsset - { - XAssetType type; - XAssetHeader header; - }; - - struct XBlock - { - char *data; - unsigned int size; - }; - - struct XAssetEntry - { - XAsset asset; - char zoneIndex; - bool inuse; - unsigned __int16 nextHash; - unsigned __int16 nextOverride; - unsigned __int16 usageFrame; - }; - - enum XFileLanguage : unsigned char - { - XLANG_NONE = 0x00, - XLANG_ENGLISH = 0x01, - XLANG_FRENCH = 0x02, - XLANG_GERMAN = 0x03, - XLANG_ITALIAN = 0x04, - XLANG_SPANISH = 0x05, - XLANG_BRITISH = 0x06, - XLANG_RUSSIAN = 0x07, - XLANG_POLISH = 0x08, - XLANG_KOREAN = 0x09, - XLANG_TAIWANESE = 0x0A, - XLANG_JAPANESE = 0x0B, - XLANG_CHINESE = 0x0C, - XLANG_THAI = 0x0D, - XLANG_LEET = 0x0E, // Wat? - XLANG_CZECH = 0x0F, - }; - -#pragma pack(push, 1) - struct XFileHeader - { - unsigned __int64 magic; - unsigned int version; - XFileLanguage language; - DWORD highDateTime; - DWORD lowDateTime; - }; -#pragma pack(pop) - - enum XFILE_BLOCK_TYPES - { - XFILE_BLOCK_TEMP = 0x0, - XFILE_BLOCK_PHYSICAL = 0x1, - XFILE_BLOCK_RUNTIME = 0x2, - XFILE_BLOCK_VIRTUAL = 0x3, - XFILE_BLOCK_LARGE = 0x4, - - // Those are probably incorrect - XFILE_BLOCK_CALLBACK, - XFILE_BLOCK_VERTEX, - XFILE_BLOCK_INDEX, - - MAX_XFILE_COUNT, - - XFILE_BLOCK_INVALID = -1 - }; - - struct XFile - { - unsigned int size; - unsigned int externalSize; - unsigned int blockSize[MAX_XFILE_COUNT]; - }; - - struct ScriptStringList - { - int count; - const char **strings; - }; - - struct XAssetList - { - ScriptStringList stringList; - int assetCount; - XAsset *assets; - }; - - struct ZoneHeader - { - XFile xFile; - XAssetList assetList; - }; - - struct XNKID - { - char ab[8]; - }; - - struct XNADDR - { - in_addr ina; - in_addr inaOnline; - unsigned __int16 wPortOnline; - char abEnet[6]; - char abOnline[20]; - }; - - struct XNKEY - { - char ab[16]; - }; - - struct _XSESSION_INFO - { - XNKID sessionID; - XNADDR hostAddress; - XNKEY keyExchangeKey; - }; - - struct mapArena_t - { - char uiName[32]; - char mapName[16]; - char description[32]; - char mapimage[32]; - char keys[32][16]; - char values[32][64]; - char pad[144]; - }; - - struct newMapArena_t - { - char uiName[32]; - char oldMapName[16]; - char description[32]; - char mapimage[32]; - char keys[32][16]; - char values[32][64]; - char other[144]; - char mapName[32]; - }; - - struct gameTypeName_t - { - char gameType[12]; - char uiName[32]; - }; - - typedef struct party_s - { - unsigned char pad1[544]; - int privateSlots; - int publicSlots; - } party_t; - - typedef struct PartyData_s - { - DWORD unk; - } PartyData_t; - - struct fileInIwd_s - { - unsigned int pos; - char *name; - fileInIwd_s *next; - }; - - struct iwd_t - { - char iwdFilename[256]; - char iwdBasename[256]; - char iwdGamename[256]; - char *handle; - int checksum; - int pure_checksum; - volatile int hasOpenFile; - int numfiles; - char referenced; - unsigned int hashSize; - fileInIwd_s **hashTable; - fileInIwd_s *buildBuffer; - }; - -#ifndef __cplusplus - typedef void _iobuf; -#endif - - union qfile_gus - { - _iobuf *o; - char *z; - }; - - struct qfile_us - { - qfile_gus file; - int iwdIsClone; - }; - - struct fileHandleData_t - { - qfile_us handleFiles; - int handleSync; - int fileSize; - int zipFilePos; - iwd_t *zipFile; - int streamed; - char name[256]; - }; - - typedef struct { - char path[256]; // c:\quake3 - char gamedir[256]; // baseq3 - } directory_t; - - typedef struct searchpath_s - { - searchpath_s* next; - iwd_t *iwd; - directory_t* dir; - int bLocalized; - int ignore; - int ignorePureCheck; - int language; - } searchpath_t; - - struct SafeArea - { - int fontHeight; - int textHeight; - int textWidth; - float left; - float top; - float right; - float bottom; - }; - -#pragma pack(push, 4) - struct SpawnVar - { - bool spawnVarsValid; - int numSpawnVars; - char *spawnVars[64][2]; - int numSpawnVarChars; - char spawnVarChars[2048]; - }; -#pragma pack(pop) - - // Probably incomplete or wrong! -#pragma pack(push, 4) - struct usercmd_s - { - int serverTime; - int buttons; - char weapon; - char offHandIndex; - int angles[3]; - char forwardmove; - char rightmove; - float meleeChargeYaw; - char meleeChargeDist; - }; -#pragma pack(pop) - - typedef char mapname_t[40]; - - struct traceWork_t - { - /*TraceExtents*/int extents; - float delta[3]; - float deltaLen; - float deltaLenSq; - float delta2DLen; - float delta2DLenSq; - float size[3]; - Bounds bounds; - int contents; - bool isPoint; - bool axialCullOnly; - float radius; - float offset[3]; - float radiusOffset[3]; - float boundingRadius; - /*TraceThreadInfo*/ int threadInfo; - /*CM_WorldTraceCallbacks*/ void *callbacks; - }; - - -#ifdef __cplusplus -} -#endif +#define PROTOCOL 0x92 + +// This allows us to compile our structures in IDA, for easier reversing :3 +#ifdef __cplusplus +namespace Game +{ +#endif + + typedef float vec_t; + typedef vec_t vec2_t[2]; + typedef vec_t vec3_t[3]; + typedef vec_t vec4_t[4]; + + typedef enum + { + ASSET_TYPE_PHYSPRESET = 0, + ASSET_TYPE_PHYS_COLLMAP = 1, + ASSET_TYPE_XANIMPARTS = 2, + ASSET_TYPE_XMODELSURFS = 3, + ASSET_TYPE_XMODEL = 4, + ASSET_TYPE_MATERIAL = 5, + ASSET_TYPE_PIXELSHADER = 6, + ASSET_TYPE_VERTEXSHADER = 7, + ASSET_TYPE_VERTEXDECL = 8, + ASSET_TYPE_TECHNIQUE_SET = 9, + ASSET_TYPE_IMAGE = 10, + ASSET_TYPE_SOUND = 11, + ASSET_TYPE_SOUND_CURVE = 12, + ASSET_TYPE_LOADED_SOUND = 13, + ASSET_TYPE_CLIPMAP = 14, + ASSET_TYPE_CLIPMAP_PVS = 15, + ASSET_TYPE_COMWORLD = 16, + ASSET_TYPE_GAMEWORLD_SP = 17, + ASSET_TYPE_GAMEWORLD_MP = 18, + ASSET_TYPE_MAP_ENTS = 19, + ASSET_TYPE_FX_MAP = 20, + ASSET_TYPE_GFXWORLD = 21, + ASSET_TYPE_LIGHT_DEF = 22, + ASSET_TYPE_UI_MAP = 23, + ASSET_TYPE_FONT = 24, + ASSET_TYPE_MENULIST = 25, + ASSET_TYPE_MENU = 26, + ASSET_TYPE_LOCALIZE_ENTRY = 27, + ASSET_TYPE_WEAPON = 28, + ASSET_TYPE_SNDDRIVER_GLOBALS = 29, + ASSET_TYPE_FX = 30, + ASSET_TYPE_IMPACT_FX = 31, + ASSET_TYPE_AITYPE = 32, + ASSET_TYPE_MPTYPE = 33, + ASSET_TYPE_CHARACTER = 34, + ASSET_TYPE_XMODELALIAS = 35, + ASSET_TYPE_RAWFILE = 36, + ASSET_TYPE_STRINGTABLE = 37, + ASSET_TYPE_LEADERBOARDDEF = 38, + ASSET_TYPE_STRUCTUREDDATADEF = 39, + ASSET_TYPE_TRACER = 40, + ASSET_TYPE_VEHICLE = 41, + ASSET_TYPE_ADDON_MAP_ENTS = 42, + + ASSET_TYPE_COUNT, + ASSET_TYPE_INVALID = -1, + } XAssetType; + + typedef enum + { + DVAR_FLAG_NONE = 0x0, //no flags + DVAR_FLAG_SAVED = 0x1, //saves in config_mp.cfg for clients + DVAR_FLAG_LATCHED = 0x2, //no changing apart from initial value (although it might apply on a map reload, I think) + DVAR_FLAG_CHEAT = 0x4, //cheat + DVAR_FLAG_REPLICATED = 0x8, //on change, this is sent to all clients (if you are host) + DVAR_FLAG_UNKNOWN10 = 0x10, //unknown + DVAR_FLAG_UNKNOWN20 = 0x20, //unknown + DVAR_FLAG_UNKNOWN40 = 0x40, //unknown + DVAR_FLAG_UNKNOWN80 = 0x80, //unknown + DVAR_FLAG_USERCREATED = 0x100, //a 'set' type command created it + DVAR_FLAG_USERINFO = 0x200, //userinfo? + DVAR_FLAG_SERVERINFO = 0x400, //in the getstatus oob + DVAR_FLAG_WRITEPROTECTED = 0x800, //write protected + DVAR_FLAG_UNKNOWN1000 = 0x1000, //unknown + DVAR_FLAG_READONLY = 0x2000, //read only (same as 0x800?) + DVAR_FLAG_UNKNOWN4000 = 0x4000, //unknown + DVAR_FLAG_UNKNOWN8000 = 0x8000, //unknown + DVAR_FLAG_UNKNOWN10000 = 0x10000, //unknown + DVAR_FLAG_DEDISAVED = 0x1000000, //unknown + DVAR_FLAG_NONEXISTENT = 0xFFFFFFFF //no such dvar + } dvar_flag; + + typedef enum + { + DVAR_TYPE_BOOL = 0, + DVAR_TYPE_FLOAT = 1, + DVAR_TYPE_FLOAT_2 = 2, + DVAR_TYPE_FLOAT_3 = 3, + DVAR_TYPE_FLOAT_4 = 4, + DVAR_TYPE_INT = 5, + DVAR_TYPE_ENUM = 6, + DVAR_TYPE_STRING = 7, + DVAR_TYPE_COLOR = 8, + //DVAR_TYPE_INT64 = 9 only in Tx + } dvar_type; + // 67/72 bytes figured out + union dvar_value_t { + char* string; + int integer; + float value; + bool boolean; + float vec2[2]; + float vec3[3]; + float vec4[4]; + unsigned char color[4]; //to get float: multiply by 0.003921568859368563 - BaberZz + //__int64 integer64; only in Tx + }; + union dvar_maxmin_t { + int i; + float f; + }; + typedef struct dvar_t + { + //startbyte:endbyte + const char* name; //0:3 + const char* description; //4:7 + unsigned int flags; //8:11 + char type; //12:12 + bool modified; //13:15 + dvar_value_t current; //16:31 + dvar_value_t latched; //32:47 + dvar_value_t _default; //48:64 + dvar_maxmin_t min; //65:67 + dvar_maxmin_t max; //68:72 woooo + } dvar_t; + + typedef struct cmd_function_s + { + cmd_function_s *next; + const char *name; + const char *autoCompleteDir; + const char *autoCompleteExt; + void(__cdecl *function)(); + bool isKey; // Looks like this is true when the command is a key/button + } cmd_function_t; + +#pragma pack(push, 4) + struct kbutton_t + { + int down[2]; + unsigned int downtime; + unsigned int msec; + bool active; + bool wasPressed; + }; +#pragma pack(pop) + + struct SpeakerLevels + { + int speaker; + int numLevels; + float levels[2]; + }; + + struct ChannelMap + { + int entryCount; // how many entries are used + SpeakerLevels speakers[6]; + }; + + struct SpeakerMap + { + bool isDefault; + const char *name; + ChannelMap channelMaps[2][2]; + }; + + enum snd_alias_type_t : char + { + SAT_UNKNOWN = 0x0, + SAT_LOADED = 0x1, + SAT_STREAMED = 0x2 + }; + + struct StreamedSound + { + const char *dir; + const char *name; + }; + + struct SndCurve + { + const char *filename; + unsigned __int16 knotCount; + vec2_t knots[16]; + }; + + struct MssSound + { + char unknown1[8]; + int size; + char unknown2[22]; + char *data; // size = soundSize + }; + + struct LoadedSound + { + const char *name; + MssSound mssSound; + }; + + union SoundData + { + LoadedSound* loaded; + StreamedSound stream; + }; + + struct SoundFile // 0xC + { + snd_alias_type_t type; + bool exists; + SoundData data; + }; + + struct snd_alias_t + { + const char *name; + const char *subtitle; + const char *secondaryAliasName; + const char *chainAliasName; + const char *string4; + SoundFile *soundFile; + int sequence; + float volMin; + float volMax; + float pitchMin; + float pitchMax; + float distMin; + float distMax; + int flags; + float slavePercentage; + float probability; + float lfePercentage; + float centerPercentage; + int startDelay; + int pad; + SndCurve *volumeFalloffCurve; + float envelopMin; + float envelopMax; + float envelopPercentage; + SpeakerMap *speakerMap; + }; + + struct snd_alias_list_t + { + const char *name; + snd_alias_t *head; + int count; + }; + + typedef struct + { + const char *name; + int allocFlags; + int freeFlags; + } XZoneInfo; + + struct expression_s; + struct statement_s; + struct menuDef_t; + enum operationEnum; + + enum IWI_COMPRESSION + { + IWI_INVALID = 0x0, + IWI_ARGB = 0x1, + IWI_RGB8 = 0x2, + IWI_DXT1 = 0xB, + IWI_DXT3 = 0xC, + IWI_DXT5 = 0xD, + }; + + struct GfxImageFileHeader + { + char tag[3]; + char version; + int flags; + char format; + short dimensions[3]; + int fileSizeForPicmip[4]; + }; + + struct __declspec(align(4)) GfxImageLoadDef + { + char levelCount; + char flags; + __int16 dimensions[3]; + int format; + int resourceSize; + char data[1]; + }; + + enum MapType : char + { + MAPTYPE_NONE = 0x0, + MAPTYPE_INVALID1 = 0x1, + MAPTYPE_INVALID2 = 0x2, + MAPTYPE_2D = 0x3, + MAPTYPE_3D = 0x4, + MAPTYPE_CUBE = 0x5, + MAPTYPE_COUNT = 0x6, + }; + + struct GfxImage + { + union + { + GfxImageLoadDef* loadDef; +#ifdef __cplusplus + IDirect3DBaseTexture9 *basemap; + IDirect3DTexture9 *map; + IDirect3DVolumeTexture9 *volmap; + IDirect3DCubeTexture9 *cubemap; +#endif + }; + + MapType mapType; + char semantic; + char category; + char flags; + int cardMemory; + int dataLen1; + int dataLen2; + short height; + short width; + short depth; + char loaded; + char pad; + char* name; + }; + + struct WaterWritable + { + float floatTime; + }; + + struct complex_s + { + float real; + float imag; + }; + + struct water_t + { + WaterWritable writable; + complex_s *H0; + float *wTerm; + int M; + int N; + float Lx; + float Lz; + float gravity; + float windvel; + float winddir[2]; + float amplitude; + float codeConstant[4]; + GfxImage *image; + }; + +#define SEMANTIC_WATER_MAP 11 + + union MaterialTextureDefInfo + { + GfxImage *image; // MaterialTextureDef->semantic != SEMANTIC_WATER_MAP + water_t *water; // MaterialTextureDef->semantic == SEMANTIC_WATER_MAP + }; + + struct MaterialTextureDef + { + unsigned int nameHash; + char nameStart; + char nameEnd; + char sampleState; + char semantic; + MaterialTextureDefInfo info; + }; + + struct MaterialShaderArgument + { + short type; + short dest; + short paramID; + short more; + }; + + struct MaterialVertexDeclaration + { + const char* name; + int unknown; + char pad[28]; + /*IDirect3DVertexDeclaration9**/void* declarations[16]; + }; + + struct GfxPixelShaderLoadDef + { + char *cachedPart; + char *physicalPart; + unsigned __int16 cachedPartSize; + unsigned __int16 physicalPartSize; + }; + + struct MaterialPixelShader + { + const char* name; + GfxPixelShaderLoadDef loadDef; + }; + + struct GfxVertexShaderLoadDef + { + char *cachedPart; + char *physicalPart; + unsigned __int16 cachedPartSize; + unsigned __int16 physicalPartSize; + }; + + struct MaterialVertexShader + { + const char* name; + GfxVertexShaderLoadDef loadDef; + }; + + struct MaterialPass + { + MaterialVertexDeclaration* vertexDecl; + MaterialVertexShader* vertexShader; + MaterialPixelShader* pixelShader; + char argCount1; + char argCount2; + char argCount3; + char unk; + MaterialShaderArgument* argumentDef; + }; + + struct MaterialTechnique + { + char* name; + short pad2; + short numPasses; + MaterialPass passes[1]; + }; + + enum MaterialTechniqueType + { + TECHNIQUE_DEPTH_PREPASS = 0x0, + TECHNIQUE_BUILD_FLOAT_Z = 0x1, + TECHNIQUE_BUILD_SHADOWMAP_DEPTH = 0x2, + TECHNIQUE_BUILD_SHADOWMAP_COLOR = 0x3, + TECHNIQUE_UNLIT = 0x4, + TECHNIQUE_EMISSIVE = 0x5, + TECHNIQUE_EMISSIVE_DFOG = 0x6, + TECHNIQUE_EMISSIVE_SHADOW = 0x7, + TECHNIQUE_EMISSIVE_SHADOW_DFOG = 0x8, + TECHNIQUE_LIT_BEGIN = 0x9, + TECHNIQUE_LIT = 0x9, + TECHNIQUE_LIT_DFOG = 0xA, + TECHNIQUE_LIT_SUN = 0xB, + TECHNIQUE_LIT_SUN_DFOG = 0xC, + TECHNIQUE_LIT_SUN_SHADOW = 0xD, + TECHNIQUE_LIT_SUN_SHADOW_DFOG = 0xE, + TECHNIQUE_LIT_SPOT = 0xF, + TECHNIQUE_LIT_SPOT_DFOG = 0x10, + TECHNIQUE_LIT_SPOT_SHADOW = 0x11, + TECHNIQUE_LIT_SPOT_SHADOW_DFOG = 0x12, + TECHNIQUE_LIT_OMNI = 0x13, + TECHNIQUE_LIT_OMNI_DFOG = 0x14, + TECHNIQUE_LIT_OMNI_SHADOW = 0x15, + TECHNIQUE_LIT_OMNI_SHADOW_DFOG = 0x16, + TECHNIQUE_LIT_INSTANCED = 0x17, + TECHNIQUE_LIT_INSTANCED_DFOG = 0x18, + TECHNIQUE_LIT_INSTANCED_SUN = 0x19, + TECHNIQUE_LIT_INSTANCED_SUN_DFOG = 0x1A, + TECHNIQUE_LIT_INSTANCED_SUN_SHADOW = 0x1B, + TECHNIQUE_LIT_INSTANCED_SUN_SHADOW_DFOG = 0x1C, + TECHNIQUE_LIT_INSTANCED_SPOT = 0x1D, + TECHNIQUE_LIT_INSTANCED_SPOT_DFOG = 0x1E, + TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW = 0x1F, + TECHNIQUE_LIT_INSTANCED_SPOT_SHADOW_DFOG = 0x20, + TECHNIQUE_LIT_INSTANCED_OMNI = 0x21, + TECHNIQUE_LIT_INSTANCED_OMNI_DFOG = 0x22, + TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW = 0x23, + TECHNIQUE_LIT_INSTANCED_OMNI_SHADOW_DFOG = 0x24, + TECHNIQUE_LIT_END = 0x25, + TECHNIQUE_LIGHT_SPOT = 0x25, + TECHNIQUE_LIGHT_OMNI = 0x26, + TECHNIQUE_LIGHT_SPOT_SHADOW = 0x27, + TECHNIQUE_FAKELIGHT_NORMAL = 0x28, + TECHNIQUE_FAKELIGHT_VIEW = 0x29, + TECHNIQUE_SUNLIGHT_PREVIEW = 0x2A, + TECHNIQUE_CASE_TEXTURE = 0x2B, + TECHNIQUE_WIREFRAME_SOLID = 0x2C, + TECHNIQUE_WIREFRAME_SHADED = 0x2D, + TECHNIQUE_DEBUG_BUMPMAP = 0x2E, + TECHNIQUE_DEBUG_BUMPMAP_INSTANCED = 0x2F, + TECHNIQUE_COUNT = 0x30, + TECHNIQUE_TOTAL_COUNT = 0x31, + TECHNIQUE_NONE = 0x32, + }; + + struct MaterialTechniqueSet + { + const char* name; + char worldVertFormat; + bool hasBeenUploaded; + char unused[1]; + MaterialTechniqueSet* remappedTechniques; + MaterialTechnique* techniques[48]; + }; + + struct MaterialConstantDef + { + int nameHash; + char name[12]; + vec4_t literal; + }; + + struct GfxDrawSurfFields + { + __int64 _bf0; + }; + + union GfxDrawSurf + { + GfxDrawSurfFields fields; + unsigned __int64 packed; + }; + + struct GfxStateBits + { + unsigned int loadBits[2]; + }; + +#pragma pack(push, 4) + struct Material + { + const char *name; + char gameFlags; + char sortKey; + char textureAtlasRowCount; + char textureAtlasColumnCount; + GfxDrawSurf drawSurf; + int surfaceTypeBits; + unsigned __int16 hashIndex; + unsigned __int16 pad; + char stateBitsEntry[48]; + char textureCount; + char constantCount; + char stateBitsCount; + char stateFlags; + char cameraRegion; + MaterialTechniqueSet *techniqueSet; + MaterialTextureDef *textureTable; + MaterialConstantDef *constantTable; + GfxStateBits *stateBitTable; + }; +#pragma pack(pop) + + struct TracerDef + { + const char * name; + Material * material; + unsigned int drawInterval; + float speed; + float beamLength; + float beamWidth; + float screwRadius; + float screwDist; + float colors[5][4]; + }; + + struct keyname_t + { + const char *name; + int keynum; + }; + + struct ItemFloatExpressionEntry + { + int target; + const char *s1; + const char *s2; + }; + + // Taken from menudefinition.h + enum itemTextStyle + { + ITEM_TEXTSTYLE_NORMAL = 0, // normal text + ITEM_TEXTSTYLE_SHADOWED = 3, // drop shadow ( need a color for this ) + ITEM_TEXTSTYLE_SHADOWEDMORE = 6, // drop shadow ( need a color for this ) + ITEM_TEXTSTYLE_BORDERED = 7, // border (stroke) + ITEM_TEXTSTYLE_BORDEREDMORE = 8, // more border :P + ITEM_TEXTSTYLE_MONOSPACE = 128, + ITEM_TEXTSTYLE_MONOSPACESHADOWED = 132, + }; + +#define ITEM_TYPE_TEXT 0 // simple text +#define ITEM_TYPE_BUTTON 1 // button, basically text with a border +#define ITEM_TYPE_RADIOBUTTON 2 // toggle button, may be grouped +#define ITEM_TYPE_CHECKBOX 3 // check box +#define ITEM_TYPE_EDITFIELD 4 // editable text, associated with a dvar +#define ITEM_TYPE_COMBO 5 // drop down list +#define ITEM_TYPE_LISTBOX 6 // scrollable list +#define ITEM_TYPE_MODEL 7 // model +#define ITEM_TYPE_OWNERDRAW 8 // owner draw, name specs what it is +#define ITEM_TYPE_NUMERICFIELD 9 // editable text, associated with a dvar +#define ITEM_TYPE_SLIDER 10 // mouse speed, volume, etc. +#define ITEM_TYPE_YESNO 11 // yes no dvar setting +#define ITEM_TYPE_MULTI 12 // multiple list setting, enumerated +#define ITEM_TYPE_DVARENUM 13 // multiple list setting, enumerated from a dvar +#define ITEM_TYPE_BIND 14 // bind +#define ITEM_TYPE_MENUMODEL 15 // special menu model +#define ITEM_TYPE_VALIDFILEFIELD 16 // text must be valid for use in a dos filename +#define ITEM_TYPE_DECIMALFIELD 17 // editable text, associated with a dvar, which allows decimal input +#define ITEM_TYPE_UPREDITFIELD 18 // editable text, associated with a dvar +#define ITEM_TYPE_GAME_MESSAGE_WINDOW 19 // game message window +#define ITEM_TYPE_NEWSTICKER 20 // horizontal scrollbox +#define ITEM_TYPE_TEXTSCROLL 21 // vertical scrollbox +#define ITEM_TYPE_EMAILFIELD 22 +#define ITEM_TYPE_PASSWORDFIELD 23 + + struct MenuEventHandlerSet; + struct Statement_s; + + struct UIFunctionList + { + int totalFunctions; + Statement_s **functions; + }; + + struct StaticDvar + { + /*dvar_t*/ + void *dvar; + char *dvarName; + }; + + struct StaticDvarList + { + int numStaticDvars; + StaticDvar **staticDvars; + }; + + struct StringList + { + int totalStrings; + const char **strings; + }; + + struct ExpressionSupportingData + { + UIFunctionList uifunctions; + StaticDvarList staticDvarList; + StringList uiStrings; + }; + + enum expDataType : int + { + VAL_INT = 0x0, + VAL_FLOAT = 0x1, + VAL_STRING = 0x2, + VAL_FUNCTION = 0x3, + }; + + struct ExpressionString + { + const char *string; + }; + + union operandInternalDataUnion + { + int intVal; + float floatVal; + ExpressionString stringVal; + Statement_s *function; + }; + + struct Operand + { + expDataType dataType; + operandInternalDataUnion internals; + }; + + union entryInternalData + { + //operationEnum op; + Operand operand; + }; + + /* expressionEntry->type */ +#define OPERATOR 0 +#define OPERAND 1 + + struct expressionEntry // 0xC + { + int type; + entryInternalData data; + }; + + struct Statement_s // 0x18 + { + int numEntries; + expressionEntry *entries; + ExpressionSupportingData *supportingData; + char unknown[0xC]; // ? + }; + + struct SetLocalVarData + { + const char *localVarName; + Statement_s *expression; + }; + + struct ConditionalScript + { + MenuEventHandlerSet *eventHandlerSet; + Statement_s *eventExpression; // loads this first + }; + + union EventData + { + const char *unconditionalScript; + ConditionalScript *conditionalScript; + MenuEventHandlerSet *elseScript; + SetLocalVarData *setLocalVarData; + }; + + enum EventType + { + EVENT_UNCONDITIONAL = 0x0, + EVENT_IF = 0x1, + EVENT_ELSE = 0x2, + EVENT_SET_LOCAL_VAR_BOOL = 0x3, + EVENT_SET_LOCAL_VAR_INT = 0x4, + EVENT_SET_LOCAL_VAR_FLOAT = 0x5, + EVENT_SET_LOCAL_VAR_STRING = 0x6, + EVENT_COUNT = 0x7, + }; + + struct MenuEventHandler + { + EventData eventData; + EventType eventType; + }; + + struct MenuEventHandlerSet + { + int eventHandlerCount; + MenuEventHandler **eventHandlers; + }; + + struct ItemKeyHandler + { + int key; + MenuEventHandlerSet *action; + ItemKeyHandler *next; + }; + +#pragma pack(push, 4) + struct rectDef_s + { + float x; + float y; + float w; + float h; + char horzAlign; + char vertAlign; + }; +#pragma pack(pop) + + /* windowDef_t->dynamicFlags */ + // 0x1 +#define WINDOWDYNAMIC_HASFOCUS 0x00000002 +#define WINDOWDYNAMIC_VISIBLE 0x00000004 +#define WINDOWDYNAMIC_FADEOUT 0x00000010 +#define WINDOWDYNAMIC_FADEIN 0x00000020 + // 0x40 + // 0x80 +#define WINDOWDYNAMIC_CLOSED 0x00000800 + // 0x2000 +#define WINDOWDYNAMIC_BACKCOLOR 0x00008000 +#define WINDOWDYNAMIC_FORECOLOR 0x00010000 + + /* windowDef_t->staticFlags */ +#define WINDOWSTATIC_DECORATION 0x00100000 +#define WINDOWSTATIC_HORIZONTALSCROLL 0x00200000 +#define WINDOWSTATIC_SCREENSPACE 0x00400000 +#define WINDOWSTATIC_AUTOWRAPPED 0x00800000 +#define WINDOWSTATIC_POPUP 0x01000000 +#define WINDOWSTATIC_OUTOFBOUNDSCLICK 0x02000000 +#define WINDOWSTATIC_LEGACYSPLITSCREENSCALE 0x04000000 +#define WINDOWSTATIC_HIDDENDURINGFLASH 0x10000000 +#define WINDOWSTATIC_HIDDENDURINGSCOPE 0x20000000 +#define WINDOWSTATIC_HIDDENDURINGUI 0x40000000 +#define WINDOWSTATIC_TEXTONLYFOCUS 0x80000000 + + struct windowDef_t // 0xA4 + { + const char *name; // 0x00 + rectDef_s rect; + rectDef_s rectClient; + char *group; // 0x2C + int style; // 0x30 + int border; // 0x34 + int ownerDraw; // 0x38 + int ownerDrawFlags; // 0x3C + float borderSize; // 0x40 + int staticFlags; // 0x44 + int dynamicFlags; // 0x48 + int nextTime; // 0x4C + float foreColor[4]; // 0x50 + float backColor[4]; // 0x60 + float borderColor[4];// 0x70 + float outlineColor[4];// 0x80 + float disableColor[4];// 0x90 + Material *background; // 0xA0 + }; + + enum ItemFloatExpressionTarget + { + ITEM_FLOATEXP_TGT_RECT_X = 0x0, + ITEM_FLOATEXP_TGT_RECT_Y = 0x1, + ITEM_FLOATEXP_TGT_RECT_W = 0x2, + ITEM_FLOATEXP_TGT_RECT_H = 0x3, + ITEM_FLOATEXP_TGT_FORECOLOR_R = 0x4, + ITEM_FLOATEXP_TGT_FORECOLOR_G = 0x5, + ITEM_FLOATEXP_TGT_FORECOLOR_B = 0x6, + ITEM_FLOATEXP_TGT_FORECOLOR_RGB = 0x7, + ITEM_FLOATEXP_TGT_FORECOLOR_A = 0x8, + ITEM_FLOATEXP_TGT_GLOWCOLOR_R = 0x9, + ITEM_FLOATEXP_TGT_GLOWCOLOR_G = 0xA, + ITEM_FLOATEXP_TGT_GLOWCOLOR_B = 0xB, + ITEM_FLOATEXP_TGT_GLOWCOLOR_RGB = 0xC, + ITEM_FLOATEXP_TGT_GLOWCOLOR_A = 0xD, + ITEM_FLOATEXP_TGT_BACKCOLOR_R = 0xE, + ITEM_FLOATEXP_TGT_BACKCOLOR_G = 0xF, + ITEM_FLOATEXP_TGT_BACKCOLOR_B = 0x10, + ITEM_FLOATEXP_TGT_BACKCOLOR_RGB = 0x11, + ITEM_FLOATEXP_TGT_BACKCOLOR_A = 0x12, + ITEM_FLOATEXP_TGT__COUNT = 0x13, + }; + + struct ItemFloatExpression + { + ItemFloatExpressionTarget target; + Statement_s *expression; + }; + + struct editFieldDef_s + { + float minVal; + float maxVal; + float defVal; + float range; + int maxChars; + int maxCharsGotoNext; + int maxPaintChars; + int paintOffset; + }; + + struct multiDef_s // 0x188 + { + const char *dvarList[32]; + const char *dvarStr[32]; + float dvarValue[32]; + int count; + int strDef; + }; + + struct columnInfo_s + { + int xpos; + int width; + int maxChars; + int alignment; + }; + + struct listBoxDef_s // 0x144 + { + // somethings not right here + int startPos[2]; + int endPos[2]; + float elementWidth; + float elementHeight; + int elementStyle; + int numColumns; + columnInfo_s columnInfo[16]; + MenuEventHandlerSet *doubleClick; // 0xC8 + int notselectable; + int noscrollbars; + int usepaging; + float selectBorder[4]; + Material *selectIcon; + }; + + struct newsTickerDef_s + { + int feedId; + int speed; + int spacing; + }; + + struct textScrollDef_s + { + int startTime; + }; + + union itemDefData_t + { + listBoxDef_s *listBox; + editFieldDef_s *editField; + newsTickerDef_s *ticker; + multiDef_s *multiDef; + const char *enumDvarName; + textScrollDef_s *scroll; + void *data; + }; + + struct itemDef_t + { + windowDef_t window; + rectDef_s textRect; + int type; + int dataType; + int alignment; + int fontEnum; + int textAlignMode; + float textAlignX; + float textAlignY; + float textScale; + int textStyle; + int gameMsgWindowIndex; + int gameMsgWindowMode; + const char *text; + int textSaveGameInfo; + int parent; + MenuEventHandlerSet *mouseEnterText; + MenuEventHandlerSet *mouseExitText; + MenuEventHandlerSet *mouseEnter; + MenuEventHandlerSet *mouseExit; + MenuEventHandlerSet *action; + MenuEventHandlerSet *accept; + MenuEventHandlerSet *onFocus; + MenuEventHandlerSet *leaveFocus; + const char *dvar; + const char *dvarTest; + ItemKeyHandler *onKey; + const char *enableDvar; + const char *localVar; + int dvarFlags; + const char *focusSound; + float special; + int cursorPos; + itemDefData_t typeData; + int imageTrack; + int floatExpressionCount; + ItemFloatExpression *floatExpressions; + Statement_s *visibleExp; + Statement_s *disabledExp; + Statement_s *textExp; + Statement_s *materialExp; + float glowColor[4]; + bool decayActive; + int fxBirthTime; + int fxLetterTime; + int fxDecayStartTime; + int fxDecayDuration; + int lastSoundPlayedTime; + }; + + struct menuTransition // 0x18 + { + int transitionType; + int startTime; + float startVal; + float endVal; + float time; + int endTriggerType; + }; + + struct menuDef_t + { + windowDef_t window; + int font; + int fullscreen; + int itemCount; + int fontIndex; + int cursorItems; + int fadeCycle; + float fadeClamp; + float fadeAmount; + float fadeInAmount; + float blurRadius; + MenuEventHandlerSet *onOpen; + MenuEventHandlerSet *onRequestClose; + MenuEventHandlerSet *onClose; + MenuEventHandlerSet *onEsc; + ItemKeyHandler *onKey; + Statement_s *visibleExp; + const char *allowedBinding; + const char *soundLoop; + int imageTrack; + float focusColor[4]; + Statement_s *rectXExp; + Statement_s *rectYExp; + Statement_s *rectHExp; + Statement_s *rectWExp; + Statement_s *openSoundExp; + Statement_s *closeSoundExp; + itemDef_t **items; + char unknown[112]; + ExpressionSupportingData *expressionData; + }; + + struct MenuList + { + char *name; + int menuCount; + menuDef_t **menus; + }; + +#define FS_SEEK_CUR 0 +#define FS_SEEK_END 1 +#define FS_SEEK_SET 2 + + enum FsListBehavior_e + { + FS_LIST_PURE_ONLY = 0x0, + FS_LIST_ALL = 0x1, + }; + + typedef enum + { + NA_BOT, + NA_BAD, // an address lookup failed + NA_LOOPBACK, + NA_BROADCAST, + NA_IP, + NA_IP6, // custom type + } netadrtype_t; + + typedef enum + { + NS_CLIENT, + NS_SERVER + } netsrc_t; + + typedef union + { + unsigned char bytes[4]; + DWORD full; + } netIP_t; + + typedef struct + { + netadrtype_t type; + netIP_t ip; + unsigned short port; + unsigned char ipx[8]; + } netadr_t; + + typedef struct + { + int overflowed; + int readOnly; + char *data; + char *splitData; + int maxsize; + int cursize; + int splitSize; + int readcount; + int bit; + int lastEntityRef; + } msg_t; + + enum playerFlag + { + PLAYER_FLAG_NOCLIP = 1 << 0, + PLAYER_FLAG_UFO = 1 << 1, + PLAYER_FLAG_FROZEN = 1 << 2, + }; + + typedef struct gclient_s + { + unsigned char pad[12764]; + unsigned int team; + char pad2[436]; + int flags; + char pad3[724]; + } gclient_t; + + typedef struct gentity_s + { + unsigned char pad[312]; // 0 + float origin[3]; // 312 + float angles[3]; // 324 + char pad2[8]; + gclient_t* client; // 344 + unsigned char pad3[28]; + short classname; + short pad4; + unsigned char pad5[248]; + } gentity_t; + +#pragma pack(push, 1) + typedef struct client_s + { + // 0 + int state; + // 4 + char pad[36]; + // 40 + netadr_t addr; + // 60 + char pad1[1568]; + // 1628 + char connectInfoString[1024]; + // 2652 + char pad2[133192]; + // 135844 + char name[16]; + // 135860 + char pad3[12]; + // 135872 + int snapNum; + // 135876 + int pad4; + // 135880 + short ping; + // 135882 + //char pad5[142390]; + char pad5[133158]; + // 269040 + int isBot; + // 269044 + char pad6[9228]; + // 278272 + unsigned __int64 steamid; + // 278280 + char pad7[403592]; + } client_t; +#pragma pack(pop) + + // Q3TA precompiler code + + //undef if binary numbers of the form 0b... or 0B... are not allowed +#define BINARYNUMBERS + //undef if not using the token.intvalue and token.floatvalue +#define NUMBERVALUE + //use dollar sign also as punctuation +#define DOLLAR + + //maximum token length +#define MAX_TOKEN 1024 + + //punctuation + typedef struct punctuation_s + { + char *p; //punctuation character(s) + int n; //punctuation indication + struct punctuation_s *next; //next punctuation + } punctuation_t; + + //token + typedef struct token_s + { + char string[MAX_TOKEN]; //available token + int type; //last read token type + int subtype; //last read token sub type +#ifdef NUMBERVALUE + unsigned long int intvalue; //integer value + long double floatvalue; //floating point value +#endif //NUMBERVALUE + char *whitespace_p; //start of white space before token + char *endwhitespace_p; //start of white space before token + int line; //line the token was on + int linescrossed; //lines crossed in white space + struct token_s *next; //next token in chain + } token_t; + + //script file + typedef struct script_s + { + char filename[64]; //file name of the script + char *buffer; //buffer containing the script + char *script_p; //current pointer in the script + char *end_p; //pointer to the end of the script + char *lastscript_p; //script pointer before reading token + char *whitespace_p; //begin of the white space + char *endwhitespace_p; //end of the white space + int length; //length of the script in bytes + int line; //current line in script + int lastline; //line before reading token + int tokenavailable; //set by UnreadLastToken + int flags; //several script flags + punctuation_t *punctuations; //the punctuations used in the script + punctuation_t **punctuationtable; + token_t token; //available token + struct script_s *next; //next script in a chain + } script_t; + + //macro definitions + typedef struct define_s + { + char *name; //define name + int flags; //define flags + int builtin; // > 0 if builtin define + int numparms; //number of define parameters + token_t *parms; //define parameters + token_t *tokens; //macro tokens (possibly containing parm tokens) + struct define_s *next; //next defined macro in a list + struct define_s *hashnext; //next define in the hash chain + } define_t; + + //indents + //used for conditional compilation directives: + //#if, #else, #elif, #ifdef, #ifndef + typedef struct indent_s + { + int type; //indent type + int skip; //true if skipping current indent + script_t *script; //script the indent was in + struct indent_s *next; //next indent on the indent stack + } indent_t; + + //source file + typedef struct source_s + { + char filename[64]; //file name of the script + char includepath[64]; //path to include files + punctuation_t *punctuations; //punctuations to use + script_t *scriptstack; //stack with scripts of the source + token_t *tokens; //tokens to read first + define_t *defines; //list with macro definitions + define_t **definehash; //hash chain with defines + indent_t *indentstack; //stack with indents + int skip; // > 0 if skipping conditional code + token_t token; //last read token + } source_t; + +#define MAX_TOKENLENGTH 1024 + + typedef struct pc_token_s + { + int type; + int subtype; + int intvalue; + float floatvalue; + char string[MAX_TOKENLENGTH]; + } pc_token_t; + + //token types +#define TT_STRING 1 // string +#define TT_LITERAL 2 // literal +#define TT_NUMBER 3 // number +#define TT_NAME 4 // name +#define TT_PUNCTUATION 5 // punctuation + +#define KEYWORDHASH_SIZE 512 + + typedef struct keywordHash_s + { + char *keyword; + bool(*func)(menuDef_t *item, int handle); + //struct keywordHash_s *next; + } keywordHash_t; + + enum UILocalVarType + { + UILOCALVAR_INT = 0x0, + UILOCALVAR_FLOAT = 0x1, + UILOCALVAR_STRING = 0x2, + }; + + struct UILocalVar + { + UILocalVarType type; + const char *name; + union + { + int integer; + float value; + const char *string; + }; + }; + + struct UILocalVarContext + { + UILocalVar table[256]; + }; + + struct UiContext + { +// int localClientNum; +// float bias; +// int realTime; +// int frameTime; +// int cursorx; +// int cursory; +// int debug; +// int screenWidth; +// int screenHeight; +// float screenAspect; +// float FPS; +// float blurRadiusOut; + char pad[56]; + menuDef_t *menus[512]; + char pad2[512]; + int menuCount; + // Unsure if below is correct + menuDef_t *menuStack[16]; + int openMenuCount; + UILocalVarContext localVars; + }; + + struct LocalizeEntry + { + const char* value; + const char* name; + }; + + struct Bounds + { + vec3_t midPoint; + vec3_t halfSize; + }; + + struct TriggerModel + { + int contents; + unsigned short hullCount; + unsigned short firstHull; + }; + + struct TriggerHull + { + Bounds bounds; + int contents; + unsigned short slabCount; + unsigned short firstSlab; + }; + + struct TriggerSlab + { + vec3_t dir; + float midPoint; + float halfSize; + }; + + struct MapTriggers + { + int modelCount; + TriggerModel* models; // sizeof 8 + int hullCount; + TriggerHull* hulls; // sizeof 32 + int slabCount; + TriggerSlab* slabs; // sizeof 20 + }; + + struct Stage + { + char * stageName; + float offset[3]; + int flags; + }; + + struct MapEnts + { + const char *name; + char *entityString; + int numEntityChars; + MapTriggers trigger; + Stage * stages; + char stageCount; + }; + + struct StringTableCell + { + const char *string; + int hash; + }; + + struct StringTable + { + const char *name; + int columnCount; + int rowCount; + StringTableCell *values; + }; + + struct RawFile + { + const char* name; + int sizeCompressed; + int sizeUnCompressed; + char * compressedData; + }; + + struct FontEntry + { + unsigned short character; + unsigned char padLeft; + unsigned char padTop; + unsigned char padRight; + unsigned char width; + unsigned char height; + unsigned char const0; + float uvLeft; + float uvTop; + float uvRight; + float uvBottom; + }; + + typedef struct Font_s + { + char* name; + int size; + int entries; + Material* image; + Material* glowImage; + FontEntry* characters; + } Font; + + typedef enum + { + STRUCTURED_DATA_INT = 0, + STRUCTURED_DATA_BYTE = 1, + STRUCTURED_DATA_BOOL = 2, + STRUCTURED_DATA_STRING = 3, + STRUCTURED_DATA_ENUM = 4, + STRUCTURED_DATA_STRUCT = 5, + STRUCTURED_DATA_INDEXEDARR = 6, + STRUCTURED_DATA_ENUMARR = 7, + STRUCTURED_DATA_FLOAT = 8, + STRUCTURED_DATA_SHORT = 9 + } StructuredDataType; + + typedef struct + { + StructuredDataType type; + union + { + int index; + }; + int offset; + } StructuredDataItem; + + typedef struct + { + const char* name; + StructuredDataItem item; + } StructuredDataStructProperty; + + typedef struct + { + int numProperties; + StructuredDataStructProperty* property; + int unknown1; + int unknown2; + } StructuredDataStruct; + + typedef struct + { + int enumIndex; + StructuredDataItem item; + } StructuredDataEnumedArray; + + typedef struct + { + const char* key; + int index; + } StructuredDataEnumEntry; + + typedef struct + { + int numIndices; + int unknown; + StructuredDataEnumEntry* indices; + } StructuredDataEnum; + + typedef struct + { + int numItems; + StructuredDataItem item; + } StructuredDataIndexedArray; + + typedef struct + { + int version; + unsigned int hash; + int numEnums; + StructuredDataEnum* enums; + int numStructs; + StructuredDataStruct* structs; + int numIndexedArrays; + StructuredDataIndexedArray* indexedArrays; + int numEnumArrays; + StructuredDataEnumedArray* enumArrays; + StructuredDataItem rootItem; + } StructuredDataDef; + + typedef struct + { + const char* name; + int count; + StructuredDataDef* data; + } StructuredDataDefSet; + + typedef struct + { + StructuredDataDef* data; + StructuredDataItem* item; + int offset; + int error; + } structuredDataFindState_t; + + struct XSurfaceCollisionLeaf + { + unsigned __int16 triangleBeginIndex; + }; + + struct XSurfaceCollisionAabb + { + unsigned __int16 mins[3]; + unsigned __int16 maxs[3]; + }; + + struct XSurfaceCollisionNode + { + XSurfaceCollisionAabb aabb; + unsigned __int16 childBeginIndex; + unsigned __int16 childCount; + }; + + struct XSurfaceCollisionTree + { + float trans[3]; + float scale[3]; + unsigned int nodeCount; + XSurfaceCollisionNode *nodes; + unsigned int leafCount; + XSurfaceCollisionLeaf *leafs; + }; + + struct XRigidVertList + { + unsigned __int16 boneOffset; + unsigned __int16 vertCount; + unsigned __int16 triOffset; + unsigned __int16 triCount; + XSurfaceCollisionTree *collisionTree; + }; + + union PackedTexCoords + { + unsigned int packed; + }; + + union GfxColor + { + unsigned int packed; + char array[4]; + unsigned char uArray[4]; + }; + + union PackedUnitVec + { + unsigned int packed; + }; + + struct GfxPackedVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + PackedTexCoords texCoord; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct XSurfaceVertexInfo + { + __int16 vertCount[4]; + unsigned __int16 *vertsBlend; + }; + + struct XSurface + { + char tileMode; + bool deformed; + unsigned __int16 vertCount; + unsigned __int16 triCount; + char zoneHandle; + unsigned __int16 baseTriIndex; + unsigned __int16 baseVertIndex; + unsigned __int16 *triIndices; + XSurfaceVertexInfo vertInfo; + GfxPackedVertex *verts0; + unsigned int vertListCount; + XRigidVertList *vertList; + int partBits[6]; + }; + + struct XModelSurfs + { + const char* name; + XSurface* surfaces; + int numSurfaces; + int partBits[6]; + }; + + struct XModelLodInfo + { + float dist; + unsigned __int16 numsurfs; + unsigned __int16 surfIndex; + XModelSurfs *modelSurfs; + int partBits[6]; + XSurface *surfs; + char lod; + char smcBaseIndexPlusOne; + char smcSubIndexMask; + char smcBucket; + }; + + struct cplane_t + { + float normal[3]; + float dist; + char type; + char signbits; + char pad[2]; + }; + + struct cbrushside_t + { + cplane_t *plane; + unsigned __int16 materialNum; + char firstAdjacentSideOffset; + char edgeCount; + }; + + struct cbrushWrapper_t + { + short count; + cbrushside_t* brushSide; + char * brushEdge; + char pad[24]; + }; + +#pragma pack(push, 4) + struct BrushWrapper + { + float mins[3]; + float maxs[3]; + cbrushWrapper_t brush; + int totalEdgeCount; + cplane_t *planes; + }; +#pragma pack(pop) + + struct PhysGeomInfo + { + BrushWrapper *brush; + int type; + float orientation[3][3]; + float offset[3]; + float halfLengths[3]; + }; + + struct PhysMass + { + float centerOfMass[3]; + float momentsOfInertia[3]; + float productsOfInertia[3]; + }; + + struct PhysCollmap + { + const char *name; + unsigned int count; + PhysGeomInfo *geoms; + char unknown[0x18]; + PhysMass mass; + }; + + struct DObjAnimMat + { + float quat[4]; + float trans[3]; + float transWeight; + }; + + struct XModelCollTri_s + { + float plane[4]; + float svec[4]; + float tvec[4]; + }; + + struct XModelCollSurf_s + { + XModelCollTri_s *collTris; + int numCollTris; + Bounds bounds; + int boneIdx; + int contents; + int surfFlags; + }; + + struct PhysPreset + { + const char *name; + int type; + float mass; + float bounce; + float friction; + float bulletForceScale; + float explosiveForceScale; + const char *sndAliasPrefix; + float piecesSpreadFraction; + float piecesUpwardVelocity; + bool tempDefaultToCylinder; + }; + + struct XBoneInfo + { + float offset[3]; + float bounds[3]; + float radiusSquared; + }; + + struct XModel + { + const char *name; + char numBones; + char numRootBones; + char numsurfs; + char lodRampType; + float scale; + unsigned int noScalePartBits[6]; + unsigned __int16 *boneNames; + char *parentList; + __int16 *quats; + float *trans; + char *partClassification; + DObjAnimMat *baseMat; + Material **materialHandles; + XModelLodInfo lodInfo[4]; + char maxLoadedLod; + char numLods; + char collLod; + char flags; + XModelCollSurf_s *collSurfs; + int numCollSurfs; + int contents; + XBoneInfo *boneInfo; + float radius; + Bounds bounds; + int memUsage; + bool bad; + PhysPreset *physPreset; + PhysCollmap *physCollmap; + }; + + struct CModelAllocData + { + void* mainArray; + void* vertexBuffer; + void* indexBuffer; + }; + + struct CModelSectionHeader + { + int size; + int offset; + int fixupStart; + int fixupCount; + void* buffer; + }; + + enum CModelSection + { + SECTION_MAIN = 0, + SECTION_INDEX = 1, + SECTION_VERTEX = 2, + SECTION_FIXUP = 3, + }; + + struct CModelHeader + { + int version; + unsigned int signature; + CModelSectionHeader sectionHeader[4]; + }; + + struct DSkelPartBits + { + int anim[4]; + int control[4]; + int skel[4]; + }; + + struct DSkel + { + DSkelPartBits partBits; + int timeStamp; + DObjAnimMat *mat; + }; + +#pragma pack(push, 2) + struct DObj + { + /*XAnimTree_s*/void *tree; + unsigned __int16 duplicateParts; + unsigned __int16 entnum; + char duplicatePartsSize; + char numModels; + char numBones; + char pad; + unsigned int ignoreCollision; + volatile int locked; + DSkel skel; + float radius; + int hidePartBits[4]; + char pad2[56]; + XModel **models; + }; +#pragma pack(pop) + + union XAnimDynamicIndices + { + char _1[1]; + unsigned __int16 _2[1]; + }; + + union XAnimDynamicFrames + { + char(*_1)[3]; + unsigned __int16(*_2)[3]; + }; + + struct XAnimNotifyInfo + { + unsigned __int16 name; + float time; + }; + + union XAnimIndices + { + char* _1; + unsigned short *_2; + void* data; + }; + + struct XAnimPartTransFrames + { + float mins[3]; + float size[3]; + XAnimDynamicFrames frames; + XAnimDynamicIndices indices; + }; + + union XAnimPartTransData + { + XAnimPartTransFrames frames; + float frame0[3]; + }; + + struct XAnimPartTrans + { + unsigned __int16 size; + char smallTrans; + XAnimPartTransData u; + }; + + struct XAnimDeltaPartQuatDataFrames2 + { + __int16 *frames; + char indices[1]; + }; + + union XAnimDeltaPartQuatData2 + { + XAnimDeltaPartQuatDataFrames2 frames; + __int16 frame0[2]; + }; + + struct XAnimDeltaPartQuat2 + { + unsigned __int16 size; + XAnimDeltaPartQuatData2 u; + }; + + struct XAnimDeltaPartQuatDataFrames + { + __int16 *frames; + char indices[1]; + }; + + union XAnimDeltaPartQuatData + { + XAnimDeltaPartQuatDataFrames frames; + __int16 frame0[4]; + }; + + struct XAnimDeltaPartQuat + { + unsigned __int16 size; + XAnimDeltaPartQuatData u; + }; + + struct XAnimDeltaPart + { + XAnimPartTrans *trans; + XAnimDeltaPartQuat2 *quat2; + XAnimDeltaPartQuat *quat; + }; + + enum XAnimPartType + { + PART_TYPE_NO_QUAT = 0x0, + PART_TYPE_HALF_QUAT = 0x1, + PART_TYPE_FULL_QUAT = 0x2, + PART_TYPE_HALF_QUAT_NO_SIZE = 0x3, + PART_TYPE_FULL_QUAT_NO_SIZE = 0x4, + PART_TYPE_SMALL_TRANS = 0x5, + PART_TYPE_TRANS = 0x6, + PART_TYPE_TRANS_NO_SIZE = 0x7, + PART_TYPE_NO_TRANS = 0x8, + PART_TYPE_ALL = 0x9, + }; + + enum XAnimFlags + { + XANIM_LOOP_SYNC_TIME = 0x1, + XANIM_NONLOOP_SYNC_TIME = 0x2, + XANIM_SYNC_ROOT = 0x4, + XANIM_COMPLETE = 0x8, + XANIM_ADDITIVE = 0x10, + XANIM_CLIENT = 0x20, + XANIM_SEPARATE = 0x40, + XANIM_FORCELOAD = 0x80, + XANIM_PROPOGATE_FLAGS = 0x63, + }; + + struct XAnimParts + { + const char * name; // 0 + unsigned short dataByteCount; // 4 + unsigned short dataShortCount; // 6 + unsigned short dataIntCount; // 8 + unsigned short randomDataByteCount; // 10 - 0xA + unsigned short randomDataIntCount;// 12 - 0xC + unsigned short framecount; // 14 - 0xE + char bLoop; // 16 + char boneCount[10]; // 17 + char notetrackCount; // 27 + bool pad1; // 28 + bool bDelta; // 29 + char assetType; // 30 + char pad2; // 31 + int randomDataShortCount; // 32 - 0x20 + int indexcount; // 36 - 0x24 + float framerate; // 40 - 0x28 + float frequency; // 44 - 0x2C + short * tagnames; // 48 - 0x30 + char *dataByte;// 52 - 0x34 + short *dataShort; // 56 - 0x38 + int *dataInt; // 60 - 0x3C + short *randomDataShort; // 64 - 0x40 + char *randomDataByte; // 68 - 0x44 + int *randomDataInt; // 72 - 0x48 + XAnimIndices indices; // 76 - 0x4C + XAnimNotifyInfo* notetracks; // 80 - 0x50 + XAnimDeltaPart * delta; // 84 - 0x54 + // 88 - 0x58 + }; + +/* FxEffectDef::flags */ +#define FX_ELEM_LOOPING 0x1 +#define FX_ELEM_USE_RAND_COLOR 0x2 +#define FX_ELEM_USE_RAND_ALPHA 0x4 +#define FX_ELEM_USE_RAND_SIZE0 0x8 +#define FX_ELEM_USE_RAND_SIZE1 0x10 +#define FX_ELEM_USE_RAND_SCALE 0x20 +#define FX_ELEM_USE_RAND_ROT_DELTA 0x40 +#define FX_ELEM_MOD_COLOR_BY_ALPHA 0x80 +#define FX_ELEM_USE_RAND_VEL0 0x100 +#define FX_ELEM_USE_RAND_VEL1 0x200 +#define FX_ELEM_USE_BACK_COMPAT_VEL 0x400 +#define FX_ELEM_ABS_VEL0 0x800 +#define FX_ELEM_ABS_VEL1 0x1000 +#define FX_ELEM_PLAY_ON_TOUCH 0x2000 +#define FX_ELEM_PLAY_ON_DEATH 0x4000 +#define FX_ELEM_PLAY_ON_RUN 0x8000 +#define FX_ELEM_BOUNDING_SPHERE 0x10000 +#define FX_ELEM_USE_ITEM_CLIP 0x20000 +#define FX_ELEM_DECAL_FADE_IN 0x40000 +#define FX_ELEM_DISABLED 0x80000000 + +/* FxElemDef::flags */ +#define FX_ELEM_SPAWN_RELATIVE_TO_EFFECT 0x2 +#define FX_ELEM_SPAWN_FRUSTUM_CULL 0x4 +#define FX_ELEM_RUNNER_USES_RAND_ROT 0x8 +#define FX_ELEM_SPAWN_OFFSET_NONE 0x0 +#define FX_ELEM_SPAWN_OFFSET_SPHERE 0x10 +#define FX_ELEM_SPAWN_OFFSET_CYLINDER 0x20 +#define FX_ELEM_SPAWN_OFFSET_MASK 0x30 +#define FX_ELEM_RUN_RELATIVE_TO_WORLD 0x0 +#define FX_ELEM_RUN_RELATIVE_TO_SPAWN 0x40 +#define FX_ELEM_RUN_RELATIVE_TO_EFFECT 0x80 +#define FX_ELEM_RUN_RELATIVE_TO_OFFSET 0xC0 +#define FX_ELEM_RUN_MASK 0xC0 +#define FX_ELEM_USE_COLLISION 0x100 +#define FX_ELEM_DIE_ON_TOUCH 0x200 +#define FX_ELEM_DRAW_PAST_FOG 0x400 +#define FX_ELEM_DRAW_WITH_VIEWMODEL 0x800 +#define FX_ELEM_BLOCK_SIGHT 0x1000 +#define FX_ELEM_HAS_VELOCITY_GRAPH_LOCAL 0x1000000 +#define FX_ELEM_HAS_VELOCITY_GRAPH_WORLD 0x2000000 +#define FX_ELEM_HAS_GRAVITY 0x4000000 +#define FX_ELEM_USE_MODEL_PHYSICS 0x8000000 +#define FX_ELEM_NONUNIFORM_SCALE 0x10000000 +#define FX_ELEM_CLOUD_SHAPE_CUBE 0x0 +#define FX_ELEM_CLOUD_SHAPE_SPHERE_LARGE 0x20000000 +#define FX_ELEM_CLOUD_SHAPE_SPHERE_MEDIUM 0x40000000 +#define FX_ELEM_CLOUD_SHAPE_SPHERE_SMALL 0x60000000 +#define FX_ELEM_CLOUD_MASK 0x60000000 +#define FX_ELEM_DISABLE_FOUNTAIN_COLLISION 0x80000000 +#define FX_ELEM_DRAW_IN_THERMAL_ONLY 0x2000 +#define FX_ELEM_TRAIL_ORIENT_BY_VELOCITY 0x4000 +#define FX_ELEM_EMIT_ORIENT_BY_ELEM 0x8000 + +/* FxElemAtlas::behavior */ +#define FX_ATLAS_START_MASK 0x3 +#define FX_ATLAS_START_FIXED 0x0 +#define FX_ATLAS_START_RANDOM 0x1 +#define FX_ATLAS_START_INDEXED 0x2 +#define FX_ATLAS_PLAY_OVER_LIFE 0x4 +#define FX_ATLAS_LOOP_ONLY_N_TIMES 0x8 + + enum FxElemType : char + { + FX_ELEM_TYPE_SPRITE_BILLBOARD = 0x0, + FX_ELEM_TYPE_SPRITE_ORIENTED = 0x1, + FX_ELEM_TYPE_TAIL = 0x2, + FX_ELEM_TYPE_TRAIL = 0x3, + FX_ELEM_TYPE_CLOUD = 0x4, + FX_ELEM_TYPE_SPARKCLOUD = 0x5, + FX_ELEM_TYPE_SPARKFOUNTAIN = 0x6, + FX_ELEM_TYPE_MODEL = 0x7, + FX_ELEM_TYPE_OMNI_LIGHT = 0x8, + FX_ELEM_TYPE_SPOT_LIGHT = 0x9, + FX_ELEM_TYPE_SOUND = 0xA, + FX_ELEM_TYPE_DECAL = 0xB, + FX_ELEM_TYPE_RUNNER = 0xC, + FX_ELEM_TYPE_COUNT = 0xD, + FX_ELEM_TYPE_LAST_SPRITE = 0x3, + FX_ELEM_TYPE_LAST_DRAWN = 0x9, + }; + + struct FxElemVec3Range + { + float base[3]; + float amplitude[3]; + }; + + struct FxIntRange + { + int base; + int amplitude; + }; + + struct FxFloatRange + { + float base; + float amplitude; + }; + + struct FxSpawnDefLooping + { + int intervalMsec; + int count; + }; + + struct FxSpawnDefOneShot + { + FxIntRange count; + }; + + union FxSpawnDef + { + FxSpawnDefLooping looping; + FxSpawnDefOneShot oneShot; + }; + + struct FxEffectDef; + + union FxEffectDefRef + { + FxEffectDef *handle; + const char *name; + }; + + union FxElemVisuals + { + const void *anonymous; + Material *material; + XModel *xmodel; + FxEffectDefRef *effectDef; + const char *soundName; + }; + + struct FxElemMarkVisuals + { + Material* data[2]; + }; + + union FxElemDefVisuals + { + FxElemVisuals instance; + //If parent FxElemDef::elemType == 0x7, use xmodel + //If parent FxElemDef::elemType == 0xC, use effectDef + //If parent FxElemDef::elemType == 0xA, use soundName + //If parent FxElemDef::elemType != 0x9 || 0x8, use material + + FxElemVisuals *array; //Total count = parent FxElemDef::visualCount + FxElemMarkVisuals *markArray; //Total count = parent FxElemDef::visualCount + }; + + struct FxTrailVertex + { + /* + float pos[2]; + float normal[2]; + float texCoord[2]; + */ + char pad[20]; + }; + + struct FxTrailDef + { + int scrollTimeMsec; + int repeatDist; + float splitArcDist; + int splitDist; + int splitTime; + int vertCount; + FxTrailVertex *verts; + int indCount; + unsigned __int16 *inds; + }; + + struct FxSparkFountain + { + float sparkFountainGravity; + float sparkFountainBounceFrac; + float sparkFountainBounceRand; + float sparkFountainSparkSpacing; + float sparkFountainSparkLength; + int sparkFountainSparkCount; + float sparkFountainLoopTime; + float sparkFountainVelMin; + float sparkFountainVelMax; + float sparkFountainVelConeAngle; + float sparkFountainRestSpeed; + float sparkFountainBoostTime; + float sparkFountainBoostFactor; + }; + + union FxElemExtendedDef + { + char *unknownBytes; + FxSparkFountain *sparkFountain; + FxTrailDef *trailDef; + }; + + struct FxElemAtlas + { + char behavior; + char index; + char fps; + char loopCount; + char colIndexBits; + char rowIndexBits; + __int16 entryCount; + }; + + struct FxElemVelStateInFrame + { + FxElemVec3Range velocity; + FxElemVec3Range totalDelta; + }; + + struct FxElemVelStateSample + { + FxElemVelStateInFrame local; + FxElemVelStateInFrame world; + }; + + struct FxElemVisualState + { + char color[4]; + float rotationDelta; + float rotationTotal; + float size[2]; + float scale; + }; + + struct FxElemVisStateSample + { + FxElemVisualState base; + FxElemVisualState amplitude; + }; + + struct FxElemDef // 0xFC + { + int flags; + FxSpawnDef spawn; + FxFloatRange spawnRange; + FxFloatRange fadeInRange; + FxFloatRange fadeOutRange; + float spawnFrustumCullRadius; + FxIntRange spawnDelayMsec; + FxIntRange lifeSpanMsec; + FxFloatRange spawnOrigin[3]; + FxFloatRange spawnOffsetRadius; + FxFloatRange spawnOffsetHeight; + FxFloatRange spawnAngles[3]; + FxFloatRange angularVelocity[3]; + FxFloatRange initialRotation; + FxFloatRange gravity; + FxFloatRange reflectionFactor; + FxElemAtlas atlas; + char elemType; + char visualCount; + char velIntervalCount; + char visStateIntervalCount; + FxElemVelStateSample *velSamples; // count = velIntervalCount + FxElemVisStateSample *visSamples; // count = visStateIntervalCount + FxElemDefVisuals visuals; + //If elemType is 0xB, then use markVisuals + //If elemType is not 0xB and visualCount == 1, then use visual + //If elemType is not 0xB and visualCount != 1, then use visualsArray + vec3_t collMins; + vec3_t collMaxs; + FxEffectDefRef *effectOnImpact; + FxEffectDefRef *effectOnDeath; + FxEffectDefRef *effectEmitted; + FxFloatRange emitDist; + FxFloatRange emitDistVariance; + FxElemExtendedDef extendedDef; + //If elemType == 3, then use trailDef + //If elemType == 6, then use sparkFountain + //If elemType != 3 && elemType != 6 use unknownBytes (size = 1) + char sortOrder; + char lightingFrac; + char unused[2]; + }; + + struct FxEffectDef + { + const char *name; + int flags; + int totalSize; + int msecLoopingLife; + int elemDefCountLooping; + int elemDefCountOneShot; + int elemDefCountEmission; + FxElemDef *elemDefs; //Count = elemDefCountOneShot + elemDefCountEmission + elemDefCountLooping + }; + +#define FX_ELEM_FIELD_COUNT 90 + + struct FxEditorElemAtlas + { + int behavior; + int index; + int fps; + int loopCount; + int indexRange; + int colIndexBits; + int rowIndexBits; + int entryCount; + }; + + struct FxCurve + { + int dimensionCount; + int keyCount; + float keys[1]; + }; + + struct FxEditorTrailDef + { + FxTrailVertex verts[64]; + int vertCount; + unsigned __int16 inds[128]; + int indCount; + }; + + struct FxEditorElemDef + { + char name[48]; + int editorFlags; + int flags; + FxFloatRange spawnRange; + FxFloatRange fadeInRange; + FxFloatRange fadeOutRange; + float spawnFrustumCullRadius; + FxSpawnDefLooping spawnLooping; + FxSpawnDefOneShot spawnOneShot; + FxIntRange spawnDelayMsec; + FxIntRange lifeSpanMsec; + FxFloatRange spawnOrigin[3]; + FxFloatRange spawnOffsetRadius; + FxFloatRange spawnOffsetHeight; + FxFloatRange spawnAngles[3]; + FxFloatRange angularVelocity[3]; + FxFloatRange initialRotation; + FxFloatRange gravity; + FxFloatRange elasticity; + FxEditorElemAtlas atlas; + float velScale[2][3]; + FxCurve *velShape[2][3][2]; + float rotationScale; + FxCurve *rotationShape[2]; + float sizeScale[2]; + FxCurve *sizeShape[2][2]; + float scaleScale; + FxCurve *scaleShape[2]; + FxCurve *color[2]; + FxCurve *alpha[2]; + float lightingFrac; + float decalFadeInTime; + float collOffset[3]; + float collRadius; + FxEffectDef *effectOnImpact; + FxEffectDef *effectOnDeath; + int sortOrder; + FxEffectDef *emission; + FxFloatRange emitDist; + FxFloatRange emitDistVariance; + char elemType; + __declspec(align(4)) int visualCount; + union + { + FxElemVisuals visuals[32]; + FxElemMarkVisuals markVisuals[16]; + } visuals; + int trailSplitDist; + int trailSplitArcDist; + int trailSplitTime; + int trailRepeatDist; + float trailScrollTime; + FxEditorTrailDef trailDef; + int sparkFountainGravity; + int sparkFountainBounceFrac; + int sparkFountainBounceRand; + int sparkFountainSparkSpacing; + int sparkFountainSparkLength; + int sparkFountainSparkCount; + int sparkFountainLoopTime; + int sparkFountainVelMin; + int sparkFountainVelMax; + int sparkFountainVelConeAngle; + int sparkFountainRestSpeed; + int sparkFountainBoostTime; + int sparkFountainBoostFactor; + }; + + struct FxEditorEffectDef + { + char name[64]; + int elemCount; + FxEditorElemDef elems[32]; + }; + + struct FxElemField + { + const char *keyName; + bool(__cdecl *handler)(const char**, FxEditorElemDef*); + }; + + struct cStaticModel_t + { + XModel *xmodel; + float origin[3]; + float invScaledAxis[3][3]; + float absmin[3]; + float absmax[3]; + }; + + struct ClipMaterial + { + char* name; + int unk; + int unk2; + }; + + struct cNode_t + { + cplane_t* plane; + short children[2]; + }; + + struct cbrush_t + { + unsigned __int16 numsides; + unsigned __int16 glassPieceIndex; + cbrushside_t *sides; + char *baseAdjacentSide; + __int16 axialMaterialNum[2][3]; + char firstAdjacentSideOffsets[2][3]; + char edgeCount[2][3]; + }; + + + struct cLeaf_t + { + unsigned __int16 firstCollAabbIndex; + unsigned __int16 collAabbCount; + int brushContents; + int terrainContents; + float mins[3]; + float maxs[3]; + int leafBrushNode; + }; + + struct cLeafBrushNodeLeaf_t + { + unsigned __int16 *brushes; + }; + + struct cLeafBrushNode_t + { + char axis; + __int16 leafBrushCount; + int contents; + cLeafBrushNodeLeaf_t data; + char pad[8]; + }; + + struct cmodel_t + { + float mins[3]; + float maxs[3]; + float radius; + cLeaf_t leaf; + }; + + enum DynEntityType + { + DYNENT_TYPE_INVALID = 0x0, + DYNENT_TYPE_CLUTTER = 0x1, + DYNENT_TYPE_DESTRUCT = 0x2, + DYNENT_TYPE_COUNT = 0x3, + }; + + struct GfxPlacement + { + float quat[4]; + float origin[3]; + }; + + struct DynEntityDef + { + DynEntityType type; + GfxPlacement pose; + XModel *xModel; + unsigned __int16 brushModel; + unsigned __int16 physicsBrushModel; + FxEffectDef *destroyFx; + PhysPreset *physPreset; + int health; + PhysMass mass; + int contents; + }; + + struct DynEntityPose + { + GfxPlacement pose; + float radius; + }; + + struct DynEntityClient + { + int physObjId; + unsigned __int16 flags; + unsigned __int16 lightingHandle; + int health; + }; + + struct DynEntityColl + { + unsigned __int16 sector; + unsigned __int16 nextEntInSector; + float linkMins[2]; + float linkMaxs[2]; + }; + + struct CollisionBorder + { + float distEq[3]; + float zBase; + float zSlope; + float start; + float length; + }; + + struct CollisionPartition + { + char triCount; + char borderCount; + int firstTri; + CollisionBorder *borders; + }; + + union CollisionAabbTreeIndex + { + int firstChildIndex; + int partitionIndex; + }; + + struct CollisionAabbTree + { + float midPoint[3]; + unsigned __int16 materialIndex; + unsigned __int16 childCount; + float halfSize[3]; + CollisionAabbTreeIndex u; + }; + + struct SModelAabbNode + { + Bounds bounds; + unsigned __int16 firstChild; + unsigned __int16 childCount; + }; + + + struct clipMap_t + { + const char* name; + int isInUse; // +8 + int numCPlanes; // +8 + cplane_t* cPlanes; // sizeof 20, +12 + int numStaticModels; // +16 + cStaticModel_t* staticModelList; // sizeof 76, +20 + int numMaterials; // +24 + ClipMaterial* materials; // sizeof 12 with a string (possibly name?), +28 + int numCBrushSides; // +32 + cbrushside_t* cBrushSides; // sizeof 8, +36 + int numCBrushEdges; // +40 + char* cBrushEdges; // +44 + int numCNodes; // +48 + cNode_t* cNodes; // sizeof 8, +52 + int numCLeaf; // +56 + cLeaf_t* cLeaf; // +60 + int numCLeafBrushNodes; // +64 + cLeafBrushNode_t* cLeafBrushNodes; // +68 + int numLeafBrushes; // +72 + short* leafBrushes; // +76 + int numLeafSurfaces; // +80 + int* leafSurfaces; // +84 + int numVerts; // +88 + vec3_t* verts; // +92 + int numTriIndices; // +96 + short* triIndices; // +100 + bool* triEdgeIsWalkable; // +104 + int numCollisionBorders; // +108 + CollisionBorder* collisionBorders;// sizeof 28, +112 + int numCollisionPartitions; // +116 + CollisionPartition* collisionPartitions; // sizeof 12, +120 + int numCollisionAABBTrees; // +124 + CollisionAabbTree* collisionAABBTrees;// sizeof 32, +128 + int numCModels; // +132 + cmodel_t* cModels; // sizeof 68, +136 + short numCBrushes; // +140 + short pad2; // +142 + cbrush_t * cBrushes; // sizeof 36, +144 + Bounds* cBrushBounds; // same count as cBrushes, +148 + int * cBrushContents; // same count as cBrushes, +152 + MapEnts * mapEnts; // +156 + unsigned __int16 smodelNodeCount; + short pad; // +160 + SModelAabbNode *smodelNodes; + unsigned __int16 dynEntCount[2]; + DynEntityDef *dynEntDefList[2]; + DynEntityPose *dynEntPoseList[2]; + DynEntityClient *dynEntClientList[2]; + DynEntityColl *dynEntCollList[2]; + unsigned int checksum; + char unknown5[0x30]; + }; // +256 + + struct G_GlassPiece + { + char pad[12]; + }; + + struct G_GlassName + { + char *nameStr; + __int16 name; + unsigned __int16 pieceCount; + unsigned __int16 *pieceIndices; + }; + + struct G_GlassData + { + G_GlassPiece *glassPieces; + unsigned int pieceCount; + unsigned __int16 damageToWeaken; + unsigned __int16 damageToDestroy; + unsigned int glassNameCount; + G_GlassName *glassNames; + char pad[108]; + }; + + struct pathbasenode_t + { + float vOrigin[3]; + unsigned int type; + }; + + struct pathnode_tree_nodes_t + { + int nodeCount; + unsigned __int16 *nodes; + }; + + struct pathnode_tree_t; + + union pathnode_tree_info_t + { + pathnode_tree_t *child[2]; + pathnode_tree_nodes_t s; + }; + + struct pathnode_tree_t + { + int axis; + float dist; + pathnode_tree_info_t u; + }; + + enum nodeType + { + NODE_BADNODE = 0x0, + NODE_PATHNODE = 0x1, + NODE_COVER_STAND = 0x2, + NODE_COVER_CROUCH = 0x3, + NODE_COVER_CROUCH_WINDOW = 0x4, + NODE_COVER_PRONE = 0x5, + NODE_COVER_RIGHT = 0x6, + NODE_COVER_LEFT = 0x7, + NODE_COVER_WIDE_RIGHT = 0x8, + NODE_COVER_WIDE_LEFT = 0x9, + NODE_CONCEALMENT_STAND = 0xA, + NODE_CONCEALMENT_CROUCH = 0xB, + NODE_CONCEALMENT_PRONE = 0xC, + NODE_REACQUIRE = 0xD, + NODE_BALCONY = 0xE, + NODE_SCRIPTED = 0xF, + NODE_NEGOTIATION_BEGIN = 0x10, + NODE_NEGOTIATION_END = 0x11, + NODE_TURRET = 0x12, + NODE_GUARD = 0x13, + NODE_NUMTYPES = 0x14, + NODE_DONTLINK = 0x14, + }; + + struct pathlink_s + { + float fDist; + unsigned __int16 nodeNum; + char disconnectCount; + char negotiationLink; + char ubBadPlaceCount[4]; + }; + + struct pathnode_constant_t + { + nodeType type; + unsigned __int16 spawnflags; + unsigned __int16 targetname; + unsigned __int16 script_linkName; + unsigned __int16 script_noteworthy; + unsigned __int16 target; + unsigned __int16 animscript; + int animscriptfunc; + float vOrigin[3]; + float fAngle; + float forward[2]; + float fRadius; + float minUseDistSq; + __int16 wOverlapNode[2]; +// __int16 wChainId; +// __int16 wChainDepth; +// __int16 wChainParent; + unsigned __int16 totalLinkCount; + pathlink_s *Links; + }; + + struct pathnode_dynamic_t + { + void *pOwner; + int iFreeTime; + int iValidTime[3]; + int inPlayerLOSTime; + __int16 wLinkCount; + __int16 wOverlapCount; + __int16 turretEntNumber; + __int16 userCount; + }; + + struct pathnode_t; + + struct pathnode_transient_t + { + int iSearchFrame; + pathnode_t *pNextOpen; + pathnode_t *pPrevOpen; + pathnode_t *pParent; + float fCost; + float fHeuristic; + float costFactor; + }; + + struct pathnode_t + { + pathnode_constant_t constant; + pathnode_dynamic_t dynamic; + pathnode_transient_t transient; + char pad[12]; + }; + + struct PathData + { + unsigned int nodeCount; + pathnode_t *nodes; + pathbasenode_t *basenodes; + unsigned int chainNodeCount; + unsigned __int16 *chainNodeForNode; + unsigned __int16 *nodeForChainNode; + int visBytes; + char *pathVis; + int nodeTreeCount; + pathnode_tree_t *nodeTree; + }; + + struct VehicleTrackObstacle + { + char pad[12]; + }; + + struct VehicleTrackSector + { + char pad[52]; + VehicleTrackObstacle* trackObstacles; + int trackObstacleCount; + }; + + struct VehicleTrackSegment + { + const char* name; + VehicleTrackSector* trackSectors; + int trackSectorCount; + VehicleTrackSegment** trackSegments1; + int trackSegmentCount1; + VehicleTrackSegment** trackSegments2; + int trackSegmentCount2; + int pad[4]; + }; + + struct VehicleTrack + { + VehicleTrackSegment* trackSegments; + int trackSegmentCount; + }; + + struct GameWorldSp + { + const char* name; + PathData pathData; + VehicleTrack vehicleTrack; + G_GlassData* data; + }; + + struct GameWorldMp + { + const char* name; + G_GlassData* data; + }; + + struct VehicleDef + { + const char* name; + char pad[408]; + void* weaponDef; + char pad2[304]; + }; + + struct XModelDrawInfo + { + unsigned __int16 lod; + unsigned __int16 surfId; + }; + + struct GfxSceneDynModel + { + XModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct BModelDrawInfo + { + unsigned __int16 surfId; + }; + + struct GfxSceneDynBrush + { + BModelDrawInfo info; + unsigned __int16 dynEntId; + }; + + struct GfxStreamingAabbTree + { + unsigned __int16 firstItem; + unsigned __int16 itemCount; + unsigned __int16 firstChild; + unsigned __int16 childCount; + float mins[3]; + float maxs[3]; + }; + + struct GfxWorldStreamInfo + { + int aabbTreeCount; + GfxStreamingAabbTree *aabbTrees; + int leafRefCount; + int *leafRefs; + }; + + struct GfxWorldVertex + { + float xyz[3]; + float binormalSign; + GfxColor color; + float texCoord[2]; + float lmapCoord[2]; + PackedUnitVec normal; + PackedUnitVec tangent; + }; + + struct GfxWorldVertexData + { + GfxWorldVertex *vertices; + void/*IDirect3DVertexBuffer9*/* worldVb; + }; + +#pragma pack(push, 4) + struct GfxLightImage + { + GfxImage *image; + char samplerState; + }; +#pragma pack(pop) + + struct GfxLightDef + { + const char *name; + GfxLightImage attenuation; + int lmapLookupStart; + }; + + struct GfxLight + { + char type; + char canUseShadowMap; + char unused[2]; + float color[3]; + float dir[3]; + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + int exponent; + unsigned int spotShadowIndex; + GfxLightDef *def; + }; + + struct GfxReflectionProbe + { + float origin[3]; + }; + + struct GfxWorldDpvsPlanes + { + int cellCount; + cplane_t *planes; + unsigned __int16 *nodes; + unsigned int *sceneEntCellBits; //Size = cellCount << 11 + }; + + struct GfxAabbTree + { + float mins[3]; + float maxs[3]; + int pad; + unsigned __int16 childCount; + unsigned __int16 surfaceCount; + unsigned __int16 startSurfIndex; + unsigned __int16 smodelIndexCount; + unsigned __int16 *smodelIndexes; + int childrenOffset; + }; + + struct GfxCellTree + { + GfxAabbTree *aabbTree; + }; + + struct GfxCellTreeCount + { + int aabbTreeCount; + }; + + struct GfxLightGridEntry + { + unsigned __int16 colorsIndex; + char primaryLightIndex; + char needsTrace; + }; + + struct GfxLightGridColors + { + char rgb[56][3]; + }; + + struct GfxStaticModelInst + { + float mins[3]; + float maxs[3]; + float lightingOrigin[3]; + }; + + enum surfaceType_t + { + SF_TRIANGLES = 0x0, + SF_TRIANGLES_NO_SUN_SHADOW = 0x1, + SF_BEGIN_STATICMODEL = 0x2, + SF_STATICMODEL_RIGID = 0x2, + SF_STATICMODEL_INSTANCED = 0x3, + SF_STATICMODEL_RIGID_NO_SUN_SHADOW = 0x4, + SF_STATICMODEL_INSTANCED_NO_SUN_SHADOW = 0x5, + SF_END_STATICMODEL = 0x6, + SF_BMODEL = 0x6, + SF_BEGIN_XMODEL = 0x7, + SF_XMODEL_RIGID = 0x7, + SF_XMODEL_SKINNED = 0x8, + SF_END_XMODEL = 0x9, + SF_BEGIN_FX = 0x9, + SF_CODE = 0x9, + SF_GLASS = 0xA, + SF_MARK = 0xB, + SF_SPARK = 0xC, + SF_PARTICLE_CLOUD = 0xD, + SF_PARTICLE_SPARK_CLOUD = 0xE, + SF_END_FX = 0xF, + SF_NUM_SURFACE_TYPES = 0xF, + SF_FORCE_32_BITS = 0xFFFFFFFF, + }; + + struct srfTriangles_t + { + int vertexLayerData; + int firstVertex; + unsigned __int16 vertexCount; + unsigned __int16 triCount; + int baseIndex; + }; + + struct GfxSurface + { + srfTriangles_t tris; + Material *material; + char lightmapIndex; + char reflectionProbeIndex; + char primaryLightIndex; + char castsSunShadow; + }; + + struct GfxSurfaceBounds + { + Bounds bounds; + //char flags; + }; + + struct GfxPackedPlacement + { + float origin[3]; + vec3_t axis[3]; + float scale; + }; + + struct GfxStaticModelDrawInst + { + GfxPackedPlacement placement; + XModel *model; + unsigned __int16 cullDist; + unsigned __int16 lightingHandle; + char reflectionProbeIndex; + char primaryLightIndex; + char flags; + char firstMtlSkinIndex; + GfxColor groundLighting; + unsigned __int16 cacheId[4]; + }; + + struct GfxWorldDpvsStatic + { + unsigned int smodelCount; + unsigned int staticSurfaceCount; + unsigned int staticSurfaceCountNoDecal; + unsigned int litOpaqueSurfsBegin; + unsigned int litOpaqueSurfsEnd; + unsigned int litTransSurfsBegin; + unsigned int litTransSurfsEnd; + unsigned int shadowCasterSurfsBegin; + unsigned int shadowCasterSurfsEnd; + unsigned int emissiveSurfsBegin; + unsigned int emissiveSurfsEnd; + unsigned int smodelVisDataCount; + unsigned int surfaceVisDataCount; + char *smodelVisData[3]; + char *surfaceVisData[3]; + unsigned __int16 *sortedSurfIndex; + GfxStaticModelInst *smodelInsts; + GfxSurface *surfaces; + GfxSurfaceBounds *surfacesBounds; + GfxStaticModelDrawInst *smodelDrawInsts; + GfxDrawSurf *surfaceMaterials; + unsigned int *surfaceCastsSunShadow; + volatile int usageCount; + }; + +#pragma pack(push, 4) + + struct GfxPortalWritable + { + char isQueued; + char isAncestor; + char recursionDepth; + char hullPointCount; + float(*hullPoints)[2]; + }; + + struct DpvsPlane + { + float coeffs[4]; + char side[3]; + char pad; + }; + + struct GfxPortal + { + GfxPortalWritable writable; + DpvsPlane plane; + vec3_t* vertices; + unsigned __int16 cellIndex; + char vertexCount; + float hullAxis[2][3]; + }; + + struct GfxCell + { + float mins[3]; + float maxs[3]; + int portalCount; + GfxPortal *portals; + char reflectionProbeCount; + char *reflectionProbes; + }; + + struct GfxLightmapArray + { + GfxImage *primary; + GfxImage *secondary; + }; + + struct GfxLightGrid + { + bool hasLightRegions; + unsigned int sunPrimaryLightIndex; + unsigned __int16 mins[3]; + unsigned __int16 maxs[3]; + unsigned int rowAxis; + unsigned int colAxis; + unsigned __int16 *rowDataStart; + unsigned int rawRowDataSize; + char *rawRowData; + unsigned int entryCount; + GfxLightGridEntry *entries; + unsigned int colorCount; + GfxLightGridColors *colors; + }; + + struct GfxBrushModelWritable + { + float mins[3]; + float maxs[3]; + }; + + struct GfxBrushModel + { + GfxBrushModelWritable writable; + float bounds[2][3]; + unsigned int surfaceCount; + unsigned int startSurfIndex; + int pad; + }; + + struct MaterialMemory + { + Material *material; + int memory; + }; + + struct sunflare_t + { + bool hasValidData; + Material *spriteMaterial; + Material *flareMaterial; + float spriteSize; + float flareMinSize; + float flareMinDot; + float flareMaxSize; + float flareMaxDot; + float flareMaxAlpha; + int flareFadeInTime; + int flareFadeOutTime; + float blindMinDot; + float blindMaxDot; + float blindMaxDarken; + int blindFadeInTime; + int blindFadeOutTime; + float glareMinDot; + float glareMaxDot; + float glareMaxLighten; + int glareFadeInTime; + int glareFadeOutTime; + float sunFxPosition[3]; + }; + + struct GfxShadowGeometry + { + unsigned __int16 surfaceCount; + unsigned __int16 smodelCount; + unsigned __int16 *sortedSurfIndex; + unsigned __int16 *smodelIndex; + }; + + struct GfxLightRegionAxis + { + float dir[3]; + float midPoint; + float halfSize; + }; + + struct GfxLightRegionHull + { + float kdopMidPoint[9]; + float kdopHalfSize[9]; + unsigned int axisCount; + GfxLightRegionAxis *axis; + }; + + struct GfxLightRegion + { + unsigned int hullCount; + GfxLightRegionHull *hulls; + }; + + struct GfxWorldDpvsDynamic + { + unsigned int dynEntClientWordCount[2]; + unsigned int dynEntClientCount[2]; + unsigned int *dynEntCellBits[2]; + char *dynEntVisData[2][3]; + }; + + struct SunLightParseParams + { + char name[64]; + float ambientScale; + float ambientColor[3]; + float diffuseFraction; + float sunLight; + float sunColor[3]; + float diffuseColor[3]; + bool diffuseColorHasBeenSet; + float angles[3]; + }; + + struct GfxWorldVertexLayerData + { + char *data; + void/*IDirect3DVertexBuffer9*/* layerVb; + }; + + typedef unsigned int GfxRawTexture; + + struct GfxWorldDraw + { + unsigned int reflectionProbeCount; + GfxImage * * reflectionImages; + GfxReflectionProbe *reflectionProbes; + GfxRawTexture * reflectionProbeTextures; //Count = refelctionProbeCount + int lightmapCount; + GfxLightmapArray *lightmaps; + GfxRawTexture * lightmapPrimaryTextures; //Count = lightmapCount + GfxRawTexture * lightmapSecondaryTextures; //Count = lightmapCount + GfxImage *skyImage; + GfxImage *outdoorImage; + unsigned int vertexCount; + GfxWorldVertexData vd; + unsigned int vertexLayerDataSize; + GfxWorldVertexLayerData vld; + int indexCount; + unsigned __int16 *indices; + }; + + struct GfxSky + { + int skySurfCount; + int * skyStartSurfs; + GfxImage * skyImage; + int skySamplerState; + }; + + struct GfxHeroOnlyLight + { + char pad[56]; + }; + + struct GfxWorld + { + const char *name; + const char *baseName; + int planeCount; + int nodeCount; + unsigned int surfaceCount; + int skyCount; + GfxSky *skies; + unsigned int lastSunPrimaryLightIndex; + unsigned int primaryLightCount; + unsigned int sortKeyLitDecal; + unsigned int sortKeyEffectDecal; + unsigned int sortKeyEffectAuto; + unsigned int sortKeyDistortion; + GfxWorldDpvsPlanes dpvsPlanes; + GfxCellTreeCount *aabbTreeCounts; + GfxCellTree *aabbTrees; + GfxCell *cells; + GfxWorldDraw draw; + GfxLightGrid lightGrid; + int modelCount; + GfxBrushModel *models; + Bounds bounds; + unsigned int checksum; + int materialMemoryCount; + MaterialMemory *materialMemory; + sunflare_t sun; + float outdoorLookupMatrix[4][4]; + GfxImage *outdoorImage; + unsigned int *cellCasterBits; + unsigned int *cellHasSunLitSurfsBits; + GfxSceneDynModel *sceneDynModel; + GfxSceneDynBrush *sceneDynBrush; + unsigned int *primaryLightEntityShadowVis; + unsigned int *primaryLightDynEntShadowVis[2]; + char *nonSunPrimaryLightForModelDynEnt; + GfxShadowGeometry *shadowGeom; + GfxLightRegion *lightRegion; + GfxWorldDpvsStatic dpvs; + GfxWorldDpvsDynamic dpvsDyn; + unsigned int mapVtxChecksum; + unsigned int heroOnlyLightCount; + GfxHeroOnlyLight *heroOnlyLights; + char fogTypesAllowed; + }; +#pragma pack(pop) + + struct rgpStruct + { + int pad[2117]; + GfxWorld* world; + }; + + struct ComPrimaryLight + { + char type; + char canUseShadowMap; + char exponent; + char unused; + float color[3]; + float dir[3]; + float origin[3]; + float radius; + float cosHalfFovOuter; + float cosHalfFovInner; + float cosHalfFovExpanded; + float rotationLimit; + float translationLimit; + char* name; + }; + + struct ComWorld + { + char *name; + int isInUse; + int lightCount; + ComPrimaryLight* lights; + }; + +#pragma pack(push, 4) + struct FxGlassDef + { + float halfThickness; + float texVecs[2][2]; + GfxColor color; + Material *material; + Material *materialShattered; + PhysPreset *physPreset; + }; +#pragma pack(pop) + + struct FxSpatialFrame + { + float quat[4]; + float origin[3]; + }; + + union FxGlassPiecePlace + { + struct + { + FxSpatialFrame frame; + float radius; + }; + unsigned int nextFree; + }; + + struct FxGlassPieceState + { + float texCoordOrigin[2]; + unsigned int supportMask; + unsigned __int16 initIndex; + unsigned __int16 geoDataStart; + unsigned __int16 lightingIndex; + char defIndex; + char pad[3]; + char vertCount; + char holeDataCount; + char crackDataCount; + char fanDataCount; + unsigned __int16 flags; + float areaX2; + }; + + struct FxGlassPieceDynamics + { + char pad[36]; + }; + + struct FxGlassVertex + { + __int16 x; + __int16 y; + }; + + struct FxGlassHoleHeader + { + unsigned __int16 uniqueVertCount; + char touchVert; + char pad[1]; + }; + + struct FxGlassCrackHeader + { + unsigned __int16 uniqueVertCount; + char beginVertIndex; + char endVertIndex; + }; + + union FxGlassGeometryData + { + FxGlassVertex vert; + FxGlassHoleHeader hole; + FxGlassCrackHeader crack; + char asBytes[4]; + __int16 anonymous[2]; + }; + +#pragma pack(push, 4) + struct FxGlassInitPieceState //Note, on MW3 this is missing 4 bytes, just not sure whats missing yet + { + /* + FxSpatialFrame frame; + float radius; + float texCoordOrigin[2]; + unsigned int supportMask; + float areaX2; + unsigned __int16 lightingIndex; + char defIndex; + char vertCount; + char fanDataCount; + */ + char pad[52]; + }; +#pragma pack(pop) + +#pragma pack(push, 8) + struct FxGlassSystem + { + int time; + int prevTime; + unsigned int defCount; + unsigned int pieceLimit; + unsigned int pieceWordCount; + unsigned int initPieceCount; + unsigned int cellCount; + unsigned int activePieceCount; + unsigned int firstFreePiece; + unsigned int geoDataLimit; + unsigned int geoDataCount; + unsigned int initGeoDataCount; + FxGlassDef *defs; + FxGlassPiecePlace *piecePlaces; + FxGlassPieceState *pieceStates; + FxGlassPieceDynamics *pieceDynamics; + FxGlassGeometryData *geoData; + unsigned int *isInUse; + unsigned int *cellBits; + char *visData; + float(*linkOrg)[3]; + float *halfThickness; + unsigned __int16 *lightingHandles; + FxGlassInitPieceState *initPieceStates; + FxGlassGeometryData *initGeoData; + bool needToCompactData; + char initCount; + float effectChanceAccum; + int lastPieceDeletionTime; + }; +#pragma pack(pop) + + struct FxWorld + { + const char * name; + FxGlassSystem glassSys; + }; + + union XAssetHeader + { + void *data; + + MenuList *menuList; + menuDef_t *menu; + Material *material; + snd_alias_list_t* sound; + LocalizeEntry* localize; + StringTable* stringTable; + MapEnts* mapEnts; + RawFile* rawfile; + GfxImage* image; + Font* font; + MaterialTechniqueSet* techniqueSet; + MaterialVertexDeclaration* vertexDecl; + MaterialVertexShader* vertexShader; + MaterialPixelShader* pixelShader; + StructuredDataDefSet* structuredData; + XModel* model; + PhysPreset* physPreset; + PhysCollmap* physCollmap; + XModelSurfs* surfaces; + XAnimParts* parts; + clipMap_t* clipMap; + FxEffectDef* fx; + GameWorldMp* gameWorldMp; + GameWorldSp* gameWorldSp; + TracerDef* tracer; + VehicleDef* vehicle; + FxWorld* fxWorld; + GfxWorld* gfxWorld; + GfxLightDef* lightDef; + SndCurve* sndCurve; + LoadedSound* loadSnd; + ComWorld* comWorld; + }; + + struct XAsset + { + XAssetType type; + XAssetHeader header; + }; + + struct XBlock + { + char *data; + unsigned int size; + }; + + struct XAssetEntry + { + XAsset asset; + char zoneIndex; + bool inuse; + unsigned __int16 nextHash; + unsigned __int16 nextOverride; + unsigned __int16 usageFrame; + }; + + enum XFileLanguage : unsigned char + { + XLANG_NONE = 0x00, + XLANG_ENGLISH = 0x01, + XLANG_FRENCH = 0x02, + XLANG_GERMAN = 0x03, + XLANG_ITALIAN = 0x04, + XLANG_SPANISH = 0x05, + XLANG_BRITISH = 0x06, + XLANG_RUSSIAN = 0x07, + XLANG_POLISH = 0x08, + XLANG_KOREAN = 0x09, + XLANG_TAIWANESE = 0x0A, + XLANG_JAPANESE = 0x0B, + XLANG_CHINESE = 0x0C, + XLANG_THAI = 0x0D, + XLANG_LEET = 0x0E, // Wat? + XLANG_CZECH = 0x0F, + }; + +#pragma pack(push, 1) + struct XFileHeader + { + unsigned __int64 magic; + unsigned int version; + XFileLanguage language; + DWORD highDateTime; + DWORD lowDateTime; + }; +#pragma pack(pop) + + enum XFILE_BLOCK_TYPES + { + XFILE_BLOCK_TEMP = 0x0, + XFILE_BLOCK_PHYSICAL = 0x1, + XFILE_BLOCK_RUNTIME = 0x2, + XFILE_BLOCK_VIRTUAL = 0x3, + XFILE_BLOCK_LARGE = 0x4, + + // Those are probably incorrect + XFILE_BLOCK_CALLBACK, + XFILE_BLOCK_VERTEX, + XFILE_BLOCK_INDEX, + + MAX_XFILE_COUNT, + + XFILE_BLOCK_INVALID = -1 + }; + + struct XFile + { + unsigned int size; + unsigned int externalSize; + unsigned int blockSize[MAX_XFILE_COUNT]; + }; + + struct ScriptStringList + { + int count; + const char **strings; + }; + + struct XAssetList + { + ScriptStringList stringList; + int assetCount; + XAsset *assets; + }; + + struct ZoneHeader + { + XFile xFile; + XAssetList assetList; + }; + + struct XNKID + { + char ab[8]; + }; + + struct XNADDR + { + in_addr ina; + in_addr inaOnline; + unsigned __int16 wPortOnline; + char abEnet[6]; + char abOnline[20]; + }; + + struct XNKEY + { + char ab[16]; + }; + + struct _XSESSION_INFO + { + XNKID sessionID; + XNADDR hostAddress; + XNKEY keyExchangeKey; + }; + + struct mapArena_t + { + char uiName[32]; + char mapName[16]; + char description[32]; + char mapimage[32]; + char keys[32][16]; + char values[32][64]; + char pad[144]; + }; + + struct newMapArena_t + { + char uiName[32]; + char oldMapName[16]; + char description[32]; + char mapimage[32]; + char keys[32][16]; + char values[32][64]; + char other[144]; + char mapName[32]; + }; + + struct gameTypeName_t + { + char gameType[12]; + char uiName[32]; + }; + + typedef struct party_s + { + unsigned char pad1[544]; + int privateSlots; + int publicSlots; + } party_t; + + typedef struct PartyData_s + { + DWORD unk; + } PartyData_t; + + struct fileInIwd_s + { + unsigned int pos; + char *name; + fileInIwd_s *next; + }; + + struct iwd_t + { + char iwdFilename[256]; + char iwdBasename[256]; + char iwdGamename[256]; + char *handle; + int checksum; + int pure_checksum; + volatile int hasOpenFile; + int numfiles; + char referenced; + unsigned int hashSize; + fileInIwd_s **hashTable; + fileInIwd_s *buildBuffer; + }; + +#ifndef __cplusplus + typedef void _iobuf; +#endif + + union qfile_gus + { + _iobuf *o; + char *z; + }; + + struct qfile_us + { + qfile_gus file; + int iwdIsClone; + }; + + struct fileHandleData_t + { + qfile_us handleFiles; + int handleSync; + int fileSize; + int zipFilePos; + iwd_t *zipFile; + int streamed; + char name[256]; + }; + + typedef struct { + char path[256]; // c:\quake3 + char gamedir[256]; // baseq3 + } directory_t; + + typedef struct searchpath_s + { + searchpath_s* next; + iwd_t *iwd; + directory_t* dir; + int bLocalized; + int ignore; + int ignorePureCheck; + int language; + } searchpath_t; + + struct SafeArea + { + int fontHeight; + int textHeight; + int textWidth; + float left; + float top; + float right; + float bottom; + }; + +#pragma pack(push, 4) + struct SpawnVar + { + bool spawnVarsValid; + int numSpawnVars; + char *spawnVars[64][2]; + int numSpawnVarChars; + char spawnVarChars[2048]; + }; +#pragma pack(pop) + + // Probably incomplete or wrong! +#pragma pack(push, 4) + struct usercmd_s + { + int serverTime; + int buttons; + char weapon; + char offHandIndex; + int angles[3]; + char forwardmove; + char rightmove; + float meleeChargeYaw; + char meleeChargeDist; + }; +#pragma pack(pop) + + typedef char mapname_t[40]; + + struct traceWork_t + { + /*TraceExtents*/int extents; + float delta[3]; + float deltaLen; + float deltaLenSq; + float delta2DLen; + float delta2DLenSq; + float size[3]; + Bounds bounds; + int contents; + bool isPoint; + bool axialCullOnly; + float radius; + float offset[3]; + float radiusOffset[3]; + float boundingRadius; + /*TraceThreadInfo*/ int threadInfo; + /*CM_WorldTraceCallbacks*/ void *callbacks; + }; + + +#ifdef __cplusplus +} +#endif diff --git a/src/Main.cpp b/src/Main.cpp index 8e8808f5..712aa386 100644 --- a/src/Main.cpp +++ b/src/Main.cpp @@ -1,84 +1,84 @@ -#include "STDInclude.hpp" - -namespace Main -{ - Utils::Hook EntryPointHook; - - void SetEnvironment() - { - wchar_t exeName[512]; - GetModuleFileName(GetModuleHandle(NULL), exeName, sizeof(exeName) / 2); - - wchar_t* exeBaseName = wcsrchr(exeName, L'\\'); - exeBaseName[0] = L'\0'; - exeBaseName++; - - SetCurrentDirectory(exeName); - } - - void Initialize() - { - Main::EntryPointHook.uninstall(); - - Main::SetEnvironment(); - - Utils::Cryptography::Initialize(); - Components::Loader::Initialize(); - -#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) - if (Components::Loader::PerformingUnitTests()) - { - DWORD result = (Components::Loader::PerformUnitTests() ? 0 : -1); - Components::Loader::Uninitialize(); - ExitProcess(result); - } -#else - if (Components::Flags::HasFlag("tests")) - { - Components::Logger::Print("Unit tests are disabled outside the debug environment!\n"); - } -#endif - } - - void Uninitialize() - { - Components::Loader::Uninitialize(); - } -} - -BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) -{ - if (ul_reason_for_call == DLL_PROCESS_ATTACH) - { - Steam::Proxy::RunMod(); - - // Ensure we're working with our desired binary - if (Utils::Hook::Get(0x4C0FFF) != 0x6824748B) - { - return FALSE; - } - - DWORD oldProtect; - std::uint8_t* module = reinterpret_cast(GetModuleHandle(NULL)); - //VirtualProtect(module, 0x6C73000, PAGE_EXECUTE_READWRITE, &oldProtect); // Unprotect the entire process - VirtualProtect(module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect); // Protect the .text segment - - Main::EntryPointHook.initialize(0x6BAC0F, [] () - { - __asm - { - // This has to be called, otherwise the hook is not uninstalled and we're looping into infinity - call Main::Initialize - - mov eax, 6BAC0Fh - jmp eax - } - })->install(); - } - else if (ul_reason_for_call == DLL_PROCESS_DETACH) - { - Main::Uninitialize(); - } - - return TRUE; -} +#include "STDInclude.hpp" + +namespace Main +{ + Utils::Hook EntryPointHook; + + void SetEnvironment() + { + wchar_t exeName[512]; + GetModuleFileName(GetModuleHandle(NULL), exeName, sizeof(exeName) / 2); + + wchar_t* exeBaseName = wcsrchr(exeName, L'\\'); + exeBaseName[0] = L'\0'; + exeBaseName++; + + SetCurrentDirectory(exeName); + } + + void Initialize() + { + Main::EntryPointHook.uninstall(); + + Main::SetEnvironment(); + + Utils::Cryptography::Initialize(); + Components::Loader::Initialize(); + +#if defined(DEBUG) || defined(FORCE_UNIT_TESTS) + if (Components::Loader::PerformingUnitTests()) + { + DWORD result = (Components::Loader::PerformUnitTests() ? 0 : -1); + Components::Loader::Uninitialize(); + ExitProcess(result); + } +#else + if (Components::Flags::HasFlag("tests")) + { + Components::Logger::Print("Unit tests are disabled outside the debug environment!\n"); + } +#endif + } + + void Uninitialize() + { + Components::Loader::Uninitialize(); + } +} + +BOOL APIENTRY DllMain(HMODULE /*hModule*/, DWORD ul_reason_for_call, LPVOID /*lpReserved*/) +{ + if (ul_reason_for_call == DLL_PROCESS_ATTACH) + { + Steam::Proxy::RunMod(); + + // Ensure we're working with our desired binary + if (Utils::Hook::Get(0x4C0FFF) != 0x6824748B) + { + return FALSE; + } + + DWORD oldProtect; + std::uint8_t* module = reinterpret_cast(GetModuleHandle(NULL)); + //VirtualProtect(module, 0x6C73000, PAGE_EXECUTE_READWRITE, &oldProtect); // Unprotect the entire process + VirtualProtect(module + 0x1000, 0x2D6000, PAGE_EXECUTE_READ, &oldProtect); // Protect the .text segment + + Main::EntryPointHook.initialize(0x6BAC0F, [] () + { + __asm + { + // This has to be called, otherwise the hook is not uninstalled and we're looping into infinity + call Main::Initialize + + mov eax, 6BAC0Fh + jmp eax + } + })->install(); + } + else if (ul_reason_for_call == DLL_PROCESS_DETACH) + { + Main::Uninitialize(); + } + + return TRUE; +} diff --git a/src/STDInclude.hpp b/src/STDInclude.hpp index 418c3546..8224c12e 100644 --- a/src/STDInclude.hpp +++ b/src/STDInclude.hpp @@ -1,162 +1,162 @@ -#pragma once - -// Version number -#include "version.h" - -#ifndef RC_INVOKED - -#define VC_EXTRALEAN -#define WIN32_LEAN_AND_MEAN - -#include -//#include -//#include -//#include -//#include -#include -#include -//#include -//#include -#include -//#include -#include -#include - -//#include -//#include -//#include -//#include -#include -#include -//#include -//#include -//#include -#include -//#include -#include -#include -//#include -#include -#include -#include - -// Experimental C++17 features -#include - -#ifdef ENABLE_DXSDK -#include -#pragma comment(lib, "D3dx9.lib") -#endif - -// Usefull for debugging -template class Sizer { }; -#define BindNum(x, y) Sizer y; -#define SizeOf(x, y) BindNum(sizeof(x), y) -#define OffsetOf(x, y, z) BindNum(offsetof(x, y), z) - -// Submodules -// Ignore the warnings, it's no our code! -#pragma warning(push) -#pragma warning(disable: 4005) -#pragma warning(disable: 4100) -#pragma warning(disable: 4389) -#pragma warning(disable: 4702) -#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS -#pragma warning(disable: 6001) -#pragma warning(disable: 6011) -#pragma warning(disable: 6031) -#pragma warning(disable: 6255) -#pragma warning(disable: 6258) -#pragma warning(disable: 6386) -#pragma warning(disable: 6387) - -#include -#include -#include -#include -#include -#ifndef DISABLE_BITMESSAGE -#include -#endif - -#ifdef max -#undef max -#endif - -#ifdef min -#undef min -#endif - -// Protobuf -#include "proto/network.pb.h" -#include "proto/party.pb.h" -#include "proto/auth.pb.h" -#include "proto/node.pb.h" -#include "proto/rcon.pb.h" - -#pragma warning(pop) - -#include "Utils\IO.hpp" -#include "Utils\CSV.hpp" -#include "Utils\Time.hpp" -#include "Utils\Cache.hpp" -#include "Utils\Chain.hpp" -#include "Utils\Utils.hpp" -#include "Utils\WebIO.hpp" -#include "Utils\Memory.hpp" -#include "Utils\String.hpp" -#include "Utils\Hooking.hpp" -#include "Utils\Library.hpp" -#include "Utils\InfoString.hpp" -#include "Utils\Compression.hpp" -#include "Utils\Cryptography.hpp" - -#include "Steam\Steam.hpp" - -#include "Game\Structs.hpp" -#include "Game\Functions.hpp" - -#include "Utils\Stream.hpp" - -#include "Components\Loader.hpp" - -// Libraries -#pragma comment(lib, "Winmm.lib") -#pragma comment(lib, "Crypt32.lib") -#pragma comment(lib, "Ws2_32.lib") -#pragma comment(lib, "d3d9.lib") -#pragma comment(lib, "Wininet.lib") -#pragma comment(lib, "shlwapi.lib") -#pragma comment(lib, "Urlmon.lib") -#pragma comment(lib, "Advapi32.lib") -#pragma comment(lib, "rpcrt4.lib") - -// Enable additional literals -using namespace std::literals; - -#endif - -#define STRINGIZE_(x) #x -#define STRINGIZE(x) STRINGIZE_(x) - -#define BASEGAME "iw4x" -#define CLIENT_CONFIG "iw4x_config.cfg" - -#define AssertSize(x, size) static_assert(sizeof(x) == size, STRINGIZE(x) " structure has an invalid size.") -#define AssertOffset(x, y, offset) static_assert(offsetof(x, y) == offset, STRINGIZE(x) "::" STRINGIZE(y) " is not at the right offset.") - -// Resource stuff -#ifdef APSTUDIO_INVOKED -#ifndef APSTUDIO_READONLY_SYMBOLS -// Defines below make accessing the resources from the code easier. -#define _APS_NEXT_RESOURCE_VALUE 102 -#define _APS_NEXT_COMMAND_VALUE 40001 -#define _APS_NEXT_CONTROL_VALUE 1001 -#define _APS_NEXT_SYMED_VALUE 101 -#endif -#endif - -// Enables custom map code -#ifdef DEBUG -#define ENABLE_EXPERIMENTAL_MAP_CODE -#endif +#pragma once + +// Version number +#include "version.h" + +#ifndef RC_INVOKED + +#define VC_EXTRALEAN +#define WIN32_LEAN_AND_MEAN + +#include +//#include +//#include +//#include +//#include +#include +#include +//#include +//#include +#include +//#include +#include +#include + +//#include +//#include +//#include +//#include +#include +#include +//#include +//#include +//#include +#include +//#include +#include +#include +//#include +#include +#include +#include + +// Experimental C++17 features +#include + +#ifdef ENABLE_DXSDK +#include +#pragma comment(lib, "D3dx9.lib") +#endif + +// Usefull for debugging +template class Sizer { }; +#define BindNum(x, y) Sizer y; +#define SizeOf(x, y) BindNum(sizeof(x), y) +#define OffsetOf(x, y, z) BindNum(offsetof(x, y), z) + +// Submodules +// Ignore the warnings, it's no our code! +#pragma warning(push) +#pragma warning(disable: 4005) +#pragma warning(disable: 4100) +#pragma warning(disable: 4389) +#pragma warning(disable: 4702) +#pragma warning(disable: 4996) // _CRT_SECURE_NO_WARNINGS +#pragma warning(disable: 6001) +#pragma warning(disable: 6011) +#pragma warning(disable: 6031) +#pragma warning(disable: 6255) +#pragma warning(disable: 6258) +#pragma warning(disable: 6386) +#pragma warning(disable: 6387) + +#include +#include +#include +#include +#include +#ifndef DISABLE_BITMESSAGE +#include +#endif + +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +// Protobuf +#include "proto/network.pb.h" +#include "proto/party.pb.h" +#include "proto/auth.pb.h" +#include "proto/node.pb.h" +#include "proto/rcon.pb.h" + +#pragma warning(pop) + +#include "Utils\IO.hpp" +#include "Utils\CSV.hpp" +#include "Utils\Time.hpp" +#include "Utils\Cache.hpp" +#include "Utils\Chain.hpp" +#include "Utils\Utils.hpp" +#include "Utils\WebIO.hpp" +#include "Utils\Memory.hpp" +#include "Utils\String.hpp" +#include "Utils\Hooking.hpp" +#include "Utils\Library.hpp" +#include "Utils\InfoString.hpp" +#include "Utils\Compression.hpp" +#include "Utils\Cryptography.hpp" + +#include "Steam\Steam.hpp" + +#include "Game\Structs.hpp" +#include "Game\Functions.hpp" + +#include "Utils\Stream.hpp" + +#include "Components\Loader.hpp" + +// Libraries +#pragma comment(lib, "Winmm.lib") +#pragma comment(lib, "Crypt32.lib") +#pragma comment(lib, "Ws2_32.lib") +#pragma comment(lib, "d3d9.lib") +#pragma comment(lib, "Wininet.lib") +#pragma comment(lib, "shlwapi.lib") +#pragma comment(lib, "Urlmon.lib") +#pragma comment(lib, "Advapi32.lib") +#pragma comment(lib, "rpcrt4.lib") + +// Enable additional literals +using namespace std::literals; + +#endif + +#define STRINGIZE_(x) #x +#define STRINGIZE(x) STRINGIZE_(x) + +#define BASEGAME "iw4x" +#define CLIENT_CONFIG "iw4x_config.cfg" + +#define AssertSize(x, size) static_assert(sizeof(x) == size, STRINGIZE(x) " structure has an invalid size.") +#define AssertOffset(x, y, offset) static_assert(offsetof(x, y) == offset, STRINGIZE(x) "::" STRINGIZE(y) " is not at the right offset.") + +// Resource stuff +#ifdef APSTUDIO_INVOKED +#ifndef APSTUDIO_READONLY_SYMBOLS +// Defines below make accessing the resources from the code easier. +#define _APS_NEXT_RESOURCE_VALUE 102 +#define _APS_NEXT_COMMAND_VALUE 40001 +#define _APS_NEXT_CONTROL_VALUE 1001 +#define _APS_NEXT_SYMED_VALUE 101 +#endif +#endif + +// Enables custom map code +#ifdef DEBUG +#define ENABLE_EXPERIMENTAL_MAP_CODE +#endif diff --git a/src/Steam/Interfaces/SteamMatchmaking.cpp b/src/Steam/Interfaces/SteamMatchmaking.cpp index 79d19117..26a1451d 100644 --- a/src/Steam/Interfaces/SteamMatchmaking.cpp +++ b/src/Steam/Interfaces/SteamMatchmaking.cpp @@ -1,197 +1,197 @@ -#include "STDInclude.hpp" - -STEAM_IGNORE_WARNINGS_START - -namespace Steam -{ - int Matchmaking::GetFavoriteGameCount() - { - return 0; - } - - bool Matchmaking::GetFavoriteGame(int iGame, unsigned int *pnAppID, unsigned int *pnIP, unsigned short *pnConnPort, unsigned short *pnQueryPort, unsigned int *punFlags, unsigned int *pRTime32LastPlayedOnServer) - { - return false; - } - - int Matchmaking::AddFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, unsigned short nQueryPort, unsigned int unFlags, unsigned int rTime32LastPlayedOnServer) - { - return 0; - } - - bool Matchmaking::RemoveFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, unsigned short nQueryPort, unsigned int unFlags) - { - return false; - } - - unsigned __int64 Matchmaking::RequestLobbyList() - { - return 0; - } - - void Matchmaking::AddRequestLobbyListStringFilter(const char *pchKeyToMatch, const char *pchValueToMatch, int eComparisonType) - { - } - - void Matchmaking::AddRequestLobbyListNumericalFilter(const char *pchKeyToMatch, int nValueToMatch, int eComparisonType) - { - } - - void Matchmaking::AddRequestLobbyListNearValueFilter(const char *pchKeyToMatch, int nValueToBeCloseTo) - { - } - - void Matchmaking::AddRequestLobbyListFilterSlotsAvailable(int nSlotsAvailable) - { - } - - SteamID Matchmaking::GetLobbyByIndex(int iLobby) - { - return SteamID(); - } - - unsigned __int64 Matchmaking::CreateLobby(int eLobbyType, int cMaxMembers) - { - uint64_t result = Callbacks::RegisterCall(); - LobbyCreated* retvals = ::Utils::Memory::Allocate(); - SteamID id; - - id.AccountID = 1337132; - id.Universe = 1; - id.AccountType = 8; - id.AccountInstance = 0x40000; - - retvals->m_eResult = 1; - retvals->m_ulSteamIDLobby = id; - - Callbacks::ReturnCall(retvals, sizeof(LobbyCreated), LobbyCreated::CallbackID, result); - - Matchmaking::JoinLobby(id); - - return result; - } - - unsigned __int64 Matchmaking::JoinLobby(SteamID steamIDLobby) - { - uint64_t result = Callbacks::RegisterCall(); - LobbyEnter* retvals = ::Utils::Memory::Allocate(); - retvals->m_bLocked = false; - retvals->m_EChatRoomEnterResponse = 1; - retvals->m_rgfChatPermissions = 0xFFFFFFFF; - retvals->m_ulSteamIDLobby = steamIDLobby; - - Callbacks::ReturnCall(retvals, sizeof(LobbyEnter), LobbyEnter::CallbackID, result); - - return result; - } - - void Matchmaking::LeaveLobby(SteamID steamIDLobby) - { - Components::Party::RemoveLobby(steamIDLobby); - } - - bool Matchmaking::InviteUserToLobby(SteamID steamIDLobby, SteamID steamIDInvitee) - { - return true; - } - - int Matchmaking::GetNumLobbyMembers(SteamID steamIDLobby) - { - return 1; - } - - SteamID Matchmaking::GetLobbyMemberByIndex(SteamID steamIDLobby, int iMember) - { - return SteamUser()->GetSteamID(); - } - - const char *Matchmaking::GetLobbyData(SteamID steamIDLobby, const char *pchKey) - { - return Components::Party::GetLobbyInfo(steamIDLobby, pchKey); - } - - bool Matchmaking::SetLobbyData(SteamID steamIDLobby, const char *pchKey, const char *pchValue) - { - return true; - } - - int Matchmaking::GetLobbyDataCount(SteamID steamIDLobby) - { - return 0; - } - - bool Matchmaking::GetLobbyDataByIndex(SteamID steamIDLobby, int iLobbyData, char *pchKey, int cchKeyBufferSize, char *pchValue, int cchValueBufferSize) - { - return false; - } - - bool Matchmaking::DeleteLobbyData(SteamID steamIDLobby, const char *pchKey) - { - return false; - } - - const char *Matchmaking::GetLobbyMemberData(SteamID steamIDLobby, SteamID steamIDUser, const char *pchKey) - { - return ""; - } - - void Matchmaking::SetLobbyMemberData(SteamID steamIDLobby, const char *pchKey, const char *pchValue) - { - } - - bool Matchmaking::SendLobbyChatMsg(SteamID steamIDLobby, const void *pvMsgBody, int cubMsgBody) - { - return true; - } - - int Matchmaking::GetLobbyChatEntry(SteamID steamIDLobby, int iChatID, SteamID *pSteamIDUser, void *pvData, int cubData, int *peChatEntryType) - { - return 0; - } - - bool Matchmaking::RequestLobbyData(SteamID steamIDLobby) - { - return false; - } - - void Matchmaking::SetLobbyGameServer(SteamID steamIDLobby, unsigned int unGameServerIP, unsigned short unGameServerPort, SteamID steamIDGameServer) - { - } - - bool Matchmaking::GetLobbyGameServer(SteamID steamIDLobby, unsigned int *punGameServerIP, unsigned short *punGameServerPort, SteamID *psteamIDGameServer) - { - return false; - } - - bool Matchmaking::SetLobbyMemberLimit(SteamID steamIDLobby, int cMaxMembers) - { - return true; - } - - int Matchmaking::GetLobbyMemberLimit(SteamID steamIDLobby) - { - return 0; - } - - bool Matchmaking::SetLobbyType(SteamID steamIDLobby, int eLobbyType) - { - return true; - } - - bool Matchmaking::SetLobbyJoinable(SteamID steamIDLobby, bool bLobbyJoinable) - { - return true; - } - - SteamID Matchmaking::GetLobbyOwner(SteamID steamIDLobby) - { - return SteamUser()->GetSteamID(); - } - - bool Matchmaking::SetLobbyOwner(SteamID steamIDLobby, SteamID steamIDNewOwner) - { - return true; - } -} - -STEAM_IGNORE_WARNINGS_END +#include "STDInclude.hpp" + +STEAM_IGNORE_WARNINGS_START + +namespace Steam +{ + int Matchmaking::GetFavoriteGameCount() + { + return 0; + } + + bool Matchmaking::GetFavoriteGame(int iGame, unsigned int *pnAppID, unsigned int *pnIP, unsigned short *pnConnPort, unsigned short *pnQueryPort, unsigned int *punFlags, unsigned int *pRTime32LastPlayedOnServer) + { + return false; + } + + int Matchmaking::AddFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, unsigned short nQueryPort, unsigned int unFlags, unsigned int rTime32LastPlayedOnServer) + { + return 0; + } + + bool Matchmaking::RemoveFavoriteGame(unsigned int nAppID, unsigned int nIP, unsigned short nConnPort, unsigned short nQueryPort, unsigned int unFlags) + { + return false; + } + + unsigned __int64 Matchmaking::RequestLobbyList() + { + return 0; + } + + void Matchmaking::AddRequestLobbyListStringFilter(const char *pchKeyToMatch, const char *pchValueToMatch, int eComparisonType) + { + } + + void Matchmaking::AddRequestLobbyListNumericalFilter(const char *pchKeyToMatch, int nValueToMatch, int eComparisonType) + { + } + + void Matchmaking::AddRequestLobbyListNearValueFilter(const char *pchKeyToMatch, int nValueToBeCloseTo) + { + } + + void Matchmaking::AddRequestLobbyListFilterSlotsAvailable(int nSlotsAvailable) + { + } + + SteamID Matchmaking::GetLobbyByIndex(int iLobby) + { + return SteamID(); + } + + unsigned __int64 Matchmaking::CreateLobby(int eLobbyType, int cMaxMembers) + { + uint64_t result = Callbacks::RegisterCall(); + LobbyCreated* retvals = ::Utils::Memory::Allocate(); + SteamID id; + + id.AccountID = 1337132; + id.Universe = 1; + id.AccountType = 8; + id.AccountInstance = 0x40000; + + retvals->m_eResult = 1; + retvals->m_ulSteamIDLobby = id; + + Callbacks::ReturnCall(retvals, sizeof(LobbyCreated), LobbyCreated::CallbackID, result); + + Matchmaking::JoinLobby(id); + + return result; + } + + unsigned __int64 Matchmaking::JoinLobby(SteamID steamIDLobby) + { + uint64_t result = Callbacks::RegisterCall(); + LobbyEnter* retvals = ::Utils::Memory::Allocate(); + retvals->m_bLocked = false; + retvals->m_EChatRoomEnterResponse = 1; + retvals->m_rgfChatPermissions = 0xFFFFFFFF; + retvals->m_ulSteamIDLobby = steamIDLobby; + + Callbacks::ReturnCall(retvals, sizeof(LobbyEnter), LobbyEnter::CallbackID, result); + + return result; + } + + void Matchmaking::LeaveLobby(SteamID steamIDLobby) + { + Components::Party::RemoveLobby(steamIDLobby); + } + + bool Matchmaking::InviteUserToLobby(SteamID steamIDLobby, SteamID steamIDInvitee) + { + return true; + } + + int Matchmaking::GetNumLobbyMembers(SteamID steamIDLobby) + { + return 1; + } + + SteamID Matchmaking::GetLobbyMemberByIndex(SteamID steamIDLobby, int iMember) + { + return SteamUser()->GetSteamID(); + } + + const char *Matchmaking::GetLobbyData(SteamID steamIDLobby, const char *pchKey) + { + return Components::Party::GetLobbyInfo(steamIDLobby, pchKey); + } + + bool Matchmaking::SetLobbyData(SteamID steamIDLobby, const char *pchKey, const char *pchValue) + { + return true; + } + + int Matchmaking::GetLobbyDataCount(SteamID steamIDLobby) + { + return 0; + } + + bool Matchmaking::GetLobbyDataByIndex(SteamID steamIDLobby, int iLobbyData, char *pchKey, int cchKeyBufferSize, char *pchValue, int cchValueBufferSize) + { + return false; + } + + bool Matchmaking::DeleteLobbyData(SteamID steamIDLobby, const char *pchKey) + { + return false; + } + + const char *Matchmaking::GetLobbyMemberData(SteamID steamIDLobby, SteamID steamIDUser, const char *pchKey) + { + return ""; + } + + void Matchmaking::SetLobbyMemberData(SteamID steamIDLobby, const char *pchKey, const char *pchValue) + { + } + + bool Matchmaking::SendLobbyChatMsg(SteamID steamIDLobby, const void *pvMsgBody, int cubMsgBody) + { + return true; + } + + int Matchmaking::GetLobbyChatEntry(SteamID steamIDLobby, int iChatID, SteamID *pSteamIDUser, void *pvData, int cubData, int *peChatEntryType) + { + return 0; + } + + bool Matchmaking::RequestLobbyData(SteamID steamIDLobby) + { + return false; + } + + void Matchmaking::SetLobbyGameServer(SteamID steamIDLobby, unsigned int unGameServerIP, unsigned short unGameServerPort, SteamID steamIDGameServer) + { + } + + bool Matchmaking::GetLobbyGameServer(SteamID steamIDLobby, unsigned int *punGameServerIP, unsigned short *punGameServerPort, SteamID *psteamIDGameServer) + { + return false; + } + + bool Matchmaking::SetLobbyMemberLimit(SteamID steamIDLobby, int cMaxMembers) + { + return true; + } + + int Matchmaking::GetLobbyMemberLimit(SteamID steamIDLobby) + { + return 0; + } + + bool Matchmaking::SetLobbyType(SteamID steamIDLobby, int eLobbyType) + { + return true; + } + + bool Matchmaking::SetLobbyJoinable(SteamID steamIDLobby, bool bLobbyJoinable) + { + return true; + } + + SteamID Matchmaking::GetLobbyOwner(SteamID steamIDLobby) + { + return SteamUser()->GetSteamID(); + } + + bool Matchmaking::SetLobbyOwner(SteamID steamIDLobby, SteamID steamIDNewOwner) + { + return true; + } +} + +STEAM_IGNORE_WARNINGS_END diff --git a/src/Steam/Interfaces/SteamUser.cpp b/src/Steam/Interfaces/SteamUser.cpp index 2e99b4de..5eba1b5f 100644 --- a/src/Steam/Interfaces/SteamUser.cpp +++ b/src/Steam/Interfaces/SteamUser.cpp @@ -1,104 +1,104 @@ -#include "STDInclude.hpp" - -STEAM_IGNORE_WARNINGS_START - -namespace Steam -{ - int User::GetHSteamUser() - { - return NULL; - } - - bool User::LoggedOn() - { - return true; - } - - SteamID User::GetSteamID() - { - static unsigned __int64 idBits = 0; - - SteamID id; - - if (!idBits) - { - if (Components::Dedicated::IsEnabled() || Components::ZoneBuilder::IsEnabled()) // Dedi guid - { - idBits = *reinterpret_cast("DEDICATE"); - } - else if (Components::Singleton::IsFirstInstance()) // ECDSA guid - { - idBits = Components::Auth::GetKeyHash(); - } - else // Random guid - { - idBits = (static_cast(Game::Sys_Milliseconds()) << 32) | timeGetTime(); - } - } - - id.Bits = idBits; - return id; - } - - int User::InitiateGameConnection(void *pAuthBlob, int cbMaxAuthBlob, SteamID steamIDGameServer, unsigned int unIPServer, unsigned short usPortServer, bool bSecure) - { - Components::Logger::Print("%s\n", __FUNCTION__); - return 0; - } - - void User::TerminateGameConnection(unsigned int unIPServer, unsigned short usPortServer) - { - } - - void User::TrackAppUsageEvent(SteamID gameID, int eAppUsageEvent, const char *pchExtraInfo) - { - } - - bool User::GetUserDataFolder(char *pchBuffer, int cubBuffer) - { - return false; - } - - void User::StartVoiceRecording() - { - } - - void User::StopVoiceRecording() - { - } - - int User::GetCompressedVoice(void *pDestBuffer, unsigned int cbDestBufferSize, unsigned int *nBytesWritten) - { - return 0; - } - - int User::DecompressVoice(void *pCompressed, unsigned int cbCompressed, void *pDestBuffer, unsigned int cbDestBufferSize, unsigned int *nBytesWritten) - { - return 0; - } - - unsigned int User::GetAuthSessionTicket(void *pTicket, int cbMaxTicket, unsigned int *pcbTicket) - { - return 0; - } - - int User::BeginAuthSession(const void *pAuthTicket, int cbAuthTicket, SteamID steamID) - { - return 0; - } - - void User::EndAuthSession(SteamID steamID) - { - } - - void User::CancelAuthTicket(unsigned int hAuthTicket) - { - } - - unsigned int User::UserHasLicenseForApp(SteamID steamID, unsigned int appID) - { - return 0; - } -} - -STEAM_IGNORE_WARNINGS_END +#include "STDInclude.hpp" + +STEAM_IGNORE_WARNINGS_START + +namespace Steam +{ + int User::GetHSteamUser() + { + return NULL; + } + + bool User::LoggedOn() + { + return true; + } + + SteamID User::GetSteamID() + { + static unsigned __int64 idBits = 0; + + SteamID id; + + if (!idBits) + { + if (Components::Dedicated::IsEnabled() || Components::ZoneBuilder::IsEnabled()) // Dedi guid + { + idBits = *reinterpret_cast("DEDICATE"); + } + else if (Components::Singleton::IsFirstInstance()) // ECDSA guid + { + idBits = Components::Auth::GetKeyHash(); + } + else // Random guid + { + idBits = (static_cast(Game::Sys_Milliseconds()) << 32) | timeGetTime(); + } + } + + id.Bits = idBits; + return id; + } + + int User::InitiateGameConnection(void *pAuthBlob, int cbMaxAuthBlob, SteamID steamIDGameServer, unsigned int unIPServer, unsigned short usPortServer, bool bSecure) + { + Components::Logger::Print("%s\n", __FUNCTION__); + return 0; + } + + void User::TerminateGameConnection(unsigned int unIPServer, unsigned short usPortServer) + { + } + + void User::TrackAppUsageEvent(SteamID gameID, int eAppUsageEvent, const char *pchExtraInfo) + { + } + + bool User::GetUserDataFolder(char *pchBuffer, int cubBuffer) + { + return false; + } + + void User::StartVoiceRecording() + { + } + + void User::StopVoiceRecording() + { + } + + int User::GetCompressedVoice(void *pDestBuffer, unsigned int cbDestBufferSize, unsigned int *nBytesWritten) + { + return 0; + } + + int User::DecompressVoice(void *pCompressed, unsigned int cbCompressed, void *pDestBuffer, unsigned int cbDestBufferSize, unsigned int *nBytesWritten) + { + return 0; + } + + unsigned int User::GetAuthSessionTicket(void *pTicket, int cbMaxTicket, unsigned int *pcbTicket) + { + return 0; + } + + int User::BeginAuthSession(const void *pAuthTicket, int cbAuthTicket, SteamID steamID) + { + return 0; + } + + void User::EndAuthSession(SteamID steamID) + { + } + + void User::CancelAuthTicket(unsigned int hAuthTicket) + { + } + + unsigned int User::UserHasLicenseForApp(SteamID steamID, unsigned int appID) + { + return 0; + } +} + +STEAM_IGNORE_WARNINGS_END diff --git a/src/Steam/Interfaces/SteamUtils.cpp b/src/Steam/Interfaces/SteamUtils.cpp index 55925a9a..66fda75e 100644 --- a/src/Steam/Interfaces/SteamUtils.cpp +++ b/src/Steam/Interfaces/SteamUtils.cpp @@ -1,101 +1,101 @@ -#include "STDInclude.hpp" - -STEAM_IGNORE_WARNINGS_START - -namespace Steam -{ - unsigned int Utils::GetSecondsSinceAppActive() - { - return 0; - } - - unsigned int Utils::GetSecondsSinceComputerActive() - { - return 0; - } - - int Utils::GetConnectedUniverse() - { - return 1; - } - - unsigned int Utils::GetServerRealTime() - { - return 0; - } - - const char* Utils::GetIPCountry() - { - return "US"; - } - - bool Utils::GetImageSize(int iImage, unsigned int *pnWidth, unsigned int *pnHeight) - { - return false; - } - - bool Utils::GetImageRGBA(int iImage, unsigned char *pubDest, int nDestBufferSize) - { - return false; - } - - bool Utils::GetCSERIPPort(unsigned int *unIP, unsigned short *usPort) - { - return false; - } - - unsigned char Utils::GetCurrentBatteryPower() - { - return 255; - } - - unsigned int Utils::GetAppID() - { - return 10190; - } - - void Utils::SetOverlayNotificationPosition(int eNotificationPosition) - { - Proxy::SetOverlayNotificationPosition(eNotificationPosition); - } - - bool Utils::IsAPICallCompleted(unsigned __int64 hSteamAPICall, bool *pbFailed) - { - return false; - } - - int Utils::GetAPICallFailureReason(unsigned __int64 hSteamAPICall) - { - return -1; - } - - bool Utils::GetAPICallResult(unsigned __int64 hSteamAPICall, void *pCallback, int cubCallback, int iCallbackExpected, bool *pbFailed) - { - return false; - } - - void Utils::RunFrame() - { - } - - unsigned int Utils::GetIPCCallCount() - { - return 0; - } - - void Utils::SetWarningMessageHook(void(*pFunction)(int hpipe, const char *message)) - { - } - - bool Utils::IsOverlayEnabled() - { - return false; - } - - bool Utils::BOverlayNeedsPresent() - { - return false; - } -} - -STEAM_IGNORE_WARNINGS_END +#include "STDInclude.hpp" + +STEAM_IGNORE_WARNINGS_START + +namespace Steam +{ + unsigned int Utils::GetSecondsSinceAppActive() + { + return 0; + } + + unsigned int Utils::GetSecondsSinceComputerActive() + { + return 0; + } + + int Utils::GetConnectedUniverse() + { + return 1; + } + + unsigned int Utils::GetServerRealTime() + { + return 0; + } + + const char* Utils::GetIPCountry() + { + return "US"; + } + + bool Utils::GetImageSize(int iImage, unsigned int *pnWidth, unsigned int *pnHeight) + { + return false; + } + + bool Utils::GetImageRGBA(int iImage, unsigned char *pubDest, int nDestBufferSize) + { + return false; + } + + bool Utils::GetCSERIPPort(unsigned int *unIP, unsigned short *usPort) + { + return false; + } + + unsigned char Utils::GetCurrentBatteryPower() + { + return 255; + } + + unsigned int Utils::GetAppID() + { + return 10190; + } + + void Utils::SetOverlayNotificationPosition(int eNotificationPosition) + { + Proxy::SetOverlayNotificationPosition(eNotificationPosition); + } + + bool Utils::IsAPICallCompleted(unsigned __int64 hSteamAPICall, bool *pbFailed) + { + return false; + } + + int Utils::GetAPICallFailureReason(unsigned __int64 hSteamAPICall) + { + return -1; + } + + bool Utils::GetAPICallResult(unsigned __int64 hSteamAPICall, void *pCallback, int cubCallback, int iCallbackExpected, bool *pbFailed) + { + return false; + } + + void Utils::RunFrame() + { + } + + unsigned int Utils::GetIPCCallCount() + { + return 0; + } + + void Utils::SetWarningMessageHook(void(*pFunction)(int hpipe, const char *message)) + { + } + + bool Utils::IsOverlayEnabled() + { + return false; + } + + bool Utils::BOverlayNeedsPresent() + { + return false; + } +} + +STEAM_IGNORE_WARNINGS_END diff --git a/src/Steam/Proxy.cpp b/src/Steam/Proxy.cpp index 84c09bde..45c502e3 100644 --- a/src/Steam/Proxy.cpp +++ b/src/Steam/Proxy.cpp @@ -1,170 +1,170 @@ -#include "STDInclude.hpp" - -namespace Steam -{ - ::Utils::Library Proxy::Client; - ::Utils::Library Proxy::Overlay; - - ISteamClient008* Proxy::SteamClient; - IClientEngine* Proxy::ClientEngine; - IClientUser* Proxy::ClientUser; - - void* Proxy::SteamPipe; - void* Proxy::SteamUser; - - Friends* Proxy::SteamFriends; - Utils* Proxy::SteamUtils; - - uint32_t Proxy::AppId; - - void Proxy::SetGame(uint32_t appId) - { - Proxy::AppId = appId; - - SetEnvironmentVariableA("SteamAppId", ::Utils::String::VA("%lu", appId)); - SetEnvironmentVariableA("SteamGameId", ::Utils::String::VA("%llu", appId & 0xFFFFFF)); - - ::Utils::IO::WriteFile("steam_appid.txt", ::Utils::String::VA("%lu", appId), false); - } - - void Proxy::SetMod(std::string mod) - { -#if 0 - if (!Proxy::ClientUser) return; - - GameID_t gameID; - gameID.m_nType = 1; // k_EGameIDTypeGameMod - gameID.m_nAppID = Proxy::AppId & 0xFFFFFF; - gameID.m_nModID = 0x01010101; - - char ourPath[MAX_PATH] = { 0 }; - GetModuleFileNameA(GetModuleHandle(NULL), ourPath, sizeof(ourPath)); - - char ourDirectory[MAX_PATH] = { 0 }; - GetCurrentDirectoryA(sizeof(ourDirectory), ourDirectory); - - char blob[1] = { 0 }; - std::string cmdline = ::Utils::String::VA("\"%s\" -parentProc %d", ourPath, GetCurrentProcessId()); - Proxy::ClientUser->SpawnProcess(blob, 0, ourPath, cmdline.data(), 0, ourDirectory, gameID, Proxy::AppId, mod.data(), 0); -#endif - } - - void Proxy::RunMod() - { - char* command = "-parentProc "; - char* parentProc = strstr(GetCommandLineA(), command); - - OutputDebugStringA(GetCommandLineA()); - - if (parentProc) - { - parentProc += strlen(command); - int pid = atoi(parentProc); - - HANDLE processHandle = OpenProcess(SYNCHRONIZE, FALSE, pid); - - if (processHandle && processHandle != INVALID_HANDLE_VALUE) - { - WaitForSingleObject(processHandle, INFINITE); - CloseHandle(processHandle); - } - - TerminateProcess(GetCurrentProcess(), 0); - } - } - - bool Proxy::Inititalize() - { - std::string directoy = Proxy::GetSteamDirectory(); - if (directoy.empty()) return false; - - SetDllDirectoryA(Proxy::GetSteamDirectory().data()); - - Proxy::Client = ::Utils::Library(STEAMCLIENT_LIB, false); - Proxy::Overlay = ::Utils::Library(GAMEOVERLAY_LIB, false); - if (!Proxy::Client.valid() || !Proxy::Overlay.valid()) return false; - - Proxy::SteamClient = Proxy::Client.get("CreateInterface")("SteamClient008", nullptr); - if(!Proxy::SteamClient) return false; - - Proxy::SteamPipe = Proxy::SteamClient->CreateSteamPipe(); - if (!Proxy::SteamPipe) return false; - - Proxy::SteamUser = Proxy::SteamClient->ConnectToGlobalUser(Proxy::SteamPipe); - if (!Proxy::SteamUser) return false; - - Proxy::ClientEngine = Proxy::Client.get("CreateInterface")("CLIENTENGINE_INTERFACE_VERSION004", nullptr); - if (!Proxy::ClientEngine) return false; - - Proxy::ClientUser = Proxy::ClientEngine->GetIClientUser(Proxy::SteamUser, Proxy::SteamPipe, "CLIENTUSER_INTERFACE_VERSION001"); - if (!Proxy::ClientUser) return false; - - Proxy::SteamFriends = reinterpret_cast(Proxy::SteamClient->GetISteamFriends(Proxy::SteamUser, Proxy::SteamPipe, "SteamFriends005")); - if (!Proxy::SteamFriends) return false; - - Proxy::SteamUtils = reinterpret_cast(Proxy::SteamClient->GetISteamFriends(Proxy::SteamUser, Proxy::SteamPipe, "SteamUtils005")); - if (!Proxy::SteamUtils) return false; - - return true; - } - - void Proxy::Uninititalize() - { - if (Proxy::SteamClient && Proxy::SteamPipe) - { - if (Proxy::SteamUser) - { - //Proxy::SteamClient->ReleaseUser(Proxy::SteamPipe, Proxy::SteamUser); - } - - //Proxy::SteamClient->ReleaseSteamPipe(Proxy::SteamPipe); - } - - Proxy::Client = ::Utils::Library(); - Proxy::Overlay = ::Utils::Library(); - } - - std::string Proxy::GetSteamDirectory() - { - HKEY hRegKey; - char SteamPath[MAX_PATH]; - if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, STEAM_REGISTRY_PATH, 0, KEY_QUERY_VALUE, &hRegKey) == ERROR_SUCCESS) - { - DWORD dwLength = sizeof(SteamPath); - RegQueryValueExA(hRegKey, "InstallPath", NULL, NULL, reinterpret_cast(SteamPath), &dwLength); - RegCloseKey(hRegKey); - - return SteamPath; - } - - return ""; - } - - void Proxy::SetOverlayNotificationPosition(uint32_t eNotificationPosition) - { - if (Proxy::Overlay.valid()) - { - Proxy::Overlay.get("SetNotificationPosition")(eNotificationPosition); - } - } - - bool Proxy::IsOverlayEnabled() - { - if (Proxy::Overlay.valid()) - { - return Proxy::Overlay.get("IsOverlayEnabled")(); - } - - return false; - } - - bool Proxy::BOverlayNeedsPresent() - { - if (Proxy::Overlay.valid()) - { - return Proxy::Overlay.get("BOverlayNeedsPresent")(); - } - - return false; - } -} +#include "STDInclude.hpp" + +namespace Steam +{ + ::Utils::Library Proxy::Client; + ::Utils::Library Proxy::Overlay; + + ISteamClient008* Proxy::SteamClient; + IClientEngine* Proxy::ClientEngine; + IClientUser* Proxy::ClientUser; + + void* Proxy::SteamPipe; + void* Proxy::SteamUser; + + Friends* Proxy::SteamFriends; + Utils* Proxy::SteamUtils; + + uint32_t Proxy::AppId; + + void Proxy::SetGame(uint32_t appId) + { + Proxy::AppId = appId; + + SetEnvironmentVariableA("SteamAppId", ::Utils::String::VA("%lu", appId)); + SetEnvironmentVariableA("SteamGameId", ::Utils::String::VA("%llu", appId & 0xFFFFFF)); + + ::Utils::IO::WriteFile("steam_appid.txt", ::Utils::String::VA("%lu", appId), false); + } + + void Proxy::SetMod(std::string mod) + { +#if 0 + if (!Proxy::ClientUser) return; + + GameID_t gameID; + gameID.m_nType = 1; // k_EGameIDTypeGameMod + gameID.m_nAppID = Proxy::AppId & 0xFFFFFF; + gameID.m_nModID = 0x01010101; + + char ourPath[MAX_PATH] = { 0 }; + GetModuleFileNameA(GetModuleHandle(NULL), ourPath, sizeof(ourPath)); + + char ourDirectory[MAX_PATH] = { 0 }; + GetCurrentDirectoryA(sizeof(ourDirectory), ourDirectory); + + char blob[1] = { 0 }; + std::string cmdline = ::Utils::String::VA("\"%s\" -parentProc %d", ourPath, GetCurrentProcessId()); + Proxy::ClientUser->SpawnProcess(blob, 0, ourPath, cmdline.data(), 0, ourDirectory, gameID, Proxy::AppId, mod.data(), 0); +#endif + } + + void Proxy::RunMod() + { + char* command = "-parentProc "; + char* parentProc = strstr(GetCommandLineA(), command); + + OutputDebugStringA(GetCommandLineA()); + + if (parentProc) + { + parentProc += strlen(command); + int pid = atoi(parentProc); + + HANDLE processHandle = OpenProcess(SYNCHRONIZE, FALSE, pid); + + if (processHandle && processHandle != INVALID_HANDLE_VALUE) + { + WaitForSingleObject(processHandle, INFINITE); + CloseHandle(processHandle); + } + + TerminateProcess(GetCurrentProcess(), 0); + } + } + + bool Proxy::Inititalize() + { + std::string directoy = Proxy::GetSteamDirectory(); + if (directoy.empty()) return false; + + SetDllDirectoryA(Proxy::GetSteamDirectory().data()); + + Proxy::Client = ::Utils::Library(STEAMCLIENT_LIB, false); + Proxy::Overlay = ::Utils::Library(GAMEOVERLAY_LIB, false); + if (!Proxy::Client.valid() || !Proxy::Overlay.valid()) return false; + + Proxy::SteamClient = Proxy::Client.get("CreateInterface")("SteamClient008", nullptr); + if(!Proxy::SteamClient) return false; + + Proxy::SteamPipe = Proxy::SteamClient->CreateSteamPipe(); + if (!Proxy::SteamPipe) return false; + + Proxy::SteamUser = Proxy::SteamClient->ConnectToGlobalUser(Proxy::SteamPipe); + if (!Proxy::SteamUser) return false; + + Proxy::ClientEngine = Proxy::Client.get("CreateInterface")("CLIENTENGINE_INTERFACE_VERSION004", nullptr); + if (!Proxy::ClientEngine) return false; + + Proxy::ClientUser = Proxy::ClientEngine->GetIClientUser(Proxy::SteamUser, Proxy::SteamPipe, "CLIENTUSER_INTERFACE_VERSION001"); + if (!Proxy::ClientUser) return false; + + Proxy::SteamFriends = reinterpret_cast(Proxy::SteamClient->GetISteamFriends(Proxy::SteamUser, Proxy::SteamPipe, "SteamFriends005")); + if (!Proxy::SteamFriends) return false; + + Proxy::SteamUtils = reinterpret_cast(Proxy::SteamClient->GetISteamFriends(Proxy::SteamUser, Proxy::SteamPipe, "SteamUtils005")); + if (!Proxy::SteamUtils) return false; + + return true; + } + + void Proxy::Uninititalize() + { + if (Proxy::SteamClient && Proxy::SteamPipe) + { + if (Proxy::SteamUser) + { + //Proxy::SteamClient->ReleaseUser(Proxy::SteamPipe, Proxy::SteamUser); + } + + //Proxy::SteamClient->ReleaseSteamPipe(Proxy::SteamPipe); + } + + Proxy::Client = ::Utils::Library(); + Proxy::Overlay = ::Utils::Library(); + } + + std::string Proxy::GetSteamDirectory() + { + HKEY hRegKey; + char SteamPath[MAX_PATH]; + if (RegOpenKeyExA(HKEY_LOCAL_MACHINE, STEAM_REGISTRY_PATH, 0, KEY_QUERY_VALUE, &hRegKey) == ERROR_SUCCESS) + { + DWORD dwLength = sizeof(SteamPath); + RegQueryValueExA(hRegKey, "InstallPath", NULL, NULL, reinterpret_cast(SteamPath), &dwLength); + RegCloseKey(hRegKey); + + return SteamPath; + } + + return ""; + } + + void Proxy::SetOverlayNotificationPosition(uint32_t eNotificationPosition) + { + if (Proxy::Overlay.valid()) + { + Proxy::Overlay.get("SetNotificationPosition")(eNotificationPosition); + } + } + + bool Proxy::IsOverlayEnabled() + { + if (Proxy::Overlay.valid()) + { + return Proxy::Overlay.get("IsOverlayEnabled")(); + } + + return false; + } + + bool Proxy::BOverlayNeedsPresent() + { + if (Proxy::Overlay.valid()) + { + return Proxy::Overlay.get("BOverlayNeedsPresent")(); + } + + return false; + } +} diff --git a/src/Steam/Proxy.hpp b/src/Steam/Proxy.hpp index 7b5f8601..be4e7b26 100644 --- a/src/Steam/Proxy.hpp +++ b/src/Steam/Proxy.hpp @@ -1,356 +1,356 @@ -#ifdef _WIN64 -#define GAMEOVERLAY_LIB "gameoverlayrenderer64.dll" -#define STEAMCLIENT_LIB "steamclient64.dll" -#define STEAM_REGISTRY_PATH "Software\\Wow6432Node\\Valve\\Steam" -#else -#define GAMEOVERLAY_LIB "gameoverlayrenderer.dll" -#define STEAMCLIENT_LIB "steamclient.dll" -#define STEAM_REGISTRY_PATH "Software\\Valve\\Steam" -#endif - -namespace Steam -{ - class ISteamClient008 - { - public: - virtual void* CreateSteamPipe() = 0; - virtual bool ReleaseSteamPipe(void* hSteamPipe) = 0; - virtual void* ConnectToGlobalUser(void* hSteamPipe) = 0; - virtual void* CreateLocalUser(void* *phSteamPipe, int eAccountType) = 0; - virtual void ReleaseUser(void* hSteamPipe, void* hUser) = 0; - virtual void *GetISteamUser(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void *GetISteamGameServer(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void SetLocalIPBinding(uint32_t unIP, uint16_t usPort) = 0; - virtual void *GetISteamFriends(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void *GetISteamUtils(void* hSteamPipe, const char *pchVersion) = 0; - virtual void *GetISteamMatchmaking(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void *GetISteamMasterServerUpdater(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void *GetISteamMatchmakingServers(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void *GetISteamGenericInterface(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void *GetISteamUserStats(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void *GetISteamApps(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void *GetISteamNetworking(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void*GetISteamRemoteStorage(void* hSteamuser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void RunFrame() = 0; - virtual uint32_t GetIPCCallCount() = 0; - virtual void SetWarningMessageHook(void* pFunction) = 0; - }; - - class IClientUser - { - public: - virtual int32_t GetHSteamUser() = 0; - virtual void LogOn(bool bInteractive, SteamID steamID) = 0; - virtual void LogOnWithPassword(bool bInteractive, const char * pchLogin, const char * pchPassword) = 0; - virtual void LogOnAndCreateNewSteamAccountIfNeeded(bool bInteractive) = 0; - virtual int LogOnConnectionless() = 0; - virtual void LogOff() = 0; - virtual bool BLoggedOn() = 0; - virtual int GetLogonState() = 0; - virtual bool BConnected() = 0; - virtual bool BTryingToLogin() = 0; - virtual SteamID GetSteamID() = 0; - virtual SteamID GetConsoleSteamID() = 0; - virtual bool IsVACBanned(uint32_t nGameID) = 0; - virtual bool RequireShowVACBannedMessage(uint32_t nAppID) = 0; - virtual void AcknowledgeVACBanning(uint32_t nAppID) = 0; - virtual void SetSteam2Ticket(uint8_t* pubTicket, int32_t cubTicket) = 0; - virtual bool BExistsSteam2Ticket() = 0; - virtual bool SetEmail(const char *pchEmail) = 0; - virtual bool SetConfigString(int eRegistrySubTree, const char *pchKey, const char *pchValue) = 0; - virtual bool GetConfigString(int eRegistrySubTree, const char *pchKey, char *pchValue, int32_t cbValue) = 0; - virtual bool SetConfigInt(int eRegistrySubTree, const char *pchKey, int32_t iValue) = 0; - virtual bool GetConfigInt(int eRegistrySubTree, const char *pchKey, int32_t *pValue) = 0; - virtual bool GetConfigStoreKeyName(int eRegistrySubTree, const char *pchKey, char *pchStoreName, int32_t cbStoreName) = 0; - virtual int32_t InitiateGameConnection(void *pOutputBlob, int32_t cbBlobMax, SteamID steamIDGS, uint64_t gameID, uint32_t unIPServer, uint16_t usPortServer, bool bSecure) = 0; - virtual int32_t InitiateGameConnectionOld(void *pOutputBlob, int32_t cbBlobMax, SteamID steamIDGS, uint64_t gameID, uint32_t unIPServer, uint16_t usPortServer, bool bSecure, void *pvSteam2GetEncryptionKey, int32_t cbSteam2GetEncryptionKey) = 0; - virtual void TerminateGameConnection(uint32_t unIPServer, uint16_t usPortServer) = 0; - virtual bool TerminateAppMultiStep(uint32_t, uint32_t) = 0; - virtual void SetSelfAsPrimaryChatDestination() = 0; - virtual bool IsPrimaryChatDestination() = 0; - virtual void RequestLegacyCDKey(uint32_t iAppID) = 0; - virtual bool AckGuestPass(const char *pchGuestPassCode) = 0; - virtual bool RedeemGuestPass(const char *pchGuestPassCode) = 0; - virtual uint32_t GetGuestPassToGiveCount() = 0; - virtual uint32_t GetGuestPassToRedeemCount() = 0; - virtual bool GetGuestPassToGiveInfo(uint32_t nPassIndex, uint64_t *pgidGuestPassID, uint32_t* pnPackageID, uint32_t* pRTime32Created, uint32_t* pRTime32Expiration, uint32_t* pRTime32Sent, uint32_t* pRTime32Redeemed, char* pchRecipientAddress, int32_t cRecipientAddressSize) = 0; - virtual bool GetGuestPassToRedeemInfo(uint32_t nPassIndex, uint64_t *pgidGuestPassID, uint32_t* pnPackageID, uint32_t* pRTime32Created, uint32_t* pRTime32Expiration, uint32_t* pRTime32Sent, uint32_t* pRTime32Redeemed) = 0; - virtual bool GetGuestPassToRedeemSenderName(uint32_t nPassIndex, char* pchSenderName, int32_t cSenderNameSize) = 0; - virtual void AcknowledgeMessageByGID(const char *pchMessageGID) = 0; - virtual bool SetLanguage(const char *pchLanguage) = 0; - virtual void TrackAppUsageEvent(uint64_t gameID, int32_t eAppUsageEvent, const char *pchExtraInfo = "") = 0; - virtual int32_t RaiseConnectionPriority(int eConnectionPriority) = 0; - virtual void ResetConnectionPriority(int32_t hRaiseConnectionPriorityPrev) = 0; - virtual void SetAccountNameFromSteam2(const char *pchAccountName) = 0; - virtual bool SetPasswordFromSteam2(const char *pchPassword) = 0; - virtual bool BHasCachedCredentials(const char * pchUnk) = 0; - virtual bool SetAccountNameForCachedCredentialLogin(const char *pchAccountName, bool bUnk) = 0; - virtual void SetLoginInformation(const char *pchAccountName, const char *pchPassword, bool bRememberPassword) = 0; - virtual void ClearAllLoginInformation() = 0; - virtual void SetAccountCreationTime(uint32_t rtime32Time) = 0; - virtual uint64_t RequestWebAuthToken() = 0; - virtual bool GetCurrentWebAuthToken(char *pchBuffer, int32_t cubBuffer) = 0; - virtual bool GetLanguage(char* pchLanguage, int32_t cbLanguage) = 0; - virtual bool BIsCyberCafe() = 0; - virtual bool BIsAcademicAccount() = 0; - virtual void CreateAccount(const char *pchAccountName, const char *pchNewPassword, const char *pchNewEmail, int32_t iQuestion, const char *pchNewQuestion, const char *pchNewAnswer) = 0; - virtual uint64_t ResetPassword(const char *pchAccountName, const char *pchOldPassword, const char *pchNewPassword, const char *pchValidationCode, const char *pchAnswer) = 0; - virtual void TrackNatTraversalStat(const void *pNatStat) = 0; - virtual void TrackSteamUsageEvent(int eSteamUsageEvent, const uint8_t *pubKV, uint32_t cubKV) = 0; - virtual void TrackSteamGUIUsage(const char *) = 0; - virtual void SetComputerInUse() = 0; - virtual bool BIsGameRunning(uint64_t gameID) = 0; - virtual uint64_t GetCurrentSessionToken() = 0; - virtual bool BUpdateAppOwnershipTicket(uint32_t nAppID, bool bOnlyUpdateIfStale, bool bIsDepot) = 0; - virtual bool RequestCustomBinary(const char *pszAbsolutePath, uint32_t nAppID, bool bForceUpdate, bool bAppLaunchRequest) = 0; - virtual int GetCustomBinariesState(uint32_t unAppID, uint32_t *punProgress) = 0; - virtual int RequestCustomBinaries(uint32_t unAppID, bool, bool, uint32_t *) = 0; - virtual void SetCellID(uint32_t cellID) = 0; - virtual void SetWinningPingTimeForCellID(uint32_t uPing) = 0; - virtual const char *GetUserBaseFolder() = 0; - virtual bool GetUserDataFolder(uint64_t gameID, char* pchBuffer, int32_t cubBuffer) = 0; - virtual bool GetUserConfigFolder(char *pchBuffer, int32_t cubBuffer) = 0; - virtual bool GetAccountName(char* pchAccountName, uint32_t cb) = 0; - virtual bool GetAccountName(SteamID userID, char * pchAccountName, uint32_t cb) = 0; - virtual bool IsPasswordRemembered() = 0; - virtual bool RequiresLegacyCDKey(uint32_t nAppID, bool * pbUnk) = 0; - virtual bool GetLegacyCDKey(uint32_t nAppID, char* pchKeyData, int32_t cbKeyData) = 0; - virtual bool SetLegacyCDKey(uint32_t nAppID, const char* pchKeyData) = 0; - virtual bool WriteLegacyCDKey(uint32_t nAppID) = 0; - virtual void RemoveLegacyCDKey(uint32_t nAppID) = 0; - virtual void RequestLegacyCDKeyFromApp(uint32_t nMainAppID, uint32_t nDLCAppID) = 0; - virtual bool BIsAnyGameRunning() = 0; - virtual void TestAvailablePassword(const uint8_t *pubDigestPassword, int32_t cubDigestPassword) = 0; - virtual void GetSteamGuardDetails() = 0; - virtual void ChangePassword(const char *pchOldPassword, const char *pchNewPassword) = 0; - virtual void ChangeEmail(const char *, const char *pchEmail) = 0; - virtual void ChangeSecretQuestionAndAnswer(const char *, int32_t iQuestion, const char *pchNewQuestion, const char *pchNewAnswer) = 0; - virtual void SetSteam2FullASTicket(uint8_t* pubTicket, int32_t cubTicket) = 0; - virtual int32_t GetSteam2FullASTicket(uint8_t* pubTicket, int32_t cubTicket) = 0; - virtual bool GetEmail(char* pchEmail, int32_t cchEmail, bool* pbValidated) = 0; - virtual void RequestForgottenPasswordEmail(const char *pchAccountName, const char *pchTriedPassword) = 0; - virtual void FindAccountsByEmailAddress(const char *pchEmailAddress) = 0; - virtual void FindAccountsByCdKey(const char *pchCdKey) = 0; - virtual void GetNumAccountsWithEmailAddress(const char * pchEmailAddress) = 0; - virtual void IsAccountNameInUse(const char * pchAccountName) = 0; - virtual void Test_FakeConnectionTimeout() = 0; - virtual bool RunInstallScript(uint32_t *pAppIDs, int32_t cAppIDs, const char *pchInstallPath, const char *pchLanguage, bool bUninstall) = 0; - virtual uint32_t IsInstallScriptRunning() = 0; - virtual bool GetInstallScriptState(char* pchDescription, uint32_t cchDescription, uint32_t* punNumSteps, uint32_t* punCurrStep) = 0; - - virtual void Test1() = 0; - virtual void Test2() = 0; - - virtual bool SpawnProcess(void *lpVACBlob, uint32_t cbBlobSize, const char *lpApplicationName, const char *lpCommandLine, uint32_t dwCreationFlags, const char *lpCurrentDirectory, GameID_t gameID, uint32_t nAppID, const char *pchGameName, uint32_t uUnk) = 0; - virtual uint32_t GetAppOwnershipTicketLength(uint32_t nAppID) = 0; - virtual uint32_t GetAppOwnershipTicketData(uint32_t nAppID, void *pvBuffer, uint32_t cbBufferLength) = 0; - virtual uint32_t GetAppOwnershipTicketExtendedData(uint32_t nAppID, void *pvBuffer, uint32_t cbBufferLength, uint32_t* piAppId, uint32_t* piSteamId, uint32_t* piSignature, uint32_t* pcbSignature) = 0; - virtual int32_t GetMarketingMessageCount() = 0; - virtual bool GetMarketingMessage(int32_t cMarketingMessage, uint64_t* gidMarketingMessageID, char* pubMsgUrl, int32_t cubMessageUrl, int *eMarketingMssageFlags) = 0; - virtual uint32_t GetAuthSessionTicket(void *pMyAuthTicket, int32_t cbMaxMyAuthTicket, uint32_t* pcbAuthTicket) = 0; - virtual int BeginAuthSession(void const* pTheirAuthTicket, int32_t cbTicket, SteamID steamID) = 0; - virtual void EndAuthSession(SteamID steamID) = 0; - virtual void CancelAuthTicket(uint32_t hAuthTicket) = 0; - virtual int IsUserSubscribedAppInTicket(SteamID steamID, uint32_t appID) = 0; - virtual void AdvertiseGame(uint64_t gameID, SteamID steamIDGameServer, uint32_t unIPServer, uint16_t usPortServer) = 0; - virtual uint64_t RequestEncryptedAppTicket(const void *pUserData, int32_t cbUserData) = 0; - virtual bool GetEncryptedAppTicket(void *pTicket, int32_t cbMaxTicket, uint32_t *pcbTicket) = 0; - virtual int32_t GetGameBadgeLevel(int32_t nSeries, bool bFoil) = 0; - virtual int32_t GetPlayerSteamLevel() = 0; - virtual void SetAccountLimited(bool bAccountLimited) = 0; - virtual bool BIsAccountLimited() = 0; - virtual void SetAccountCommunityBanned(bool bBanned) = 0; - virtual bool BIsAccountCommunityBanned() = 0; - virtual void SetLimitedAccountCanInviteFriends(bool bCanInviteFriends) = 0; - virtual bool BLimitedAccountCanInviteFriends() = 0; - virtual void SendValidationEmail() = 0; - virtual bool BGameConnectTokensAvailable() = 0; - virtual int32_t NumGamesRunning() = 0; - virtual uint64_t GetRunningGameID(int32_t iGame) = 0; - virtual uint32_t GetAccountSecurityPolicyFlags() = 0; - virtual void RequestChangeEmail(const char *pchPassword, int32_t eRequestType) = 0; - virtual void ChangePasswordWithCode(const char *pchOldPassword, const char *pchCode, const char *pchNewPassword) = 0; - virtual void ChangeEmailWithCode(const char *pchPassword, const char *pchCode, const char *pchEmail, bool bFinal) = 0; - virtual void ChangeSecretQuestionAndAnswerWithCode(const char *pchPassword, const char *pchCode, const char *pchNewQuestion, const char *pchNewAnswer) = 0; - virtual void SetClientStat(int eStat, int64_t llValue, uint32_t nAppID, uint32_t nDepotID, uint32_t nCellID) = 0; - virtual void VerifyPassword(const char *pchPassword) = 0; - virtual bool BSupportUser() = 0; - virtual bool BNeedsSSANextSteamLogon() = 0; - virtual void ClearNeedsSSANextSteamLogon() = 0; - virtual bool BIsAppOverlayEnabled(uint64_t gameID) = 0; - virtual bool BIsBehindNAT() = 0; - virtual uint32_t GetMicroTxnAppID(uint64_t gidTransID) = 0; - virtual uint64_t GetMicroTxnOrderID(uint64_t gidTransID) = 0; - virtual bool BGetMicroTxnPrice(uint64_t gidTransID, uint64_t *pamtTotal, uint64_t *pamtTax, bool *pbVat, uint64_t * pUnk) = 0; - virtual int32_t GetMicroTxnLineItemCount(uint64_t gidTransID) = 0; - virtual bool BGetMicroTxnLineItem(uint64_t gidTransID, uint32_t unLineItem, uint64_t *pamt, uint32_t *punQuantity, char *pchDescription, uint32_t cubDescriptionLength, int32_t *pRecurringTimeUnit, uint8_t *pRecurringFrequency, uint64_t *pRecurringAmount, bool * pbUnk) = 0; - virtual bool BIsSandboxMicroTxn(uint64_t gidTransID, bool* pbSandbox) = 0; - virtual bool BMicroTxnRequiresCachedPmtMethod(uint64_t gidTransID, bool *pbRequired) = 0; - virtual uint64_t AuthorizeMicroTxn(uint64_t gidTransID, int eMicroTxnAuthResponse) = 0; - virtual bool BGetWalletBalance(bool *pbHasWallet, uint64_t *pamtBalance) = 0; - virtual uint64_t RequestMicroTxnInfo(uint64_t gidTransID) = 0; - virtual bool BGetAppMinutesPlayed(uint32_t nAppId, int32_t *pnForever, int32_t *pnLastTwoWeeks) = 0; - virtual uint32_t GetAppLastPlayedTime(uint32_t nAppId) = 0; - virtual bool BGetGuideURL(uint32_t uAppID, char *pchURL, uint32_t cchURL) = 0; - virtual void PostUIResultToClientJob(uint64_t ulJobID, int eResult) = 0; - virtual bool BPromptToVerifyEmail() = 0; - virtual bool BPromptToChangePassword() = 0; - virtual bool BAccountLocked() = 0; - virtual bool BAccountShouldShowLockUI() = 0; - virtual bool BAccountLockedByIPT() = 0; - virtual int32_t GetCountAuthedComputers() = 0; - virtual int GetSteamGuardProvider() = 0; - virtual bool GetSteamGuardRequireCodeByDefault() = 0; - virtual bool ShowSteamGuardProviderOptions() = 0; - virtual bool SteamGuardProviderMobileIsOption() = 0; - virtual bool BSteamGuardNewMachineNotification() = 0; - virtual uint32_t GetSteamGuardEnabledTime() = 0; - virtual bool GetSteamGuardHistoryEntry(int32_t iEntryIndex, uint32_t *puTimestamp, uint32_t *puIP, bool *pbIsRemembered, char *pchGeolocInfo, int32_t cchGeolocInfo, char * pchUnk, int32_t cbUnk) = 0; - virtual void SetSteamGuardNewMachineDialogResponse(bool bIsApproved, bool bIsWizardComplete) = 0; - virtual void RequestSteamGuardCodeForOtherLogin() = 0; - virtual bool BAccountCanUseIPT() = 0; - virtual void ChangeTwoFactorAuthOptions(int32_t eOption) = 0; - virtual void ChangeSteamGuardOptions(const char * pchUnk, int eProvider, bool bRequireCode) = 0; - virtual void Set2ndFactorAuthCode(const char* pchAuthCode, bool bDontRememberComputer) = 0; - virtual void SetUserMachineName(const char * pchMachineName) = 0; - virtual bool GetUserMachineName(char * pchMachineName, int32_t cbMachineName) = 0; - virtual bool BAccountHasIPTConfig() = 0; - virtual bool GetEmailDomainFromLogonFailure(char * pchEmailDomain, int32_t cbEmailDomain) = 0; - virtual bool BIsSubscribedApp(uint32_t nAppId) = 0; - virtual uint64_t RegisterActivationCode(const char * pchActivationCode) = 0; - virtual void OptionalDLCInstallation(uint32_t nAppID, uint32_t uUnk, bool bInstall) = 0; - virtual void AckSystemIM(uint64_t) = 0; - virtual uint64_t RequestSpecialSurvey(uint32_t uSurveyId) = 0; - virtual uint64_t SendSpecialSurveyResponse(uint32_t uSurveyId, const uint8_t * pubData, uint32_t cubData) = 0; - virtual void RequestNotifications() = 0; - virtual bool GetAppOwnershipInfo(uint32_t unAppId, uint32_t* pRTime32Created, char* pchCountry) = 0; - virtual void SendGameWebCallback(uint32_t unAppId, const char *szData) = 0; - virtual bool BIsCurrentlyStreaming() = 0; - virtual void RequestStopStreaming() = 0; - virtual void OnBigPictureStreamingResult(bool, void *) = 0; - virtual void OnBigPictureStreamingDone() = 0; - virtual void OnBigPictureStreamRestarting() = 0; - virtual void LockParentalLock() = 0; - virtual bool UnlockParentalLock(const char * pchUnk) = 0; - virtual bool BIsParentalLockEnabled() = 0; - virtual bool BIsParentalLockLocked() = 0; - virtual void BlockApp(uint32_t unAppID) = 0; - virtual void UnblockApp(uint32_t unAppID) = 0; - virtual bool BIsAppBlocked(uint32_t unAppID) = 0; - virtual bool BIsAppInBlockList(uint32_t unAppID) = 0; - virtual void BlockFeature(int eParentalFeature) = 0; - virtual void UnblockFeature(int eParentalFeature) = 0; - virtual bool BIsFeatureBlocked(int eParentalFeature) = 0; - virtual bool BIsFeatureInBlockList(int eParentalFeature) = 0; - virtual uint32_t GetParentalUnlockTime() = 0; - virtual bool BGetSerializedParentalSettings(void * pBuffer) = 0; - virtual bool BSetParentalSettings(void * pBuffer) = 0; - virtual bool BDisableParentalSettings() = 0; - virtual bool BGetParentalWebToken(void *, void *) = 0; - virtual bool BCanLogonOfflineMode() = 0; - virtual int LogOnOfflineMode() = 0; - virtual int ValidateOfflineLogonTicket(const char * pchUnk) = 0; - virtual bool BGetOfflineLogonTicket(const char * pchUnk, void * pTicket) = 0; - virtual void UploadLocalClientLogs() = 0; - virtual void SetAsyncNotificationEnabled(uint32_t, bool) = 0; - virtual bool BIsOtherSessionPlaying(uint32_t *) = 0; - virtual bool BKickOtherPlayingSession() = 0; - virtual uint32_t GetStreamPortForGame(uint64_t gameID) = 0; - }; - - class IClientEngine - { - public: - virtual void* CreateSteamPipe() = 0; - virtual bool BReleaseSteamPipe(void* hSteamPipe) = 0; - virtual void* CreateGlobalUser(void** phSteamPipe) = 0; - virtual void* ConnectToGlobalUser(void* hSteamPipe) = 0; - virtual void* CreateLocalUser(void** phSteamPipe, int eAccountType) = 0; - virtual void CreatePipeToLocalUser(void* hSteamUser, void** phSteamPipe) = 0; - virtual void ReleaseUser(void* hSteamPipe, void* hUser) = 0; - virtual bool IsValidHSteamUserPipe(void* hSteamPipe, void* hUser) = 0; - virtual IClientUser *GetIClientUser(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientGameServer(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void SetLocalIPBinding(uint32_t unIP, uint16_t usPort) = 0; - virtual char const *GetUniverseName(int eUniverse) = 0; - virtual void *GetIClientFriends(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientUtils(void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientBilling(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientMatchmaking(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientApps(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientMatchmakingServers(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void RunFrame() = 0; - virtual uint32_t GetIPCCallCount() = 0; - virtual void *GetIClientUserStats(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientGameServerStats(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientNetworking(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientRemoteStorage(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientScreenshots(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void SetWarningMessageHook(void* pFunction) = 0; - virtual void *GetIClientGameCoordinator(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; - virtual void SetOverlayNotificationPosition(int eNotificationPosition) = 0; - virtual void SetOverlayNotificationInsert(int32_t, int32_t) = 0; - virtual bool HookScreenshots(bool bHook) = 0; - virtual bool IsOverlayEnabled() = 0; - virtual bool GetAPICallResult(void* hSteamPipe, uint64_t hSteamAPICall, void* pCallback, int cubCallback, int iCallbackExpected, bool* pbFailed) = 0; - virtual void *GetIClientProductBuilder(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientDepotBuilder(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientNetworkDeviceManager(void* hSteamPipe, char const* pchVersion) = 0; - virtual void ConCommandInit(void *pAccessor) = 0; - virtual void *GetIClientAppManager(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientConfigStore(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual bool BOverlayNeedsPresent() = 0; - virtual void *GetIClientGameStats(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientHTTP(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual bool BShutdownIfAllPipesClosed() = 0; - virtual void *GetIClientAudio(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientMusic(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientUnifiedMessages(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientController(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientParentalSettings(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientStreamLauncher(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientDeviceAuth(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientRemoteClientManager(void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientStreamClient(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientShortcuts(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientRemoteControlManager(void* hSteamPipe, char const* pchVersion) = 0; - virtual void Set_ClientAPI_CPostAPIResultInProcess(void(*)(uint64_t ulUnk, void * pUnk, uint32_t uUnk, int32_t iUnk)) = 0; - virtual void Remove_ClientAPI_CPostAPIResultInProcess(void(*)(uint64_t ulUnk, void * pUnk, uint32_t uUnk, int32_t iUnk)) = 0; - virtual void *GetIClientUGC(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; - virtual void *GetIClientVR(char const * pchVersion) = 0; - }; - - class Proxy - { - public: - static bool Inititalize(); - static void Uninititalize(); - - static void SetGame(uint32_t appId); - static void SetMod(std::string mod); - static void RunMod(); - - //Overlay related proxies - static void SetOverlayNotificationPosition(uint32_t eNotificationPosition); - static bool IsOverlayEnabled(); - static bool BOverlayNeedsPresent(); - - static Friends* SteamFriends; - static Utils* SteamUtils; - - private: - static ::Utils::Library Client; - static ::Utils::Library Overlay; - - static ISteamClient008* SteamClient; - static IClientEngine* ClientEngine; - static IClientUser* ClientUser; - - static void* SteamPipe; - static void* SteamUser; - - static uint32_t AppId; - - static std::string GetSteamDirectory(); - }; -} +#ifdef _WIN64 +#define GAMEOVERLAY_LIB "gameoverlayrenderer64.dll" +#define STEAMCLIENT_LIB "steamclient64.dll" +#define STEAM_REGISTRY_PATH "Software\\Wow6432Node\\Valve\\Steam" +#else +#define GAMEOVERLAY_LIB "gameoverlayrenderer.dll" +#define STEAMCLIENT_LIB "steamclient.dll" +#define STEAM_REGISTRY_PATH "Software\\Valve\\Steam" +#endif + +namespace Steam +{ + class ISteamClient008 + { + public: + virtual void* CreateSteamPipe() = 0; + virtual bool ReleaseSteamPipe(void* hSteamPipe) = 0; + virtual void* ConnectToGlobalUser(void* hSteamPipe) = 0; + virtual void* CreateLocalUser(void* *phSteamPipe, int eAccountType) = 0; + virtual void ReleaseUser(void* hSteamPipe, void* hUser) = 0; + virtual void *GetISteamUser(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void *GetISteamGameServer(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void SetLocalIPBinding(uint32_t unIP, uint16_t usPort) = 0; + virtual void *GetISteamFriends(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void *GetISteamUtils(void* hSteamPipe, const char *pchVersion) = 0; + virtual void *GetISteamMatchmaking(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void *GetISteamMasterServerUpdater(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void *GetISteamMatchmakingServers(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void *GetISteamGenericInterface(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void *GetISteamUserStats(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void *GetISteamApps(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void *GetISteamNetworking(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void*GetISteamRemoteStorage(void* hSteamuser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void RunFrame() = 0; + virtual uint32_t GetIPCCallCount() = 0; + virtual void SetWarningMessageHook(void* pFunction) = 0; + }; + + class IClientUser + { + public: + virtual int32_t GetHSteamUser() = 0; + virtual void LogOn(bool bInteractive, SteamID steamID) = 0; + virtual void LogOnWithPassword(bool bInteractive, const char * pchLogin, const char * pchPassword) = 0; + virtual void LogOnAndCreateNewSteamAccountIfNeeded(bool bInteractive) = 0; + virtual int LogOnConnectionless() = 0; + virtual void LogOff() = 0; + virtual bool BLoggedOn() = 0; + virtual int GetLogonState() = 0; + virtual bool BConnected() = 0; + virtual bool BTryingToLogin() = 0; + virtual SteamID GetSteamID() = 0; + virtual SteamID GetConsoleSteamID() = 0; + virtual bool IsVACBanned(uint32_t nGameID) = 0; + virtual bool RequireShowVACBannedMessage(uint32_t nAppID) = 0; + virtual void AcknowledgeVACBanning(uint32_t nAppID) = 0; + virtual void SetSteam2Ticket(uint8_t* pubTicket, int32_t cubTicket) = 0; + virtual bool BExistsSteam2Ticket() = 0; + virtual bool SetEmail(const char *pchEmail) = 0; + virtual bool SetConfigString(int eRegistrySubTree, const char *pchKey, const char *pchValue) = 0; + virtual bool GetConfigString(int eRegistrySubTree, const char *pchKey, char *pchValue, int32_t cbValue) = 0; + virtual bool SetConfigInt(int eRegistrySubTree, const char *pchKey, int32_t iValue) = 0; + virtual bool GetConfigInt(int eRegistrySubTree, const char *pchKey, int32_t *pValue) = 0; + virtual bool GetConfigStoreKeyName(int eRegistrySubTree, const char *pchKey, char *pchStoreName, int32_t cbStoreName) = 0; + virtual int32_t InitiateGameConnection(void *pOutputBlob, int32_t cbBlobMax, SteamID steamIDGS, uint64_t gameID, uint32_t unIPServer, uint16_t usPortServer, bool bSecure) = 0; + virtual int32_t InitiateGameConnectionOld(void *pOutputBlob, int32_t cbBlobMax, SteamID steamIDGS, uint64_t gameID, uint32_t unIPServer, uint16_t usPortServer, bool bSecure, void *pvSteam2GetEncryptionKey, int32_t cbSteam2GetEncryptionKey) = 0; + virtual void TerminateGameConnection(uint32_t unIPServer, uint16_t usPortServer) = 0; + virtual bool TerminateAppMultiStep(uint32_t, uint32_t) = 0; + virtual void SetSelfAsPrimaryChatDestination() = 0; + virtual bool IsPrimaryChatDestination() = 0; + virtual void RequestLegacyCDKey(uint32_t iAppID) = 0; + virtual bool AckGuestPass(const char *pchGuestPassCode) = 0; + virtual bool RedeemGuestPass(const char *pchGuestPassCode) = 0; + virtual uint32_t GetGuestPassToGiveCount() = 0; + virtual uint32_t GetGuestPassToRedeemCount() = 0; + virtual bool GetGuestPassToGiveInfo(uint32_t nPassIndex, uint64_t *pgidGuestPassID, uint32_t* pnPackageID, uint32_t* pRTime32Created, uint32_t* pRTime32Expiration, uint32_t* pRTime32Sent, uint32_t* pRTime32Redeemed, char* pchRecipientAddress, int32_t cRecipientAddressSize) = 0; + virtual bool GetGuestPassToRedeemInfo(uint32_t nPassIndex, uint64_t *pgidGuestPassID, uint32_t* pnPackageID, uint32_t* pRTime32Created, uint32_t* pRTime32Expiration, uint32_t* pRTime32Sent, uint32_t* pRTime32Redeemed) = 0; + virtual bool GetGuestPassToRedeemSenderName(uint32_t nPassIndex, char* pchSenderName, int32_t cSenderNameSize) = 0; + virtual void AcknowledgeMessageByGID(const char *pchMessageGID) = 0; + virtual bool SetLanguage(const char *pchLanguage) = 0; + virtual void TrackAppUsageEvent(uint64_t gameID, int32_t eAppUsageEvent, const char *pchExtraInfo = "") = 0; + virtual int32_t RaiseConnectionPriority(int eConnectionPriority) = 0; + virtual void ResetConnectionPriority(int32_t hRaiseConnectionPriorityPrev) = 0; + virtual void SetAccountNameFromSteam2(const char *pchAccountName) = 0; + virtual bool SetPasswordFromSteam2(const char *pchPassword) = 0; + virtual bool BHasCachedCredentials(const char * pchUnk) = 0; + virtual bool SetAccountNameForCachedCredentialLogin(const char *pchAccountName, bool bUnk) = 0; + virtual void SetLoginInformation(const char *pchAccountName, const char *pchPassword, bool bRememberPassword) = 0; + virtual void ClearAllLoginInformation() = 0; + virtual void SetAccountCreationTime(uint32_t rtime32Time) = 0; + virtual uint64_t RequestWebAuthToken() = 0; + virtual bool GetCurrentWebAuthToken(char *pchBuffer, int32_t cubBuffer) = 0; + virtual bool GetLanguage(char* pchLanguage, int32_t cbLanguage) = 0; + virtual bool BIsCyberCafe() = 0; + virtual bool BIsAcademicAccount() = 0; + virtual void CreateAccount(const char *pchAccountName, const char *pchNewPassword, const char *pchNewEmail, int32_t iQuestion, const char *pchNewQuestion, const char *pchNewAnswer) = 0; + virtual uint64_t ResetPassword(const char *pchAccountName, const char *pchOldPassword, const char *pchNewPassword, const char *pchValidationCode, const char *pchAnswer) = 0; + virtual void TrackNatTraversalStat(const void *pNatStat) = 0; + virtual void TrackSteamUsageEvent(int eSteamUsageEvent, const uint8_t *pubKV, uint32_t cubKV) = 0; + virtual void TrackSteamGUIUsage(const char *) = 0; + virtual void SetComputerInUse() = 0; + virtual bool BIsGameRunning(uint64_t gameID) = 0; + virtual uint64_t GetCurrentSessionToken() = 0; + virtual bool BUpdateAppOwnershipTicket(uint32_t nAppID, bool bOnlyUpdateIfStale, bool bIsDepot) = 0; + virtual bool RequestCustomBinary(const char *pszAbsolutePath, uint32_t nAppID, bool bForceUpdate, bool bAppLaunchRequest) = 0; + virtual int GetCustomBinariesState(uint32_t unAppID, uint32_t *punProgress) = 0; + virtual int RequestCustomBinaries(uint32_t unAppID, bool, bool, uint32_t *) = 0; + virtual void SetCellID(uint32_t cellID) = 0; + virtual void SetWinningPingTimeForCellID(uint32_t uPing) = 0; + virtual const char *GetUserBaseFolder() = 0; + virtual bool GetUserDataFolder(uint64_t gameID, char* pchBuffer, int32_t cubBuffer) = 0; + virtual bool GetUserConfigFolder(char *pchBuffer, int32_t cubBuffer) = 0; + virtual bool GetAccountName(char* pchAccountName, uint32_t cb) = 0; + virtual bool GetAccountName(SteamID userID, char * pchAccountName, uint32_t cb) = 0; + virtual bool IsPasswordRemembered() = 0; + virtual bool RequiresLegacyCDKey(uint32_t nAppID, bool * pbUnk) = 0; + virtual bool GetLegacyCDKey(uint32_t nAppID, char* pchKeyData, int32_t cbKeyData) = 0; + virtual bool SetLegacyCDKey(uint32_t nAppID, const char* pchKeyData) = 0; + virtual bool WriteLegacyCDKey(uint32_t nAppID) = 0; + virtual void RemoveLegacyCDKey(uint32_t nAppID) = 0; + virtual void RequestLegacyCDKeyFromApp(uint32_t nMainAppID, uint32_t nDLCAppID) = 0; + virtual bool BIsAnyGameRunning() = 0; + virtual void TestAvailablePassword(const uint8_t *pubDigestPassword, int32_t cubDigestPassword) = 0; + virtual void GetSteamGuardDetails() = 0; + virtual void ChangePassword(const char *pchOldPassword, const char *pchNewPassword) = 0; + virtual void ChangeEmail(const char *, const char *pchEmail) = 0; + virtual void ChangeSecretQuestionAndAnswer(const char *, int32_t iQuestion, const char *pchNewQuestion, const char *pchNewAnswer) = 0; + virtual void SetSteam2FullASTicket(uint8_t* pubTicket, int32_t cubTicket) = 0; + virtual int32_t GetSteam2FullASTicket(uint8_t* pubTicket, int32_t cubTicket) = 0; + virtual bool GetEmail(char* pchEmail, int32_t cchEmail, bool* pbValidated) = 0; + virtual void RequestForgottenPasswordEmail(const char *pchAccountName, const char *pchTriedPassword) = 0; + virtual void FindAccountsByEmailAddress(const char *pchEmailAddress) = 0; + virtual void FindAccountsByCdKey(const char *pchCdKey) = 0; + virtual void GetNumAccountsWithEmailAddress(const char * pchEmailAddress) = 0; + virtual void IsAccountNameInUse(const char * pchAccountName) = 0; + virtual void Test_FakeConnectionTimeout() = 0; + virtual bool RunInstallScript(uint32_t *pAppIDs, int32_t cAppIDs, const char *pchInstallPath, const char *pchLanguage, bool bUninstall) = 0; + virtual uint32_t IsInstallScriptRunning() = 0; + virtual bool GetInstallScriptState(char* pchDescription, uint32_t cchDescription, uint32_t* punNumSteps, uint32_t* punCurrStep) = 0; + + virtual void Test1() = 0; + virtual void Test2() = 0; + + virtual bool SpawnProcess(void *lpVACBlob, uint32_t cbBlobSize, const char *lpApplicationName, const char *lpCommandLine, uint32_t dwCreationFlags, const char *lpCurrentDirectory, GameID_t gameID, uint32_t nAppID, const char *pchGameName, uint32_t uUnk) = 0; + virtual uint32_t GetAppOwnershipTicketLength(uint32_t nAppID) = 0; + virtual uint32_t GetAppOwnershipTicketData(uint32_t nAppID, void *pvBuffer, uint32_t cbBufferLength) = 0; + virtual uint32_t GetAppOwnershipTicketExtendedData(uint32_t nAppID, void *pvBuffer, uint32_t cbBufferLength, uint32_t* piAppId, uint32_t* piSteamId, uint32_t* piSignature, uint32_t* pcbSignature) = 0; + virtual int32_t GetMarketingMessageCount() = 0; + virtual bool GetMarketingMessage(int32_t cMarketingMessage, uint64_t* gidMarketingMessageID, char* pubMsgUrl, int32_t cubMessageUrl, int *eMarketingMssageFlags) = 0; + virtual uint32_t GetAuthSessionTicket(void *pMyAuthTicket, int32_t cbMaxMyAuthTicket, uint32_t* pcbAuthTicket) = 0; + virtual int BeginAuthSession(void const* pTheirAuthTicket, int32_t cbTicket, SteamID steamID) = 0; + virtual void EndAuthSession(SteamID steamID) = 0; + virtual void CancelAuthTicket(uint32_t hAuthTicket) = 0; + virtual int IsUserSubscribedAppInTicket(SteamID steamID, uint32_t appID) = 0; + virtual void AdvertiseGame(uint64_t gameID, SteamID steamIDGameServer, uint32_t unIPServer, uint16_t usPortServer) = 0; + virtual uint64_t RequestEncryptedAppTicket(const void *pUserData, int32_t cbUserData) = 0; + virtual bool GetEncryptedAppTicket(void *pTicket, int32_t cbMaxTicket, uint32_t *pcbTicket) = 0; + virtual int32_t GetGameBadgeLevel(int32_t nSeries, bool bFoil) = 0; + virtual int32_t GetPlayerSteamLevel() = 0; + virtual void SetAccountLimited(bool bAccountLimited) = 0; + virtual bool BIsAccountLimited() = 0; + virtual void SetAccountCommunityBanned(bool bBanned) = 0; + virtual bool BIsAccountCommunityBanned() = 0; + virtual void SetLimitedAccountCanInviteFriends(bool bCanInviteFriends) = 0; + virtual bool BLimitedAccountCanInviteFriends() = 0; + virtual void SendValidationEmail() = 0; + virtual bool BGameConnectTokensAvailable() = 0; + virtual int32_t NumGamesRunning() = 0; + virtual uint64_t GetRunningGameID(int32_t iGame) = 0; + virtual uint32_t GetAccountSecurityPolicyFlags() = 0; + virtual void RequestChangeEmail(const char *pchPassword, int32_t eRequestType) = 0; + virtual void ChangePasswordWithCode(const char *pchOldPassword, const char *pchCode, const char *pchNewPassword) = 0; + virtual void ChangeEmailWithCode(const char *pchPassword, const char *pchCode, const char *pchEmail, bool bFinal) = 0; + virtual void ChangeSecretQuestionAndAnswerWithCode(const char *pchPassword, const char *pchCode, const char *pchNewQuestion, const char *pchNewAnswer) = 0; + virtual void SetClientStat(int eStat, int64_t llValue, uint32_t nAppID, uint32_t nDepotID, uint32_t nCellID) = 0; + virtual void VerifyPassword(const char *pchPassword) = 0; + virtual bool BSupportUser() = 0; + virtual bool BNeedsSSANextSteamLogon() = 0; + virtual void ClearNeedsSSANextSteamLogon() = 0; + virtual bool BIsAppOverlayEnabled(uint64_t gameID) = 0; + virtual bool BIsBehindNAT() = 0; + virtual uint32_t GetMicroTxnAppID(uint64_t gidTransID) = 0; + virtual uint64_t GetMicroTxnOrderID(uint64_t gidTransID) = 0; + virtual bool BGetMicroTxnPrice(uint64_t gidTransID, uint64_t *pamtTotal, uint64_t *pamtTax, bool *pbVat, uint64_t * pUnk) = 0; + virtual int32_t GetMicroTxnLineItemCount(uint64_t gidTransID) = 0; + virtual bool BGetMicroTxnLineItem(uint64_t gidTransID, uint32_t unLineItem, uint64_t *pamt, uint32_t *punQuantity, char *pchDescription, uint32_t cubDescriptionLength, int32_t *pRecurringTimeUnit, uint8_t *pRecurringFrequency, uint64_t *pRecurringAmount, bool * pbUnk) = 0; + virtual bool BIsSandboxMicroTxn(uint64_t gidTransID, bool* pbSandbox) = 0; + virtual bool BMicroTxnRequiresCachedPmtMethod(uint64_t gidTransID, bool *pbRequired) = 0; + virtual uint64_t AuthorizeMicroTxn(uint64_t gidTransID, int eMicroTxnAuthResponse) = 0; + virtual bool BGetWalletBalance(bool *pbHasWallet, uint64_t *pamtBalance) = 0; + virtual uint64_t RequestMicroTxnInfo(uint64_t gidTransID) = 0; + virtual bool BGetAppMinutesPlayed(uint32_t nAppId, int32_t *pnForever, int32_t *pnLastTwoWeeks) = 0; + virtual uint32_t GetAppLastPlayedTime(uint32_t nAppId) = 0; + virtual bool BGetGuideURL(uint32_t uAppID, char *pchURL, uint32_t cchURL) = 0; + virtual void PostUIResultToClientJob(uint64_t ulJobID, int eResult) = 0; + virtual bool BPromptToVerifyEmail() = 0; + virtual bool BPromptToChangePassword() = 0; + virtual bool BAccountLocked() = 0; + virtual bool BAccountShouldShowLockUI() = 0; + virtual bool BAccountLockedByIPT() = 0; + virtual int32_t GetCountAuthedComputers() = 0; + virtual int GetSteamGuardProvider() = 0; + virtual bool GetSteamGuardRequireCodeByDefault() = 0; + virtual bool ShowSteamGuardProviderOptions() = 0; + virtual bool SteamGuardProviderMobileIsOption() = 0; + virtual bool BSteamGuardNewMachineNotification() = 0; + virtual uint32_t GetSteamGuardEnabledTime() = 0; + virtual bool GetSteamGuardHistoryEntry(int32_t iEntryIndex, uint32_t *puTimestamp, uint32_t *puIP, bool *pbIsRemembered, char *pchGeolocInfo, int32_t cchGeolocInfo, char * pchUnk, int32_t cbUnk) = 0; + virtual void SetSteamGuardNewMachineDialogResponse(bool bIsApproved, bool bIsWizardComplete) = 0; + virtual void RequestSteamGuardCodeForOtherLogin() = 0; + virtual bool BAccountCanUseIPT() = 0; + virtual void ChangeTwoFactorAuthOptions(int32_t eOption) = 0; + virtual void ChangeSteamGuardOptions(const char * pchUnk, int eProvider, bool bRequireCode) = 0; + virtual void Set2ndFactorAuthCode(const char* pchAuthCode, bool bDontRememberComputer) = 0; + virtual void SetUserMachineName(const char * pchMachineName) = 0; + virtual bool GetUserMachineName(char * pchMachineName, int32_t cbMachineName) = 0; + virtual bool BAccountHasIPTConfig() = 0; + virtual bool GetEmailDomainFromLogonFailure(char * pchEmailDomain, int32_t cbEmailDomain) = 0; + virtual bool BIsSubscribedApp(uint32_t nAppId) = 0; + virtual uint64_t RegisterActivationCode(const char * pchActivationCode) = 0; + virtual void OptionalDLCInstallation(uint32_t nAppID, uint32_t uUnk, bool bInstall) = 0; + virtual void AckSystemIM(uint64_t) = 0; + virtual uint64_t RequestSpecialSurvey(uint32_t uSurveyId) = 0; + virtual uint64_t SendSpecialSurveyResponse(uint32_t uSurveyId, const uint8_t * pubData, uint32_t cubData) = 0; + virtual void RequestNotifications() = 0; + virtual bool GetAppOwnershipInfo(uint32_t unAppId, uint32_t* pRTime32Created, char* pchCountry) = 0; + virtual void SendGameWebCallback(uint32_t unAppId, const char *szData) = 0; + virtual bool BIsCurrentlyStreaming() = 0; + virtual void RequestStopStreaming() = 0; + virtual void OnBigPictureStreamingResult(bool, void *) = 0; + virtual void OnBigPictureStreamingDone() = 0; + virtual void OnBigPictureStreamRestarting() = 0; + virtual void LockParentalLock() = 0; + virtual bool UnlockParentalLock(const char * pchUnk) = 0; + virtual bool BIsParentalLockEnabled() = 0; + virtual bool BIsParentalLockLocked() = 0; + virtual void BlockApp(uint32_t unAppID) = 0; + virtual void UnblockApp(uint32_t unAppID) = 0; + virtual bool BIsAppBlocked(uint32_t unAppID) = 0; + virtual bool BIsAppInBlockList(uint32_t unAppID) = 0; + virtual void BlockFeature(int eParentalFeature) = 0; + virtual void UnblockFeature(int eParentalFeature) = 0; + virtual bool BIsFeatureBlocked(int eParentalFeature) = 0; + virtual bool BIsFeatureInBlockList(int eParentalFeature) = 0; + virtual uint32_t GetParentalUnlockTime() = 0; + virtual bool BGetSerializedParentalSettings(void * pBuffer) = 0; + virtual bool BSetParentalSettings(void * pBuffer) = 0; + virtual bool BDisableParentalSettings() = 0; + virtual bool BGetParentalWebToken(void *, void *) = 0; + virtual bool BCanLogonOfflineMode() = 0; + virtual int LogOnOfflineMode() = 0; + virtual int ValidateOfflineLogonTicket(const char * pchUnk) = 0; + virtual bool BGetOfflineLogonTicket(const char * pchUnk, void * pTicket) = 0; + virtual void UploadLocalClientLogs() = 0; + virtual void SetAsyncNotificationEnabled(uint32_t, bool) = 0; + virtual bool BIsOtherSessionPlaying(uint32_t *) = 0; + virtual bool BKickOtherPlayingSession() = 0; + virtual uint32_t GetStreamPortForGame(uint64_t gameID) = 0; + }; + + class IClientEngine + { + public: + virtual void* CreateSteamPipe() = 0; + virtual bool BReleaseSteamPipe(void* hSteamPipe) = 0; + virtual void* CreateGlobalUser(void** phSteamPipe) = 0; + virtual void* ConnectToGlobalUser(void* hSteamPipe) = 0; + virtual void* CreateLocalUser(void** phSteamPipe, int eAccountType) = 0; + virtual void CreatePipeToLocalUser(void* hSteamUser, void** phSteamPipe) = 0; + virtual void ReleaseUser(void* hSteamPipe, void* hUser) = 0; + virtual bool IsValidHSteamUserPipe(void* hSteamPipe, void* hUser) = 0; + virtual IClientUser *GetIClientUser(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientGameServer(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void SetLocalIPBinding(uint32_t unIP, uint16_t usPort) = 0; + virtual char const *GetUniverseName(int eUniverse) = 0; + virtual void *GetIClientFriends(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientUtils(void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientBilling(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientMatchmaking(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientApps(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientMatchmakingServers(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void RunFrame() = 0; + virtual uint32_t GetIPCCallCount() = 0; + virtual void *GetIClientUserStats(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientGameServerStats(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientNetworking(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientRemoteStorage(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientScreenshots(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void SetWarningMessageHook(void* pFunction) = 0; + virtual void *GetIClientGameCoordinator(void* hSteamUser, void* hSteamPipe, const char *pchVersion) = 0; + virtual void SetOverlayNotificationPosition(int eNotificationPosition) = 0; + virtual void SetOverlayNotificationInsert(int32_t, int32_t) = 0; + virtual bool HookScreenshots(bool bHook) = 0; + virtual bool IsOverlayEnabled() = 0; + virtual bool GetAPICallResult(void* hSteamPipe, uint64_t hSteamAPICall, void* pCallback, int cubCallback, int iCallbackExpected, bool* pbFailed) = 0; + virtual void *GetIClientProductBuilder(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientDepotBuilder(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientNetworkDeviceManager(void* hSteamPipe, char const* pchVersion) = 0; + virtual void ConCommandInit(void *pAccessor) = 0; + virtual void *GetIClientAppManager(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientConfigStore(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual bool BOverlayNeedsPresent() = 0; + virtual void *GetIClientGameStats(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientHTTP(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual bool BShutdownIfAllPipesClosed() = 0; + virtual void *GetIClientAudio(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientMusic(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientUnifiedMessages(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientController(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientParentalSettings(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientStreamLauncher(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientDeviceAuth(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientRemoteClientManager(void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientStreamClient(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientShortcuts(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientRemoteControlManager(void* hSteamPipe, char const* pchVersion) = 0; + virtual void Set_ClientAPI_CPostAPIResultInProcess(void(*)(uint64_t ulUnk, void * pUnk, uint32_t uUnk, int32_t iUnk)) = 0; + virtual void Remove_ClientAPI_CPostAPIResultInProcess(void(*)(uint64_t ulUnk, void * pUnk, uint32_t uUnk, int32_t iUnk)) = 0; + virtual void *GetIClientUGC(void* hSteamUser, void* hSteamPipe, char const* pchVersion) = 0; + virtual void *GetIClientVR(char const * pchVersion) = 0; + }; + + class Proxy + { + public: + static bool Inititalize(); + static void Uninititalize(); + + static void SetGame(uint32_t appId); + static void SetMod(std::string mod); + static void RunMod(); + + //Overlay related proxies + static void SetOverlayNotificationPosition(uint32_t eNotificationPosition); + static bool IsOverlayEnabled(); + static bool BOverlayNeedsPresent(); + + static Friends* SteamFriends; + static Utils* SteamUtils; + + private: + static ::Utils::Library Client; + static ::Utils::Library Overlay; + + static ISteamClient008* SteamClient; + static IClientEngine* ClientEngine; + static IClientUser* ClientUser; + + static void* SteamPipe; + static void* SteamUser; + + static uint32_t AppId; + + static std::string GetSteamDirectory(); + }; +} diff --git a/src/Steam/Steam.cpp b/src/Steam/Steam.cpp index 9181a355..19588399 100644 --- a/src/Steam/Steam.cpp +++ b/src/Steam/Steam.cpp @@ -1,181 +1,181 @@ -#include "STDInclude.hpp" - -namespace Steam -{ - uint64_t Callbacks::CallID = 0; - std::map Callbacks::Calls; - std::map Callbacks::ResultHandlers; - std::vector Callbacks::Results; - std::vector Callbacks::CallbackList; - - uint64_t Callbacks::RegisterCall() - { - Callbacks::Calls[++Callbacks::CallID] = false; - return Callbacks::CallID; - } - - void Callbacks::RegisterCallback(Callbacks::Base* handler, int callback) - { - handler->SetICallback(callback); - Callbacks::CallbackList.push_back(handler); - } - - void Callbacks::RegisterCallResult(uint64_t call, Callbacks::Base* result) - { - Callbacks::ResultHandlers[call] = result; - } - - void Callbacks::ReturnCall(void* data, int size, int type, uint64_t call) - { - Callbacks::Result result; - - Callbacks::Calls[call] = true; - - result.call = call; - result.data = data; - result.size = size; - result.type = type; - - Callbacks::Results.push_back(result); - } - - void Callbacks::RunCallbacks() - { - for (auto result : Callbacks::Results) - { - if (Callbacks::ResultHandlers.find(result.call) != Callbacks::ResultHandlers.end()) - { - Callbacks::ResultHandlers[result.call]->Run(result.data, false, result.call); - } - - for (auto callback : Callbacks::CallbackList) - { - if (callback && callback->GetICallback() == result.type) - { - callback->Run(result.data, false, 0); - } - } - - if (result.data) - { - ::Utils::Memory::Free(result.data); - } - } - - Callbacks::Results.clear(); - } - - extern "C" - { - bool SteamAPI_Init() - { -#ifndef DISABLE_STEAM_GAME - Proxy::SetGame(10190); -#endif - - if (!Proxy::Inititalize()) - { - OutputDebugStringA("Steamproxy not initialized properly"); - } - else - { -#ifndef DISABLE_STEAM_GAME - Proxy::SetMod("IW4x - Modern Warfare 2"); -#endif - } - - return true; - } - - void SteamAPI_RegisterCallResult(Callbacks::Base* result, uint64_t call) - { - Callbacks::RegisterCallResult(call, result); - } - - void SteamAPI_RegisterCallback(Callbacks::Base* handler, int callback) - { - Callbacks::RegisterCallback(handler, callback); - } - - void SteamAPI_RunCallbacks() - { - Callbacks::RunCallbacks(); - } - - void SteamAPI_Shutdown() - { - Proxy::Uninititalize(); - } - - void SteamAPI_UnregisterCallResult() - { - } - - void SteamAPI_UnregisterCallback() - { - } - - - bool SteamGameServer_Init() - { - return true; - } - - void SteamGameServer_RunCallbacks() - { - } - - void SteamGameServer_Shutdown() - { - } - - - Steam::Friends* SteamFriends() - { - static Steam::Friends iFriends; - return &iFriends; - } - - Steam::Matchmaking* SteamMatchmaking() - { - static Steam::Matchmaking iMatchmaking; - return &iMatchmaking; - } - - Steam::GameServer* SteamGameServer() - { - static Steam::GameServer iGameServer; - return &iGameServer; - } - - Steam::MasterServerUpdater* SteamMasterServerUpdater() - { - static Steam::MasterServerUpdater iMasterServerUpdater; - return &iMasterServerUpdater; - } - - Steam::Networking* SteamNetworking() - { - static Steam::Networking iNetworking; - return &iNetworking; - } - - Steam::RemoteStorage* SteamRemoteStorage() - { - static Steam::RemoteStorage iRemoteStorage; - return &iRemoteStorage; - } - - Steam::User* SteamUser() - { - static Steam::User iUser; - return &iUser; - } - - Steam::Utils* SteamUtils() - { - static Steam::Utils iUtils; - return &iUtils; - } - } -} +#include "STDInclude.hpp" + +namespace Steam +{ + uint64_t Callbacks::CallID = 0; + std::map Callbacks::Calls; + std::map Callbacks::ResultHandlers; + std::vector Callbacks::Results; + std::vector Callbacks::CallbackList; + + uint64_t Callbacks::RegisterCall() + { + Callbacks::Calls[++Callbacks::CallID] = false; + return Callbacks::CallID; + } + + void Callbacks::RegisterCallback(Callbacks::Base* handler, int callback) + { + handler->SetICallback(callback); + Callbacks::CallbackList.push_back(handler); + } + + void Callbacks::RegisterCallResult(uint64_t call, Callbacks::Base* result) + { + Callbacks::ResultHandlers[call] = result; + } + + void Callbacks::ReturnCall(void* data, int size, int type, uint64_t call) + { + Callbacks::Result result; + + Callbacks::Calls[call] = true; + + result.call = call; + result.data = data; + result.size = size; + result.type = type; + + Callbacks::Results.push_back(result); + } + + void Callbacks::RunCallbacks() + { + for (auto result : Callbacks::Results) + { + if (Callbacks::ResultHandlers.find(result.call) != Callbacks::ResultHandlers.end()) + { + Callbacks::ResultHandlers[result.call]->Run(result.data, false, result.call); + } + + for (auto callback : Callbacks::CallbackList) + { + if (callback && callback->GetICallback() == result.type) + { + callback->Run(result.data, false, 0); + } + } + + if (result.data) + { + ::Utils::Memory::Free(result.data); + } + } + + Callbacks::Results.clear(); + } + + extern "C" + { + bool SteamAPI_Init() + { +#ifndef DISABLE_STEAM_GAME + Proxy::SetGame(10190); +#endif + + if (!Proxy::Inititalize()) + { + OutputDebugStringA("Steamproxy not initialized properly"); + } + else + { +#ifndef DISABLE_STEAM_GAME + Proxy::SetMod("IW4x - Modern Warfare 2"); +#endif + } + + return true; + } + + void SteamAPI_RegisterCallResult(Callbacks::Base* result, uint64_t call) + { + Callbacks::RegisterCallResult(call, result); + } + + void SteamAPI_RegisterCallback(Callbacks::Base* handler, int callback) + { + Callbacks::RegisterCallback(handler, callback); + } + + void SteamAPI_RunCallbacks() + { + Callbacks::RunCallbacks(); + } + + void SteamAPI_Shutdown() + { + Proxy::Uninititalize(); + } + + void SteamAPI_UnregisterCallResult() + { + } + + void SteamAPI_UnregisterCallback() + { + } + + + bool SteamGameServer_Init() + { + return true; + } + + void SteamGameServer_RunCallbacks() + { + } + + void SteamGameServer_Shutdown() + { + } + + + Steam::Friends* SteamFriends() + { + static Steam::Friends iFriends; + return &iFriends; + } + + Steam::Matchmaking* SteamMatchmaking() + { + static Steam::Matchmaking iMatchmaking; + return &iMatchmaking; + } + + Steam::GameServer* SteamGameServer() + { + static Steam::GameServer iGameServer; + return &iGameServer; + } + + Steam::MasterServerUpdater* SteamMasterServerUpdater() + { + static Steam::MasterServerUpdater iMasterServerUpdater; + return &iMasterServerUpdater; + } + + Steam::Networking* SteamNetworking() + { + static Steam::Networking iNetworking; + return &iNetworking; + } + + Steam::RemoteStorage* SteamRemoteStorage() + { + static Steam::RemoteStorage iRemoteStorage; + return &iRemoteStorage; + } + + Steam::User* SteamUser() + { + static Steam::User iUser; + return &iUser; + } + + Steam::Utils* SteamUtils() + { + static Steam::Utils iUtils; + return &iUtils; + } + } +} diff --git a/src/Steam/Steam.hpp b/src/Steam/Steam.hpp index 4793085c..7485d00b 100644 --- a/src/Steam/Steam.hpp +++ b/src/Steam/Steam.hpp @@ -1,110 +1,110 @@ -#pragma once - -#define STEAM_EXPORT extern "C" __declspec(dllexport) - -#define STEAM_IGNORE_WARNINGS_START __pragma(warning(push)) \ - __pragma(warning(disable: 4100)) - -#define STEAM_IGNORE_WARNINGS_END __pragma(warning(pop)) - -typedef union -{ - struct - { - unsigned int AccountID : 32; - unsigned int AccountInstance : 20; - unsigned int AccountType : 4; - int Universe : 8; - }; - - unsigned long long Bits; -} SteamID; - - -#pragma pack( push, 1 ) - -struct GameID_t -{ - unsigned int m_nAppID : 24; - unsigned int m_nType : 8; - unsigned int m_nModID : 32; -}; - -#pragma pack( pop ) - -#include "Interfaces\SteamUser.hpp" -#include "Interfaces\SteamUtils.hpp" -#include "Interfaces\SteamFriends.hpp" -#include "Interfaces\SteamGameServer.hpp" -#include "Interfaces\SteamNetworking.hpp" -#include "Interfaces\SteamMatchmaking.hpp" -#include "Interfaces\SteamRemoteStorage.hpp" -#include "Interfaces\SteamMasterServerUpdater.hpp" - -#include "Proxy.hpp" - -namespace Steam -{ - class Callbacks - { - public: - class Base - { - public: - Base() : Flags(0), Callback(0) {}; - - virtual void Run(void *pvParam) = 0; - virtual void Run(void *pvParam, bool bIOFailure, uint64_t hSteamAPICall) = 0; - virtual int GetCallbackSizeBytes() = 0; - - int GetICallback() { return Callback; } - void SetICallback(int iCallback) { Callback = iCallback; } - - protected: - unsigned char Flags; - int Callback; - }; - - struct Result - { - void* data; - int size; - int type; - uint64_t call; - }; - - static uint64_t RegisterCall(); - static void RegisterCallback(Base* handler, int callback); - static void RegisterCallResult(uint64_t call, Base* result); - static void ReturnCall(void* data, int size, int type, uint64_t call); - static void RunCallbacks(); - - private: - static uint64_t CallID; - static std::map Calls; - static std::map ResultHandlers; - static std::vector Results; - static std::vector CallbackList; - }; - - STEAM_EXPORT bool SteamAPI_Init(); - STEAM_EXPORT void SteamAPI_RegisterCallResult(Callbacks::Base* result, uint64_t call); - STEAM_EXPORT void SteamAPI_RegisterCallback(Callbacks::Base* handler, int callback); - STEAM_EXPORT void SteamAPI_RunCallbacks(); - STEAM_EXPORT void SteamAPI_Shutdown(); - STEAM_EXPORT void SteamAPI_UnregisterCallResult(); - STEAM_EXPORT void SteamAPI_UnregisterCallback(); - - STEAM_EXPORT bool SteamGameServer_Init(); - STEAM_EXPORT void SteamGameServer_RunCallbacks(); - STEAM_EXPORT void SteamGameServer_Shutdown(); - - STEAM_EXPORT Steam::Friends* SteamFriends(); - STEAM_EXPORT Steam::Matchmaking* SteamMatchmaking(); - STEAM_EXPORT Steam::GameServer* SteamGameServer(); - STEAM_EXPORT Steam::MasterServerUpdater* SteamMasterServerUpdater(); - STEAM_EXPORT Steam::Networking* SteamNetworking(); - STEAM_EXPORT Steam::RemoteStorage* SteamRemoteStorage(); - STEAM_EXPORT Steam::User* SteamUser(); - STEAM_EXPORT Steam::Utils* SteamUtils(); -} +#pragma once + +#define STEAM_EXPORT extern "C" __declspec(dllexport) + +#define STEAM_IGNORE_WARNINGS_START __pragma(warning(push)) \ + __pragma(warning(disable: 4100)) + +#define STEAM_IGNORE_WARNINGS_END __pragma(warning(pop)) + +typedef union +{ + struct + { + unsigned int AccountID : 32; + unsigned int AccountInstance : 20; + unsigned int AccountType : 4; + int Universe : 8; + }; + + unsigned long long Bits; +} SteamID; + + +#pragma pack( push, 1 ) + +struct GameID_t +{ + unsigned int m_nAppID : 24; + unsigned int m_nType : 8; + unsigned int m_nModID : 32; +}; + +#pragma pack( pop ) + +#include "Interfaces\SteamUser.hpp" +#include "Interfaces\SteamUtils.hpp" +#include "Interfaces\SteamFriends.hpp" +#include "Interfaces\SteamGameServer.hpp" +#include "Interfaces\SteamNetworking.hpp" +#include "Interfaces\SteamMatchmaking.hpp" +#include "Interfaces\SteamRemoteStorage.hpp" +#include "Interfaces\SteamMasterServerUpdater.hpp" + +#include "Proxy.hpp" + +namespace Steam +{ + class Callbacks + { + public: + class Base + { + public: + Base() : Flags(0), Callback(0) {}; + + virtual void Run(void *pvParam) = 0; + virtual void Run(void *pvParam, bool bIOFailure, uint64_t hSteamAPICall) = 0; + virtual int GetCallbackSizeBytes() = 0; + + int GetICallback() { return Callback; } + void SetICallback(int iCallback) { Callback = iCallback; } + + protected: + unsigned char Flags; + int Callback; + }; + + struct Result + { + void* data; + int size; + int type; + uint64_t call; + }; + + static uint64_t RegisterCall(); + static void RegisterCallback(Base* handler, int callback); + static void RegisterCallResult(uint64_t call, Base* result); + static void ReturnCall(void* data, int size, int type, uint64_t call); + static void RunCallbacks(); + + private: + static uint64_t CallID; + static std::map Calls; + static std::map ResultHandlers; + static std::vector Results; + static std::vector CallbackList; + }; + + STEAM_EXPORT bool SteamAPI_Init(); + STEAM_EXPORT void SteamAPI_RegisterCallResult(Callbacks::Base* result, uint64_t call); + STEAM_EXPORT void SteamAPI_RegisterCallback(Callbacks::Base* handler, int callback); + STEAM_EXPORT void SteamAPI_RunCallbacks(); + STEAM_EXPORT void SteamAPI_Shutdown(); + STEAM_EXPORT void SteamAPI_UnregisterCallResult(); + STEAM_EXPORT void SteamAPI_UnregisterCallback(); + + STEAM_EXPORT bool SteamGameServer_Init(); + STEAM_EXPORT void SteamGameServer_RunCallbacks(); + STEAM_EXPORT void SteamGameServer_Shutdown(); + + STEAM_EXPORT Steam::Friends* SteamFriends(); + STEAM_EXPORT Steam::Matchmaking* SteamMatchmaking(); + STEAM_EXPORT Steam::GameServer* SteamGameServer(); + STEAM_EXPORT Steam::MasterServerUpdater* SteamMasterServerUpdater(); + STEAM_EXPORT Steam::Networking* SteamNetworking(); + STEAM_EXPORT Steam::RemoteStorage* SteamRemoteStorage(); + STEAM_EXPORT Steam::User* SteamUser(); + STEAM_EXPORT Steam::Utils* SteamUtils(); +} diff --git a/src/Utils/CSV.cpp b/src/Utils/CSV.cpp index 766f0ebd..427bee90 100644 --- a/src/Utils/CSV.cpp +++ b/src/Utils/CSV.cpp @@ -1,144 +1,144 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - CSV::CSV(std::string file, bool isFile, bool allowComments) - { - this->parse(file, isFile, allowComments); - } - - CSV::~CSV() - { - for (auto row : this->dataMap) - { - for (auto entry : row) - { - entry.clear(); - } - - row.clear(); - } - - this->dataMap.clear(); - } - - int CSV::getRows() - { - return this->dataMap.size(); - } - - int CSV::getColumns(size_t row) - { - if (this->dataMap.size() > row) - { - return this->dataMap[row].size(); - } - - return 0; - } - - int CSV::getColumns() - { - int count = 0; - - for (int i = 0; i < this->getRows(); ++i) - { - count = std::max(this->getColumns(i), count); - } - - return count; - } - - std::string CSV::getElementAt(size_t row, size_t column) - { - if (this->dataMap.size() > row) - { - auto _row = this->dataMap[row]; - - if (_row.size() > column) - { - return _row[column]; - } - } - - return ""; - } - - void CSV::parse(std::string file, bool isFile, bool allowComments) - { - std::string buffer; - - if (isFile) - { - if (!Utils::IO::FileExists(file)) return; - buffer = Utils::IO::ReadFile(file); - } - else - { - buffer = file; - } - - if (!buffer.empty()) - { - auto rows = Utils::String::Explode(buffer, '\n'); - - for (auto row : rows) - { - this->parseRow(row, allowComments); - } - } - } - - void CSV::parseRow(std::string row, bool allowComments) - { - bool isString = false; - std::string element; - std::vector _row; - char tempStr = 0; - - for (unsigned int i = 0; i < row.size(); ++i) - { - if (row[i] == ',' && !isString) // FLush entry - { - _row.push_back(element); - element.clear(); - continue; - } - else if (row[i] == '"') // Start/Terminate string - { - isString = !isString; - continue; - } - else if (i < (row.size() - 1) && row[i] == '\\' &&row[i + 1] == '"' && isString) // Handle quotes in strings as \" - { - tempStr = '"'; - ++i; - } - else if (!isString && (row[i] == '\n' || row[i] == '\x0D' || row[i] == '\x0A' || row[i] == '\t')) - { - //++i; - continue; - } - else if (!isString && row[i] == '#' && allowComments) // Skip comments. I know CSVs usually don't have comments, but in this case it's useful - { - return; - } - else - { - tempStr = row[i]; - } - - element.append(&tempStr, 1); - } - - // Push last element - _row.push_back(element); - - if (_row.size() == 0 || (_row.size() == 1 && !_row[0].size())) // Skip empty rows - { - return; - } - - this->dataMap.push_back(_row); - } -} +#include "STDInclude.hpp" + +namespace Utils +{ + CSV::CSV(std::string file, bool isFile, bool allowComments) + { + this->parse(file, isFile, allowComments); + } + + CSV::~CSV() + { + for (auto row : this->dataMap) + { + for (auto entry : row) + { + entry.clear(); + } + + row.clear(); + } + + this->dataMap.clear(); + } + + int CSV::getRows() + { + return this->dataMap.size(); + } + + int CSV::getColumns(size_t row) + { + if (this->dataMap.size() > row) + { + return this->dataMap[row].size(); + } + + return 0; + } + + int CSV::getColumns() + { + int count = 0; + + for (int i = 0; i < this->getRows(); ++i) + { + count = std::max(this->getColumns(i), count); + } + + return count; + } + + std::string CSV::getElementAt(size_t row, size_t column) + { + if (this->dataMap.size() > row) + { + auto _row = this->dataMap[row]; + + if (_row.size() > column) + { + return _row[column]; + } + } + + return ""; + } + + void CSV::parse(std::string file, bool isFile, bool allowComments) + { + std::string buffer; + + if (isFile) + { + if (!Utils::IO::FileExists(file)) return; + buffer = Utils::IO::ReadFile(file); + } + else + { + buffer = file; + } + + if (!buffer.empty()) + { + auto rows = Utils::String::Explode(buffer, '\n'); + + for (auto row : rows) + { + this->parseRow(row, allowComments); + } + } + } + + void CSV::parseRow(std::string row, bool allowComments) + { + bool isString = false; + std::string element; + std::vector _row; + char tempStr = 0; + + for (unsigned int i = 0; i < row.size(); ++i) + { + if (row[i] == ',' && !isString) // FLush entry + { + _row.push_back(element); + element.clear(); + continue; + } + else if (row[i] == '"') // Start/Terminate string + { + isString = !isString; + continue; + } + else if (i < (row.size() - 1) && row[i] == '\\' &&row[i + 1] == '"' && isString) // Handle quotes in strings as \" + { + tempStr = '"'; + ++i; + } + else if (!isString && (row[i] == '\n' || row[i] == '\x0D' || row[i] == '\x0A' || row[i] == '\t')) + { + //++i; + continue; + } + else if (!isString && row[i] == '#' && allowComments) // Skip comments. I know CSVs usually don't have comments, but in this case it's useful + { + return; + } + else + { + tempStr = row[i]; + } + + element.append(&tempStr, 1); + } + + // Push last element + _row.push_back(element); + + if (_row.size() == 0 || (_row.size() == 1 && !_row[0].size())) // Skip empty rows + { + return; + } + + this->dataMap.push_back(_row); + } +} diff --git a/src/Utils/Chain.hpp b/src/Utils/Chain.hpp index a504322e..86534f32 100644 --- a/src/Utils/Chain.hpp +++ b/src/Utils/Chain.hpp @@ -1,147 +1,147 @@ -namespace Utils -{ - template - class Chain - { - public: - class Entry - { - private: - std::shared_ptr object; - std::shared_ptr next; - - public: - bool hasNext() - { - return (this->next.use_count() > 0); - } - - bool isValid() - { - return (this->Object.use_count() > 0); - } - - void set(T object) - { - this->object = std::shared_ptr(new T()); - *this->object.get() = object; - } - - std::shared_ptr get() - { - return this->object; - } - - Entry getNext() - { - if (this->hasNext()) - { - return *(this->next.get()); - } - else - { - return Entry(); - } - } - - std::shared_ptr getNextEntry() - { - return this->next; - } - - void setNextEntry(std::shared_ptr entry) - { - this->next = entry; - } - - T *operator->() - { - return (this->object.get()); - } - - Entry& operator++ () - { - *this = this->getNext(); - return *this; - } - - Entry operator++ (int) - { - Entry result = *this; - this->operator++(); - return result; - } - }; - - private: - std::mutex mutex; - Entry object; - - public: - void add(T object) - { - std::lock_guard _(this->mutex); - - if (!this->Empty()) - { - // Create new chain entry - std::shared_ptr currentObject = std::shared_ptr(new Entry); - *currentObject.get() = this->object; - - // Add it to the chain - this->object = Entry(); - this->object.setNextEntry(currentObject); - } - - this->object.set(object); - } - - void remove(std::shared_ptr object) - { - std::lock_guard _(this->mutex); - - if (!this->empty()) - { - if (this->object.get().get() == object.get()) - { - this->object = this->object.getNext(); - } - else if(this->object.hasNext()) - { - for (auto entry = this->object; entry.isValid(); ++entry) - { - auto next = entry.getNext(); - - if (next.isValid() && next.get().get() == object.get()) - { - *entry.getNextEntry().get() = next.getNext(); - } - } - } - } - } - - void remove(Entry entry) - { - if (entry.isValid()) - { - this->remove(entry.Get()); - } - } - - bool empty() - { - return !this->object.isValid(); - } - - Entry begin() - { - return this->object; - } - - void clear() - { - this->object = Entry(); - } - }; -} +namespace Utils +{ + template + class Chain + { + public: + class Entry + { + private: + std::shared_ptr object; + std::shared_ptr next; + + public: + bool hasNext() + { + return (this->next.use_count() > 0); + } + + bool isValid() + { + return (this->Object.use_count() > 0); + } + + void set(T object) + { + this->object = std::shared_ptr(new T()); + *this->object.get() = object; + } + + std::shared_ptr get() + { + return this->object; + } + + Entry getNext() + { + if (this->hasNext()) + { + return *(this->next.get()); + } + else + { + return Entry(); + } + } + + std::shared_ptr getNextEntry() + { + return this->next; + } + + void setNextEntry(std::shared_ptr entry) + { + this->next = entry; + } + + T *operator->() + { + return (this->object.get()); + } + + Entry& operator++ () + { + *this = this->getNext(); + return *this; + } + + Entry operator++ (int) + { + Entry result = *this; + this->operator++(); + return result; + } + }; + + private: + std::mutex mutex; + Entry object; + + public: + void add(T object) + { + std::lock_guard _(this->mutex); + + if (!this->Empty()) + { + // Create new chain entry + std::shared_ptr currentObject = std::shared_ptr(new Entry); + *currentObject.get() = this->object; + + // Add it to the chain + this->object = Entry(); + this->object.setNextEntry(currentObject); + } + + this->object.set(object); + } + + void remove(std::shared_ptr object) + { + std::lock_guard _(this->mutex); + + if (!this->empty()) + { + if (this->object.get().get() == object.get()) + { + this->object = this->object.getNext(); + } + else if(this->object.hasNext()) + { + for (auto entry = this->object; entry.isValid(); ++entry) + { + auto next = entry.getNext(); + + if (next.isValid() && next.get().get() == object.get()) + { + *entry.getNextEntry().get() = next.getNext(); + } + } + } + } + } + + void remove(Entry entry) + { + if (entry.isValid()) + { + this->remove(entry.Get()); + } + } + + bool empty() + { + return !this->object.isValid(); + } + + Entry begin() + { + return this->object; + } + + void clear() + { + this->object = Entry(); + } + }; +} diff --git a/src/Utils/Compression.cpp b/src/Utils/Compression.cpp index 39d9beff..f6f0b28d 100644 --- a/src/Utils/Compression.cpp +++ b/src/Utils/Compression.cpp @@ -1,74 +1,74 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - namespace Compression - { - std::string ZLib::Compress(std::string data) - { - Utils::Memory::Allocator allocator; - unsigned long length = (data.size() * 2); - - // Make sure the buffer is large enough - if (length < 100) length *= 10; - - char* buffer = allocator.allocateArray(length); - - if (compress2(reinterpret_cast(buffer), &length, reinterpret_cast(const_cast(data.data())), data.size(), Z_BEST_COMPRESSION) != Z_OK) - { - Utils::Memory::Free(buffer); - return ""; - } - - data.clear(); - data.append(buffer, length); - - return data; - } - - std::string ZLib::Decompress(std::string data) - { - z_stream stream; - ZeroMemory(&stream, sizeof(stream)); - std::string buffer; - - if (inflateInit(&stream) != Z_OK) - { - return ""; - } - - int ret = 0; - Utils::Memory::Allocator allocator; - - uint8_t* dest = allocator.allocateArray(CHUNK); - const char* dataPtr = data.data(); - - do - { - stream.avail_in = std::min(static_cast(CHUNK), data.size() - (dataPtr - data.data())); - stream.next_in = reinterpret_cast(dataPtr); - dataPtr += stream.avail_in; - - do - { - stream.avail_out = CHUNK; - stream.next_out = dest; - - ret = inflate(&stream, Z_NO_FLUSH); - if (ret != Z_OK && ret != Z_STREAM_END) - { - inflateEnd(&stream); - return ""; - } - - buffer.append(reinterpret_cast(dest), CHUNK - stream.avail_out); - - } while (stream.avail_out == 0); - - } while (ret != Z_STREAM_END); - - inflateEnd(&stream); - return buffer; - } - }; -} +#include "STDInclude.hpp" + +namespace Utils +{ + namespace Compression + { + std::string ZLib::Compress(std::string data) + { + Utils::Memory::Allocator allocator; + unsigned long length = (data.size() * 2); + + // Make sure the buffer is large enough + if (length < 100) length *= 10; + + char* buffer = allocator.allocateArray(length); + + if (compress2(reinterpret_cast(buffer), &length, reinterpret_cast(const_cast(data.data())), data.size(), Z_BEST_COMPRESSION) != Z_OK) + { + Utils::Memory::Free(buffer); + return ""; + } + + data.clear(); + data.append(buffer, length); + + return data; + } + + std::string ZLib::Decompress(std::string data) + { + z_stream stream; + ZeroMemory(&stream, sizeof(stream)); + std::string buffer; + + if (inflateInit(&stream) != Z_OK) + { + return ""; + } + + int ret = 0; + Utils::Memory::Allocator allocator; + + uint8_t* dest = allocator.allocateArray(CHUNK); + const char* dataPtr = data.data(); + + do + { + stream.avail_in = std::min(static_cast(CHUNK), data.size() - (dataPtr - data.data())); + stream.next_in = reinterpret_cast(dataPtr); + dataPtr += stream.avail_in; + + do + { + stream.avail_out = CHUNK; + stream.next_out = dest; + + ret = inflate(&stream, Z_NO_FLUSH); + if (ret != Z_OK && ret != Z_STREAM_END) + { + inflateEnd(&stream); + return ""; + } + + buffer.append(reinterpret_cast(dest), CHUNK - stream.avail_out); + + } while (stream.avail_out == 0); + + } while (ret != Z_STREAM_END); + + inflateEnd(&stream); + return buffer; + } + }; +} diff --git a/src/Utils/Cryptography.cpp b/src/Utils/Cryptography.cpp index be8b79bc..9a5520f9 100644 --- a/src/Utils/Cryptography.cpp +++ b/src/Utils/Cryptography.cpp @@ -1,285 +1,285 @@ -#include "STDInclude.hpp" - -/// http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/libtomcrypt/doc/libTomCryptDoc.pdf - -namespace Utils -{ - namespace Cryptography - { - void Initialize() - { - DES3::Initialize(); - Rand::Initialize(); - } - -#pragma region Rand - - prng_state Rand::State; - - uint32_t Rand::GenerateInt() - { - uint32_t number = 0; - fortuna_read(reinterpret_cast(&number), sizeof(number), &Rand::State); - return number; - } - - void Rand::Initialize() - { - ltc_mp = ltm_desc; - register_prng(&fortuna_desc); - rng_make_prng(128, find_prng("fortuna"), &Rand::State, NULL); - } - -#pragma endregion - -#pragma region ECC - - ECC::Key ECC::GenerateKey(int bits) - { - ECC::Key key; - - register_prng(&sprng_desc); - - ltc_mp = ltm_desc; - - ecc_make_key(NULL, find_prng("sprng"), bits / 8, key.getKeyPtr()); - - return key; - } - - std::string ECC::SignMessage(Key key, std::string message) - { - if (!key.isValid()) return ""; - - uint8_t buffer[512]; - DWORD length = sizeof(buffer); - - register_prng(&sprng_desc); - - ltc_mp = ltm_desc; - - ecc_sign_hash(reinterpret_cast(message.data()), message.size(), buffer, &length, NULL, find_prng("sprng"), key.getKeyPtr()); - - return std::string(reinterpret_cast(buffer), length); - } - - bool ECC::VerifyMessage(Key key, std::string message, std::string signature) - { - if (!key.isValid()) return false; - - ltc_mp = ltm_desc; - - int result = 0; - return (ecc_verify_hash(reinterpret_cast(signature.data()), signature.size(), reinterpret_cast(message.data()), message.size(), &result, key.getKeyPtr()) == CRYPT_OK && result != 0); - } - -#pragma endregion - -#pragma region RSA - - RSA::Key RSA::GenerateKey(int bits) - { - RSA::Key key; - - register_prng(&sprng_desc); - register_hash(&sha1_desc); - - ltc_mp = ltm_desc; - - rsa_make_key(NULL, find_prng("sprng"), bits / 8, 65537, key.getKeyPtr()); - - return key; - } - - std::string RSA::SignMessage(RSA::Key key, std::string message) - { - if (!key.isValid()) return ""; - - uint8_t buffer[512]; - DWORD length = sizeof(buffer); - - register_prng(&sprng_desc); - register_hash(&sha1_desc); - - ltc_mp = ltm_desc; - - rsa_sign_hash(reinterpret_cast(message.data()), message.size(), buffer, &length, NULL, find_prng("sprng"), find_hash("sha1"), 0, key.getKeyPtr()); - - return std::string(reinterpret_cast(buffer), length); - } - - bool RSA::VerifyMessage(Key key, std::string message, std::string signature) - { - if (!key.isValid()) return false; - - register_hash(&sha1_desc); - - ltc_mp = ltm_desc; - - int result = 0; - return (rsa_verify_hash(reinterpret_cast(signature.data()), signature.size(), reinterpret_cast(message.data()), message.size(), find_hash("sha1"), 0, &result, key.getKeyPtr()) == CRYPT_OK && result != 0); - } - -#pragma endregion - -#pragma region DES3 - - void DES3::Initialize() - { - register_cipher(&des3_desc); - } - - std::string DES3::Encrypt(std::string text, std::string iv, std::string key) - { - std::string encData; - encData.resize(text.size()); - - symmetric_CBC cbc; - int des3 = find_cipher("3des"); - - cbc_start(des3, reinterpret_cast(iv.data()), reinterpret_cast(key.data()), key.size(), 0, &cbc); - cbc_encrypt(reinterpret_cast(text.data()), reinterpret_cast(const_cast(encData.data())), text.size(), &cbc); - cbc_done(&cbc); - - return encData; - } - - std::string DES3::Decrpyt(std::string data, std::string iv, std::string key) - { - std::string decData; - decData.resize(data.size()); - - symmetric_CBC cbc; - int des3 = find_cipher("3des"); - - cbc_start(des3, reinterpret_cast(iv.data()), reinterpret_cast(key.data()), key.size(), 0, &cbc); - cbc_decrypt(reinterpret_cast(data.data()), reinterpret_cast(const_cast(decData.data())), data.size(), &cbc); - cbc_done(&cbc); - - return decData; - } - -#pragma endregion - -#pragma region Tiger - - std::string Tiger::Compute(std::string data, bool hex) - { - return Tiger::Compute(reinterpret_cast(data.data()), data.size(), hex); - } - - std::string Tiger::Compute(const uint8_t* data, size_t length, bool hex) - { - uint8_t buffer[24] = { 0 }; - - hash_state state; - tiger_init(&state); - tiger_process(&state, data, length); - tiger_done(&state, buffer); - - std::string hash(reinterpret_cast(buffer), sizeof(buffer)); - if (!hex) return hash; - - return Utils::String::DumpHex(hash, ""); - } - -#pragma endregion - -#pragma region SHA1 - - std::string SHA1::Compute(std::string data, bool hex) - { - return SHA1::Compute(reinterpret_cast(data.data()), data.size(), hex); - } - - std::string SHA1::Compute(const uint8_t* data, size_t length, bool hex) - { - uint8_t buffer[20] = { 0 }; - - hash_state state; - sha1_init(&state); - sha1_process(&state, data, length); - sha1_done(&state, buffer); - - std::string hash(reinterpret_cast(buffer), sizeof(buffer)); - if (!hex) return hash; - - return Utils::String::DumpHex(hash, ""); - } - -#pragma endregion - -#pragma region SHA256 - - std::string SHA256::Compute(std::string data, bool hex) - { - return SHA256::Compute(reinterpret_cast(data.data()), data.size(), hex); - } - - std::string SHA256::Compute(const uint8_t* data, size_t length, bool hex) - { - uint8_t buffer[32] = { 0 }; - - hash_state state; - sha256_init(&state); - sha256_process(&state, data, length); - sha256_done(&state, buffer); - - std::string hash(reinterpret_cast(buffer), sizeof(buffer)); - if (!hex) return hash; - - return Utils::String::DumpHex(hash, ""); - } - -#pragma endregion - -#pragma region SHA512 - - std::string SHA512::Compute(std::string data, bool hex) - { - return SHA512::Compute(reinterpret_cast(data.data()), data.size(), hex); - } - - std::string SHA512::Compute(const uint8_t* data, size_t length, bool hex) - { - uint8_t buffer[64] = { 0 }; - - hash_state state; - sha512_init(&state); - sha512_process(&state, data, length); - sha512_done(&state, buffer); - - std::string hash(reinterpret_cast(buffer), sizeof(buffer)); - if (!hex) return hash; - - return Utils::String::DumpHex(hash, ""); - } - -#pragma endregion - -#pragma region JenkinsOneAtATime - - unsigned int JenkinsOneAtATime::Compute(std::string data) - { - return JenkinsOneAtATime::Compute(data.data(), data.size()); - } - - unsigned int JenkinsOneAtATime::Compute(const char *key, size_t len) - { - unsigned int hash, i; - for (hash = i = 0; i < len; ++i) - { - hash += key[i]; - hash += (hash << 10); - hash ^= (hash >> 6); - } - hash += (hash << 3); - hash ^= (hash >> 11); - hash += (hash << 15); - return hash; - } - -#pragma endregion - - } -} +#include "STDInclude.hpp" + +/// http://www.opensource.apple.com/source/CommonCrypto/CommonCrypto-55010/Source/libtomcrypt/doc/libTomCryptDoc.pdf + +namespace Utils +{ + namespace Cryptography + { + void Initialize() + { + DES3::Initialize(); + Rand::Initialize(); + } + +#pragma region Rand + + prng_state Rand::State; + + uint32_t Rand::GenerateInt() + { + uint32_t number = 0; + fortuna_read(reinterpret_cast(&number), sizeof(number), &Rand::State); + return number; + } + + void Rand::Initialize() + { + ltc_mp = ltm_desc; + register_prng(&fortuna_desc); + rng_make_prng(128, find_prng("fortuna"), &Rand::State, NULL); + } + +#pragma endregion + +#pragma region ECC + + ECC::Key ECC::GenerateKey(int bits) + { + ECC::Key key; + + register_prng(&sprng_desc); + + ltc_mp = ltm_desc; + + ecc_make_key(NULL, find_prng("sprng"), bits / 8, key.getKeyPtr()); + + return key; + } + + std::string ECC::SignMessage(Key key, std::string message) + { + if (!key.isValid()) return ""; + + uint8_t buffer[512]; + DWORD length = sizeof(buffer); + + register_prng(&sprng_desc); + + ltc_mp = ltm_desc; + + ecc_sign_hash(reinterpret_cast(message.data()), message.size(), buffer, &length, NULL, find_prng("sprng"), key.getKeyPtr()); + + return std::string(reinterpret_cast(buffer), length); + } + + bool ECC::VerifyMessage(Key key, std::string message, std::string signature) + { + if (!key.isValid()) return false; + + ltc_mp = ltm_desc; + + int result = 0; + return (ecc_verify_hash(reinterpret_cast(signature.data()), signature.size(), reinterpret_cast(message.data()), message.size(), &result, key.getKeyPtr()) == CRYPT_OK && result != 0); + } + +#pragma endregion + +#pragma region RSA + + RSA::Key RSA::GenerateKey(int bits) + { + RSA::Key key; + + register_prng(&sprng_desc); + register_hash(&sha1_desc); + + ltc_mp = ltm_desc; + + rsa_make_key(NULL, find_prng("sprng"), bits / 8, 65537, key.getKeyPtr()); + + return key; + } + + std::string RSA::SignMessage(RSA::Key key, std::string message) + { + if (!key.isValid()) return ""; + + uint8_t buffer[512]; + DWORD length = sizeof(buffer); + + register_prng(&sprng_desc); + register_hash(&sha1_desc); + + ltc_mp = ltm_desc; + + rsa_sign_hash(reinterpret_cast(message.data()), message.size(), buffer, &length, NULL, find_prng("sprng"), find_hash("sha1"), 0, key.getKeyPtr()); + + return std::string(reinterpret_cast(buffer), length); + } + + bool RSA::VerifyMessage(Key key, std::string message, std::string signature) + { + if (!key.isValid()) return false; + + register_hash(&sha1_desc); + + ltc_mp = ltm_desc; + + int result = 0; + return (rsa_verify_hash(reinterpret_cast(signature.data()), signature.size(), reinterpret_cast(message.data()), message.size(), find_hash("sha1"), 0, &result, key.getKeyPtr()) == CRYPT_OK && result != 0); + } + +#pragma endregion + +#pragma region DES3 + + void DES3::Initialize() + { + register_cipher(&des3_desc); + } + + std::string DES3::Encrypt(std::string text, std::string iv, std::string key) + { + std::string encData; + encData.resize(text.size()); + + symmetric_CBC cbc; + int des3 = find_cipher("3des"); + + cbc_start(des3, reinterpret_cast(iv.data()), reinterpret_cast(key.data()), key.size(), 0, &cbc); + cbc_encrypt(reinterpret_cast(text.data()), reinterpret_cast(const_cast(encData.data())), text.size(), &cbc); + cbc_done(&cbc); + + return encData; + } + + std::string DES3::Decrpyt(std::string data, std::string iv, std::string key) + { + std::string decData; + decData.resize(data.size()); + + symmetric_CBC cbc; + int des3 = find_cipher("3des"); + + cbc_start(des3, reinterpret_cast(iv.data()), reinterpret_cast(key.data()), key.size(), 0, &cbc); + cbc_decrypt(reinterpret_cast(data.data()), reinterpret_cast(const_cast(decData.data())), data.size(), &cbc); + cbc_done(&cbc); + + return decData; + } + +#pragma endregion + +#pragma region Tiger + + std::string Tiger::Compute(std::string data, bool hex) + { + return Tiger::Compute(reinterpret_cast(data.data()), data.size(), hex); + } + + std::string Tiger::Compute(const uint8_t* data, size_t length, bool hex) + { + uint8_t buffer[24] = { 0 }; + + hash_state state; + tiger_init(&state); + tiger_process(&state, data, length); + tiger_done(&state, buffer); + + std::string hash(reinterpret_cast(buffer), sizeof(buffer)); + if (!hex) return hash; + + return Utils::String::DumpHex(hash, ""); + } + +#pragma endregion + +#pragma region SHA1 + + std::string SHA1::Compute(std::string data, bool hex) + { + return SHA1::Compute(reinterpret_cast(data.data()), data.size(), hex); + } + + std::string SHA1::Compute(const uint8_t* data, size_t length, bool hex) + { + uint8_t buffer[20] = { 0 }; + + hash_state state; + sha1_init(&state); + sha1_process(&state, data, length); + sha1_done(&state, buffer); + + std::string hash(reinterpret_cast(buffer), sizeof(buffer)); + if (!hex) return hash; + + return Utils::String::DumpHex(hash, ""); + } + +#pragma endregion + +#pragma region SHA256 + + std::string SHA256::Compute(std::string data, bool hex) + { + return SHA256::Compute(reinterpret_cast(data.data()), data.size(), hex); + } + + std::string SHA256::Compute(const uint8_t* data, size_t length, bool hex) + { + uint8_t buffer[32] = { 0 }; + + hash_state state; + sha256_init(&state); + sha256_process(&state, data, length); + sha256_done(&state, buffer); + + std::string hash(reinterpret_cast(buffer), sizeof(buffer)); + if (!hex) return hash; + + return Utils::String::DumpHex(hash, ""); + } + +#pragma endregion + +#pragma region SHA512 + + std::string SHA512::Compute(std::string data, bool hex) + { + return SHA512::Compute(reinterpret_cast(data.data()), data.size(), hex); + } + + std::string SHA512::Compute(const uint8_t* data, size_t length, bool hex) + { + uint8_t buffer[64] = { 0 }; + + hash_state state; + sha512_init(&state); + sha512_process(&state, data, length); + sha512_done(&state, buffer); + + std::string hash(reinterpret_cast(buffer), sizeof(buffer)); + if (!hex) return hash; + + return Utils::String::DumpHex(hash, ""); + } + +#pragma endregion + +#pragma region JenkinsOneAtATime + + unsigned int JenkinsOneAtATime::Compute(std::string data) + { + return JenkinsOneAtATime::Compute(data.data(), data.size()); + } + + unsigned int JenkinsOneAtATime::Compute(const char *key, size_t len) + { + unsigned int hash, i; + for (hash = i = 0; i < len; ++i) + { + hash += key[i]; + hash += (hash << 10); + hash ^= (hash >> 6); + } + hash += (hash << 3); + hash ^= (hash >> 11); + hash += (hash << 15); + return hash; + } + +#pragma endregion + + } +} diff --git a/src/Utils/Cryptography.hpp b/src/Utils/Cryptography.hpp index 8385115c..580d1358 100644 --- a/src/Utils/Cryptography.hpp +++ b/src/Utils/Cryptography.hpp @@ -1,336 +1,336 @@ -namespace Utils -{ - namespace Cryptography - { - void Initialize(); - - class Token - { - public: - Token() { this->tokenString.clear(); }; - Token(const Token& obj) : tokenString(obj.tokenString) { }; - Token(std::string token) : tokenString(token.begin(), token.end()) { }; - Token(std::basic_string token) : tokenString(token.begin(), token.end()) { }; - - Token& operator++ () - { - if (this->tokenString.empty()) - { - this->tokenString.append(reinterpret_cast("\0"), 1); - } - else - { - for (int i = static_cast(this->tokenString.size() - 1); i >= 0; --i) - { - if (this->tokenString[i] == 0xFF) - { - this->tokenString[i] = 0; - - if (!i) - { - // Prepend here, as /dev/urandom says so ;) https://github.com/IW4x/iw4x-client-node/wikis/technical-information#incrementing-the-token - this->tokenString = std::basic_string(reinterpret_cast("\0"), 1) + this->tokenString; - break; - } - } - else - { - ++this->tokenString[i]; - break; - } - } - } - - return *this; - } - - Token operator++ (int) - { - Token result = *this; - this->operator++(); - return result; - } - - bool operator==(const Token& token) const - { - return (this->toString() == token.toString()); - } - - bool operator!=(const Token& token) const - { - return !(*this == token); - } - - bool operator<(const Token& token) const - { - if (*this == token) - { - return false; - } - else if (this->toString().size() < token.toString().size()) - { - return true; - } - else if (this->toString().size() > token.toString().size()) - { - return false; - } - else - { - auto lStr = this->toString(); - auto rStr = token.toString(); - - for (unsigned int i = 0; i < lStr.size(); ++i) - { - if (lStr[i] < rStr[i]) - { - return true; - } - } - } - - return false; - } - - bool operator>(const Token& token) const - { - return (token < *this && *this != token); - } - - bool operator<=(const Token& token) const - { - return !(*this > token); - } - - bool operator>=(const Token& token) const - { - return !(*this < token); - } - - std::string toString() - { - return std::string(this->tokenString.begin(), this->tokenString.end()); - } - - const std::string toString() const - { - return std::string(this->tokenString.begin(), this->tokenString.end()); - } - - std::basic_string toUnsignedString() - { - return this->tokenString; - } - - void clear() - { - this->tokenString.clear(); - } - - private: - std::basic_string tokenString; - }; - - class Rand - { - public: - static uint32_t GenerateInt(); - static void Initialize(); - - private: - static prng_state State; - }; - - class ECC - { - public: - class Key - { - public: - Key() : keyStorage(new ecc_key) - { - ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); - }; - Key(ecc_key* key) : Key() { if(key) std::memmove(this->getKeyPtr(), key, sizeof(*key)); }; - Key(ecc_key key) : Key(&key) {}; - ~Key() - { - if (this->keyStorage.use_count() <= 1) - { - this->free(); - } - }; - - bool isValid() - { - return (!Utils::Memory::IsSet(this->getKeyPtr(), 0, sizeof(*this->getKeyPtr()))); - } - - ecc_key* getKeyPtr() - { - return this->keyStorage.get(); - } - - std::string getPublicKey() - { - uint8_t buffer[512] = { 0 }; - DWORD length = sizeof(buffer); - - if (ecc_ansi_x963_export(this->getKeyPtr(), buffer, &length) == CRYPT_OK) - { - return std::string(reinterpret_cast(buffer), length); - } - - return ""; - } - - void set(std::string pubKeyBuffer) - { - this->free(); - - if (ecc_ansi_x963_import(reinterpret_cast(pubKeyBuffer.data()), pubKeyBuffer.size(), this->getKeyPtr()) != CRYPT_OK) - { - ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); - } - } - - void deserialize(std::string key) - { - this->free(); - - if (ecc_import(reinterpret_cast(key.data()), key.size(), this->getKeyPtr()) != CRYPT_OK) - { - ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); - } - } - - std::string serialize(int type = PK_PRIVATE) - { - uint8_t buffer[4096] = { 0 }; - DWORD length = sizeof(buffer); - - if (ecc_export(buffer, &length, type, this->getKeyPtr()) == CRYPT_OK) - { - return std::string(reinterpret_cast(buffer), length); - } - - return ""; - } - - void free() - { - if (this->isValid()) - { - ecc_free(this->getKeyPtr()); - } - - ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); - } - - bool operator==(Key& key) - { - return (this->isValid() && key.isValid() && this->serialize(PK_PUBLIC) == key.serialize(PK_PUBLIC)); - } - - private: - std::shared_ptr keyStorage; - }; - - static Key GenerateKey(int bits); - static std::string SignMessage(Key key, std::string message); - static bool VerifyMessage(Key key, std::string message, std::string signature); - }; - - class RSA - { - public: - class Key - { - public: - Key() : keyStorage(new rsa_key) - { - ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); - }; - Key(rsa_key* key) : Key() { if (key) std::memmove(this->getKeyPtr(), key, sizeof(*key)); }; - Key(rsa_key key) : Key(&key) {}; - ~Key() - { - if (this->keyStorage.use_count() <= 1) - { - this->free(); - } - }; - - rsa_key* getKeyPtr() - { - return this->keyStorage.get(); - } - - bool isValid() - { - return (!Utils::Memory::IsSet(this->getKeyPtr(), 0, sizeof(*this->getKeyPtr()))); - } - - void free() - { - if (this->isValid()) - { - rsa_free(this->getKeyPtr()); - } - - ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); - } - - private: - std::shared_ptr keyStorage; - }; - - static Key GenerateKey(int bits); - static std::string SignMessage(Key key, std::string message); - static bool VerifyMessage(Key key, std::string message, std::string signature); - }; - - class DES3 - { - public: - static void Initialize(); - static std::string Encrypt(std::string text, std::string iv, std::string key); - static std::string Decrpyt(std::string text, std::string iv, std::string key); - }; - - class Tiger - { - public: - static std::string Compute(std::string data, bool hex = false); - static std::string Compute(const uint8_t* data, size_t length, bool hex = false); - }; - - class SHA1 - { - public: - static std::string Compute(std::string data, bool hex = false); - static std::string Compute(const uint8_t* data, size_t length, bool hex = false); - }; - - class SHA256 - { - public: - static std::string Compute(std::string data, bool hex = false); - static std::string Compute(const uint8_t* data, size_t length, bool hex = false); - }; - - class SHA512 - { - public: - static std::string Compute(std::string data, bool hex = false); - static std::string Compute(const uint8_t* data, size_t length, bool hex = false); - }; - - class JenkinsOneAtATime - { - public: - static unsigned int Compute(std::string data); - static unsigned int Compute(const char *key, size_t len); - }; - } -} +namespace Utils +{ + namespace Cryptography + { + void Initialize(); + + class Token + { + public: + Token() { this->tokenString.clear(); }; + Token(const Token& obj) : tokenString(obj.tokenString) { }; + Token(std::string token) : tokenString(token.begin(), token.end()) { }; + Token(std::basic_string token) : tokenString(token.begin(), token.end()) { }; + + Token& operator++ () + { + if (this->tokenString.empty()) + { + this->tokenString.append(reinterpret_cast("\0"), 1); + } + else + { + for (int i = static_cast(this->tokenString.size() - 1); i >= 0; --i) + { + if (this->tokenString[i] == 0xFF) + { + this->tokenString[i] = 0; + + if (!i) + { + // Prepend here, as /dev/urandom says so ;) https://github.com/IW4x/iw4x-client-node/wikis/technical-information#incrementing-the-token + this->tokenString = std::basic_string(reinterpret_cast("\0"), 1) + this->tokenString; + break; + } + } + else + { + ++this->tokenString[i]; + break; + } + } + } + + return *this; + } + + Token operator++ (int) + { + Token result = *this; + this->operator++(); + return result; + } + + bool operator==(const Token& token) const + { + return (this->toString() == token.toString()); + } + + bool operator!=(const Token& token) const + { + return !(*this == token); + } + + bool operator<(const Token& token) const + { + if (*this == token) + { + return false; + } + else if (this->toString().size() < token.toString().size()) + { + return true; + } + else if (this->toString().size() > token.toString().size()) + { + return false; + } + else + { + auto lStr = this->toString(); + auto rStr = token.toString(); + + for (unsigned int i = 0; i < lStr.size(); ++i) + { + if (lStr[i] < rStr[i]) + { + return true; + } + } + } + + return false; + } + + bool operator>(const Token& token) const + { + return (token < *this && *this != token); + } + + bool operator<=(const Token& token) const + { + return !(*this > token); + } + + bool operator>=(const Token& token) const + { + return !(*this < token); + } + + std::string toString() + { + return std::string(this->tokenString.begin(), this->tokenString.end()); + } + + const std::string toString() const + { + return std::string(this->tokenString.begin(), this->tokenString.end()); + } + + std::basic_string toUnsignedString() + { + return this->tokenString; + } + + void clear() + { + this->tokenString.clear(); + } + + private: + std::basic_string tokenString; + }; + + class Rand + { + public: + static uint32_t GenerateInt(); + static void Initialize(); + + private: + static prng_state State; + }; + + class ECC + { + public: + class Key + { + public: + Key() : keyStorage(new ecc_key) + { + ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); + }; + Key(ecc_key* key) : Key() { if(key) std::memmove(this->getKeyPtr(), key, sizeof(*key)); }; + Key(ecc_key key) : Key(&key) {}; + ~Key() + { + if (this->keyStorage.use_count() <= 1) + { + this->free(); + } + }; + + bool isValid() + { + return (!Utils::Memory::IsSet(this->getKeyPtr(), 0, sizeof(*this->getKeyPtr()))); + } + + ecc_key* getKeyPtr() + { + return this->keyStorage.get(); + } + + std::string getPublicKey() + { + uint8_t buffer[512] = { 0 }; + DWORD length = sizeof(buffer); + + if (ecc_ansi_x963_export(this->getKeyPtr(), buffer, &length) == CRYPT_OK) + { + return std::string(reinterpret_cast(buffer), length); + } + + return ""; + } + + void set(std::string pubKeyBuffer) + { + this->free(); + + if (ecc_ansi_x963_import(reinterpret_cast(pubKeyBuffer.data()), pubKeyBuffer.size(), this->getKeyPtr()) != CRYPT_OK) + { + ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); + } + } + + void deserialize(std::string key) + { + this->free(); + + if (ecc_import(reinterpret_cast(key.data()), key.size(), this->getKeyPtr()) != CRYPT_OK) + { + ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); + } + } + + std::string serialize(int type = PK_PRIVATE) + { + uint8_t buffer[4096] = { 0 }; + DWORD length = sizeof(buffer); + + if (ecc_export(buffer, &length, type, this->getKeyPtr()) == CRYPT_OK) + { + return std::string(reinterpret_cast(buffer), length); + } + + return ""; + } + + void free() + { + if (this->isValid()) + { + ecc_free(this->getKeyPtr()); + } + + ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); + } + + bool operator==(Key& key) + { + return (this->isValid() && key.isValid() && this->serialize(PK_PUBLIC) == key.serialize(PK_PUBLIC)); + } + + private: + std::shared_ptr keyStorage; + }; + + static Key GenerateKey(int bits); + static std::string SignMessage(Key key, std::string message); + static bool VerifyMessage(Key key, std::string message, std::string signature); + }; + + class RSA + { + public: + class Key + { + public: + Key() : keyStorage(new rsa_key) + { + ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); + }; + Key(rsa_key* key) : Key() { if (key) std::memmove(this->getKeyPtr(), key, sizeof(*key)); }; + Key(rsa_key key) : Key(&key) {}; + ~Key() + { + if (this->keyStorage.use_count() <= 1) + { + this->free(); + } + }; + + rsa_key* getKeyPtr() + { + return this->keyStorage.get(); + } + + bool isValid() + { + return (!Utils::Memory::IsSet(this->getKeyPtr(), 0, sizeof(*this->getKeyPtr()))); + } + + void free() + { + if (this->isValid()) + { + rsa_free(this->getKeyPtr()); + } + + ZeroMemory(this->getKeyPtr(), sizeof(*this->getKeyPtr())); + } + + private: + std::shared_ptr keyStorage; + }; + + static Key GenerateKey(int bits); + static std::string SignMessage(Key key, std::string message); + static bool VerifyMessage(Key key, std::string message, std::string signature); + }; + + class DES3 + { + public: + static void Initialize(); + static std::string Encrypt(std::string text, std::string iv, std::string key); + static std::string Decrpyt(std::string text, std::string iv, std::string key); + }; + + class Tiger + { + public: + static std::string Compute(std::string data, bool hex = false); + static std::string Compute(const uint8_t* data, size_t length, bool hex = false); + }; + + class SHA1 + { + public: + static std::string Compute(std::string data, bool hex = false); + static std::string Compute(const uint8_t* data, size_t length, bool hex = false); + }; + + class SHA256 + { + public: + static std::string Compute(std::string data, bool hex = false); + static std::string Compute(const uint8_t* data, size_t length, bool hex = false); + }; + + class SHA512 + { + public: + static std::string Compute(std::string data, bool hex = false); + static std::string Compute(const uint8_t* data, size_t length, bool hex = false); + }; + + class JenkinsOneAtATime + { + public: + static unsigned int Compute(std::string data); + static unsigned int Compute(const char *key, size_t len); + }; + } +} diff --git a/src/Utils/Hooking.cpp b/src/Utils/Hooking.cpp index f47d7889..751c4743 100644 --- a/src/Utils/Hooking.cpp +++ b/src/Utils/Hooking.cpp @@ -1,251 +1,251 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - std::map Hook::Interceptor::IReturn; - std::map Hook::Interceptor::ICallbacks; - - void Hook::Signature::process() - { - if (this->signatures.empty()) return; - - char* _start = reinterpret_cast(this->start); - - unsigned int sigCount = this->signatures.size(); - Hook::Signature::Container* containers = this->signatures.data(); - - for (size_t i = 0; i < this->length; ++i) - { - char* address = _start + i; - - for (unsigned int k = 0; k < sigCount; ++k) - { - Hook::Signature::Container* container = &containers[k]; - - unsigned int j = 0; - for (j = 0; j < strlen(container->mask); ++j) - { - if (container->mask[j] != '?' &&container->signature[j] != address[j]) - { - break; - } - } - - if (j == strlen(container->mask)) - { - container->callback(address); - } - } - } - } - - void Hook::Signature::add(Hook::Signature::Container& container) - { - Hook::Signature::signatures.push_back(container); - } - - void Hook::Interceptor::Install(void* place, void(*stub)()) - { - return Hook::Interceptor::Install(reinterpret_cast(place), stub); - } - - void Hook::Interceptor::Install(void** place, void(*stub)()) - { - Hook::Interceptor::IReturn[place] = *place; - Hook::Interceptor::ICallbacks[place] = stub; - *place = Hook::Interceptor::InterceptionStub; - } - - __declspec(naked) void Hook::Interceptor::InterceptionStub() - { - __asm - { - sub esp, 4h // Reserve space on the stack for the return address - pushad // Store registers - - lea eax, [esp + 20h] // Load initial stack pointer - push eax // Push it onto the stack - - call Hook::Interceptor::RunCallback // Run the callback based on the given stack pointer - call Hook::Interceptor::PopReturn // Get the initial return address according to the stack pointer - - add esp, 4h // Clear the stack - - mov [esp + 20h], eax // Store the return address at the reserved space - popad // Restore the registers - - retn // Return (jump to our return address) - } - } - - void Hook::Interceptor::RunCallback(void* place) - { - auto iCallback = Hook::Interceptor::ICallbacks.find(place); - if (iCallback != Hook::Interceptor::ICallbacks.end()) - { - iCallback->second(); - Hook::Interceptor::ICallbacks.erase(iCallback); - } - } - - void* Hook::Interceptor::PopReturn(void* _place) - { - void* retVal = nullptr; - - auto iReturn = Hook::Interceptor::IReturn.find(_place); - if (iReturn != Hook::Interceptor::IReturn.end()) - { - retVal = iReturn->second; - Hook::Interceptor::IReturn.erase(iReturn); - } - - return retVal; - } - - Hook::~Hook() - { - if (this->initialized) - { - this->uninstall(); - } - } - - Hook* Hook::initialize(DWORD _place, void(*_stub)(), bool _useJump) - { - return this->initialize(_place, reinterpret_cast(_stub), _useJump); - } - - Hook* Hook::initialize(DWORD _place, void* _stub, bool _useJump) - { - return this->initialize(reinterpret_cast(_place), _stub, _useJump); - } - - Hook* Hook::initialize(void* _place, void* _stub, bool _useJump) - { - if (this->initialized) return this; - this->initialized = true; - - this->useJump = _useJump; - this->place = _place; - this->stub = _stub; - - this->original = static_cast(this->place) + 5 + *reinterpret_cast((static_cast(this->place) + 1)); - - return this; - } - - Hook* Hook::install(bool unprotect, bool keepUnportected) - { - std::lock_guard _(this->stateMutex); - - if (!this->initialized || this->installed) - { - return this; - } - - this->installed = true; - - if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), PAGE_EXECUTE_READWRITE, &this->protection); - std::memcpy(this->buffer, this->place, sizeof(this->buffer)); - - char* code = static_cast(this->place); - - *code = static_cast(this->useJump ? 0xE9 : 0xE8); - - *reinterpret_cast(code + 1) = reinterpret_cast(this->stub) - (reinterpret_cast(this->place) + 5); - - if (unprotect && !keepUnportected) VirtualProtect(this->place, sizeof(this->buffer), this->protection, &this->protection); - - FlushInstructionCache(GetCurrentProcess(), this->place, sizeof(this->buffer)); - - return this; - } - - void Hook::quick() - { - if (Hook::installed) - { - Hook::installed = false; - } - } - - Hook* Hook::uninstall(bool unprotect) - { - std::lock_guard _(this->stateMutex); - - if (!this->initialized || !this->installed) - { - return this; - } - - this->installed = false; - - if(unprotect) VirtualProtect(this->place, sizeof(this->buffer), PAGE_EXECUTE_READWRITE, &this->protection); - - std::memcpy(this->place, this->buffer, sizeof(this->buffer)); - - if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), this->protection, &this->protection); - - FlushInstructionCache(GetCurrentProcess(), this->place, sizeof(this->buffer)); - - return this; - } - - void* Hook::getAddress() - { - return this->place; - } - - void Hook::Nop(void* place, size_t length) - { - DWORD oldProtect; - VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &oldProtect); - - memset(place, 0x90, length); - - VirtualProtect(place, length, oldProtect, &oldProtect); - FlushInstructionCache(GetCurrentProcess(), place, length); - } - - void Hook::Nop(DWORD place, size_t length) - { - Nop(reinterpret_cast(place), length); - } - - void Hook::SetString(void* place, const char* string, size_t length) - { - DWORD oldProtect; - VirtualProtect(place, length + 1, PAGE_EXECUTE_READWRITE, &oldProtect); - - strncpy_s(static_cast(place), length, string, length); - - VirtualProtect(place, length + 1, oldProtect, &oldProtect); - } - - void Hook::SetString(DWORD place, const char* string, size_t length) - { - Hook::SetString(reinterpret_cast(place), string, length); - } - - void Hook::SetString(void* place, const char* string) - { - Hook::SetString(place, string, strlen(static_cast(place))); - } - - void Hook::SetString(DWORD place, const char* string) - { - Hook::SetString(reinterpret_cast(place), string); - } - - void Hook::RedirectJump(void* place, void* stub) - { - char* operandPtr = static_cast(place) + 2; - int newOperand = reinterpret_cast(stub) - (reinterpret_cast(place) + 6); - Utils::Hook::Set(operandPtr, newOperand); - } - - void Hook::RedirectJump(DWORD place, void* stub) - { - Hook::RedirectJump(reinterpret_cast(place), stub); - } -} +#include "STDInclude.hpp" + +namespace Utils +{ + std::map Hook::Interceptor::IReturn; + std::map Hook::Interceptor::ICallbacks; + + void Hook::Signature::process() + { + if (this->signatures.empty()) return; + + char* _start = reinterpret_cast(this->start); + + unsigned int sigCount = this->signatures.size(); + Hook::Signature::Container* containers = this->signatures.data(); + + for (size_t i = 0; i < this->length; ++i) + { + char* address = _start + i; + + for (unsigned int k = 0; k < sigCount; ++k) + { + Hook::Signature::Container* container = &containers[k]; + + unsigned int j = 0; + for (j = 0; j < strlen(container->mask); ++j) + { + if (container->mask[j] != '?' &&container->signature[j] != address[j]) + { + break; + } + } + + if (j == strlen(container->mask)) + { + container->callback(address); + } + } + } + } + + void Hook::Signature::add(Hook::Signature::Container& container) + { + Hook::Signature::signatures.push_back(container); + } + + void Hook::Interceptor::Install(void* place, void(*stub)()) + { + return Hook::Interceptor::Install(reinterpret_cast(place), stub); + } + + void Hook::Interceptor::Install(void** place, void(*stub)()) + { + Hook::Interceptor::IReturn[place] = *place; + Hook::Interceptor::ICallbacks[place] = stub; + *place = Hook::Interceptor::InterceptionStub; + } + + __declspec(naked) void Hook::Interceptor::InterceptionStub() + { + __asm + { + sub esp, 4h // Reserve space on the stack for the return address + pushad // Store registers + + lea eax, [esp + 20h] // Load initial stack pointer + push eax // Push it onto the stack + + call Hook::Interceptor::RunCallback // Run the callback based on the given stack pointer + call Hook::Interceptor::PopReturn // Get the initial return address according to the stack pointer + + add esp, 4h // Clear the stack + + mov [esp + 20h], eax // Store the return address at the reserved space + popad // Restore the registers + + retn // Return (jump to our return address) + } + } + + void Hook::Interceptor::RunCallback(void* place) + { + auto iCallback = Hook::Interceptor::ICallbacks.find(place); + if (iCallback != Hook::Interceptor::ICallbacks.end()) + { + iCallback->second(); + Hook::Interceptor::ICallbacks.erase(iCallback); + } + } + + void* Hook::Interceptor::PopReturn(void* _place) + { + void* retVal = nullptr; + + auto iReturn = Hook::Interceptor::IReturn.find(_place); + if (iReturn != Hook::Interceptor::IReturn.end()) + { + retVal = iReturn->second; + Hook::Interceptor::IReturn.erase(iReturn); + } + + return retVal; + } + + Hook::~Hook() + { + if (this->initialized) + { + this->uninstall(); + } + } + + Hook* Hook::initialize(DWORD _place, void(*_stub)(), bool _useJump) + { + return this->initialize(_place, reinterpret_cast(_stub), _useJump); + } + + Hook* Hook::initialize(DWORD _place, void* _stub, bool _useJump) + { + return this->initialize(reinterpret_cast(_place), _stub, _useJump); + } + + Hook* Hook::initialize(void* _place, void* _stub, bool _useJump) + { + if (this->initialized) return this; + this->initialized = true; + + this->useJump = _useJump; + this->place = _place; + this->stub = _stub; + + this->original = static_cast(this->place) + 5 + *reinterpret_cast((static_cast(this->place) + 1)); + + return this; + } + + Hook* Hook::install(bool unprotect, bool keepUnportected) + { + std::lock_guard _(this->stateMutex); + + if (!this->initialized || this->installed) + { + return this; + } + + this->installed = true; + + if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), PAGE_EXECUTE_READWRITE, &this->protection); + std::memcpy(this->buffer, this->place, sizeof(this->buffer)); + + char* code = static_cast(this->place); + + *code = static_cast(this->useJump ? 0xE9 : 0xE8); + + *reinterpret_cast(code + 1) = reinterpret_cast(this->stub) - (reinterpret_cast(this->place) + 5); + + if (unprotect && !keepUnportected) VirtualProtect(this->place, sizeof(this->buffer), this->protection, &this->protection); + + FlushInstructionCache(GetCurrentProcess(), this->place, sizeof(this->buffer)); + + return this; + } + + void Hook::quick() + { + if (Hook::installed) + { + Hook::installed = false; + } + } + + Hook* Hook::uninstall(bool unprotect) + { + std::lock_guard _(this->stateMutex); + + if (!this->initialized || !this->installed) + { + return this; + } + + this->installed = false; + + if(unprotect) VirtualProtect(this->place, sizeof(this->buffer), PAGE_EXECUTE_READWRITE, &this->protection); + + std::memcpy(this->place, this->buffer, sizeof(this->buffer)); + + if (unprotect) VirtualProtect(this->place, sizeof(this->buffer), this->protection, &this->protection); + + FlushInstructionCache(GetCurrentProcess(), this->place, sizeof(this->buffer)); + + return this; + } + + void* Hook::getAddress() + { + return this->place; + } + + void Hook::Nop(void* place, size_t length) + { + DWORD oldProtect; + VirtualProtect(place, length, PAGE_EXECUTE_READWRITE, &oldProtect); + + memset(place, 0x90, length); + + VirtualProtect(place, length, oldProtect, &oldProtect); + FlushInstructionCache(GetCurrentProcess(), place, length); + } + + void Hook::Nop(DWORD place, size_t length) + { + Nop(reinterpret_cast(place), length); + } + + void Hook::SetString(void* place, const char* string, size_t length) + { + DWORD oldProtect; + VirtualProtect(place, length + 1, PAGE_EXECUTE_READWRITE, &oldProtect); + + strncpy_s(static_cast(place), length, string, length); + + VirtualProtect(place, length + 1, oldProtect, &oldProtect); + } + + void Hook::SetString(DWORD place, const char* string, size_t length) + { + Hook::SetString(reinterpret_cast(place), string, length); + } + + void Hook::SetString(void* place, const char* string) + { + Hook::SetString(place, string, strlen(static_cast(place))); + } + + void Hook::SetString(DWORD place, const char* string) + { + Hook::SetString(reinterpret_cast(place), string); + } + + void Hook::RedirectJump(void* place, void* stub) + { + char* operandPtr = static_cast(place) + 2; + int newOperand = reinterpret_cast(stub) - (reinterpret_cast(place) + 6); + Utils::Hook::Set(operandPtr, newOperand); + } + + void Hook::RedirectJump(DWORD place, void* stub) + { + Hook::RedirectJump(reinterpret_cast(place), stub); + } +} diff --git a/src/Utils/IO.cpp b/src/Utils/IO.cpp index c4109f2f..f008c0ef 100644 --- a/src/Utils/IO.cpp +++ b/src/Utils/IO.cpp @@ -1,74 +1,74 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - namespace IO - { - bool FileExists(std::string file) - { - return std::ifstream(file).good(); - } - - void WriteFile(std::string file, std::string data, bool append) - { - auto pos = file.find_last_of("/\\"); - if (pos != std::string::npos) - { - CreateDirectory(file.substr(0, pos)); - } - - std::ofstream stream(file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : std::ofstream::out)); - - if (stream.is_open()) - { - stream.write(data.data(), data.size()); - stream.close(); - } - } - - std::string ReadFile(std::string file) - { - std::string buffer; - - if (FileExists(file)) - { - std::streamsize size = 0; - std::ifstream stream(file, std::ios::binary); - if (!stream.is_open()) return buffer; - - stream.seekg(0, std::ios::end); - size = stream.tellg(); - stream.seekg(0, std::ios::beg); - - if (size > -1) - { - buffer.clear(); - buffer.resize((uint32_t)size); - - stream.read(const_cast(buffer.data()), size); - } - - stream.close(); - } - - return buffer; - } - - bool CreateDirectory(std::string dir) - { - return std::experimental::filesystem::create_directories(dir); - } - - std::vector ListFiles(std::string dir) - { - std::vector files; - - for (auto& file : std::experimental::filesystem::directory_iterator(dir)) - { - files.push_back(file.path().generic_string()); - } - - return files; - } - } -} +#include "STDInclude.hpp" + +namespace Utils +{ + namespace IO + { + bool FileExists(std::string file) + { + return std::ifstream(file).good(); + } + + void WriteFile(std::string file, std::string data, bool append) + { + auto pos = file.find_last_of("/\\"); + if (pos != std::string::npos) + { + CreateDirectory(file.substr(0, pos)); + } + + std::ofstream stream(file, std::ios::binary | std::ofstream::out | (append ? std::ofstream::app : std::ofstream::out)); + + if (stream.is_open()) + { + stream.write(data.data(), data.size()); + stream.close(); + } + } + + std::string ReadFile(std::string file) + { + std::string buffer; + + if (FileExists(file)) + { + std::streamsize size = 0; + std::ifstream stream(file, std::ios::binary); + if (!stream.is_open()) return buffer; + + stream.seekg(0, std::ios::end); + size = stream.tellg(); + stream.seekg(0, std::ios::beg); + + if (size > -1) + { + buffer.clear(); + buffer.resize((uint32_t)size); + + stream.read(const_cast(buffer.data()), size); + } + + stream.close(); + } + + return buffer; + } + + bool CreateDirectory(std::string dir) + { + return std::experimental::filesystem::create_directories(dir); + } + + std::vector ListFiles(std::string dir) + { + std::vector files; + + for (auto& file : std::experimental::filesystem::directory_iterator(dir)) + { + files.push_back(file.path().generic_string()); + } + + return files; + } + } +} diff --git a/src/Utils/IO.hpp b/src/Utils/IO.hpp index fecd7840..4b8531bc 100644 --- a/src/Utils/IO.hpp +++ b/src/Utils/IO.hpp @@ -1,11 +1,11 @@ -namespace Utils -{ - namespace IO - { - bool FileExists(std::string file); - void WriteFile(std::string file, std::string data, bool append = false); - std::string ReadFile(std::string file); - bool CreateDirectory(std::string dir); - std::vector ListFiles(std::string dir); - } -} +namespace Utils +{ + namespace IO + { + bool FileExists(std::string file); + void WriteFile(std::string file, std::string data, bool append = false); + std::string ReadFile(std::string file); + bool CreateDirectory(std::string dir); + std::vector ListFiles(std::string dir); + } +} diff --git a/src/Utils/InfoString.cpp b/src/Utils/InfoString.cpp index dfa1a78a..a76cb46b 100644 --- a/src/Utils/InfoString.cpp +++ b/src/Utils/InfoString.cpp @@ -1,66 +1,66 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - void InfoString::set(std::string key, std::string value) - { - this->keyValuePairs[key] = value; - } - - std::string InfoString::get(std::string key) - { - if (this->keyValuePairs.find(key) != this->keyValuePairs.end()) - { - return this->keyValuePairs[key]; - } - - return ""; - } - - void InfoString::parse(std::string buffer) - { - if (buffer[0] == '\\') - { - buffer = buffer.substr(1); - } - - std::vector KeyValues = Utils::String::Explode(buffer, '\\'); - - for (unsigned int i = 0; i < (KeyValues.size() - 1); i += 2) - { - this->keyValuePairs[KeyValues[i]] = KeyValues[i + 1]; - } - } - - std::string InfoString::build() - { - std::string infoString; - - bool first = true; - - for (auto i = this->keyValuePairs.begin(); i != this->keyValuePairs.end(); ++i) - { - if (first) first = false; - else infoString.append("\\"); - - infoString.append(i->first); // Key - infoString.append("\\"); - infoString.append(i->second); // Value - } - - return infoString; - } - - void InfoString::dump() - { - for (auto i = this->keyValuePairs.begin(); i != this->keyValuePairs.end(); ++i) - { - OutputDebugStringA(Utils::String::VA("%s: %s", i->first.data(), i->second.data())); - } - } - - json11::Json InfoString::to_json() - { - return this->keyValuePairs; - } -} +#include "STDInclude.hpp" + +namespace Utils +{ + void InfoString::set(std::string key, std::string value) + { + this->keyValuePairs[key] = value; + } + + std::string InfoString::get(std::string key) + { + if (this->keyValuePairs.find(key) != this->keyValuePairs.end()) + { + return this->keyValuePairs[key]; + } + + return ""; + } + + void InfoString::parse(std::string buffer) + { + if (buffer[0] == '\\') + { + buffer = buffer.substr(1); + } + + std::vector KeyValues = Utils::String::Explode(buffer, '\\'); + + for (unsigned int i = 0; i < (KeyValues.size() - 1); i += 2) + { + this->keyValuePairs[KeyValues[i]] = KeyValues[i + 1]; + } + } + + std::string InfoString::build() + { + std::string infoString; + + bool first = true; + + for (auto i = this->keyValuePairs.begin(); i != this->keyValuePairs.end(); ++i) + { + if (first) first = false; + else infoString.append("\\"); + + infoString.append(i->first); // Key + infoString.append("\\"); + infoString.append(i->second); // Value + } + + return infoString; + } + + void InfoString::dump() + { + for (auto i = this->keyValuePairs.begin(); i != this->keyValuePairs.end(); ++i) + { + OutputDebugStringA(Utils::String::VA("%s: %s", i->first.data(), i->second.data())); + } + } + + json11::Json InfoString::to_json() + { + return this->keyValuePairs; + } +} diff --git a/src/Utils/InfoString.hpp b/src/Utils/InfoString.hpp index d45616e1..df747d57 100644 --- a/src/Utils/InfoString.hpp +++ b/src/Utils/InfoString.hpp @@ -1,23 +1,23 @@ -namespace Utils -{ - class InfoString - { - public: - InfoString() {}; - InfoString(std::string buffer) : InfoString() { this->parse(buffer); }; - InfoString(const InfoString &obj) : keyValuePairs(obj.keyValuePairs) {}; - - void set(std::string key, std::string value); - std::string get(std::string key); - - std::string build(); - - void dump(); - - json11::Json to_json(); - - private: - std::map keyValuePairs; - void parse(std::string buffer); - }; -} +namespace Utils +{ + class InfoString + { + public: + InfoString() {}; + InfoString(std::string buffer) : InfoString() { this->parse(buffer); }; + InfoString(const InfoString &obj) : keyValuePairs(obj.keyValuePairs) {}; + + void set(std::string key, std::string value); + std::string get(std::string key); + + std::string build(); + + void dump(); + + json11::Json to_json(); + + private: + std::map keyValuePairs; + void parse(std::string buffer); + }; +} diff --git a/src/Utils/Library.cpp b/src/Utils/Library.cpp index 1929b42d..f45afed7 100644 --- a/src/Utils/Library.cpp +++ b/src/Utils/Library.cpp @@ -1,29 +1,29 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - Library::Library(std::string buffer, bool _freeOnDestroy) : freeOnDestroy(_freeOnDestroy), module(nullptr) - { - this->module = LoadLibraryExA(buffer.data(), NULL, 0); - } - - Library::~Library() - { - if (this->freeOnDestroy && this->valid()) - { - FreeLibrary(this->getModule()); - } - - this->module = nullptr; - } - - bool Library::valid() - { - return (this->getModule() != nullptr); - } - - HMODULE Library::getModule() - { - return this->module; - } -} +#include "STDInclude.hpp" + +namespace Utils +{ + Library::Library(std::string buffer, bool _freeOnDestroy) : freeOnDestroy(_freeOnDestroy), module(nullptr) + { + this->module = LoadLibraryExA(buffer.data(), NULL, 0); + } + + Library::~Library() + { + if (this->freeOnDestroy && this->valid()) + { + FreeLibrary(this->getModule()); + } + + this->module = nullptr; + } + + bool Library::valid() + { + return (this->getModule() != nullptr); + } + + HMODULE Library::getModule() + { + return this->module; + } +} diff --git a/src/Utils/Library.hpp b/src/Utils/Library.hpp index 8777398c..8bfeb451 100644 --- a/src/Utils/Library.hpp +++ b/src/Utils/Library.hpp @@ -1,28 +1,28 @@ -namespace Utils -{ - class Library - { - public: - Library() : module(nullptr), freeOnDestroy(false) {}; - Library(std::string buffer, bool freeOnDestroy = true); - ~Library(); - - bool valid(); - HMODULE getModule(); - - template - std::function get(std::string process) - { - if (!this->valid()) - { - throw new std::runtime_error("Library not loaded!"); - } - - return reinterpret_cast(GetProcAddress(this->getModule(), process.data())); - } - - private: - HMODULE module; - bool freeOnDestroy; - }; -} +namespace Utils +{ + class Library + { + public: + Library() : module(nullptr), freeOnDestroy(false) {}; + Library(std::string buffer, bool freeOnDestroy = true); + ~Library(); + + bool valid(); + HMODULE getModule(); + + template + std::function get(std::string process) + { + if (!this->valid()) + { + throw new std::runtime_error("Library not loaded!"); + } + + return reinterpret_cast(GetProcAddress(this->getModule(), process.data())); + } + + private: + HMODULE module; + bool freeOnDestroy; + }; +} diff --git a/src/Utils/Memory.cpp b/src/Utils/Memory.cpp index 7e56bd80..54192a9b 100644 --- a/src/Utils/Memory.cpp +++ b/src/Utils/Memory.cpp @@ -1,67 +1,67 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - void* Memory::AllocateAlign(size_t length, size_t alignment) - { - void* data = _aligned_malloc(length, alignment); - assert(data != nullptr); - return data; - } - - void* Memory::Allocate(size_t length) - { - void* data = calloc(length, 1); - assert(data != nullptr); - return data; - } - - char* Memory::DuplicateString(std::string string) - { - char* newString = Memory::AllocateArray(string.size() + 1); - std::memcpy(newString, string.data(), string.size()); - return newString; - } - - void Memory::Free(void* data) - { - if (data) - { - free(data); - } - } - - void Memory::Free(const void* data) - { - Memory::Free(const_cast(data)); - } - - void Memory::FreeAlign(void* data) - { - if (data) - { - _aligned_free(data); - } - } - - void Memory::FreeAlign(const void* data) - { - Memory::FreeAlign(const_cast(data)); - } - - // Complementary function for memset, which checks if memory is filled with a char - bool Memory::IsSet(void* mem, char chr, size_t length) - { - char* memArr = reinterpret_cast(mem); - - for (size_t i = 0; i < length; ++i) - { - if (memArr[i] != chr) - { - return false; - } - } - - return true; - } -} +#include "STDInclude.hpp" + +namespace Utils +{ + void* Memory::AllocateAlign(size_t length, size_t alignment) + { + void* data = _aligned_malloc(length, alignment); + assert(data != nullptr); + return data; + } + + void* Memory::Allocate(size_t length) + { + void* data = calloc(length, 1); + assert(data != nullptr); + return data; + } + + char* Memory::DuplicateString(std::string string) + { + char* newString = Memory::AllocateArray(string.size() + 1); + std::memcpy(newString, string.data(), string.size()); + return newString; + } + + void Memory::Free(void* data) + { + if (data) + { + free(data); + } + } + + void Memory::Free(const void* data) + { + Memory::Free(const_cast(data)); + } + + void Memory::FreeAlign(void* data) + { + if (data) + { + _aligned_free(data); + } + } + + void Memory::FreeAlign(const void* data) + { + Memory::FreeAlign(const_cast(data)); + } + + // Complementary function for memset, which checks if memory is filled with a char + bool Memory::IsSet(void* mem, char chr, size_t length) + { + char* memArr = reinterpret_cast(mem); + + for (size_t i = 0; i < length; ++i) + { + if (memArr[i] != chr) + { + return false; + } + } + + return true; + } +} diff --git a/src/Utils/Memory.hpp b/src/Utils/Memory.hpp index 9bfbd919..c2ac1ca2 100644 --- a/src/Utils/Memory.hpp +++ b/src/Utils/Memory.hpp @@ -1,132 +1,132 @@ -namespace Utils -{ - class Memory - { - public: - class Allocator - { - public: - typedef void(*FreeCallback)(void*); - - Allocator() - { - this->pool.clear(); - this->refMemory.clear(); - } - ~Allocator() - { - this->clear(); - } - - void clear() - { - std::lock_guard _(this->mutex); - - for (auto i = this->refMemory.begin(); i != this->refMemory.end(); ++i) - { - if (i->first && i->second) - { - i->second(i->first); - } - } - - this->refMemory.clear(); - - for (auto data : this->pool) - { - Memory::Free(data); - } - - this->pool.clear(); - } - - void free(void* data) - { - std::lock_guard _(this->mutex); - - auto i = this->refMemory.find(data); - if (i != this->refMemory.end()) - { - i->second(i->first); - this->refMemory.erase(i); - } - - auto j = std::find(this->pool.begin(), this->pool.end(), data); - if (j != this->pool.end()) - { - Memory::Free(data); - this->pool.erase(j); - } - } - - void free(const void* data) - { - this->free(const_cast(data)); - } - - void reference(void* memory, FreeCallback callback) - { - std::lock_guard _(this->mutex); - - this->refMemory[memory] = callback; - } - - void* allocate(size_t length) - { - std::lock_guard _(this->mutex); - - void* data = Memory::Allocate(length); - this->pool.push_back(data); - return data; - } - template inline T* allocate() - { - return this->allocateArray(1); - } - template inline T* allocateArray(size_t count = 1) - { - return static_cast(this->allocate(count * sizeof(T))); - } - - bool empty() - { - return (this->pool.empty() && this->refMemory.empty()); - } - - char* duplicateString(std::string string) - { - std::lock_guard _(this->mutex); - - char* data = Memory::DuplicateString(string); - this->pool.push_back(data); - return data; - } - - private: - std::vector pool; - std::map refMemory; - std::mutex mutex; - }; - - static void* AllocateAlign(size_t length, size_t alignment); - static void* Allocate(size_t length); - template static inline T* Allocate() - { - return AllocateArray(1); - } - template static inline T* AllocateArray(size_t count = 1) - { - return static_cast(Allocate(count * sizeof(T))); - } - - static char* DuplicateString(std::string string); - - static void Free(void* data); - static void Free(const void* data); - - static void FreeAlign(void* data); - static void FreeAlign(const void* data); - - static bool IsSet(void* mem, char chr, size_t length); - }; -} +namespace Utils +{ + class Memory + { + public: + class Allocator + { + public: + typedef void(*FreeCallback)(void*); + + Allocator() + { + this->pool.clear(); + this->refMemory.clear(); + } + ~Allocator() + { + this->clear(); + } + + void clear() + { + std::lock_guard _(this->mutex); + + for (auto i = this->refMemory.begin(); i != this->refMemory.end(); ++i) + { + if (i->first && i->second) + { + i->second(i->first); + } + } + + this->refMemory.clear(); + + for (auto data : this->pool) + { + Memory::Free(data); + } + + this->pool.clear(); + } + + void free(void* data) + { + std::lock_guard _(this->mutex); + + auto i = this->refMemory.find(data); + if (i != this->refMemory.end()) + { + i->second(i->first); + this->refMemory.erase(i); + } + + auto j = std::find(this->pool.begin(), this->pool.end(), data); + if (j != this->pool.end()) + { + Memory::Free(data); + this->pool.erase(j); + } + } + + void free(const void* data) + { + this->free(const_cast(data)); + } + + void reference(void* memory, FreeCallback callback) + { + std::lock_guard _(this->mutex); + + this->refMemory[memory] = callback; + } + + void* allocate(size_t length) + { + std::lock_guard _(this->mutex); + + void* data = Memory::Allocate(length); + this->pool.push_back(data); + return data; + } + template inline T* allocate() + { + return this->allocateArray(1); + } + template inline T* allocateArray(size_t count = 1) + { + return static_cast(this->allocate(count * sizeof(T))); + } + + bool empty() + { + return (this->pool.empty() && this->refMemory.empty()); + } + + char* duplicateString(std::string string) + { + std::lock_guard _(this->mutex); + + char* data = Memory::DuplicateString(string); + this->pool.push_back(data); + return data; + } + + private: + std::vector pool; + std::map refMemory; + std::mutex mutex; + }; + + static void* AllocateAlign(size_t length, size_t alignment); + static void* Allocate(size_t length); + template static inline T* Allocate() + { + return AllocateArray(1); + } + template static inline T* AllocateArray(size_t count = 1) + { + return static_cast(Allocate(count * sizeof(T))); + } + + static char* DuplicateString(std::string string); + + static void Free(void* data); + static void Free(const void* data); + + static void FreeAlign(void* data); + static void FreeAlign(const void* data); + + static bool IsSet(void* mem, char chr, size_t length); + }; +} diff --git a/src/Utils/Stream.cpp b/src/Utils/Stream.cpp index 0c2a0435..aa89970b 100644 --- a/src/Utils/Stream.cpp +++ b/src/Utils/Stream.cpp @@ -1,64 +1,64 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - std::string Stream::Reader::readString() - { - std::string str; - - while (char byte = this->readByte()) - { - str.append(&byte, 1); - } - - return str; - } - - const char* Stream::Reader::readCString() - { - return this->allocator->duplicateString(this->readString()); - } - - char Stream::Reader::readByte() - { - if ((this->position + 1) <= this->buffer.size()) - { - return this->buffer[this->position++]; - } - - return 0; - } - - void* Stream::Reader::read(size_t size, size_t count) - { - size_t bytes = size * count; - - if ((this->position + bytes) <= this->buffer.size()) - { - void* _buffer = this->allocator->allocate(bytes); - - std::memcpy(_buffer, this->buffer.data() + this->position, bytes); - this->position += bytes; - - return _buffer; - } - - return nullptr; - } - - bool Stream::Reader::end() - { - return (this->buffer.size() == this->position); - } - - void Stream::Reader::seek(unsigned int _position) - { - if (this->buffer.size() >= _position) - { - this->position = _position; - } - } - +#include "STDInclude.hpp" + +namespace Utils +{ + std::string Stream::Reader::readString() + { + std::string str; + + while (char byte = this->readByte()) + { + str.append(&byte, 1); + } + + return str; + } + + const char* Stream::Reader::readCString() + { + return this->allocator->duplicateString(this->readString()); + } + + char Stream::Reader::readByte() + { + if ((this->position + 1) <= this->buffer.size()) + { + return this->buffer[this->position++]; + } + + return 0; + } + + void* Stream::Reader::read(size_t size, size_t count) + { + size_t bytes = size * count; + + if ((this->position + bytes) <= this->buffer.size()) + { + void* _buffer = this->allocator->allocate(bytes); + + std::memcpy(_buffer, this->buffer.data() + this->position, bytes); + this->position += bytes; + + return _buffer; + } + + return nullptr; + } + + bool Stream::Reader::end() + { + return (this->buffer.size() == this->position); + } + + void Stream::Reader::seek(unsigned int _position) + { + if (this->buffer.size() >= _position) + { + this->position = _position; + } + } + void* Stream::Reader::readPointer() { void* pointer = this->read(); @@ -69,299 +69,299 @@ namespace Utils return pointer; } - void Stream::Reader::mapPointer(void* oldPointer, void* newPointer) - { - if (this->hasPointer(oldPointer)) - { - this->pointerMap[oldPointer] = newPointer; - } - } - + void Stream::Reader::mapPointer(void* oldPointer, void* newPointer) + { + if (this->hasPointer(oldPointer)) + { + this->pointerMap[oldPointer] = newPointer; + } + } + bool Stream::Reader::hasPointer(void* pointer) { return this->pointerMap.find(pointer) != this->pointerMap.end(); - } - - Stream::Stream() : criticalSectionState(0) - { - memset(this->blockSize, 0, sizeof(this->blockSize)); - -#ifdef WRITE_LOGS - this->structLevel = 0; - Utils::IO::WriteFile("userraw/logs/zb_writes.log", "", false); -#endif - } - - Stream::Stream(size_t size) : Stream() - { - this->buffer.reserve(size); - } - - Stream::~Stream() - { - this->buffer.clear(); - - if (this->criticalSectionState != 0) - { - MessageBoxA(0, Utils::String::VA("Invalid critical section state '%i' for stream destruction!", this->criticalSectionState), "WARNING", MB_ICONEXCLAMATION); - } - }; - - size_t Stream::length() - { - return this->buffer.length(); - } - - size_t Stream::capacity() - { - return this->buffer.capacity(); - } - - char* Stream::save(const void* _str, size_t size, size_t count) - { - return this->save(this->getCurrentBlock(), _str, size, count); - } - - char* Stream::save(Game::XFILE_BLOCK_TYPES stream, const void * _str, size_t size, size_t count) - { - // Only those seem to actually write data. - // everything else is allocated at runtime but XFILE_BLOCK_RUNTIME is the only one that actually allocates anything - // clearly half of this stuff is unused - if (stream == Game::XFILE_BLOCK_RUNTIME) - { - this->increaseBlockSize(stream, size * count); - return this->at(); - } - - auto data = this->data(); - - if (this->isCriticalSection() && this->length() + (size * count) > this->capacity()) - { - MessageBoxA(0, Utils::String::VA("Potential stream reallocation during critical operation detected! Writing data of the length 0x%X exceeds the allocated stream size of 0x%X\n", (size * count), this->capacity()), "ERROR", MB_ICONERROR); - __debugbreak(); - } - - this->buffer.append(static_cast(_str), size * count); - - if (this->data() != data && this->isCriticalSection()) - { - MessageBoxA(0, "Stream reallocation during critical operations not permitted!\nPlease increase the initial memory size or reallocate memory during non-critical sections!", "ERROR", MB_ICONERROR); - __debugbreak(); - } - - this->increaseBlockSize(stream, size * count); - - return this->at() - (size * count); - } - - char* Stream::save(Game::XFILE_BLOCK_TYPES stream, int value, size_t count) - { - auto ret = this->length(); - - for (size_t i = 0; i < count; ++i) - { - this->save(stream, &value, 4, 1); - } - - return this->data() + ret; - } - - char* Stream::saveString(std::string string) - { - return this->saveString(string.data()/*, string.size()*/); - } - - char* Stream::saveString(const char* string) - { - return this->saveString(string, strlen(string)); - } - - char* Stream::saveString(const char* string, size_t len) - { - auto ret = this->length(); - - if (string) - { - this->save(string, len); - } - - this->saveNull(); - - return this->data() + ret; - } - - char* Stream::saveText(std::string string) - { - return this->save(string.data(), string.length()); - } - - char* Stream::saveByte(unsigned char byte, size_t count) - { - auto ret = this->length(); - - for (size_t i = 0; i < count; ++i) - { - this->save(&byte, 1); - } - - return this->data() + ret; - } - - char* Stream::saveNull(size_t count) - { - return this->saveByte(0, count); - } - - char* Stream::saveMax(size_t count) - { - return this->saveByte(static_cast(-1), count); - } - - void Stream::align(Stream::Alignment align) - { - uint32_t size = 2 << align; - - // Not power of 2! - if (!size || (size & (size - 1))) return; - --size; - - Game::XFILE_BLOCK_TYPES stream = this->getCurrentBlock(); - this->blockSize[stream] = ~size & (this->getBlockSize(stream) + size); - } - - bool Stream::pushBlock(Game::XFILE_BLOCK_TYPES stream) - { - this->streamStack.push_back(stream); - return this->isValidBlock(stream); - } - - bool Stream::popBlock() - { - if (!this->streamStack.empty()) - { - this->streamStack.pop_back(); - return true; - } - - return false; - } - - bool Stream::isValidBlock(Game::XFILE_BLOCK_TYPES stream) - { - return (stream < Game::MAX_XFILE_COUNT && stream >= Game::XFILE_BLOCK_TEMP); - } - - void Stream::increaseBlockSize(Game::XFILE_BLOCK_TYPES stream, unsigned int size) - { - if (this->isValidBlock(stream)) - { - this->blockSize[stream] += size; - } - -#ifdef WRITE_LOGS - std::string data = fmt::sprintf("%*s%d\n", this->structLevel, "", size); - if(stream == Game::XFILE_BLOCK_RUNTIME) data = fmt::sprintf("%*s(%d)\n", this->structLevel, "", size); - Utils::IO::WriteFile("userraw/logs/zb_writes.log", data, true); -#endif - } - - void Stream::increaseBlockSize(unsigned int size) - { - return this->increaseBlockSize(this->getCurrentBlock(), size); - } - - Game::XFILE_BLOCK_TYPES Stream::getCurrentBlock() - { - if (!this->streamStack.empty()) - { - return this->streamStack.back(); - } - - return Game::XFILE_BLOCK_INVALID; - } - - char* Stream::at() - { - return reinterpret_cast(this->data() + this->length()); - } - - char* Stream::data() - { - return const_cast(this->buffer.data()); - } - - unsigned int Stream::getBlockSize(Game::XFILE_BLOCK_TYPES stream) - { - if (this->isValidBlock(stream)) - { - return this->blockSize[stream]; - } - - return 0; - } - - DWORD Stream::getPackedOffset() - { - Game::XFILE_BLOCK_TYPES block = this->getCurrentBlock(); - - Stream::Offset offset; - offset.block = block; - offset.offset = this->getBlockSize(block); - return offset.getPackedOffset(); - } - - void Stream::toBuffer(std::string& outBuffer) - { - outBuffer.clear(); - outBuffer.append(this->data(), this->length()); - } - - std::string Stream::toBuffer() - { - std::string _buffer; - this->toBuffer(_buffer); - return _buffer; - } - - void Stream::enterCriticalSection() - { - ++this->criticalSectionState; - } - - void Stream::leaveCriticalSection() - { - --this->criticalSectionState; - } - - bool Stream::isCriticalSection() - { - if (this->criticalSectionState < 0) - { - MessageBoxA(0, "CriticalSectionState in stream has been overrun!", "ERROR", MB_ICONERROR); - __debugbreak(); - } - - return (this->criticalSectionState != 0); - } - -#ifdef WRITE_LOGS - void Stream::enterStruct(const char* structName) - { - if (this->structLevel >= 0) - { - Utils::IO::WriteFile("userraw/logs/zb_writes.log", fmt::sprintf("%*s%s\n", this->structLevel++, "", structName), true); - } - } - - void Stream::leaveStruct() - { - if (--this->structLevel < 0) - { - Components::Logger::Print("Stream::leaveStruct underflow! All following writes will not be logged!\n"); - return; - } - - Utils::IO::WriteFile("userraw/logs/zb_writes.log", fmt::sprintf("%*s-----\n", this->structLevel, ""), true); - } -#endif -} + } + + Stream::Stream() : criticalSectionState(0) + { + memset(this->blockSize, 0, sizeof(this->blockSize)); + +#ifdef WRITE_LOGS + this->structLevel = 0; + Utils::IO::WriteFile("userraw/logs/zb_writes.log", "", false); +#endif + } + + Stream::Stream(size_t size) : Stream() + { + this->buffer.reserve(size); + } + + Stream::~Stream() + { + this->buffer.clear(); + + if (this->criticalSectionState != 0) + { + MessageBoxA(0, Utils::String::VA("Invalid critical section state '%i' for stream destruction!", this->criticalSectionState), "WARNING", MB_ICONEXCLAMATION); + } + }; + + size_t Stream::length() + { + return this->buffer.length(); + } + + size_t Stream::capacity() + { + return this->buffer.capacity(); + } + + char* Stream::save(const void* _str, size_t size, size_t count) + { + return this->save(this->getCurrentBlock(), _str, size, count); + } + + char* Stream::save(Game::XFILE_BLOCK_TYPES stream, const void * _str, size_t size, size_t count) + { + // Only those seem to actually write data. + // everything else is allocated at runtime but XFILE_BLOCK_RUNTIME is the only one that actually allocates anything + // clearly half of this stuff is unused + if (stream == Game::XFILE_BLOCK_RUNTIME) + { + this->increaseBlockSize(stream, size * count); + return this->at(); + } + + auto data = this->data(); + + if (this->isCriticalSection() && this->length() + (size * count) > this->capacity()) + { + MessageBoxA(0, Utils::String::VA("Potential stream reallocation during critical operation detected! Writing data of the length 0x%X exceeds the allocated stream size of 0x%X\n", (size * count), this->capacity()), "ERROR", MB_ICONERROR); + __debugbreak(); + } + + this->buffer.append(static_cast(_str), size * count); + + if (this->data() != data && this->isCriticalSection()) + { + MessageBoxA(0, "Stream reallocation during critical operations not permitted!\nPlease increase the initial memory size or reallocate memory during non-critical sections!", "ERROR", MB_ICONERROR); + __debugbreak(); + } + + this->increaseBlockSize(stream, size * count); + + return this->at() - (size * count); + } + + char* Stream::save(Game::XFILE_BLOCK_TYPES stream, int value, size_t count) + { + auto ret = this->length(); + + for (size_t i = 0; i < count; ++i) + { + this->save(stream, &value, 4, 1); + } + + return this->data() + ret; + } + + char* Stream::saveString(std::string string) + { + return this->saveString(string.data()/*, string.size()*/); + } + + char* Stream::saveString(const char* string) + { + return this->saveString(string, strlen(string)); + } + + char* Stream::saveString(const char* string, size_t len) + { + auto ret = this->length(); + + if (string) + { + this->save(string, len); + } + + this->saveNull(); + + return this->data() + ret; + } + + char* Stream::saveText(std::string string) + { + return this->save(string.data(), string.length()); + } + + char* Stream::saveByte(unsigned char byte, size_t count) + { + auto ret = this->length(); + + for (size_t i = 0; i < count; ++i) + { + this->save(&byte, 1); + } + + return this->data() + ret; + } + + char* Stream::saveNull(size_t count) + { + return this->saveByte(0, count); + } + + char* Stream::saveMax(size_t count) + { + return this->saveByte(static_cast(-1), count); + } + + void Stream::align(Stream::Alignment align) + { + uint32_t size = 2 << align; + + // Not power of 2! + if (!size || (size & (size - 1))) return; + --size; + + Game::XFILE_BLOCK_TYPES stream = this->getCurrentBlock(); + this->blockSize[stream] = ~size & (this->getBlockSize(stream) + size); + } + + bool Stream::pushBlock(Game::XFILE_BLOCK_TYPES stream) + { + this->streamStack.push_back(stream); + return this->isValidBlock(stream); + } + + bool Stream::popBlock() + { + if (!this->streamStack.empty()) + { + this->streamStack.pop_back(); + return true; + } + + return false; + } + + bool Stream::isValidBlock(Game::XFILE_BLOCK_TYPES stream) + { + return (stream < Game::MAX_XFILE_COUNT && stream >= Game::XFILE_BLOCK_TEMP); + } + + void Stream::increaseBlockSize(Game::XFILE_BLOCK_TYPES stream, unsigned int size) + { + if (this->isValidBlock(stream)) + { + this->blockSize[stream] += size; + } + +#ifdef WRITE_LOGS + std::string data = fmt::sprintf("%*s%d\n", this->structLevel, "", size); + if(stream == Game::XFILE_BLOCK_RUNTIME) data = fmt::sprintf("%*s(%d)\n", this->structLevel, "", size); + Utils::IO::WriteFile("userraw/logs/zb_writes.log", data, true); +#endif + } + + void Stream::increaseBlockSize(unsigned int size) + { + return this->increaseBlockSize(this->getCurrentBlock(), size); + } + + Game::XFILE_BLOCK_TYPES Stream::getCurrentBlock() + { + if (!this->streamStack.empty()) + { + return this->streamStack.back(); + } + + return Game::XFILE_BLOCK_INVALID; + } + + char* Stream::at() + { + return reinterpret_cast(this->data() + this->length()); + } + + char* Stream::data() + { + return const_cast(this->buffer.data()); + } + + unsigned int Stream::getBlockSize(Game::XFILE_BLOCK_TYPES stream) + { + if (this->isValidBlock(stream)) + { + return this->blockSize[stream]; + } + + return 0; + } + + DWORD Stream::getPackedOffset() + { + Game::XFILE_BLOCK_TYPES block = this->getCurrentBlock(); + + Stream::Offset offset; + offset.block = block; + offset.offset = this->getBlockSize(block); + return offset.getPackedOffset(); + } + + void Stream::toBuffer(std::string& outBuffer) + { + outBuffer.clear(); + outBuffer.append(this->data(), this->length()); + } + + std::string Stream::toBuffer() + { + std::string _buffer; + this->toBuffer(_buffer); + return _buffer; + } + + void Stream::enterCriticalSection() + { + ++this->criticalSectionState; + } + + void Stream::leaveCriticalSection() + { + --this->criticalSectionState; + } + + bool Stream::isCriticalSection() + { + if (this->criticalSectionState < 0) + { + MessageBoxA(0, "CriticalSectionState in stream has been overrun!", "ERROR", MB_ICONERROR); + __debugbreak(); + } + + return (this->criticalSectionState != 0); + } + +#ifdef WRITE_LOGS + void Stream::enterStruct(const char* structName) + { + if (this->structLevel >= 0) + { + Utils::IO::WriteFile("userraw/logs/zb_writes.log", fmt::sprintf("%*s%s\n", this->structLevel++, "", structName), true); + } + } + + void Stream::leaveStruct() + { + if (--this->structLevel < 0) + { + Components::Logger::Print("Stream::leaveStruct underflow! All following writes will not be logged!\n"); + return; + } + + Utils::IO::WriteFile("userraw/logs/zb_writes.log", fmt::sprintf("%*s-----\n", this->structLevel, ""), true); + } +#endif +} diff --git a/src/Utils/Stream.hpp b/src/Utils/Stream.hpp index 5a508c52..a4ba8846 100644 --- a/src/Utils/Stream.hpp +++ b/src/Utils/Stream.hpp @@ -1,11 +1,11 @@ -// write logs for ZoneBuilder -//#define WRITE_LOGS -#ifndef WRITE_LOGS // they take forever to run so only enable if needed -#define SaveLogEnter(x) -#define SaveLogExit() -#else -#define SaveLogEnter(x) builder->getBuffer()->enterStruct(x) -#define SaveLogExit() builder->getBuffer()->leaveStruct() +// write logs for ZoneBuilder +//#define WRITE_LOGS +#ifndef WRITE_LOGS // they take forever to run so only enable if needed +#define SaveLogEnter(x) +#define SaveLogExit() +#else +#define SaveLogEnter(x) builder->getBuffer()->enterStruct(x) +#define SaveLogExit() builder->getBuffer()->leaveStruct() #endif namespace Utils diff --git a/src/Utils/String.cpp b/src/Utils/String.cpp index a6269586..b65ba53c 100644 --- a/src/Utils/String.cpp +++ b/src/Utils/String.cpp @@ -1,15 +1,15 @@ -#include "STDInclude.hpp" -#ifndef DISABLE_BASE128 -#include "base128.h" -#endif - +#include "STDInclude.hpp" +#ifndef DISABLE_BASE128 +#include "base128.h" +#endif + #define VA_BUFFER_COUNT 32 -#define VA_BUFFER_SIZE 65536 - -namespace Utils -{ - namespace String - { +#define VA_BUFFER_SIZE 65536 + +namespace Utils +{ + namespace String + { const char *VA(const char *fmt, ...) { static char g_vaBuffer[VA_BUFFER_COUNT][VA_BUFFER_SIZE]; @@ -22,199 +22,199 @@ namespace Utils g_vaNextBufferIndex = (g_vaNextBufferIndex + 1) % VA_BUFFER_COUNT; va_end(ap); return dest; - } - - std::string ToLower(std::string input) - { - std::transform(input.begin(), input.end(), input.begin(), ::tolower); - return input; - } - - std::string ToUpper(std::string input) - { - std::transform(input.begin(), input.end(), input.begin(), ::toupper); - return input; - } - - bool EndsWith(std::string haystack, std::string needle) - { - return (strstr(haystack.data(), needle.data()) == (haystack.data() + haystack.size() - needle.size())); - } - - std::string DumpHex(std::string data, std::string separator) - { - std::string result; - - for (unsigned int i = 0; i < data.size(); ++i) - { - if (i > 0) - { - result.append(separator); - } - - result.append(Utils::String::VA("%02X", data[i] & 0xFF)); - } - - return result; - } - - std::string XOR(std::string str, char value) - { - for (unsigned int i = 0; i < str.size(); ++i) - { - str[i] ^= value; - } - - return str; - } - - std::vector Explode(const std::string& str, char delim) - { - std::vector result; - std::istringstream iss(str); - - for (std::string token; std::getline(iss, token, delim);) - { - std::string _entry = std::move(token); - - // Remove trailing 0x0 bytes - while (_entry.size() && !_entry.back()) - { - _entry = _entry.substr(0, _entry.size() - 1); - } - - result.push_back(_entry); - } - - return result; - } - - void Replace(std::string &string, std::string find, std::string replace) - { - size_t nPos = 0; - - while ((nPos = string.find(find, nPos)) != std::string::npos) - { - string = string.replace(nPos, find.length(), replace); - nPos += replace.length(); - } - } - - bool StartsWith(std::string haystack, std::string needle) - { - return (haystack.size() >= needle.size() && !strncmp(needle.data(), haystack.data(), needle.size())); - } - - int IsSpace(int c) - { - if (c < -1) return 0; - return _isspace_l(c, nullptr); - } - - // trim from start - std::string <rim(std::string &s) - { - s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(IsSpace)))); - return s; - } - - // trim from end - std::string &RTrim(std::string &s) - { - s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(IsSpace))).base(), s.end()); - return s; - } - - // trim from both ends - std::string &Trim(std::string &s) - { - return LTrim(RTrim(s)); - } - - std::string FormatTimeSpan(int milliseconds) - { - int secondsTotal = milliseconds / 1000; - int seconds = secondsTotal % 60; - int minutesTotal = secondsTotal / 60; - int minutes = minutesTotal % 60; - int hoursTotal = minutesTotal / 60; - - return Utils::String::VA("%02d:%02d:%02d", hoursTotal, minutes, seconds); - } - - std::string FormatBandwidth(size_t bytes, int milliseconds) - { - static char* sizes[] = - { - "B", - "KB", - "MB", - "GB", - "TB" - }; - - double bytesPerSecond = (1000.0 / milliseconds) * bytes; - - int i = 0; - for (i = 0; bytesPerSecond > 1000 && i < ARRAY_SIZE(sizes); ++i) // 1024 or 1000? - { - bytesPerSecond /= 1000; - } - - return Utils::String::VA("%.2f %s/s", static_cast(bytesPerSecond), sizes[i]); - } - - // Encodes a given string in Base64 - std::string EncodeBase64(const char* input, const unsigned long inputSize) - { - unsigned long outlen = long(inputSize + (inputSize / 3.0) + 16); - unsigned char* outbuf = new unsigned char[outlen]; //Reserve output memory - base64_encode(reinterpret_cast(const_cast(input)), inputSize, outbuf, &outlen); - std::string ret((char*)outbuf, outlen); - delete[] outbuf; - return ret; - } - - // Encodes a given string in Base64 - std::string EncodeBase64(const std::string& input) - { - return EncodeBase64(input.data(), input.size()); - } - -#ifndef DISABLE_BASE128 - // Encodes a given string in Base128 - std::string EncodeBase128(const std::string& input) - { - base128 encoder; - - void* inbuffer = const_cast(input.data()); - char* buffer = encoder.encode(inbuffer, input.size()); - /* - Interesting to see that the buffer returned by the encoder is not a standalone string copy - but instead is a pointer to the internal "encoded" field of the encoder. So if you deinitialize - the encoder that string will probably become garbage. - */ - std::string retval(buffer); - return retval; - } -#endif - - // Generates a UUID and returns the string representation of it - std::string GenerateUUIDString() - { - // Generate UUID data - UUID uuid; - if (!UuidCreate(&uuid)) - { - // Convert to string representation - char* strdata = nullptr; - if (!UuidToStringA(&uuid, reinterpret_cast(&strdata))) - { - return std::string(strdata); - } - } - - return ""; - } - } -} + } + + std::string ToLower(std::string input) + { + std::transform(input.begin(), input.end(), input.begin(), ::tolower); + return input; + } + + std::string ToUpper(std::string input) + { + std::transform(input.begin(), input.end(), input.begin(), ::toupper); + return input; + } + + bool EndsWith(std::string haystack, std::string needle) + { + return (strstr(haystack.data(), needle.data()) == (haystack.data() + haystack.size() - needle.size())); + } + + std::string DumpHex(std::string data, std::string separator) + { + std::string result; + + for (unsigned int i = 0; i < data.size(); ++i) + { + if (i > 0) + { + result.append(separator); + } + + result.append(Utils::String::VA("%02X", data[i] & 0xFF)); + } + + return result; + } + + std::string XOR(std::string str, char value) + { + for (unsigned int i = 0; i < str.size(); ++i) + { + str[i] ^= value; + } + + return str; + } + + std::vector Explode(const std::string& str, char delim) + { + std::vector result; + std::istringstream iss(str); + + for (std::string token; std::getline(iss, token, delim);) + { + std::string _entry = std::move(token); + + // Remove trailing 0x0 bytes + while (_entry.size() && !_entry.back()) + { + _entry = _entry.substr(0, _entry.size() - 1); + } + + result.push_back(_entry); + } + + return result; + } + + void Replace(std::string &string, std::string find, std::string replace) + { + size_t nPos = 0; + + while ((nPos = string.find(find, nPos)) != std::string::npos) + { + string = string.replace(nPos, find.length(), replace); + nPos += replace.length(); + } + } + + bool StartsWith(std::string haystack, std::string needle) + { + return (haystack.size() >= needle.size() && !strncmp(needle.data(), haystack.data(), needle.size())); + } + + int IsSpace(int c) + { + if (c < -1) return 0; + return _isspace_l(c, nullptr); + } + + // trim from start + std::string <rim(std::string &s) + { + s.erase(s.begin(), std::find_if(s.begin(), s.end(), std::not1(std::ptr_fun(IsSpace)))); + return s; + } + + // trim from end + std::string &RTrim(std::string &s) + { + s.erase(std::find_if(s.rbegin(), s.rend(), std::not1(std::ptr_fun(IsSpace))).base(), s.end()); + return s; + } + + // trim from both ends + std::string &Trim(std::string &s) + { + return LTrim(RTrim(s)); + } + + std::string FormatTimeSpan(int milliseconds) + { + int secondsTotal = milliseconds / 1000; + int seconds = secondsTotal % 60; + int minutesTotal = secondsTotal / 60; + int minutes = minutesTotal % 60; + int hoursTotal = minutesTotal / 60; + + return Utils::String::VA("%02d:%02d:%02d", hoursTotal, minutes, seconds); + } + + std::string FormatBandwidth(size_t bytes, int milliseconds) + { + static char* sizes[] = + { + "B", + "KB", + "MB", + "GB", + "TB" + }; + + double bytesPerSecond = (1000.0 / milliseconds) * bytes; + + int i = 0; + for (i = 0; bytesPerSecond > 1000 && i < ARRAY_SIZE(sizes); ++i) // 1024 or 1000? + { + bytesPerSecond /= 1000; + } + + return Utils::String::VA("%.2f %s/s", static_cast(bytesPerSecond), sizes[i]); + } + + // Encodes a given string in Base64 + std::string EncodeBase64(const char* input, const unsigned long inputSize) + { + unsigned long outlen = long(inputSize + (inputSize / 3.0) + 16); + unsigned char* outbuf = new unsigned char[outlen]; //Reserve output memory + base64_encode(reinterpret_cast(const_cast(input)), inputSize, outbuf, &outlen); + std::string ret((char*)outbuf, outlen); + delete[] outbuf; + return ret; + } + + // Encodes a given string in Base64 + std::string EncodeBase64(const std::string& input) + { + return EncodeBase64(input.data(), input.size()); + } + +#ifndef DISABLE_BASE128 + // Encodes a given string in Base128 + std::string EncodeBase128(const std::string& input) + { + base128 encoder; + + void* inbuffer = const_cast(input.data()); + char* buffer = encoder.encode(inbuffer, input.size()); + /* + Interesting to see that the buffer returned by the encoder is not a standalone string copy + but instead is a pointer to the internal "encoded" field of the encoder. So if you deinitialize + the encoder that string will probably become garbage. + */ + std::string retval(buffer); + return retval; + } +#endif + + // Generates a UUID and returns the string representation of it + std::string GenerateUUIDString() + { + // Generate UUID data + UUID uuid; + if (!UuidCreate(&uuid)) + { + // Convert to string representation + char* strdata = nullptr; + if (!UuidToStringA(&uuid, reinterpret_cast(&strdata))) + { + return std::string(strdata); + } + } + + return ""; + } + } +} diff --git a/src/Utils/String.hpp b/src/Utils/String.hpp index 4ee8b7b8..fe5aabee 100644 --- a/src/Utils/String.hpp +++ b/src/Utils/String.hpp @@ -1,32 +1,32 @@ -namespace Utils -{ - namespace String - { - const char *VA(const char *fmt, ...); - - int IsSpace(int c); - std::string ToLower(std::string input); - std::string ToUpper(std::string input); - bool EndsWith(std::string haystack, std::string needle); - std::vector Explode(const std::string& str, char delim); - void Replace(std::string &string, std::string find, std::string replace); - bool StartsWith(std::string haystack, std::string needle); - std::string <rim(std::string &s); - std::string &RTrim(std::string &s); - std::string &Trim(std::string &s); - - std::string FormatTimeSpan(int milliseconds); - std::string FormatBandwidth(size_t bytes, int milliseconds); - - std::string DumpHex(std::string data, std::string separator = " "); - - std::string XOR(std::string str, char value); - - std::string EncodeBase64(const char* input, const unsigned long inputSize); - std::string EncodeBase64(const std::string& input); - - std::string EncodeBase128(const std::string& input); - - std::string GenerateUUIDString(); - } -} +namespace Utils +{ + namespace String + { + const char *VA(const char *fmt, ...); + + int IsSpace(int c); + std::string ToLower(std::string input); + std::string ToUpper(std::string input); + bool EndsWith(std::string haystack, std::string needle); + std::vector Explode(const std::string& str, char delim); + void Replace(std::string &string, std::string find, std::string replace); + bool StartsWith(std::string haystack, std::string needle); + std::string <rim(std::string &s); + std::string &RTrim(std::string &s); + std::string &Trim(std::string &s); + + std::string FormatTimeSpan(int milliseconds); + std::string FormatBandwidth(size_t bytes, int milliseconds); + + std::string DumpHex(std::string data, std::string separator = " "); + + std::string XOR(std::string str, char value); + + std::string EncodeBase64(const char* input, const unsigned long inputSize); + std::string EncodeBase64(const std::string& input); + + std::string EncodeBase128(const std::string& input); + + std::string GenerateUUIDString(); + } +} diff --git a/src/Utils/Time.cpp b/src/Utils/Time.cpp index af2235ca..5c62d45d 100644 --- a/src/Utils/Time.cpp +++ b/src/Utils/Time.cpp @@ -1,17 +1,17 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - namespace Time - { - void Interval::update() - { - this->lastPoint = std::chrono::high_resolution_clock::now(); - } - - bool Interval::elapsed(std::chrono::nanoseconds nsecs) - { - return ((std::chrono::high_resolution_clock::now() - this->lastPoint) >= nsecs); - } - } -} +#include "STDInclude.hpp" + +namespace Utils +{ + namespace Time + { + void Interval::update() + { + this->lastPoint = std::chrono::high_resolution_clock::now(); + } + + bool Interval::elapsed(std::chrono::nanoseconds nsecs) + { + return ((std::chrono::high_resolution_clock::now() - this->lastPoint) >= nsecs); + } + } +} diff --git a/src/Utils/Time.hpp b/src/Utils/Time.hpp index c18b37b4..3d366350 100644 --- a/src/Utils/Time.hpp +++ b/src/Utils/Time.hpp @@ -1,17 +1,17 @@ -namespace Utils -{ - namespace Time - { - class Interval - { - private: - std::chrono::high_resolution_clock::time_point lastPoint; - - public: - Interval() : lastPoint(std::chrono::high_resolution_clock::now()) {} - - void update(); - bool elapsed(std::chrono::nanoseconds nsecs); - }; - } -} +namespace Utils +{ + namespace Time + { + class Interval + { + private: + std::chrono::high_resolution_clock::time_point lastPoint; + + public: + Interval() : lastPoint(std::chrono::high_resolution_clock::now()) {} + + void update(); + bool elapsed(std::chrono::nanoseconds nsecs); + }; + } +} diff --git a/src/Utils/Utils.cpp b/src/Utils/Utils.cpp index a0262043..5bfe7f9f 100644 --- a/src/Utils/Utils.cpp +++ b/src/Utils/Utils.cpp @@ -1,45 +1,45 @@ -#include "STDInclude.hpp" - -namespace Utils -{ - std::string GetMimeType(std::string url) - { - wchar_t* mimeType = nullptr; - FindMimeFromData(NULL, std::wstring(url.begin(), url.end()).data(), NULL, 0, NULL, 0, &mimeType, 0); - - if (mimeType) - { - std::wstring wMimeType(mimeType); - return std::string(wMimeType.begin(), wMimeType.end()); - } - - return "application/octet-stream"; - } - - std::string ParseChallenge(std::string data) - { - auto pos = data.find_first_of("\n "); - if (pos == std::string::npos) return data; - return data.substr(0, pos).data(); - } - - void OutputDebugLastError() - { - DWORD errorMessageID = ::GetLastError(); - LPSTR messageBuffer = nullptr; - size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, - NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); - std::string message(messageBuffer, size); - - OutputDebugStringA(Utils::String::VA("Last error code: 0x%08X (%s)\n", errorMessageID, message)); - - LocalFree(messageBuffer); - } - - bool IsWineEnvironment() - { - HMODULE hntdll = GetModuleHandleA("ntdll.dll"); - if (!hntdll) return false; - return (GetProcAddress(hntdll, "wine_get_version") != nullptr); - } -} +#include "STDInclude.hpp" + +namespace Utils +{ + std::string GetMimeType(std::string url) + { + wchar_t* mimeType = nullptr; + FindMimeFromData(NULL, std::wstring(url.begin(), url.end()).data(), NULL, 0, NULL, 0, &mimeType, 0); + + if (mimeType) + { + std::wstring wMimeType(mimeType); + return std::string(wMimeType.begin(), wMimeType.end()); + } + + return "application/octet-stream"; + } + + std::string ParseChallenge(std::string data) + { + auto pos = data.find_first_of("\n "); + if (pos == std::string::npos) return data; + return data.substr(0, pos).data(); + } + + void OutputDebugLastError() + { + DWORD errorMessageID = ::GetLastError(); + LPSTR messageBuffer = nullptr; + size_t size = FormatMessageA(FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + NULL, errorMessageID, MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), (LPSTR)&messageBuffer, 0, NULL); + std::string message(messageBuffer, size); + + OutputDebugStringA(Utils::String::VA("Last error code: 0x%08X (%s)\n", errorMessageID, message)); + + LocalFree(messageBuffer); + } + + bool IsWineEnvironment() + { + HMODULE hntdll = GetModuleHandleA("ntdll.dll"); + if (!hntdll) return false; + return (GetProcAddress(hntdll, "wine_get_version") != nullptr); + } +} diff --git a/src/Utils/Utils.hpp b/src/Utils/Utils.hpp index c2dde954..c5e7e667 100644 --- a/src/Utils/Utils.hpp +++ b/src/Utils/Utils.hpp @@ -1,83 +1,83 @@ -namespace Utils -{ - std::string GetMimeType(std::string url); - std::string ParseChallenge(std::string data); - void OutputDebugLastError(); - - bool IsWineEnvironment(); - - template void Merge(std::vector* target, T* source, size_t length) - { - if (source) - { - for (size_t i = 0; i < length; ++i) - { - target->push_back(source[i]); - } - } - } - - template void Merge(std::vector* target, std::vector source) - { - for (auto &entry : source) - { - target->push_back(entry); - } - } - - template using Slot = std::function; - template - class Signal - { - public: - void connect(Slot slot) - { - slots.push_back(slot); - } - - void clear() - { - slots.clear(); - } - - template - void operator()(Args&&... args) const - { - for (auto& slot : slots) - { - slot(std::forward(args)...); - } - } - - private: - std::vector> slots; - }; - - template - class Value - { - public: - Value() : hasValue(false) {} - Value(T _value) { this->set(_value); } - - void set(T _value) - { - this->value = _value; - this->hasValue = true; - } - - bool isValid() - { - return this->hasValue; - } - - T get() - { - return this->value; - } - - private: - bool hasValue; - T value; - }; -} +namespace Utils +{ + std::string GetMimeType(std::string url); + std::string ParseChallenge(std::string data); + void OutputDebugLastError(); + + bool IsWineEnvironment(); + + template void Merge(std::vector* target, T* source, size_t length) + { + if (source) + { + for (size_t i = 0; i < length; ++i) + { + target->push_back(source[i]); + } + } + } + + template void Merge(std::vector* target, std::vector source) + { + for (auto &entry : source) + { + target->push_back(entry); + } + } + + template using Slot = std::function; + template + class Signal + { + public: + void connect(Slot slot) + { + slots.push_back(slot); + } + + void clear() + { + slots.clear(); + } + + template + void operator()(Args&&... args) const + { + for (auto& slot : slots) + { + slot(std::forward(args)...); + } + } + + private: + std::vector> slots; + }; + + template + class Value + { + public: + Value() : hasValue(false) {} + Value(T _value) { this->set(_value); } + + void set(T _value) + { + this->value = _value; + this->hasValue = true; + } + + bool isValid() + { + return this->hasValue; + } + + T get() + { + return this->value; + } + + private: + bool hasValue; + T value; + }; +} diff --git a/src/Utils/WebIO.cpp b/src/Utils/WebIO.cpp index f541104c..8ab9696f 100644 --- a/src/Utils/WebIO.cpp +++ b/src/Utils/WebIO.cpp @@ -1,542 +1,542 @@ -#include "STDInclude.hpp" -#include - -namespace Utils -{ - WebIO::WebIO() : WebIO("WebIO") {} - - WebIO::WebIO(std::string useragent, std::string url) : WebIO(useragent) - { - this->setURL(url); - } - - WebIO::WebIO(std::string useragent) : timeout(5000) // 5 seconds timeout by default - { - this->openSession(useragent); - } - - WebIO::~WebIO() - { - this->username.clear(); - this->password.clear(); - - this->closeConnection(); - this->closeSession(); - } - - void WebIO::openSession(std::string useragent) - { - this->hSession = InternetOpenA(useragent.data(), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); - } - - void WebIO::closeSession() - { - InternetCloseHandle(this->hSession); - } - - void WebIO::SetCredentials(std::string _username, std::string _password) - { - this->username.clear(); - this->password.clear(); - - this->username.append(_username.begin(), _username.end()); - this->password.append(_password.begin(), _password.end()); - } - - void WebIO::setURL(std::string _url) - { - this->url.server.clear(); - this->url.protocol.clear(); - this->url.document.clear(); - - // Insert protocol if none - if (_url.find("://") == std::string::npos) - { - _url = "http://" + _url; - } - - PARSEDURLA pURL; - pURL.cbSize = sizeof(pURL); - ParseURLA(_url.data(), &pURL); - - // Parse protocol - if (pURL.cchProtocol && pURL.cchProtocol != 0xCCCCCCCC && pURL.pszProtocol) - { - for (UINT i = 0; i < pURL.cchProtocol; ++i) - { - char lChar = static_cast(tolower(pURL.pszProtocol[i])); - this->url.protocol.append(&lChar, 1); - } - } - else - { - this->url.protocol.append("http"); - } - - // Parse suffix - std::string server; - - if (pURL.cchSuffix && pURL.cchSuffix != 0xCCCCCCCC && pURL.pszSuffix) - { - server.append(pURL.pszSuffix, pURL.cchSuffix); - } - else - { - // TODO: Add some error handling here - return; - } - - // Remove '//' from the url - if (!server.find("//")) - { - server = server.substr(2); - } - - size_t pos = server.find("/"); - if (pos == std::string::npos) - { - this->url.server = server; - this->url.document = "/"; - } - else - { - this->url.server = server.substr(0, pos); - this->url.document = server.substr(pos); - } - - this->url.port.clear(); - - pos = this->url.server.find(":"); - if (pos != std::string::npos) - { - this->url.port = this->url.server.substr(pos + 1); - this->url.server = this->url.server.substr(0, pos); - } - - this->url.raw.clear(); - this->url.raw.append(this->url.protocol); - this->url.raw.append("://"); - this->url.raw.append(this->url.server); - - if (!this->url.port.empty()) - { - this->url.raw.append(":"); - this->url.raw.append(this->url.port); - } - - this->url.raw.append(this->url.document); - - this->isFTP = (this->url.protocol == "ftp"); - } - - std::string WebIO::buildPostBody(WebIO::Params params) - { - std::string body; - - for (auto param = params.begin(); param != params.end(); ++param) - { - std::string key = param->first; - std::string value = param->second; - - if (!body.empty()) body.append("&"); - - body.append(key); - body.append("="); - body.append(value); - } - - body.append("\0"); - - return body; - } - - std::string WebIO::postFile(std::string _url, std::string data, std::string fieldName, std::string fileName) - { - this->setURL(_url); - return this->postFile(data, fieldName, fileName); - } - - std::string WebIO::postFile(std::string data, std::string fieldName, std::string fileName) - { - WebIO::Params headers; - - std::string boundary = "----WebKitFormBoundaryHoLVocRsBxs71fU6"; - headers["Content-Type"] = "multipart/form-data, boundary=" + boundary; - - Utils::String::Replace(fieldName, "\"", "\\\""); - Utils::String::Replace(fieldName, "\\", "\\\\"); - Utils::String::Replace(fileName, "\"", "\\\""); - Utils::String::Replace(fileName, "\\", "\\\\"); - - std::string body = "--" + boundary + "\r\n"; - body += "Content-Disposition: form-data; name=\""; - body += fieldName; - body += "\"; filename=\""; - body += fileName; - body += "\"\r\n"; - body += "Content-Type: application/octet-stream\r\n\r\n"; - body += data + "\r\n"; - body += "--" + boundary + "--\r\n"; - - headers["Content-Length"] = Utils::String::VA("%u", body.size()); - - return this->execute("POST", body, headers); - } - - std::string WebIO::post(std::string _url, std::string body) - { - this->setURL(_url); - return this->post(body); - } - - std::string WebIO::post(std::string _url, WebIO::Params params) - { - this->setURL(_url); - return this->post(params); - } - - std::string WebIO::post(WebIO::Params params) - { - return this->post(this->buildPostBody(params)); - } - - std::string WebIO::post(std::string body) - { - return this->execute("POST", body); - } - - std::string WebIO::get(std::string _url) - { - this->setURL(_url); - return this->get(); - } - - std::string WebIO::get() - { - return this->execute("GET", ""); - } - - bool WebIO::openConnection() - { - WORD wPort = INTERNET_DEFAULT_HTTP_PORT; - DWORD dwService = INTERNET_SERVICE_HTTP; - DWORD dwFlag = 0; - - if (this->isFTP) - { - wPort = INTERNET_DEFAULT_FTP_PORT; - dwService = INTERNET_SERVICE_FTP; - dwFlag = INTERNET_FLAG_PASSIVE; - } - else if (this->isSecuredConnection()) - { - wPort = INTERNET_DEFAULT_HTTPS_PORT; - } - - if (!this->url.port.empty()) - { - wPort = static_cast(atoi(this->url.port.data())); - } - - const char* _username = (this->username.size() ? this->username.data() : NULL); - const char* _password = (this->password.size() ? this->password.data() : NULL); - this->hConnect = InternetConnectA(this->hSession, this->url.server.data(), wPort, _username, _password, dwService, dwFlag, 0); - - return (this->hConnect && this->hConnect != INVALID_HANDLE_VALUE); - } - - void WebIO::closeConnection() - { - if (this->hFile && this->hFile != INVALID_HANDLE_VALUE) InternetCloseHandle(this->hFile); - if (this->hConnect && this->hConnect != INVALID_HANDLE_VALUE) InternetCloseHandle(this->hConnect); - } - - WebIO* WebIO::setTimeout(DWORD mseconds) - { - this->timeout = mseconds; - return this; - } - - std::string WebIO::execute(const char* command, std::string body, WebIO::Params headers) - { - if (!this->openConnection()) return ""; - - const char *acceptTypes[] = { "application/x-www-form-urlencoded", nullptr }; - - DWORD dwFlag = INTERNET_FLAG_RELOAD | (this->isSecuredConnection() ? INTERNET_FLAG_SECURE : 0); - - // This doesn't seem to actually do anything, half of those options don't even seem to be implemented. - // Good job microsoft... ( https://msdn.microsoft.com/en-us/library/windows/desktop/aa385328%28v=vs.85%29.aspx ) - //InternetSetOption(WebIO::m_hConnect, INTERNET_OPTION_CONNECT_TIMEOUT, &m_timeout, sizeof(m_timeout)); - //InternetSetOption(WebIO::m_hConnect, INTERNET_OPTION_RECEIVE_TIMEOUT, &m_timeout, sizeof(m_timeout)); - //InternetSetOption(WebIO::m_hConnect, INTERNET_OPTION_SEND_TIMEOUT, &m_timeout, sizeof(m_timeout)); - - this->hFile = HttpOpenRequestA(this->hConnect, command, this->url.document.data(), NULL, NULL, acceptTypes, dwFlag, 0); - - if (!this->hFile || this->hFile == INVALID_HANDLE_VALUE) - { - this->closeConnection(); - return ""; - } - - if (headers.find("Content-Type") == headers.end()) - { - headers["Content-Type"] = "application/x-www-form-urlencoded"; - } - - std::string finalHeaders; - - for (auto i = headers.begin(); i != headers.end(); ++i) - { - finalHeaders.append(i->first); - finalHeaders.append(": "); - finalHeaders.append(i->second); - finalHeaders.append("\r\n"); - } - - if (HttpSendRequestA(this->hFile, finalHeaders.data(), finalHeaders.size(), const_cast(body.data()), body.size() + 1) == FALSE) - { - return ""; - } - - DWORD statusCode = 404; - DWORD length = sizeof(statusCode); - if (HttpQueryInfo(this->hFile, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, &statusCode, &length, NULL) == FALSE || (statusCode != 200 && statusCode != 201)) - { - this->closeConnection(); - return ""; - } - - std::string returnBuffer; - - DWORD size = 0; - char buffer[0x2001] = { 0 }; - - while (InternetReadFile(this->hFile, buffer, 0x2000, &size)) - { - returnBuffer.append(buffer, size); - if (!size) break; - } - - this->closeConnection(); - - return returnBuffer; - } - - bool WebIO::isSecuredConnection() - { - return (this->url.protocol == "https"); - } - - bool WebIO::connect() - { - return this->openConnection(); - } - - void WebIO::disconnect() - { - this->closeConnection(); - } - - bool WebIO::setDirectory(std::string directory) - { - return (FtpSetCurrentDirectoryA(this->hConnect, directory.data()) == TRUE); - } - - bool WebIO::setRelativeDirectory(std::string directory) - { - std::string currentDir; - - if (this->getDirectory(currentDir)) - { - this->formatPath(directory, true); - this->formatPath(currentDir, true); - - char path[MAX_PATH] = { 0 }; - PathCombineA(path, currentDir.data(), directory.data()); - - std::string newPath(path); - this->formatPath(newPath, false); - - return this->setDirectory(newPath); - } - - return false; - } - - bool WebIO::getDirectory(std::string &directory) - { - directory.clear(); - - DWORD size = MAX_PATH; - char currentDir[MAX_PATH] = { 0 }; - - if (FtpGetCurrentDirectoryA(this->hConnect, currentDir, &size) == TRUE) - { - directory.append(currentDir, size); - return true; - } - - return false; - } - - void WebIO::formatPath(std::string &path, bool win) - { - size_t nPos; - std::string find = "\\"; - std::string replace = "/"; - - if (win) - { - find = "/"; - replace = "\\"; - } - - while ((nPos = path.find(find)) != std::wstring::npos) - { - path = path.replace(nPos, find.length(), replace); - } - } - - bool WebIO::createDirectory(std::string directory) - { - return (FtpCreateDirectoryA(this->hConnect, directory.data()) == TRUE); - } - - // Recursively delete a directory - bool WebIO::deleteDirectory(std::string directory) - { - std::string tempDir; - this->getDirectory(tempDir); - - this->setRelativeDirectory(directory); - - std::vector list; - - this->listFiles(".", list); - for (auto file : list) this->deleteFile(file); - - this->listDirectories(".", list); - for (auto dir : list) this->deleteDirectory(dir); - - this->setDirectory(tempDir); - - return (FtpRemoveDirectoryA(this->hConnect, directory.data()) == TRUE); - } - - bool WebIO::renameDirectory(std::string directory, std::string newDir) - { - return (FtpRenameFileA(this->hConnect, directory.data(), newDir.data()) == TRUE); // According to the internetz, this should work - } - - bool WebIO::listElements(std::string directory, std::vector &list, bool files) - { - list.clear(); - - WIN32_FIND_DATAA findFileData; - bool result = false; - DWORD dwAttribute = (files ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_DIRECTORY); - - // Any filename. - std::string tempDir; - this->getDirectory(tempDir); - this->setRelativeDirectory(directory); - - this->hFile = FtpFindFirstFileA(this->hConnect, "*", &findFileData, INTERNET_FLAG_RELOAD, NULL); - - if (this->hFile && this->hFile != INVALID_HANDLE_VALUE) - { - do - { - //if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) continue; - //if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) continue; - - if (findFileData.dwFileAttributes == dwAttribute) // No bitwise flag check, as it might return archives/offline/hidden or other files/dirs - { - //printf("%s: %X\n", findFileData.cFileName, findFileData.dwFileAttributes); - list.push_back(findFileData.cFileName); - result = true; - } - } while (InternetFindNextFileA(this->hFile, &findFileData)); - - InternetCloseHandle(this->hFile); - } - - this->setDirectory(tempDir); - - return result; - } - - bool WebIO::listDirectories(std::string directory, std::vector &list) - { - return this->listElements(directory, list, false); - } - - bool WebIO::listFiles(std::string directory, std::vector &list) - { - return this->listElements(directory, list, true); - } - - bool WebIO::uploadFile(std::string file, std::string localfile) - { - return (FtpPutFileA(this->hConnect, localfile.data(), file.data(), FTP_TRANSFER_TYPE_BINARY, NULL) == TRUE); - } - - bool WebIO::deleteFile(std::string file) - { - return (FtpDeleteFileA(this->hConnect, file.data()) == TRUE); - } - - bool WebIO::renameFile(std::string file, std::string newFile) - { - return (FtpRenameFileA(this->hConnect, file.data(), newFile.data()) == TRUE); - } - - bool WebIO::downloadFile(std::string file, std::string localfile) - { - return (FtpGetFileA(this->hConnect, file.data(), localfile.data(), FALSE, NULL, FTP_TRANSFER_TYPE_BINARY, 0) == TRUE); - } - - bool WebIO::uploadFileData(std::string file, std::string data) - { - bool result = false; - this->hFile = FtpOpenFileA(this->hConnect, file.data(), GENERIC_WRITE, INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, 0); - - if (this->hFile) - { - DWORD size = 0; - if (InternetWriteFile(this->hFile, data.data(), data.size(), &size) == TRUE) - { - result = (size == data.size()); - } - - InternetCloseHandle(this->hFile); - } - - return result; - } - - bool WebIO::downloadFileData(std::string file, std::string &data) - { - data.clear(); - - this->hFile = FtpOpenFileA(this->hConnect, file.data(), GENERIC_READ, INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, 0); - - if (this->hFile) - { - DWORD size = 0; - char buffer[0x2001] = { 0 }; - - while (InternetReadFile(this->hFile, buffer, 0x2000, &size)) - { - data.append(buffer, size); - if (!size) break; - } - - InternetCloseHandle(this->hFile); - return true; - } - - return false; - } -} +#include "STDInclude.hpp" +#include + +namespace Utils +{ + WebIO::WebIO() : WebIO("WebIO") {} + + WebIO::WebIO(std::string useragent, std::string url) : WebIO(useragent) + { + this->setURL(url); + } + + WebIO::WebIO(std::string useragent) : timeout(5000) // 5 seconds timeout by default + { + this->openSession(useragent); + } + + WebIO::~WebIO() + { + this->username.clear(); + this->password.clear(); + + this->closeConnection(); + this->closeSession(); + } + + void WebIO::openSession(std::string useragent) + { + this->hSession = InternetOpenA(useragent.data(), INTERNET_OPEN_TYPE_DIRECT, NULL, NULL, 0); + } + + void WebIO::closeSession() + { + InternetCloseHandle(this->hSession); + } + + void WebIO::SetCredentials(std::string _username, std::string _password) + { + this->username.clear(); + this->password.clear(); + + this->username.append(_username.begin(), _username.end()); + this->password.append(_password.begin(), _password.end()); + } + + void WebIO::setURL(std::string _url) + { + this->url.server.clear(); + this->url.protocol.clear(); + this->url.document.clear(); + + // Insert protocol if none + if (_url.find("://") == std::string::npos) + { + _url = "http://" + _url; + } + + PARSEDURLA pURL; + pURL.cbSize = sizeof(pURL); + ParseURLA(_url.data(), &pURL); + + // Parse protocol + if (pURL.cchProtocol && pURL.cchProtocol != 0xCCCCCCCC && pURL.pszProtocol) + { + for (UINT i = 0; i < pURL.cchProtocol; ++i) + { + char lChar = static_cast(tolower(pURL.pszProtocol[i])); + this->url.protocol.append(&lChar, 1); + } + } + else + { + this->url.protocol.append("http"); + } + + // Parse suffix + std::string server; + + if (pURL.cchSuffix && pURL.cchSuffix != 0xCCCCCCCC && pURL.pszSuffix) + { + server.append(pURL.pszSuffix, pURL.cchSuffix); + } + else + { + // TODO: Add some error handling here + return; + } + + // Remove '//' from the url + if (!server.find("//")) + { + server = server.substr(2); + } + + size_t pos = server.find("/"); + if (pos == std::string::npos) + { + this->url.server = server; + this->url.document = "/"; + } + else + { + this->url.server = server.substr(0, pos); + this->url.document = server.substr(pos); + } + + this->url.port.clear(); + + pos = this->url.server.find(":"); + if (pos != std::string::npos) + { + this->url.port = this->url.server.substr(pos + 1); + this->url.server = this->url.server.substr(0, pos); + } + + this->url.raw.clear(); + this->url.raw.append(this->url.protocol); + this->url.raw.append("://"); + this->url.raw.append(this->url.server); + + if (!this->url.port.empty()) + { + this->url.raw.append(":"); + this->url.raw.append(this->url.port); + } + + this->url.raw.append(this->url.document); + + this->isFTP = (this->url.protocol == "ftp"); + } + + std::string WebIO::buildPostBody(WebIO::Params params) + { + std::string body; + + for (auto param = params.begin(); param != params.end(); ++param) + { + std::string key = param->first; + std::string value = param->second; + + if (!body.empty()) body.append("&"); + + body.append(key); + body.append("="); + body.append(value); + } + + body.append("\0"); + + return body; + } + + std::string WebIO::postFile(std::string _url, std::string data, std::string fieldName, std::string fileName) + { + this->setURL(_url); + return this->postFile(data, fieldName, fileName); + } + + std::string WebIO::postFile(std::string data, std::string fieldName, std::string fileName) + { + WebIO::Params headers; + + std::string boundary = "----WebKitFormBoundaryHoLVocRsBxs71fU6"; + headers["Content-Type"] = "multipart/form-data, boundary=" + boundary; + + Utils::String::Replace(fieldName, "\"", "\\\""); + Utils::String::Replace(fieldName, "\\", "\\\\"); + Utils::String::Replace(fileName, "\"", "\\\""); + Utils::String::Replace(fileName, "\\", "\\\\"); + + std::string body = "--" + boundary + "\r\n"; + body += "Content-Disposition: form-data; name=\""; + body += fieldName; + body += "\"; filename=\""; + body += fileName; + body += "\"\r\n"; + body += "Content-Type: application/octet-stream\r\n\r\n"; + body += data + "\r\n"; + body += "--" + boundary + "--\r\n"; + + headers["Content-Length"] = Utils::String::VA("%u", body.size()); + + return this->execute("POST", body, headers); + } + + std::string WebIO::post(std::string _url, std::string body) + { + this->setURL(_url); + return this->post(body); + } + + std::string WebIO::post(std::string _url, WebIO::Params params) + { + this->setURL(_url); + return this->post(params); + } + + std::string WebIO::post(WebIO::Params params) + { + return this->post(this->buildPostBody(params)); + } + + std::string WebIO::post(std::string body) + { + return this->execute("POST", body); + } + + std::string WebIO::get(std::string _url) + { + this->setURL(_url); + return this->get(); + } + + std::string WebIO::get() + { + return this->execute("GET", ""); + } + + bool WebIO::openConnection() + { + WORD wPort = INTERNET_DEFAULT_HTTP_PORT; + DWORD dwService = INTERNET_SERVICE_HTTP; + DWORD dwFlag = 0; + + if (this->isFTP) + { + wPort = INTERNET_DEFAULT_FTP_PORT; + dwService = INTERNET_SERVICE_FTP; + dwFlag = INTERNET_FLAG_PASSIVE; + } + else if (this->isSecuredConnection()) + { + wPort = INTERNET_DEFAULT_HTTPS_PORT; + } + + if (!this->url.port.empty()) + { + wPort = static_cast(atoi(this->url.port.data())); + } + + const char* _username = (this->username.size() ? this->username.data() : NULL); + const char* _password = (this->password.size() ? this->password.data() : NULL); + this->hConnect = InternetConnectA(this->hSession, this->url.server.data(), wPort, _username, _password, dwService, dwFlag, 0); + + return (this->hConnect && this->hConnect != INVALID_HANDLE_VALUE); + } + + void WebIO::closeConnection() + { + if (this->hFile && this->hFile != INVALID_HANDLE_VALUE) InternetCloseHandle(this->hFile); + if (this->hConnect && this->hConnect != INVALID_HANDLE_VALUE) InternetCloseHandle(this->hConnect); + } + + WebIO* WebIO::setTimeout(DWORD mseconds) + { + this->timeout = mseconds; + return this; + } + + std::string WebIO::execute(const char* command, std::string body, WebIO::Params headers) + { + if (!this->openConnection()) return ""; + + const char *acceptTypes[] = { "application/x-www-form-urlencoded", nullptr }; + + DWORD dwFlag = INTERNET_FLAG_RELOAD | (this->isSecuredConnection() ? INTERNET_FLAG_SECURE : 0); + + // This doesn't seem to actually do anything, half of those options don't even seem to be implemented. + // Good job microsoft... ( https://msdn.microsoft.com/en-us/library/windows/desktop/aa385328%28v=vs.85%29.aspx ) + //InternetSetOption(WebIO::m_hConnect, INTERNET_OPTION_CONNECT_TIMEOUT, &m_timeout, sizeof(m_timeout)); + //InternetSetOption(WebIO::m_hConnect, INTERNET_OPTION_RECEIVE_TIMEOUT, &m_timeout, sizeof(m_timeout)); + //InternetSetOption(WebIO::m_hConnect, INTERNET_OPTION_SEND_TIMEOUT, &m_timeout, sizeof(m_timeout)); + + this->hFile = HttpOpenRequestA(this->hConnect, command, this->url.document.data(), NULL, NULL, acceptTypes, dwFlag, 0); + + if (!this->hFile || this->hFile == INVALID_HANDLE_VALUE) + { + this->closeConnection(); + return ""; + } + + if (headers.find("Content-Type") == headers.end()) + { + headers["Content-Type"] = "application/x-www-form-urlencoded"; + } + + std::string finalHeaders; + + for (auto i = headers.begin(); i != headers.end(); ++i) + { + finalHeaders.append(i->first); + finalHeaders.append(": "); + finalHeaders.append(i->second); + finalHeaders.append("\r\n"); + } + + if (HttpSendRequestA(this->hFile, finalHeaders.data(), finalHeaders.size(), const_cast(body.data()), body.size() + 1) == FALSE) + { + return ""; + } + + DWORD statusCode = 404; + DWORD length = sizeof(statusCode); + if (HttpQueryInfo(this->hFile, HTTP_QUERY_FLAG_NUMBER | HTTP_QUERY_STATUS_CODE, &statusCode, &length, NULL) == FALSE || (statusCode != 200 && statusCode != 201)) + { + this->closeConnection(); + return ""; + } + + std::string returnBuffer; + + DWORD size = 0; + char buffer[0x2001] = { 0 }; + + while (InternetReadFile(this->hFile, buffer, 0x2000, &size)) + { + returnBuffer.append(buffer, size); + if (!size) break; + } + + this->closeConnection(); + + return returnBuffer; + } + + bool WebIO::isSecuredConnection() + { + return (this->url.protocol == "https"); + } + + bool WebIO::connect() + { + return this->openConnection(); + } + + void WebIO::disconnect() + { + this->closeConnection(); + } + + bool WebIO::setDirectory(std::string directory) + { + return (FtpSetCurrentDirectoryA(this->hConnect, directory.data()) == TRUE); + } + + bool WebIO::setRelativeDirectory(std::string directory) + { + std::string currentDir; + + if (this->getDirectory(currentDir)) + { + this->formatPath(directory, true); + this->formatPath(currentDir, true); + + char path[MAX_PATH] = { 0 }; + PathCombineA(path, currentDir.data(), directory.data()); + + std::string newPath(path); + this->formatPath(newPath, false); + + return this->setDirectory(newPath); + } + + return false; + } + + bool WebIO::getDirectory(std::string &directory) + { + directory.clear(); + + DWORD size = MAX_PATH; + char currentDir[MAX_PATH] = { 0 }; + + if (FtpGetCurrentDirectoryA(this->hConnect, currentDir, &size) == TRUE) + { + directory.append(currentDir, size); + return true; + } + + return false; + } + + void WebIO::formatPath(std::string &path, bool win) + { + size_t nPos; + std::string find = "\\"; + std::string replace = "/"; + + if (win) + { + find = "/"; + replace = "\\"; + } + + while ((nPos = path.find(find)) != std::wstring::npos) + { + path = path.replace(nPos, find.length(), replace); + } + } + + bool WebIO::createDirectory(std::string directory) + { + return (FtpCreateDirectoryA(this->hConnect, directory.data()) == TRUE); + } + + // Recursively delete a directory + bool WebIO::deleteDirectory(std::string directory) + { + std::string tempDir; + this->getDirectory(tempDir); + + this->setRelativeDirectory(directory); + + std::vector list; + + this->listFiles(".", list); + for (auto file : list) this->deleteFile(file); + + this->listDirectories(".", list); + for (auto dir : list) this->deleteDirectory(dir); + + this->setDirectory(tempDir); + + return (FtpRemoveDirectoryA(this->hConnect, directory.data()) == TRUE); + } + + bool WebIO::renameDirectory(std::string directory, std::string newDir) + { + return (FtpRenameFileA(this->hConnect, directory.data(), newDir.data()) == TRUE); // According to the internetz, this should work + } + + bool WebIO::listElements(std::string directory, std::vector &list, bool files) + { + list.clear(); + + WIN32_FIND_DATAA findFileData; + bool result = false; + DWORD dwAttribute = (files ? FILE_ATTRIBUTE_NORMAL : FILE_ATTRIBUTE_DIRECTORY); + + // Any filename. + std::string tempDir; + this->getDirectory(tempDir); + this->setRelativeDirectory(directory); + + this->hFile = FtpFindFirstFileA(this->hConnect, "*", &findFileData, INTERNET_FLAG_RELOAD, NULL); + + if (this->hFile && this->hFile != INVALID_HANDLE_VALUE) + { + do + { + //if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_OFFLINE) continue; + //if (findFileData.dwFileAttributes & FILE_ATTRIBUTE_HIDDEN) continue; + + if (findFileData.dwFileAttributes == dwAttribute) // No bitwise flag check, as it might return archives/offline/hidden or other files/dirs + { + //printf("%s: %X\n", findFileData.cFileName, findFileData.dwFileAttributes); + list.push_back(findFileData.cFileName); + result = true; + } + } while (InternetFindNextFileA(this->hFile, &findFileData)); + + InternetCloseHandle(this->hFile); + } + + this->setDirectory(tempDir); + + return result; + } + + bool WebIO::listDirectories(std::string directory, std::vector &list) + { + return this->listElements(directory, list, false); + } + + bool WebIO::listFiles(std::string directory, std::vector &list) + { + return this->listElements(directory, list, true); + } + + bool WebIO::uploadFile(std::string file, std::string localfile) + { + return (FtpPutFileA(this->hConnect, localfile.data(), file.data(), FTP_TRANSFER_TYPE_BINARY, NULL) == TRUE); + } + + bool WebIO::deleteFile(std::string file) + { + return (FtpDeleteFileA(this->hConnect, file.data()) == TRUE); + } + + bool WebIO::renameFile(std::string file, std::string newFile) + { + return (FtpRenameFileA(this->hConnect, file.data(), newFile.data()) == TRUE); + } + + bool WebIO::downloadFile(std::string file, std::string localfile) + { + return (FtpGetFileA(this->hConnect, file.data(), localfile.data(), FALSE, NULL, FTP_TRANSFER_TYPE_BINARY, 0) == TRUE); + } + + bool WebIO::uploadFileData(std::string file, std::string data) + { + bool result = false; + this->hFile = FtpOpenFileA(this->hConnect, file.data(), GENERIC_WRITE, INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, 0); + + if (this->hFile) + { + DWORD size = 0; + if (InternetWriteFile(this->hFile, data.data(), data.size(), &size) == TRUE) + { + result = (size == data.size()); + } + + InternetCloseHandle(this->hFile); + } + + return result; + } + + bool WebIO::downloadFileData(std::string file, std::string &data) + { + data.clear(); + + this->hFile = FtpOpenFileA(this->hConnect, file.data(), GENERIC_READ, INTERNET_FLAG_TRANSFER_BINARY | INTERNET_FLAG_DONT_CACHE | INTERNET_FLAG_RELOAD, 0); + + if (this->hFile) + { + DWORD size = 0; + char buffer[0x2001] = { 0 }; + + while (InternetReadFile(this->hFile, buffer, 0x2000, &size)) + { + data.append(buffer, size); + if (!size) break; + } + + InternetCloseHandle(this->hFile); + return true; + } + + return false; + } +} diff --git a/src/Utils/WebIO.hpp b/src/Utils/WebIO.hpp index a9632cec..4c669be7 100644 --- a/src/Utils/WebIO.hpp +++ b/src/Utils/WebIO.hpp @@ -1,108 +1,108 @@ -/* - This project is released under the GPL 2.0 license. - Some parts are based on research by Bas Timmer and the OpenSteamworks project. - Please do no evil. - - Initial author: (https://github.com/)momo5502 - Started: 2015-03-01 - Notes: - Small FTP and HTTP utility class using WinAPI -*/ - -namespace Utils -{ - class WebIO - { - public: - typedef std::map Params; - - WebIO(); - WebIO(std::string useragent); - WebIO(std::string useragent, std::string url); - - ~WebIO(); - - void setURL(std::string url); - void SetCredentials(std::string username, std::string password); - - std::string postFile(std::string url, std::string data, std::string fieldName, std::string fileName); - std::string postFile(std::string data, std::string fieldName, std::string fileName); - - std::string post(std::string url, WebIO::Params params); - std::string post(std::string url, std::string body); - std::string post(WebIO::Params params); - std::string post(std::string body); - - std::string get(std::string url); - std::string get(); - - WebIO* setTimeout(DWORD mseconds); - - // FTP - bool connect(); - void disconnect(); // Not necessary - - bool setDirectory(std::string directory); - bool setRelativeDirectory(std::string directory); - bool getDirectory(std::string &directory); - bool createDirectory(std::string directory); - bool deleteDirectory(std::string directory); - bool renameDirectory(std::string directory, std::string newDir); - - bool listDirectories(std::string directory, std::vector &list); - bool listFiles(std::string directory, std::vector &list); - - bool deleteFile(std::string file); - bool renameFile(std::string file, std::string newFile); - bool uploadFile(std::string file, std::string localfile); - bool downloadFile(std::string file, std::string localfile); - - bool uploadFileData(std::string file, std::string data); - bool downloadFileData(std::string file, std::string &data); - - private: - - enum Command - { - COMMAND_POST, - COMMAND_GET, - }; - - struct WebURL - { - std::string protocol; - std::string server; - std::string document; - std::string port; - std::string raw; - }; - - bool isFTP; - std::string username; - std::string password; - - WebURL url; - - HINTERNET hSession; - HINTERNET hConnect; - HINTERNET hFile; - - DWORD timeout; - - std::string buildPostBody(WebIO::Params params); - - bool isSecuredConnection(); - - std::string execute(const char* command, std::string body, WebIO::Params headers = WebIO::Params()); - - bool listElements(std::string directory, std::vector &list, bool files); - - void openSession(std::string useragent); - void closeSession(); - - bool openConnection(); - void closeConnection(); - - void formatPath(std::string &path, bool win); /* if (win == true): / -> \\ */ - }; -} +/* + This project is released under the GPL 2.0 license. + Some parts are based on research by Bas Timmer and the OpenSteamworks project. + Please do no evil. + + Initial author: (https://github.com/)momo5502 + Started: 2015-03-01 + Notes: + Small FTP and HTTP utility class using WinAPI +*/ + +namespace Utils +{ + class WebIO + { + public: + typedef std::map Params; + + WebIO(); + WebIO(std::string useragent); + WebIO(std::string useragent, std::string url); + + ~WebIO(); + + void setURL(std::string url); + void SetCredentials(std::string username, std::string password); + + std::string postFile(std::string url, std::string data, std::string fieldName, std::string fileName); + std::string postFile(std::string data, std::string fieldName, std::string fileName); + + std::string post(std::string url, WebIO::Params params); + std::string post(std::string url, std::string body); + std::string post(WebIO::Params params); + std::string post(std::string body); + + std::string get(std::string url); + std::string get(); + + WebIO* setTimeout(DWORD mseconds); + + // FTP + bool connect(); + void disconnect(); // Not necessary + + bool setDirectory(std::string directory); + bool setRelativeDirectory(std::string directory); + bool getDirectory(std::string &directory); + bool createDirectory(std::string directory); + bool deleteDirectory(std::string directory); + bool renameDirectory(std::string directory, std::string newDir); + + bool listDirectories(std::string directory, std::vector &list); + bool listFiles(std::string directory, std::vector &list); + + bool deleteFile(std::string file); + bool renameFile(std::string file, std::string newFile); + bool uploadFile(std::string file, std::string localfile); + bool downloadFile(std::string file, std::string localfile); + + bool uploadFileData(std::string file, std::string data); + bool downloadFileData(std::string file, std::string &data); + + private: + + enum Command + { + COMMAND_POST, + COMMAND_GET, + }; + + struct WebURL + { + std::string protocol; + std::string server; + std::string document; + std::string port; + std::string raw; + }; + + bool isFTP; + std::string username; + std::string password; + + WebURL url; + + HINTERNET hSession; + HINTERNET hConnect; + HINTERNET hFile; + + DWORD timeout; + + std::string buildPostBody(WebIO::Params params); + + bool isSecuredConnection(); + + std::string execute(const char* command, std::string body, WebIO::Params headers = WebIO::Params()); + + bool listElements(std::string directory, std::vector &list, bool files); + + void openSession(std::string useragent); + void closeSession(); + + bool openConnection(); + void closeConnection(); + + void formatPath(std::string &path, bool win); /* if (win == true): / -> \\ */ + }; +}