[LineEndings] Fix crlf lineendings
- added gitattributes file to force crlf for hpp/cpp/lua
This commit is contained in:
3
.gitattributes
vendored
Normal file
3
.gitattributes
vendored
Normal file
@ -0,0 +1,3 @@
|
||||
*.hpp eol=crlf
|
||||
*.cpp eol=crlf
|
||||
*.lua eol=crlf
|
File diff suppressed because it is too large
Load Diff
@ -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];
|
||||
};
|
||||
}
|
||||
|
@ -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<Game::newMapArena_t*>(0x417807, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x420717, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x49BD22, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x4A9649, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x4A97C2, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x4D077E, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x630B00, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x630B2E, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x632782, &ArenaLength::NewArenas[0]);
|
||||
|
||||
Utils::Hook::Set<char*>(0x4A967A, ArenaLength::NewArenas[0].description);
|
||||
Utils::Hook::Set<char*>(0x4A96AD, ArenaLength::NewArenas[0].mapimage);
|
||||
|
||||
Utils::Hook::Set<char*>(0x4A9616, ArenaLength::NewArenas[0].mapName);
|
||||
Utils::Hook::Set<char*>(0x4A9703, ArenaLength::NewArenas[0].mapName);
|
||||
Utils::Hook::Set<char*>(0x4064A8, ArenaLength::NewArenas[0].mapName);
|
||||
|
||||
Utils::Hook::Set<char*>(0x42F214, &ArenaLength::NewArenas[0].other[0]);
|
||||
|
||||
Utils::Hook::Set<char*>(0x4A96ED, &ArenaLength::NewArenas[0].other[0x8]);
|
||||
Utils::Hook::Set<char*>(0x4A9769, &ArenaLength::NewArenas[0].other[0x8]);
|
||||
Utils::Hook::Set<char*>(0x4A97A5, &ArenaLength::NewArenas[0].other[0x8]);
|
||||
|
||||
Utils::Hook::Set<char*>(0x631E92, &ArenaLength::NewArenas[0].other[0x8C]);
|
||||
|
||||
// Resize the array
|
||||
Utils::Hook::Set<int>(0x4064DE, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x417802, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x420736, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x42F271, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x49BD4F, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A960C, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A963F, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A9675, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A96A3, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A96E7, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A96FD, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A975A, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A979F, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A97BC, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4D0779, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x630B29, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x630B81, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x631EBC, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(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<BYTE>(0x4A95F8, 32);
|
||||
|
||||
Utils::Hook::Set<int>(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<Game::newMapArena_t*>(0x417807, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x420717, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x49BD22, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x4A9649, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x4A97C2, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x4D077E, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x630B00, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x630B2E, &ArenaLength::NewArenas[0]);
|
||||
Utils::Hook::Set<Game::newMapArena_t*>(0x632782, &ArenaLength::NewArenas[0]);
|
||||
|
||||
Utils::Hook::Set<char*>(0x4A967A, ArenaLength::NewArenas[0].description);
|
||||
Utils::Hook::Set<char*>(0x4A96AD, ArenaLength::NewArenas[0].mapimage);
|
||||
|
||||
Utils::Hook::Set<char*>(0x4A9616, ArenaLength::NewArenas[0].mapName);
|
||||
Utils::Hook::Set<char*>(0x4A9703, ArenaLength::NewArenas[0].mapName);
|
||||
Utils::Hook::Set<char*>(0x4064A8, ArenaLength::NewArenas[0].mapName);
|
||||
|
||||
Utils::Hook::Set<char*>(0x42F214, &ArenaLength::NewArenas[0].other[0]);
|
||||
|
||||
Utils::Hook::Set<char*>(0x4A96ED, &ArenaLength::NewArenas[0].other[0x8]);
|
||||
Utils::Hook::Set<char*>(0x4A9769, &ArenaLength::NewArenas[0].other[0x8]);
|
||||
Utils::Hook::Set<char*>(0x4A97A5, &ArenaLength::NewArenas[0].other[0x8]);
|
||||
|
||||
Utils::Hook::Set<char*>(0x631E92, &ArenaLength::NewArenas[0].other[0x8C]);
|
||||
|
||||
// Resize the array
|
||||
Utils::Hook::Set<int>(0x4064DE, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x417802, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x420736, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x42F271, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x49BD4F, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A960C, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A963F, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A9675, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A96A3, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A96E7, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A96FD, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A975A, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A979F, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4A97BC, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x4D0779, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x630B29, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x630B81, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(0x631EBC, sizeof(Game::newMapArena_t));
|
||||
Utils::Hook::Set<int>(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<BYTE>(0x4A95F8, 32);
|
||||
|
||||
Utils::Hook::Set<int>(0x42F22B, offsetof(Game::newMapArena_t, mapName) - offsetof(Game::newMapArena_t, other));
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
@ -1,439 +1,439 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
thread_local bool AssetHandler::BypassState;
|
||||
std::map<Game::XAssetType, AssetHandler::IAsset*> AssetHandler::AssetInterfaces;
|
||||
std::map<Game::XAssetType, Utils::Slot<AssetHandler::Callback>> AssetHandler::TypeCallbacks;
|
||||
Utils::Signal<AssetHandler::RestrictCallback> AssetHandler::RestrictSignal;
|
||||
|
||||
std::map<void*, void*> AssetHandler::Relocations;
|
||||
|
||||
std::vector<std::pair<Game::XAssetType, std::string>> AssetHandler::EmptyAssets;
|
||||
|
||||
std::map<std::string, Game::XAssetHeader> 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<AssetHandler::Callback> callback)
|
||||
{
|
||||
AssetHandler::TypeCallbacks[type] = callback;
|
||||
}
|
||||
|
||||
void AssetHandler::OnLoad(Utils::Slot<AssetHandler::RestrictCallback> 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<char*>(start) + i] = reinterpret_cast<char*>(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<void**>(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<bool>("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<char*>(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<bool>() && 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<Game::XAssetType, AssetHandler::IAsset*> AssetHandler::AssetInterfaces;
|
||||
std::map<Game::XAssetType, Utils::Slot<AssetHandler::Callback>> AssetHandler::TypeCallbacks;
|
||||
Utils::Signal<AssetHandler::RestrictCallback> AssetHandler::RestrictSignal;
|
||||
|
||||
std::map<void*, void*> AssetHandler::Relocations;
|
||||
|
||||
std::vector<std::pair<Game::XAssetType, std::string>> AssetHandler::EmptyAssets;
|
||||
|
||||
std::map<std::string, Game::XAssetHeader> 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<AssetHandler::Callback> callback)
|
||||
{
|
||||
AssetHandler::TypeCallbacks[type] = callback;
|
||||
}
|
||||
|
||||
void AssetHandler::OnLoad(Utils::Slot<AssetHandler::RestrictCallback> 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<char*>(start) + i] = reinterpret_cast<char*>(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<void**>(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<bool>("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<char*>(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<bool>() && 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();
|
||||
}
|
||||
}
|
||||
|
@ -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> callback);
|
||||
static void OnLoad(Utils::Slot<RestrictCallback> 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<std::string, Game::XAssetHeader> TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT];
|
||||
|
||||
static std::map<Game::XAssetType, IAsset*> AssetInterfaces;
|
||||
static std::map<Game::XAssetType, Utils::Slot<Callback>> TypeCallbacks;
|
||||
static Utils::Signal<RestrictCallback> RestrictSignal;
|
||||
|
||||
static std::map<void*, void*> Relocations;
|
||||
|
||||
static std::vector<std::pair<Game::XAssetType, std::string>> 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> callback);
|
||||
static void OnLoad(Utils::Slot<RestrictCallback> 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<std::string, Game::XAssetHeader> TemporaryAssets[Game::XAssetType::ASSET_TYPE_COUNT];
|
||||
|
||||
static std::map<Game::XAssetType, IAsset*> AssetInterfaces;
|
||||
static std::map<Game::XAssetType, Utils::Slot<Callback>> TypeCallbacks;
|
||||
static Utils::Signal<RestrictCallback> RestrictSignal;
|
||||
|
||||
static std::map<void*, void*> Relocations;
|
||||
|
||||
static std::vector<std::pair<Game::XAssetType, std::string>> 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"
|
||||
|
@ -1,62 +1,62 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
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<Game::ComWorld>();
|
||||
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<Game::ComPrimaryLight>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
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<Game::ComWorld>();
|
||||
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<Game::ComPrimaryLight>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -1,77 +1,77 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
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<Game::GameWorldMp>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
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<Game::GameWorldMp>();
|
||||
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<Game::G_GlassData>();
|
||||
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<Game::G_GlassName>();
|
||||
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<Game::G_GlassName>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,135 +1,135 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
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<Game::pathnode_tree_t>();
|
||||
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<Game::VehicleTrackSegment*>();
|
||||
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<Game::VehicleTrackSegment>();
|
||||
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<Game::VehicleTrackSegment*>();
|
||||
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<Game::VehicleTrackSegment>();
|
||||
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<Game::VehicleTrackSector>();
|
||||
AssertSize(Game::VehicleTrackSector, 60);
|
||||
buffer->align(Utils::Stream::ALIGN_4);
|
||||
|
||||
Game::VehicleTrackSector* destTrackSectors = buffer->dest<Game::VehicleTrackSector>();
|
||||
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<Game::GameWorldSp>();
|
||||
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<Game::pathnode_t>();
|
||||
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<Game::pathnode_tree_t>();
|
||||
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<Game::VehicleTrackSegment>();
|
||||
|
||||
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<Game::GameWorldSp>();
|
||||
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<Game::pathnode_t>();
|
||||
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<Game::pathnode_tree_t>();
|
||||
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<Game::VehicleTrackSegment>();
|
||||
|
||||
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<Game::G_GlassData>();
|
||||
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<Game::G_GlassName>();
|
||||
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<Game::G_GlassName>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,181 +1,181 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
#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<Game::GfxImage>();
|
||||
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<Game::MapType>();
|
||||
image->semantic = reader.read<char>();
|
||||
image->category = reader.read<char>();
|
||||
|
||||
image->dataLen1 = reader.read<int>();
|
||||
image->dataLen2 = image->dataLen1;
|
||||
|
||||
image->loadDef = reinterpret_cast<Game::GfxImageLoadDef*>(reader.readArray<char>(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<const Game::GfxImageFileHeader*>(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<Game::GfxImageLoadDef>();
|
||||
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<Game::GfxImage>();
|
||||
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<Game::GfxImageLoadDef>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
#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<Game::GfxImage>();
|
||||
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<Game::MapType>();
|
||||
image->semantic = reader.read<char>();
|
||||
image->category = reader.read<char>();
|
||||
|
||||
image->dataLen1 = reader.read<int>();
|
||||
image->dataLen2 = image->dataLen1;
|
||||
|
||||
image->loadDef = reinterpret_cast<Game::GfxImageLoadDef*>(reader.readArray<char>(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<const Game::GfxImageFileHeader*>(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<Game::GfxImageLoadDef>();
|
||||
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<Game::GfxImage>();
|
||||
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<Game::GfxImageLoadDef>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,40 +1,40 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
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<Game::GfxLightDef>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
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<Game::GfxLightDef>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -1,45 +1,45 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
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<Game::LoadedSound>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
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<Game::LoadedSound>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,132 +1,132 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
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>();
|
||||
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<void**>(mapEnts))
|
||||
{
|
||||
*reinterpret_cast<Game::MapEnts**>(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<Game::Stage>();
|
||||
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<Game::MapEnts>();
|
||||
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<Game::Stage>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
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>();
|
||||
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<void**>(mapEnts))
|
||||
{
|
||||
*reinterpret_cast<Game::MapEnts**>(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<Game::Stage>();
|
||||
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<Game::MapEnts>();
|
||||
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<Game::Stage>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,465 +1,465 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
#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<Game::Material>();
|
||||
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<int>();
|
||||
material->hashIndex = reader.read<unsigned __int16>();
|
||||
char* stateBitsEntry = reader.readArray<char>(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<Game::MaterialTextureDef>(material->textureCount & 0xFF);
|
||||
material->constantTable = builder->getAllocator()->allocateArray<Game::MaterialConstantDef>(material->constantCount & 0xFF);
|
||||
material->stateBitTable = builder->getAllocator()->allocateArray<Game::GfxStateBits>(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<Game::water_t>();
|
||||
material->textureTable[i].info.water->writable.floatTime = reader.read<float>();
|
||||
material->textureTable[i].info.water->M = reader.read<int>();
|
||||
material->textureTable[i].info.water->N = reader.read<int>();
|
||||
int count = material->textureTable[i].info.water->M * material->textureTable[i].info.water->N;
|
||||
material->textureTable[i].info.water->H0 = reader.readArray<Game::complex_s>(count);
|
||||
material->textureTable[i].info.water->wTerm = reader.readArray<float>(count);
|
||||
material->textureTable[i].info.water->Lx = reader.read<float>();
|
||||
material->textureTable[i].info.water->Lz = reader.read<float>();
|
||||
material->textureTable[i].info.water->gravity = reader.read<float>();
|
||||
material->textureTable[i].info.water->windvel = reader.read<float>();
|
||||
material->textureTable[i].info.water->winddir[0] = reader.read<float>();
|
||||
material->textureTable[i].info.water->winddir[1] = reader.read<float>();
|
||||
material->textureTable[i].info.water->amplitude = reader.read<float>();
|
||||
material->textureTable[i].info.water->codeConstant[0] = reader.read<float>();
|
||||
material->textureTable[i].info.water->codeConstant[1] = reader.read<float>();
|
||||
material->textureTable[i].info.water->codeConstant[2] = reader.read<float>();
|
||||
material->textureTable[i].info.water->codeConstant[3] = reader.read<float>();
|
||||
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<float>();
|
||||
material->constantTable[i].literal[1] = reader.read<float>();
|
||||
material->constantTable[i].literal[2] = reader.read<float>();
|
||||
material->constantTable[i].literal[3] = reader.read<float>();
|
||||
}
|
||||
|
||||
material->stateBitTable = reader.readArray<Game::GfxStateBits>(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<Game::Material*>(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<Game::Material>();
|
||||
|
||||
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<char>(animCoordX.number_value()) & 0xFF;
|
||||
}
|
||||
|
||||
if (animCoordY.is_number())
|
||||
{
|
||||
material->textureAtlasRowCount = static_cast<char>(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<Game::MaterialTextureDef>(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<Game::MaterialTextureDef> 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<Game::MaterialTextureDef>(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<char>(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<Game::Material>();
|
||||
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<Game::MaterialTextureDef>();
|
||||
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>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
#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<Game::Material>();
|
||||
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<int>();
|
||||
material->hashIndex = reader.read<unsigned __int16>();
|
||||
char* stateBitsEntry = reader.readArray<char>(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<Game::MaterialTextureDef>(material->textureCount & 0xFF);
|
||||
material->constantTable = builder->getAllocator()->allocateArray<Game::MaterialConstantDef>(material->constantCount & 0xFF);
|
||||
material->stateBitTable = builder->getAllocator()->allocateArray<Game::GfxStateBits>(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<Game::water_t>();
|
||||
material->textureTable[i].info.water->writable.floatTime = reader.read<float>();
|
||||
material->textureTable[i].info.water->M = reader.read<int>();
|
||||
material->textureTable[i].info.water->N = reader.read<int>();
|
||||
int count = material->textureTable[i].info.water->M * material->textureTable[i].info.water->N;
|
||||
material->textureTable[i].info.water->H0 = reader.readArray<Game::complex_s>(count);
|
||||
material->textureTable[i].info.water->wTerm = reader.readArray<float>(count);
|
||||
material->textureTable[i].info.water->Lx = reader.read<float>();
|
||||
material->textureTable[i].info.water->Lz = reader.read<float>();
|
||||
material->textureTable[i].info.water->gravity = reader.read<float>();
|
||||
material->textureTable[i].info.water->windvel = reader.read<float>();
|
||||
material->textureTable[i].info.water->winddir[0] = reader.read<float>();
|
||||
material->textureTable[i].info.water->winddir[1] = reader.read<float>();
|
||||
material->textureTable[i].info.water->amplitude = reader.read<float>();
|
||||
material->textureTable[i].info.water->codeConstant[0] = reader.read<float>();
|
||||
material->textureTable[i].info.water->codeConstant[1] = reader.read<float>();
|
||||
material->textureTable[i].info.water->codeConstant[2] = reader.read<float>();
|
||||
material->textureTable[i].info.water->codeConstant[3] = reader.read<float>();
|
||||
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<float>();
|
||||
material->constantTable[i].literal[1] = reader.read<float>();
|
||||
material->constantTable[i].literal[2] = reader.read<float>();
|
||||
material->constantTable[i].literal[3] = reader.read<float>();
|
||||
}
|
||||
|
||||
material->stateBitTable = reader.readArray<Game::GfxStateBits>(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<Game::Material*>(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<Game::Material>();
|
||||
|
||||
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<char>(animCoordX.number_value()) & 0xFF;
|
||||
}
|
||||
|
||||
if (animCoordY.is_number())
|
||||
{
|
||||
material->textureAtlasRowCount = static_cast<char>(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<Game::MaterialTextureDef>(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<Game::MaterialTextureDef> 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<Game::MaterialTextureDef>(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<char>(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<Game::Material>();
|
||||
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<Game::MaterialTextureDef>();
|
||||
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>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,60 +1,60 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
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<Game::RawFile>();
|
||||
|
||||
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<Game::RawFile>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
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<Game::RawFile>();
|
||||
|
||||
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<Game::RawFile>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,24 +1,24 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
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<Game::SndCurve>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
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<Game::SndCurve>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,354 +1,354 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
#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<int>();
|
||||
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<Game::XAnimParts>();
|
||||
|
||||
if (xanim)
|
||||
{
|
||||
if (xanim->name)
|
||||
{
|
||||
xanim->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (xanim->tagnames)
|
||||
{
|
||||
xanim->tagnames = builder->getAllocator()->allocateArray<short>(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<Game::XAnimNotifyInfo>(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<char>(xanim->dataByteCount);
|
||||
}
|
||||
|
||||
if (xanim->dataShort)
|
||||
{
|
||||
xanim->dataShort = reader.readArray<short>(xanim->dataShortCount);
|
||||
}
|
||||
|
||||
if (xanim->dataInt)
|
||||
{
|
||||
xanim->dataInt = reader.readArray<int>(xanim->dataIntCount);
|
||||
}
|
||||
|
||||
if (xanim->randomDataByte)
|
||||
{
|
||||
xanim->randomDataByte = reader.readArray<char>(xanim->randomDataByteCount);
|
||||
}
|
||||
|
||||
if (xanim->randomDataShort)
|
||||
{
|
||||
xanim->randomDataShort = reader.readArray<short>(xanim->randomDataShortCount);
|
||||
}
|
||||
|
||||
if (xanim->randomDataInt)
|
||||
{
|
||||
xanim->randomDataInt = reader.readArray<int>(xanim->randomDataIntCount);
|
||||
}
|
||||
|
||||
if (xanim->indices.data)
|
||||
{
|
||||
if (xanim->framecount < 256)
|
||||
{
|
||||
xanim->indices._1 = reader.readArray<char>(xanim->indexcount);
|
||||
}
|
||||
else
|
||||
{
|
||||
xanim->indices._2 = reader.readArray<unsigned short>(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<Game::XAnimDeltaPart>();
|
||||
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<Game::XAnimParts>();
|
||||
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<unsigned short>();
|
||||
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<Game::XAnimNotifyInfo>();
|
||||
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 <STDInclude.hpp>
|
||||
|
||||
#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<int>();
|
||||
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<Game::XAnimParts>();
|
||||
|
||||
if (xanim)
|
||||
{
|
||||
if (xanim->name)
|
||||
{
|
||||
xanim->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (xanim->tagnames)
|
||||
{
|
||||
xanim->tagnames = builder->getAllocator()->allocateArray<short>(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<Game::XAnimNotifyInfo>(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<char>(xanim->dataByteCount);
|
||||
}
|
||||
|
||||
if (xanim->dataShort)
|
||||
{
|
||||
xanim->dataShort = reader.readArray<short>(xanim->dataShortCount);
|
||||
}
|
||||
|
||||
if (xanim->dataInt)
|
||||
{
|
||||
xanim->dataInt = reader.readArray<int>(xanim->dataIntCount);
|
||||
}
|
||||
|
||||
if (xanim->randomDataByte)
|
||||
{
|
||||
xanim->randomDataByte = reader.readArray<char>(xanim->randomDataByteCount);
|
||||
}
|
||||
|
||||
if (xanim->randomDataShort)
|
||||
{
|
||||
xanim->randomDataShort = reader.readArray<short>(xanim->randomDataShortCount);
|
||||
}
|
||||
|
||||
if (xanim->randomDataInt)
|
||||
{
|
||||
xanim->randomDataInt = reader.readArray<int>(xanim->randomDataIntCount);
|
||||
}
|
||||
|
||||
if (xanim->indices.data)
|
||||
{
|
||||
if (xanim->framecount < 256)
|
||||
{
|
||||
xanim->indices._1 = reader.readArray<char>(xanim->indexcount);
|
||||
}
|
||||
else
|
||||
{
|
||||
xanim->indices._2 = reader.readArray<unsigned short>(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<Game::XAnimDeltaPart>();
|
||||
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<Game::XAnimParts>();
|
||||
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<unsigned short>();
|
||||
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<Game::XAnimNotifyInfo>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -1,9 +1,9 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
#define IW4X_MODEL_VERSION 4
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
#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<Game::XSurfaceCollisionLeaf>(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<unsigned short>(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<unsigned short>(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<int>();
|
||||
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<Game::XModel>();
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->boneNames)
|
||||
{
|
||||
asset->boneNames = builder->getAllocator()->allocateArray<unsigned short>(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<char>(asset->numBones - asset->numRootBones);
|
||||
}
|
||||
|
||||
if (asset->quats)
|
||||
{
|
||||
asset->quats = reader.readArray<short>((asset->numBones - asset->numRootBones) * 4);
|
||||
}
|
||||
|
||||
if (asset->trans)
|
||||
{
|
||||
asset->trans = reader.readArray<float>((asset->numBones - asset->numRootBones) * 3);
|
||||
}
|
||||
|
||||
if (asset->partClassification)
|
||||
{
|
||||
asset->partClassification = reader.readArray<char>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->baseMat)
|
||||
{
|
||||
asset->baseMat = reader.readArray<Game::DObjAnimMat>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->materialHandles)
|
||||
{
|
||||
asset->materialHandles = reader.readArray<Game::Material*>(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<Game::XModelSurfs>();
|
||||
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<Game::XModelCollSurf_s>(asset->numCollSurfs);
|
||||
|
||||
for (int i = 0; i < asset->numCollSurfs; ++i)
|
||||
{
|
||||
Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i];
|
||||
|
||||
if (collSurf->collTris)
|
||||
{
|
||||
collSurf->collTris = reader.readArray<Game::XModelCollTri_s>(collSurf->numCollTris);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->boneInfo)
|
||||
{
|
||||
asset->boneInfo = reader.readArray<Game::XBoneInfo>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->physPreset)
|
||||
{
|
||||
asset->physPreset = reader.readObject<Game::PhysPreset>();
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
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<int>();
|
||||
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<Game::XModel>();
|
||||
|
||||
if (asset->name)
|
||||
{
|
||||
asset->name = reader.readCString();
|
||||
}
|
||||
|
||||
if (asset->boneNames)
|
||||
{
|
||||
asset->boneNames = builder->getAllocator()->allocateArray<unsigned short>(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<char>(asset->numBones - asset->numRootBones);
|
||||
}
|
||||
|
||||
if (asset->quats)
|
||||
{
|
||||
asset->quats = reader.readArray<short>((asset->numBones - asset->numRootBones) * 4);
|
||||
}
|
||||
|
||||
if (asset->trans)
|
||||
{
|
||||
asset->trans = reader.readArray<float>((asset->numBones - asset->numRootBones) * 3);
|
||||
}
|
||||
|
||||
if (asset->partClassification)
|
||||
{
|
||||
asset->partClassification = reader.readArray<char>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->baseMat)
|
||||
{
|
||||
asset->baseMat = reader.readArray<Game::DObjAnimMat>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->materialHandles)
|
||||
{
|
||||
asset->materialHandles = reader.readArray<Game::Material*>(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<Game::XModelSurfs>();
|
||||
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<Game::XModelCollSurf_s>(asset->numCollSurfs);
|
||||
|
||||
for (int i = 0; i < asset->numCollSurfs; ++i)
|
||||
{
|
||||
Game::XModelCollSurf_s* collSurf = &asset->collSurfs[i];
|
||||
|
||||
if (collSurf->collTris)
|
||||
{
|
||||
collSurf->collTris = reader.readArray<Game::XModelCollTri_s>(collSurf->numCollTris);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (asset->boneInfo)
|
||||
{
|
||||
asset->boneInfo = reader.readArray<Game::XBoneInfo>(asset->numBones);
|
||||
}
|
||||
|
||||
if (asset->physPreset)
|
||||
{
|
||||
asset->physPreset = reader.readObject<Game::PhysPreset>();
|
||||
|
||||
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<Game::XModel>();
|
||||
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<unsigned short>();
|
||||
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<Game::Material*>();
|
||||
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<Game::XModelCollSurf_s>();
|
||||
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<Game::XModel>();
|
||||
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<unsigned short>();
|
||||
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<Game::Material*>();
|
||||
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<Game::XModelCollSurf_s>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -1,176 +1,176 @@
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
namespace Assets
|
||||
{
|
||||
#include <STDInclude.hpp>
|
||||
|
||||
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<Game::snd_alias_list_t>();
|
||||
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<Game::snd_alias_list_t>();
|
||||
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<Game::snd_alias_t>();
|
||||
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<Game::SoundFile>();
|
||||
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<Game::SpeakerMap>();
|
||||
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<Game::SoundFile>();
|
||||
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<Game::SpeakerMap>();
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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();
|
||||
};
|
||||
}
|
||||
|
@ -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<std::mutex> _(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<std::string> idVector;
|
||||
std::vector<std::string> 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<std::mutex> _(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<bool>())
|
||||
{
|
||||
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<std::mutex> _(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<std::string> idVector;
|
||||
std::vector<std::string> 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<std::mutex> _(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<bool>())
|
||||
{
|
||||
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()
|
||||
{
|
||||
|
||||
}
|
||||
}
|
||||
|
@ -1,31 +1,31 @@
|
||||
namespace Components
|
||||
{
|
||||
class Bans : public Component
|
||||
{
|
||||
public:
|
||||
typedef std::pair<SteamID, Game::netIP_t> 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<SteamID> idList;
|
||||
std::vector<Game::netIP_t> ipList;
|
||||
};
|
||||
|
||||
static std::mutex AccessMutex;
|
||||
static void LoadBans(BanList* list);
|
||||
};
|
||||
}
|
||||
namespace Components
|
||||
{
|
||||
class Bans : public Component
|
||||
{
|
||||
public:
|
||||
typedef std::pair<SteamID, Game::netIP_t> 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<SteamID> idList;
|
||||
std::vector<Game::netIP_t> ipList;
|
||||
};
|
||||
|
||||
static std::mutex AccessMutex;
|
||||
static void LoadBans(BanList* list);
|
||||
};
|
||||
}
|
||||
|
@ -1,54 +1,54 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::vector<std::string> 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<std::string> 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<const char*>(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<std::string> 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<std::string> 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<const char*>(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();
|
||||
}
|
||||
}
|
||||
|
@ -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<std::string> 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<std::string> BotNames;
|
||||
|
||||
static void BuildConnectString(char* buffer, const char* connectString, int num, int, int protocol, int checksum, int statVer, int statStuff, int port);
|
||||
};
|
||||
}
|
||||
|
@ -1,254 +1,254 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Dvar::Var Colors::NewColors;
|
||||
std::vector<DWORD> 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<uint8_t>(h / 43);
|
||||
remainder = (h - (region * 43)) * 6;
|
||||
|
||||
p = static_cast<uint8_t>((v * (255 - s)) >> 8);
|
||||
q = static_cast<uint8_t>((v * (255 - ((s * remainder) >> 8))) >> 8);
|
||||
t = static_cast<uint8_t>((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<char>(0x535629, limit); // DrawText2d
|
||||
Utils::Hook::Set<char>(0x4C1BE4, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x4863DD, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x486429, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x49A5A8, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x505721, limit); // R_TextWidth
|
||||
Utils::Hook::Set<char>(0x505801, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x50597F, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x5815DB, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x592ED0, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x5A2E2E, limit); // No idea :P
|
||||
|
||||
Utils::Hook::Set<char>(0x5A2733, limit - '0'); // No idea :P
|
||||
}
|
||||
|
||||
char Colors::Add(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
char index = '0' + static_cast<char>(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<DWORD*>(0x66E5F70);
|
||||
}
|
||||
else if (index == '9') // Color 9
|
||||
{
|
||||
*color = *reinterpret_cast<DWORD*>(0x66E5F74);
|
||||
}
|
||||
else if (index == ':')
|
||||
{
|
||||
*color = Colors::HsvToRgb({ static_cast<uint8_t>((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<bool>())
|
||||
{
|
||||
*color = reinterpret_cast<DWORD*>(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<BYTE>(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<bool>("cg_newColors", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Use Warfare<72> 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<DWORD> 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<uint8_t>(h / 43);
|
||||
remainder = (h - (region * 43)) * 6;
|
||||
|
||||
p = static_cast<uint8_t>((v * (255 - s)) >> 8);
|
||||
q = static_cast<uint8_t>((v * (255 - ((s * remainder) >> 8))) >> 8);
|
||||
t = static_cast<uint8_t>((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<char>(0x535629, limit); // DrawText2d
|
||||
Utils::Hook::Set<char>(0x4C1BE4, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x4863DD, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x486429, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x49A5A8, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x505721, limit); // R_TextWidth
|
||||
Utils::Hook::Set<char>(0x505801, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x50597F, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x5815DB, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x592ED0, limit); // No idea :P
|
||||
Utils::Hook::Set<char>(0x5A2E2E, limit); // No idea :P
|
||||
|
||||
Utils::Hook::Set<char>(0x5A2733, limit - '0'); // No idea :P
|
||||
}
|
||||
|
||||
char Colors::Add(uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
char index = '0' + static_cast<char>(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<DWORD*>(0x66E5F70);
|
||||
}
|
||||
else if (index == '9') // Color 9
|
||||
{
|
||||
*color = *reinterpret_cast<DWORD*>(0x66E5F74);
|
||||
}
|
||||
else if (index == ':')
|
||||
{
|
||||
*color = Colors::HsvToRgb({ static_cast<uint8_t>((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<bool>())
|
||||
{
|
||||
*color = reinterpret_cast<DWORD*>(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<BYTE>(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<bool>("cg_newColors", true, Game::dvar_flag::DVAR_FLAG_SAVED, "Use Warfare<72> 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();
|
||||
}
|
||||
}
|
||||
|
@ -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<DWORD> 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<DWORD> ColorTable;
|
||||
};
|
||||
}
|
||||
|
@ -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> callback);
|
||||
static void AddSV(const char* name, Utils::Slot<Callback> 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<std::string, Utils::Slot<Callback>> FunctionMap;
|
||||
static std::unordered_map<std::string, Utils::Slot<Callback>> 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> callback);
|
||||
static void AddSV(const char* name, Utils::Slot<Callback> 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<std::string, Utils::Slot<Callback>> FunctionMap;
|
||||
static std::unordered_map<std::string, Utils::Slot<Callback>> FunctionMapSV;
|
||||
|
||||
static void MainCallback();
|
||||
static void MainCallbackSV();
|
||||
};
|
||||
}
|
||||
|
@ -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<BYTE*>(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<const BYTE*>(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<const BYTE*>(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<const BYTE*>(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<BYTE>(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<BYTE*>(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<const BYTE*>(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<const BYTE*>(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<const BYTE*>(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<BYTE>(0x60BECF, 0xEB);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -1,477 +1,477 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Utils::Signal<Dedicated::Callback> Dedicated::FrameSignal;
|
||||
Utils::Signal<Dedicated::Callback> Dedicated::FrameOnceSignal;
|
||||
|
||||
bool Dedicated::SendChat;
|
||||
|
||||
bool Dedicated::IsEnabled()
|
||||
{
|
||||
static Utils::Value<bool> 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<void*>(0x66E1CB0), &fastfiles, sizeof(fastfiles));
|
||||
Game::R_LoadGraphicsAssets();
|
||||
|
||||
if (Dvar::Var("com_logFilter").get<bool>())
|
||||
{
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Utils::Signal<Dedicated::Callback> Dedicated::FrameSignal;
|
||||
Utils::Signal<Dedicated::Callback> Dedicated::FrameOnceSignal;
|
||||
|
||||
bool Dedicated::SendChat;
|
||||
|
||||
bool Dedicated::IsEnabled()
|
||||
{
|
||||
static Utils::Value<bool> 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<void*>(0x66E1CB0), &fastfiles, sizeof(fastfiles));
|
||||
Game::R_LoadGraphicsAssets();
|
||||
|
||||
if (Dvar::Var("com_logFilter").get<bool>())
|
||||
{
|
||||
Utils::Hook::Nop(0x647466, 5); // 'dvar set' lines
|
||||
Utils::Hook::Nop(0x5DF4F2, 5); // 'sending splash open' lines
|
||||
}
|
||||
|
||||
Utils::Hook::Call<void()>(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<void()>(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<bool>();
|
||||
mapname = Dvar::Var("mapname").get<std::string>();
|
||||
|
||||
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<bool>())
|
||||
{
|
||||
Dvar::Var("sv_dontrotate").setRaw(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Dvar::Var("party_enable").get<bool>() && Dvar::Var("party_host").get<bool>())
|
||||
{
|
||||
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<std::string>().empty())
|
||||
{
|
||||
Logger::Print("No rotation defined, restarting map.\n");
|
||||
|
||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
||||
{
|
||||
Command::Execute(Utils::String::VA("map %s", Dvar::Var("mapname").get<const char*>()), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Command::Execute(Utils::String::VA("devmap %s", Dvar::Var("mapname").get<const char*>()), true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// first, check if the string contains nothing
|
||||
if (Dvar::Var("sv_mapRotationCurrent").get<std::string>().empty())
|
||||
{
|
||||
Logger::Print("Current map rotation has finished, reloading...\n");
|
||||
Dvar::Var("sv_mapRotationCurrent").set(Dvar::Var("sv_mapRotation").get<const char*>());
|
||||
}
|
||||
|
||||
std::string rotation = Dvar::Var("sv_mapRotationCurrent").get<std::string>();
|
||||
|
||||
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<int>();
|
||||
const char* masterServerName = Dvar::Var("masterServerName").get<const char*>();
|
||||
|
||||
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<Dedicated::Callback> callback)
|
||||
{
|
||||
Dedicated::FrameOnceSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Dedicated::OnFrame(Utils::Slot<Dedicated::Callback> 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<bool>("sv_dontrotate", false, Game::dvar_flag::DVAR_FLAG_CHEAT, "");
|
||||
Dvar::Register<bool>("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<bool>("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<BYTE>(0x683370, 0xC3); // steam sometimes doesn't like the server
|
||||
|
||||
Utils::Hook::Set<BYTE>(0x5B4FF0, 0xC3); // self-registration on party
|
||||
Utils::Hook::Set<BYTE>(0x426130, 0xC3); // other party stuff?
|
||||
|
||||
Utils::Hook::Set<BYTE>(0x4D7030, 0xC3); // upnp stuff
|
||||
|
||||
Utils::Hook::Set<BYTE>(0x4B0FC3, 0x04); // make CL_Frame do client packets, even for game state 9
|
||||
Utils::Hook::Set<BYTE>(0x4F5090, 0xC3); // init sound system (1)
|
||||
Utils::Hook::Set<BYTE>(0x507B80, 0xC3); // start render thread
|
||||
Utils::Hook::Set<BYTE>(0x4F84C0, 0xC3); // R_Init caller
|
||||
Utils::Hook::Set<BYTE>(0x46A630, 0xC3); // init sound system (2)
|
||||
Utils::Hook::Set<BYTE>(0x41FDE0, 0xC3); // Com_Frame audio processor?
|
||||
Utils::Hook::Set<BYTE>(0x41B9F0, 0xC3); // called from Com_Frame, seems to do renderer stuff
|
||||
Utils::Hook::Set<BYTE>(0x41D010, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly
|
||||
Utils::Hook::Set<BYTE>(0x62B6C0, 0xC3); // UI expression 'DebugPrint', mainly to prevent some console spam
|
||||
|
||||
Utils::Hook::Set<BYTE>(0x468960, 0xC3); // some mixer-related function called on shutdown
|
||||
Utils::Hook::Set<BYTE>(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<DWORD>(0x5DEC04, 0);
|
||||
|
||||
// sv_network_fps max 1000, and uncheat
|
||||
Utils::Hook::Set<BYTE>(0x4D3C67, 0); // ?
|
||||
Utils::Hook::Set<DWORD>(0x4D3C69, 1000);
|
||||
|
||||
// r_loadForRenderer default to 0
|
||||
Utils::Hook::Set<BYTE>(0x519DDF, 0);
|
||||
|
||||
// disable cheat protection on onlinegame
|
||||
Utils::Hook::Set<BYTE>(0x404CF7, 0x80);
|
||||
|
||||
// some d3d9 call on error
|
||||
Utils::Hook::Set<BYTE>(0x508470, 0xC3);
|
||||
|
||||
// stop saving a config_mp.cfg
|
||||
Utils::Hook::Set<BYTE>(0x60B240, 0xC3);
|
||||
|
||||
// don't load the config
|
||||
Utils::Hook::Set<BYTE>(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<DWORD>(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<int>() > 0 && !LastHeartbeat || (Game::Com_Milliseconds() - LastHeartbeat) > 120 * 1000)
|
||||
{
|
||||
LastHeartbeat = Game::Com_Milliseconds();
|
||||
Dedicated::Heartbeat();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
Dvar::OnInit([] ()
|
||||
{
|
||||
Dvar::Register<const char*>("sv_sayName", "^7Console", Game::dvar_flag::DVAR_FLAG_NONE, "The name to pose as for 'say' commands");
|
||||
Dvar::Register<const char*>("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<std::string>();
|
||||
|
||||
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<std::string>();
|
||||
|
||||
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<int*>(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<void()>(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<void()>(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<bool>();
|
||||
mapname = Dvar::Var("mapname").get<std::string>();
|
||||
|
||||
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<bool>())
|
||||
{
|
||||
Dvar::Var("sv_dontrotate").setRaw(0);
|
||||
return;
|
||||
}
|
||||
|
||||
if (Dvar::Var("party_enable").get<bool>() && Dvar::Var("party_host").get<bool>())
|
||||
{
|
||||
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<std::string>().empty())
|
||||
{
|
||||
Logger::Print("No rotation defined, restarting map.\n");
|
||||
|
||||
if (!Dvar::Var("sv_cheats").get<bool>())
|
||||
{
|
||||
Command::Execute(Utils::String::VA("map %s", Dvar::Var("mapname").get<const char*>()), true);
|
||||
}
|
||||
else
|
||||
{
|
||||
Command::Execute(Utils::String::VA("devmap %s", Dvar::Var("mapname").get<const char*>()), true);
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
// first, check if the string contains nothing
|
||||
if (Dvar::Var("sv_mapRotationCurrent").get<std::string>().empty())
|
||||
{
|
||||
Logger::Print("Current map rotation has finished, reloading...\n");
|
||||
Dvar::Var("sv_mapRotationCurrent").set(Dvar::Var("sv_mapRotation").get<const char*>());
|
||||
}
|
||||
|
||||
std::string rotation = Dvar::Var("sv_mapRotationCurrent").get<std::string>();
|
||||
|
||||
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<int>();
|
||||
const char* masterServerName = Dvar::Var("masterServerName").get<const char*>();
|
||||
|
||||
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<Dedicated::Callback> callback)
|
||||
{
|
||||
Dedicated::FrameOnceSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Dedicated::OnFrame(Utils::Slot<Dedicated::Callback> 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<bool>("sv_dontrotate", false, Game::dvar_flag::DVAR_FLAG_CHEAT, "");
|
||||
Dvar::Register<bool>("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<bool>("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<BYTE>(0x683370, 0xC3); // steam sometimes doesn't like the server
|
||||
|
||||
Utils::Hook::Set<BYTE>(0x5B4FF0, 0xC3); // self-registration on party
|
||||
Utils::Hook::Set<BYTE>(0x426130, 0xC3); // other party stuff?
|
||||
|
||||
Utils::Hook::Set<BYTE>(0x4D7030, 0xC3); // upnp stuff
|
||||
|
||||
Utils::Hook::Set<BYTE>(0x4B0FC3, 0x04); // make CL_Frame do client packets, even for game state 9
|
||||
Utils::Hook::Set<BYTE>(0x4F5090, 0xC3); // init sound system (1)
|
||||
Utils::Hook::Set<BYTE>(0x507B80, 0xC3); // start render thread
|
||||
Utils::Hook::Set<BYTE>(0x4F84C0, 0xC3); // R_Init caller
|
||||
Utils::Hook::Set<BYTE>(0x46A630, 0xC3); // init sound system (2)
|
||||
Utils::Hook::Set<BYTE>(0x41FDE0, 0xC3); // Com_Frame audio processor?
|
||||
Utils::Hook::Set<BYTE>(0x41B9F0, 0xC3); // called from Com_Frame, seems to do renderer stuff
|
||||
Utils::Hook::Set<BYTE>(0x41D010, 0xC3); // CL_CheckForResend, which tries to connect to the local server constantly
|
||||
Utils::Hook::Set<BYTE>(0x62B6C0, 0xC3); // UI expression 'DebugPrint', mainly to prevent some console spam
|
||||
|
||||
Utils::Hook::Set<BYTE>(0x468960, 0xC3); // some mixer-related function called on shutdown
|
||||
Utils::Hook::Set<BYTE>(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<DWORD>(0x5DEC04, 0);
|
||||
|
||||
// sv_network_fps max 1000, and uncheat
|
||||
Utils::Hook::Set<BYTE>(0x4D3C67, 0); // ?
|
||||
Utils::Hook::Set<DWORD>(0x4D3C69, 1000);
|
||||
|
||||
// r_loadForRenderer default to 0
|
||||
Utils::Hook::Set<BYTE>(0x519DDF, 0);
|
||||
|
||||
// disable cheat protection on onlinegame
|
||||
Utils::Hook::Set<BYTE>(0x404CF7, 0x80);
|
||||
|
||||
// some d3d9 call on error
|
||||
Utils::Hook::Set<BYTE>(0x508470, 0xC3);
|
||||
|
||||
// stop saving a config_mp.cfg
|
||||
Utils::Hook::Set<BYTE>(0x60B240, 0xC3);
|
||||
|
||||
// don't load the config
|
||||
Utils::Hook::Set<BYTE>(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<DWORD>(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<int>() > 0 && !LastHeartbeat || (Game::Com_Milliseconds() - LastHeartbeat) > 120 * 1000)
|
||||
{
|
||||
LastHeartbeat = Game::Com_Milliseconds();
|
||||
Dedicated::Heartbeat();
|
||||
}
|
||||
});
|
||||
#endif
|
||||
}
|
||||
|
||||
Dvar::OnInit([] ()
|
||||
{
|
||||
Dvar::Register<const char*>("sv_sayName", "^7Console", Game::dvar_flag::DVAR_FLAG_NONE, "The name to pose as for 'say' commands");
|
||||
Dvar::Register<const char*>("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<std::string>();
|
||||
|
||||
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<std::string>();
|
||||
|
||||
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<int*>(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();
|
||||
}
|
||||
}
|
||||
|
@ -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> callback);
|
||||
static void Once(Utils::Slot<Callback> callback);
|
||||
|
||||
private:
|
||||
static Utils::Signal<Callback> FrameSignal;
|
||||
static Utils::Signal<Callback> 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> callback);
|
||||
static void Once(Utils::Slot<Callback> callback);
|
||||
|
||||
private:
|
||||
static Utils::Signal<Callback> FrameSignal;
|
||||
static Utils::Signal<Callback> 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);
|
||||
};
|
||||
}
|
||||
|
@ -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<int>("net_discoveryPortRangeMin", 25000, 0, 65535, Game::dvar_flag::DVAR_FLAG_SAVED, "Minimum scan range port for local server discovery");
|
||||
Dvar::Register<int>("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>();
|
||||
unsigned int maxPort = Dvar::Var("net_discoveryPortRangeMax").get<unsigned int>();
|
||||
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<void()>(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<int>("net_discoveryPortRangeMin", 25000, 0, 65535, Game::dvar_flag::DVAR_FLAG_SAVED, "Minimum scan range port for local server discovery");
|
||||
Dvar::Register<int>("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>();
|
||||
unsigned int maxPort = Dvar::Var("net_discoveryPortRangeMax").get<unsigned int>();
|
||||
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<void()>(0x4AA720)();
|
||||
}, HOOK_CALL).install()->quick();
|
||||
#endif
|
||||
}
|
||||
|
||||
Discovery::~Discovery()
|
||||
{
|
||||
Discovery::IsPerforming = false;
|
||||
Discovery::IsTerminating = true;
|
||||
|
||||
if (Discovery::Thread.joinable())
|
||||
{
|
||||
Discovery::Thread.join();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<File> 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<File> 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);
|
||||
};
|
||||
}
|
||||
|
@ -1,207 +1,207 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Utils::Signal<Dvar::Callback> 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<char*>();
|
||||
}
|
||||
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<unsigned int>(this->get<int>());
|
||||
}
|
||||
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<const char*>();
|
||||
}
|
||||
|
||||
void Dvar::Var::set(char* string)
|
||||
{
|
||||
this->set(const_cast<const char*>(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<Dvar::Callback> 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<char*>();
|
||||
|
||||
// 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<const char*>(name, "Unknown Soldier", Dvar::Flag(flag | Game::dvar_flag::DVAR_FLAG_SAVED).val, description).get<Game::dvar_t*>();
|
||||
}
|
||||
|
||||
Dvar::Dvar()
|
||||
{
|
||||
// set flags of cg_drawFPS to archive
|
||||
Utils::Hook::Or<BYTE>(0x4F8F69, Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// un-cheat cg_fov and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8E35, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// un-cheat cg_debugInfoCornerOffset and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8FC2, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// remove archive flags for cg_hudchatposition
|
||||
Utils::Hook::Xor<BYTE>(0x4F9992, Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// remove write protection from fs_game
|
||||
Utils::Hook::Xor<DWORD>(0x6431EA, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED);
|
||||
|
||||
// set cg_fov max to 90.0
|
||||
static float cgFov90 = 90.0f;
|
||||
Utils::Hook::Set<float*>(0x4F8E28, &cgFov90);
|
||||
|
||||
// set max volume to 1
|
||||
static float volume = 1.0f;
|
||||
Utils::Hook::Set<float*>(0x408078, &volume);
|
||||
|
||||
// Uncheat ui_showList
|
||||
Utils::Hook::Xor<BYTE>(0x6310DC, Game::dvar_flag::DVAR_FLAG_CHEAT);
|
||||
|
||||
// Uncheat ui_debugMode
|
||||
Utils::Hook::Xor<BYTE>(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::Callback> 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<char*>();
|
||||
}
|
||||
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<unsigned int>(this->get<int>());
|
||||
}
|
||||
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<const char*>();
|
||||
}
|
||||
|
||||
void Dvar::Var::set(char* string)
|
||||
{
|
||||
this->set(const_cast<const char*>(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<Dvar::Callback> 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<char*>();
|
||||
|
||||
// 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<const char*>(name, "Unknown Soldier", Dvar::Flag(flag | Game::dvar_flag::DVAR_FLAG_SAVED).val, description).get<Game::dvar_t*>();
|
||||
}
|
||||
|
||||
Dvar::Dvar()
|
||||
{
|
||||
// set flags of cg_drawFPS to archive
|
||||
Utils::Hook::Or<BYTE>(0x4F8F69, Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// un-cheat cg_fov and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8E35, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// un-cheat cg_debugInfoCornerOffset and add archive flags
|
||||
Utils::Hook::Xor<BYTE>(0x4F8FC2, Game::dvar_flag::DVAR_FLAG_CHEAT | Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// remove archive flags for cg_hudchatposition
|
||||
Utils::Hook::Xor<BYTE>(0x4F9992, Game::dvar_flag::DVAR_FLAG_SAVED);
|
||||
|
||||
// remove write protection from fs_game
|
||||
Utils::Hook::Xor<DWORD>(0x6431EA, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED);
|
||||
|
||||
// set cg_fov max to 90.0
|
||||
static float cgFov90 = 90.0f;
|
||||
Utils::Hook::Set<float*>(0x4F8E28, &cgFov90);
|
||||
|
||||
// set max volume to 1
|
||||
static float volume = 1.0f;
|
||||
Utils::Hook::Set<float*>(0x408078, &volume);
|
||||
|
||||
// Uncheat ui_showList
|
||||
Utils::Hook::Xor<BYTE>(0x6310DC, Game::dvar_flag::DVAR_FLAG_CHEAT);
|
||||
|
||||
// Uncheat ui_debugMode
|
||||
Utils::Hook::Xor<BYTE>(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();
|
||||
}
|
||||
}
|
||||
|
@ -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<Game::dvar_flag>(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<Game::dvar_t**>(ppdvar)) {};
|
||||
Var(std::string dvarName);
|
||||
|
||||
template<typename T> 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> callback);
|
||||
|
||||
// Only strings and bools use this type of declaration
|
||||
template<typename T> static Var Register(const char* name, T value, Flag flag, const char* description);
|
||||
template<typename T> static Var Register(const char* name, T value, T min, T max, Flag flag, const char* description);
|
||||
|
||||
private:
|
||||
static Utils::Signal<Callback> 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<Game::dvar_flag>(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<Game::dvar_t**>(ppdvar)) {};
|
||||
Var(std::string dvarName);
|
||||
|
||||
template<typename T> 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> callback);
|
||||
|
||||
// Only strings and bools use this type of declaration
|
||||
template<typename T> static Var Register(const char* name, T value, Flag flag, const char* description);
|
||||
template<typename T> static Var Register(const char* name, T value, T min, T max, Flag flag, const char* description);
|
||||
|
||||
private:
|
||||
static Utils::Signal<Callback> RegistrationSignal;
|
||||
|
||||
static Game::dvar_t* RegisterName(const char* name, const char* default, Game::dvar_flag flag, const char* description);
|
||||
};
|
||||
}
|
||||
|
@ -1,176 +1,176 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
// Stuff causes warnings
|
||||
#pragma warning(push)
|
||||
#pragma warning(disable: 4091)
|
||||
#include <dbghelp.h>
|
||||
#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<DWORD*>(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<DWORD>(_ReturnAddress());
|
||||
ContextRecord.Esp = reinterpret_cast<DWORD>(_AddressOfReturnAddress());
|
||||
ContextRecord.Ebp = *reinterpret_cast<DWORD*>(_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 <dbghelp.h>
|
||||
#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<DWORD*>(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<DWORD>(_ReturnAddress());
|
||||
ContextRecord.Esp = reinterpret_cast<DWORD>(_AddressOfReturnAddress());
|
||||
ContextRecord.Ebp = *reinterpret_cast<DWORD*>(_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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<std::string> 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<std::string> 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);
|
||||
};
|
||||
}
|
||||
|
@ -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<char*>(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<char>(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<size_t>(this->size) < _size || Game::FS_Read(buffer, _size, this->handle) != static_cast<int>(_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<std::string> FileSystem::GetFileList(std::string path, std::string extension)
|
||||
{
|
||||
std::vector<std::string> 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<std::string> FileSystem::GetSysFileList(std::string path, std::string extension, bool folders)
|
||||
{
|
||||
std::vector<std::string> 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<const char*>(), reinterpret_cast<char*>(0x63D0BB8), Utils::String::VA("%s/%s", folder.data(), file.data()), reinterpret_cast<char**>(&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<std::mutex> _(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<char>(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>();
|
||||
std::string fs_basepath = Dvar::Var("fs_basepath").get<std::string>();
|
||||
std::string fs_homepath = Dvar::Var("fs_homepath").get<std::string>();
|
||||
|
||||
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<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
return Utils::Hook::Call<void(const char*)>(0x4823A0)(a1); // FS_Startup
|
||||
}
|
||||
|
||||
void FileSystem::FsRestartSync(int a1, int a2)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
return Utils::Hook::Call<void(int, int)>(0x461A50)(a1, a2); // FS_Restart
|
||||
}
|
||||
|
||||
void FileSystem::DelayLoadImagesSync()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
return Utils::Hook::Call<void()>(0x494060)(); // DB_LoadDelayedImages
|
||||
}
|
||||
|
||||
int FileSystem::LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(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<WORD>(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<char*>(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<char>(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<size_t>(this->size) < _size || Game::FS_Read(buffer, _size, this->handle) != static_cast<int>(_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<std::string> FileSystem::GetFileList(std::string path, std::string extension)
|
||||
{
|
||||
std::vector<std::string> 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<std::string> FileSystem::GetSysFileList(std::string path, std::string extension, bool folders)
|
||||
{
|
||||
std::vector<std::string> 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<const char*>(), reinterpret_cast<char*>(0x63D0BB8), Utils::String::VA("%s/%s", folder.data(), file.data()), reinterpret_cast<char**>(&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<std::mutex> _(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<char>(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>();
|
||||
std::string fs_basepath = Dvar::Var("fs_basepath").get<std::string>();
|
||||
std::string fs_homepath = Dvar::Var("fs_homepath").get<std::string>();
|
||||
|
||||
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<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
return Utils::Hook::Call<void(const char*)>(0x4823A0)(a1); // FS_Startup
|
||||
}
|
||||
|
||||
void FileSystem::FsRestartSync(int a1, int a2)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
return Utils::Hook::Call<void(int, int)>(0x461A50)(a1, a2); // FS_Restart
|
||||
}
|
||||
|
||||
void FileSystem::DelayLoadImagesSync()
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(FileSystem::FSMutex);
|
||||
return Utils::Hook::Call<void()>(0x494060)(); // DB_LoadDelayedImages
|
||||
}
|
||||
|
||||
int FileSystem::LoadTextureSync(Game::GfxImageLoadDef **loadDef, Game::GfxImage *image)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> _(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<WORD>(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());
|
||||
}
|
||||
}
|
||||
|
@ -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<std::string> GetFileList(std::string path, std::string extension);
|
||||
static std::vector<std::string> 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<std::string> GetFileList(std::string path, std::string extension);
|
||||
static std::vector<std::string> 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);
|
||||
};
|
||||
}
|
||||
|
@ -1,49 +1,49 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::vector<std::string> 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<std::string> 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();
|
||||
}
|
||||
}
|
||||
|
@ -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<std::string> 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<std::string> EnabledFlags;
|
||||
|
||||
static void ParseFlags();
|
||||
};
|
||||
}
|
||||
|
@ -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<bool>()) // com_sv_running
|
||||
{
|
||||
Utils::Hook::Call<void()>(0x458160)();
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils::Hook::Call<void()>(0x49F0B0)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FrameTime::SVFrameWaitFunc()
|
||||
{
|
||||
int sv_residualTime = *reinterpret_cast<int*>(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<uint32_t*>(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<void()>(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<bool>()) // com_sv_running
|
||||
{
|
||||
Utils::Hook::Call<void()>(0x458160)();
|
||||
}
|
||||
else
|
||||
{
|
||||
Utils::Hook::Call<void()>(0x49F0B0)();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void FrameTime::SVFrameWaitFunc()
|
||||
{
|
||||
int sv_residualTime = *reinterpret_cast<int*>(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<uint32_t*>(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<void()>(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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
@ -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<unsigned int>(*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<unsigned int>(*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<std::string> 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<std::string> 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<std::string>* rawGametypes = reinterpret_cast<std::vector<std::string>*>(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<void()>(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<unsigned int>(*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<unsigned int>(*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<std::string> 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<std::string> 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<std::string>* rawGametypes = reinterpret_cast<std::vector<std::string>*>(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<void()>(0x50AB20)();
|
||||
}, HOOK_CALL).install()->quick();
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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<Pipe::PacketCallback> 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<Pipe::PacketCallback> 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<Pipe::PacketCallback> 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<Pipe::PacketCallback> 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", "");
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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<PacketCallback> callback);
|
||||
void onConnect(Callback callback);
|
||||
|
||||
private:
|
||||
Utils::Slot<void()> connectCallback;
|
||||
std::map<std::string, Utils::Slot<PacketCallback>> 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<Pipe::PacketCallback> 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<PacketCallback> callback);
|
||||
void onConnect(Callback callback);
|
||||
|
||||
private:
|
||||
Utils::Slot<void()> connectCallback;
|
||||
std::map<std::string, Utils::Slot<PacketCallback>> 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<Pipe::PacketCallback> callback);
|
||||
|
||||
private:
|
||||
static Pipe ServerPipe;
|
||||
static Pipe ClientPipe;
|
||||
|
||||
static void ConnectClient();
|
||||
};
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -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<std::string, Game::LocalizeEntry*> Localization::LocalizeMap;
|
||||
std::unordered_map<std::string, Game::LocalizeEntry*> Localization::TempLocalizeMap;
|
||||
|
||||
void Localization::Set(std::string key, std::string value)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(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<Game::LocalizeEntry>();
|
||||
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<bool>()) return key;
|
||||
|
||||
Game::LocalizeEntry* entry = nullptr;
|
||||
std::lock_guard<std::mutex> _(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<std::mutex> _(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<Game::LocalizeEntry>();
|
||||
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<std::mutex> _(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<std::mutex> _(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<bool>("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<std::string, Game::LocalizeEntry*> Localization::LocalizeMap;
|
||||
std::unordered_map<std::string, Game::LocalizeEntry*> Localization::TempLocalizeMap;
|
||||
|
||||
void Localization::Set(std::string key, std::string value)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(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<Game::LocalizeEntry>();
|
||||
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<bool>()) return key;
|
||||
|
||||
Game::LocalizeEntry* entry = nullptr;
|
||||
std::lock_guard<std::mutex> _(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<std::mutex> _(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<Game::LocalizeEntry>();
|
||||
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<std::mutex> _(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<std::mutex> _(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<bool>("ui_localize", true, Game::dvar_flag::DVAR_FLAG_NONE, "Use localization strings");
|
||||
}
|
||||
|
||||
Localization::~Localization()
|
||||
{
|
||||
Localization::ClearTemp();
|
||||
|
||||
Localization::LocalizeMap.clear();
|
||||
Localization::MemAllocator.clear();
|
||||
}
|
||||
}
|
||||
|
@ -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<std::string, Game::LocalizeEntry*> LocalizeMap;
|
||||
static std::unordered_map<std::string, Game::LocalizeEntry*> 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<std::string, Game::LocalizeEntry*> LocalizeMap;
|
||||
static std::unordered_map<std::string, Game::LocalizeEntry*> TempLocalizeMap;
|
||||
static Dvar::Var UseLocalization;
|
||||
|
||||
static void __stdcall SetStringStub(const char* key, const char* value, bool isEnglish);
|
||||
static void LoadLanguageStrings();
|
||||
static void SELoadLanguageStub();
|
||||
};
|
||||
}
|
||||
|
@ -1,325 +1,325 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::mutex Logger::MessageMutex;
|
||||
std::vector<std::string> Logger::MessageQueue;
|
||||
std::vector<Network::Address> Logger::LoggingAddresses[2];
|
||||
void(*Logger::PipeCallback)(std::string) = nullptr;
|
||||
|
||||
bool Logger::IsConsoleReady()
|
||||
{
|
||||
return (IsWindow(*reinterpret_cast<HWND*>(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<char*>(const_cast<char**>(&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<std::mutex> _(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<unsigned int>(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<unsigned int>(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<int*>(0x1AD8F28))
|
||||
{
|
||||
Game::FS_FCloseFile(fh);
|
||||
}
|
||||
}
|
||||
}
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::mutex Logger::MessageMutex;
|
||||
std::vector<std::string> Logger::MessageQueue;
|
||||
std::vector<Network::Address> Logger::LoggingAddresses[2];
|
||||
void(*Logger::PipeCallback)(std::string) = nullptr;
|
||||
|
||||
bool Logger::IsConsoleReady()
|
||||
{
|
||||
return (IsWindow(*reinterpret_cast<HWND*>(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<char*>(const_cast<char**>(&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<std::mutex> _(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<unsigned int>(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<unsigned int>(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<int*>(0x1AD8F28))
|
||||
{
|
||||
Game::FS_FCloseFile(fh);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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<std::string> MessageQueue;
|
||||
static std::vector<Network::Address> 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<std::string> MessageQueue;
|
||||
static std::vector<Network::Address> 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);
|
||||
};
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<std::string, std::string> GetTeamsForMap(std::string map);
|
||||
static std::vector<std::string> GetDependenciesForMap(std::string map);
|
||||
|
||||
static std::string CurrentMainZone;
|
||||
|
||||
private:
|
||||
class DLC
|
||||
{
|
||||
public:
|
||||
int index;
|
||||
std::string url;
|
||||
std::vector<std::string> maps;
|
||||
};
|
||||
|
||||
static bool IsSPMap;
|
||||
static std::vector<DLC> DlcPacks;
|
||||
static std::vector<Game::XAssetEntry> EntryPool;
|
||||
|
||||
static std::vector<std::pair<std::string, std::string>> DependencyList;
|
||||
static std::vector<std::string> 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<std::string, std::string> GetTeamsForMap(std::string map);
|
||||
static std::vector<std::string> GetDependenciesForMap(std::string map);
|
||||
|
||||
static std::string CurrentMainZone;
|
||||
|
||||
private:
|
||||
class DLC
|
||||
{
|
||||
public:
|
||||
int index;
|
||||
std::string url;
|
||||
std::vector<std::string> maps;
|
||||
};
|
||||
|
||||
static bool IsSPMap;
|
||||
static std::vector<DLC> DlcPacks;
|
||||
static std::vector<Game::XAssetEntry> EntryPool;
|
||||
|
||||
static std::vector<std::pair<std::string, std::string>> DependencyList;
|
||||
static std::vector<std::string> 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();
|
||||
};
|
||||
}
|
||||
|
@ -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<unsigned int>(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<char>(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<int(Game::Material*, Game::Material*)>(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<void*>(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<unsigned int>(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<char>(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<int(Game::Material*, Game::Material*)>(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<void*>(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();
|
||||
}
|
||||
}
|
||||
|
@ -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
|
||||
};
|
||||
}
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -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<std::string, Game::menuDef_t*> MenuList;
|
||||
static std::unordered_map<std::string, Game::MenuList*> MenuListList;
|
||||
static std::vector<std::string> 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<Game::menuDef_t*> LoadMenu(Game::menuDef_t* menudef);
|
||||
static std::vector<Game::menuDef_t*> 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<std::string, Game::menuDef_t*> MenuList;
|
||||
static std::unordered_map<std::string, Game::MenuList*> MenuListList;
|
||||
static std::vector<std::string> 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<Game::menuDef_t*> LoadMenu(Game::menuDef_t* menudef);
|
||||
static std::vector<Game::menuDef_t*> 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);
|
||||
};
|
||||
}
|
||||
|
@ -1,107 +1,107 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::vector<std::string> ModList::Mods;
|
||||
unsigned int ModList::CurrentMod;
|
||||
|
||||
bool ModList::HasMod(std::string modName)
|
||||
{
|
||||
auto list = FileSystem::GetSysFileList(Dvar::Var("fs_basepath").get<std::string>() + "\\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<std::string>() + "\\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<Game::dvar_t*>()->modified = true;
|
||||
|
||||
if (Dvar::Var("cl_modVidRestart").get<bool>())
|
||||
{
|
||||
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<Game::dvar_t*>()->modified = true;
|
||||
|
||||
if (Dvar::Var("cl_modVidRestart").get<bool>())
|
||||
{
|
||||
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<std::string> ModList::Mods;
|
||||
unsigned int ModList::CurrentMod;
|
||||
|
||||
bool ModList::HasMod(std::string modName)
|
||||
{
|
||||
auto list = FileSystem::GetSysFileList(Dvar::Var("fs_basepath").get<std::string>() + "\\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<std::string>() + "\\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<Game::dvar_t*>()->modified = true;
|
||||
|
||||
if (Dvar::Var("cl_modVidRestart").get<bool>())
|
||||
{
|
||||
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<Game::dvar_t*>()->modified = true;
|
||||
|
||||
if (Dvar::Var("cl_modVidRestart").get<bool>())
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
@ -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<std::string> 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<std::string> 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);
|
||||
};
|
||||
}
|
@ -1,348 +1,348 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::unordered_map<void*, IUnknown*> ModelSurfs::BufferMap;
|
||||
std::unordered_map<std::string, Game::CModelAllocData*> 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<char>(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<unsigned int*>(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<DWORD*>(reinterpret_cast<char*>(section->buffer) + (fixup >> 3)) += reinterpret_cast<DWORD>(header.sectionHeader[fixup & 3].buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Store allocation data (not sure if this is correct)
|
||||
Game::CModelAllocData* allocationData = Utils::Memory::AllocateArray<Game::CModelAllocData>();
|
||||
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<Game::XModelSurfs*>(allocationData->mainArray);
|
||||
Game::XSurface* tempSurfaces = allocator.allocateArray<Game::XSurface>(modelSurfs->numSurfaces);
|
||||
char* surfaceData = reinterpret_cast<char*>(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<void**>(reinterpret_cast<char*>(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<short>(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<void*, IUnknown*> ModelSurfs::BufferMap;
|
||||
std::unordered_map<std::string, Game::CModelAllocData*> 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<char>(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<unsigned int*>(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<DWORD*>(reinterpret_cast<char*>(section->buffer) + (fixup >> 3)) += reinterpret_cast<DWORD>(header.sectionHeader[fixup & 3].buffer);
|
||||
}
|
||||
}
|
||||
|
||||
// Store allocation data (not sure if this is correct)
|
||||
Game::CModelAllocData* allocationData = Utils::Memory::AllocateArray<Game::CModelAllocData>();
|
||||
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<Game::XModelSurfs*>(allocationData->mainArray);
|
||||
Game::XSurface* tempSurfaces = allocator.allocateArray<Game::XSurface>(modelSurfs->numSurfaces);
|
||||
char* surfaceData = reinterpret_cast<char*>(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<void**>(reinterpret_cast<char*>(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<short>(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());
|
||||
}
|
||||
}
|
||||
|
@ -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<void*, IUnknown*> BufferMap;
|
||||
static std::unordered_map<std::string, Game::CModelAllocData*> 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<void*, IUnknown*> BufferMap;
|
||||
static std::unordered_map<std::string, Game::CModelAllocData*> 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();
|
||||
};
|
||||
}
|
||||
|
@ -1,47 +1,47 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::unordered_map<std::string, const char*> 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<std::string, const char*> 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();
|
||||
}
|
||||
}
|
||||
|
@ -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<std::string, const char*> 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<std::string, const char*> SoundAliasList;
|
||||
static Game::XAssetHeader ModifyAliases(Game::XAssetType type, std::string filename);
|
||||
};
|
||||
}
|
||||
|
@ -1,378 +1,378 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::string Network::SelectedPacket;
|
||||
Utils::Signal<Network::CallbackRaw> Network::StartupSignal;
|
||||
std::map<std::string, Utils::Slot<Network::Callback>> 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<sockaddr*>(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<int>() & 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<uint16_t>(protoAddress.port()));
|
||||
this->setType(Game::netadrtype_t::NA_IP);
|
||||
}
|
||||
|
||||
void Network::Handle(std::string packet, Utils::Slot<Network::Callback> callback)
|
||||
{
|
||||
Network::PacketHandlers[Utils::String::ToLower(packet)] = callback;
|
||||
}
|
||||
|
||||
void Network::OnStart(Utils::Slot<Network::CallbackRaw> 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<unsigned short>(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<size_t>(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<DWORD>(0x4AEF08, 0x1FFFC);
|
||||
Utils::Hook::Set<DWORD>(0x4AEFA3, 0x1FFFC);
|
||||
|
||||
// increase max port binding attempts from 10 to 100
|
||||
Utils::Hook::Set<BYTE>(0x4FD48A, 100);
|
||||
|
||||
// increase cl_maxpackets limit
|
||||
Utils::Hook::Set<BYTE>(0x4050A1, 125);
|
||||
|
||||
// Parse port as short in Net_AddrToString
|
||||
Utils::Hook::Set<char*>(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::CallbackRaw> Network::StartupSignal;
|
||||
std::map<std::string, Utils::Slot<Network::Callback>> 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<sockaddr*>(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<int>() & 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<uint16_t>(protoAddress.port()));
|
||||
this->setType(Game::netadrtype_t::NA_IP);
|
||||
}
|
||||
|
||||
void Network::Handle(std::string packet, Utils::Slot<Network::Callback> callback)
|
||||
{
|
||||
Network::PacketHandlers[Utils::String::ToLower(packet)] = callback;
|
||||
}
|
||||
|
||||
void Network::OnStart(Utils::Slot<Network::CallbackRaw> 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<unsigned short>(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<size_t>(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<DWORD>(0x4AEF08, 0x1FFFC);
|
||||
Utils::Hook::Set<DWORD>(0x4AEFA3, 0x1FFFC);
|
||||
|
||||
// increase max port binding attempts from 10 to 100
|
||||
Utils::Hook::Set<BYTE>(0x4FD48A, 100);
|
||||
|
||||
// increase cl_maxpackets limit
|
||||
Utils::Hook::Set<BYTE>(0x4050A1, 125);
|
||||
|
||||
// Parse port as short in Net_AddrToString
|
||||
Utils::Hook::Set<char*>(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();
|
||||
}
|
||||
}
|
||||
|
@ -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<sockaddr*>(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> callback);
|
||||
static void OnStart(Utils::Slot<CallbackRaw> 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<CallbackRaw> StartupSignal;
|
||||
static std::map<std::string, Utils::Slot<Callback>> 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<sockaddr*>(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> callback);
|
||||
static void OnStart(Utils::Slot<CallbackRaw> 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<CallbackRaw> StartupSignal;
|
||||
static std::map<std::string, Utils::Slot<Callback>> 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();
|
||||
};
|
||||
}
|
||||
|
@ -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 = "<Cache ID=\"iw4x\" Version=\"";
|
||||
auto pos = caches.find(str);
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
caches = caches.substr(pos + str.size());
|
||||
|
||||
pos = caches.find_first_of("\"");
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
caches = caches.substr(0, pos);
|
||||
|
||||
int version = atoi(caches.data());
|
||||
|
||||
Dvar::Var("cl_updateversion").get<Game::dvar_t*>()->current.integer = version;
|
||||
Dvar::Var("cl_updateavailable").get<Game::dvar_t*>()->current.boolean = (version > REVISION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
News::News()
|
||||
{
|
||||
if (ZoneBuilder::IsEnabled()) return; // Maybe also dedi?
|
||||
|
||||
Dvar::Register<int>("cl_updateoldversion", REVISION, REVISION, REVISION, Game::DVAR_FLAG_WRITEPROTECTED, "Current version number.");
|
||||
Dvar::Register<int>("cl_updateversion", 0, 0, -1, Game::DVAR_FLAG_WRITEPROTECTED, "New version number.");
|
||||
Dvar::Register<bool>("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<Game::dvar_t*>()->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 = "<Cache ID=\"iw4x\" Version=\"";
|
||||
auto pos = caches.find(str);
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
caches = caches.substr(pos + str.size());
|
||||
|
||||
pos = caches.find_first_of("\"");
|
||||
|
||||
if (pos != std::string::npos)
|
||||
{
|
||||
caches = caches.substr(0, pos);
|
||||
|
||||
int version = atoi(caches.data());
|
||||
|
||||
Dvar::Var("cl_updateversion").get<Game::dvar_t*>()->current.integer = version;
|
||||
Dvar::Var("cl_updateavailable").get<Game::dvar_t*>()->current.boolean = (version > REVISION);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
News::News()
|
||||
{
|
||||
if (ZoneBuilder::IsEnabled()) return; // Maybe also dedi?
|
||||
|
||||
Dvar::Register<int>("cl_updateoldversion", REVISION, REVISION, REVISION, Game::DVAR_FLAG_WRITEPROTECTED, "Current version number.");
|
||||
Dvar::Register<int>("cl_updateversion", 0, 0, -1, Game::DVAR_FLAG_WRITEPROTECTED, "New version number.");
|
||||
Dvar::Register<bool>("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<Game::dvar_t*>()->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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
};
|
||||
}
|
||||
|
@ -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<NodeEntry> Nodes;
|
||||
static std::vector<ClientSession> 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<NodeEntry> Nodes;
|
||||
static std::vector<ClientSession> 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);
|
||||
};
|
||||
}
|
||||
|
@ -1,459 +1,459 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Party::JoinContainer Party::Container;
|
||||
std::map<uint64_t, Network::Address> 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<int>(name, 1, 1, max, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED | flag, description).get<Game::dvar_t*>();
|
||||
}
|
||||
|
||||
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<DWORD(char*)>(0x4D5390)(dvar);
|
||||
}
|
||||
|
||||
Party::Party()
|
||||
{
|
||||
static Game::dvar_t* partyEnable = Dvar::Register<bool>("party_enable", Dedicated::IsEnabled(), Game::dvar_flag::DVAR_FLAG_NONE, "Enable party system").get<Game::dvar_t*>();
|
||||
Dvar::Register<bool>("xblive_privatematch", true, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED, "").get<Game::dvar_t*>();
|
||||
|
||||
// various changes to SV_DirectConnect-y stuff to allow non-party joinees
|
||||
Utils::Hook::Set<WORD>(0x460D96, 0x90E9);
|
||||
Utils::Hook::Set<BYTE>(0x460F0A, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x401CA4, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x401C15, 0xEB);
|
||||
|
||||
// disable configstring checksum matching (it's unreliable at most)
|
||||
Utils::Hook::Set<BYTE>(0x4A75A7, 0xEB); // SV_SpawnServer
|
||||
Utils::Hook::Set<BYTE>(0x5AC2CF, 0xEB); // CL_ParseGamestate
|
||||
Utils::Hook::Set<BYTE>(0x5AC2C3, 0xEB); // CL_ParseGamestate
|
||||
|
||||
// AnonymousAddRequest
|
||||
Utils::Hook::Set<BYTE>(0x5B5E18, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x5B5E64, 0xEB);
|
||||
Utils::Hook::Nop(0x5B5E5C, 2);
|
||||
|
||||
// HandleClientHandshake
|
||||
Utils::Hook::Set<BYTE>(0x5B6EA5, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x5B6EF3, 0xEB);
|
||||
Utils::Hook::Nop(0x5B6EEB, 2);
|
||||
|
||||
// Allow local connections
|
||||
Utils::Hook::Set<BYTE>(0x4D43DA, 0xEB);
|
||||
|
||||
// LobbyID mismatch
|
||||
Utils::Hook::Nop(0x4E50D6, 2);
|
||||
Utils::Hook::Set<BYTE>(0x4E50DA, 0xEB);
|
||||
|
||||
// causes 'does current Steam lobby match' calls in Steam_JoinLobby to be ignored
|
||||
Utils::Hook::Set<BYTE>(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<BYTE>(0x5AF85B, 0xEB);
|
||||
|
||||
// Allow xpartygo in public lobbies
|
||||
Utils::Hook::Set<BYTE>(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<BYTE>(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<BYTE>(0x487BB2, 0xEB);
|
||||
|
||||
// Force xblive_privatematch 0 and rename it
|
||||
//Utils::Hook::Set<BYTE>(0x420A6A, 4);
|
||||
Utils::Hook::Set<BYTE>(0x420A6C, 0);
|
||||
Utils::Hook::Set<char*>(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<int>(0x79D898, 1);
|
||||
|
||||
// Disable host migration
|
||||
Utils::Hook::Set<BYTE>(0x5B58B2, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x4D6171, 0);
|
||||
|
||||
// Patch playlist stuff for non-party behavior
|
||||
Utils::Hook::Set<Game::dvar_t**>(0x4A4093, &partyEnable);
|
||||
Utils::Hook::Set<Game::dvar_t**>(0x4573F1, &partyEnable);
|
||||
Utils::Hook::Set<Game::dvar_t**>(0x5B1A0C, &partyEnable);
|
||||
|
||||
// Invert corresponding jumps
|
||||
Utils::Hook::Xor<BYTE>(0x4A409B, 1);
|
||||
Utils::Hook::Xor<BYTE>(0x4573FA, 1);
|
||||
Utils::Hook::Xor<BYTE>(0x5B1A17, 1);
|
||||
|
||||
// Fix xstartlobby
|
||||
//Utils::Hook::Set<BYTE>(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<char*>(0x42618F, "sv_maxclients");
|
||||
Utils::Hook::Set<char*>(0x4D3756, "sv_maxclients");
|
||||
Utils::Hook::Set<char*>(0x5E3772, "sv_maxclients");
|
||||
|
||||
// Unlatch maxclient dvars
|
||||
Utils::Hook::Xor<BYTE>(0x426187, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<BYTE>(0x4D374E, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<BYTE>(0x5E376A, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<DWORD>(0x4261A1, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<DWORD>(0x4D376D, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<DWORD>(0x5E3789, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
|
||||
// Patch Live_PlayerHasLoopbackAddr
|
||||
//Utils::Hook::Set<DWORD>(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<int>();
|
||||
maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame);
|
||||
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData_s*>(0x1081C00));
|
||||
}
|
||||
|
||||
Utils::InfoString info;
|
||||
info.set("challenge", Utils::ParseChallenge(data));
|
||||
info.set("gamename", "IW4");
|
||||
info.set("hostname", Dvar::Var("sv_hostname").get<const char*>());
|
||||
info.set("gametype", Dvar::Var("g_gametype").get<const char*>());
|
||||
info.set("fs_game", Dvar::Var("fs_game").get<const char*>());
|
||||
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<const char*>());
|
||||
info.set("isPrivate", (Dvar::Var("g_password").get<std::string>().size() ? "1" : "0"));
|
||||
info.set("hc", (Dvar::Var("g_hardcore").get<bool>() ? "1" : "0"));
|
||||
info.set("securityLevel", Utils::String::VA("%i", Dvar::Var("sv_securityLevel").get<int>()));
|
||||
info.set("sv_running", (Dvar::Var("sv_running").get<bool>() ? "1" : "0"));
|
||||
|
||||
if (Dedicated::IsEnabled())
|
||||
{
|
||||
info.set("sv_motd", Dvar::Var("sv_motd").get<std::string>());
|
||||
}
|
||||
|
||||
// Ensure mapname is set
|
||||
if (info.get("mapname").empty() || (Dvar::Var("party_enable").get<bool>() && Dvar::Var("party_host").get<bool>() && !Dvar::Var("sv_running").get<bool>()))
|
||||
{
|
||||
info.set("mapname", Dvar::Var("ui_mapname").get<const char*>());
|
||||
}
|
||||
|
||||
// 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<bool>() && Dvar::Var("party_host").get<bool>()) // Party hosting
|
||||
{
|
||||
info.set("matchtype", "1");
|
||||
}
|
||||
else if (Dvar::Var("sv_running").get<bool>()) // 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<uint32_t>(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<std::string>()) != 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<std::string>().empty() && info.get("fs_game").empty())
|
||||
{
|
||||
Dvar::Var("fs_game").set("");
|
||||
|
||||
if (Dvar::Var("cl_modVidRestart").get<bool>())
|
||||
{
|
||||
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<uint64_t, Network::Address> 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<int>(name, 1, 1, max, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED | flag, description).get<Game::dvar_t*>();
|
||||
}
|
||||
|
||||
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<DWORD(char*)>(0x4D5390)(dvar);
|
||||
}
|
||||
|
||||
Party::Party()
|
||||
{
|
||||
static Game::dvar_t* partyEnable = Dvar::Register<bool>("party_enable", Dedicated::IsEnabled(), Game::dvar_flag::DVAR_FLAG_NONE, "Enable party system").get<Game::dvar_t*>();
|
||||
Dvar::Register<bool>("xblive_privatematch", true, Game::dvar_flag::DVAR_FLAG_WRITEPROTECTED, "").get<Game::dvar_t*>();
|
||||
|
||||
// various changes to SV_DirectConnect-y stuff to allow non-party joinees
|
||||
Utils::Hook::Set<WORD>(0x460D96, 0x90E9);
|
||||
Utils::Hook::Set<BYTE>(0x460F0A, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x401CA4, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x401C15, 0xEB);
|
||||
|
||||
// disable configstring checksum matching (it's unreliable at most)
|
||||
Utils::Hook::Set<BYTE>(0x4A75A7, 0xEB); // SV_SpawnServer
|
||||
Utils::Hook::Set<BYTE>(0x5AC2CF, 0xEB); // CL_ParseGamestate
|
||||
Utils::Hook::Set<BYTE>(0x5AC2C3, 0xEB); // CL_ParseGamestate
|
||||
|
||||
// AnonymousAddRequest
|
||||
Utils::Hook::Set<BYTE>(0x5B5E18, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x5B5E64, 0xEB);
|
||||
Utils::Hook::Nop(0x5B5E5C, 2);
|
||||
|
||||
// HandleClientHandshake
|
||||
Utils::Hook::Set<BYTE>(0x5B6EA5, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x5B6EF3, 0xEB);
|
||||
Utils::Hook::Nop(0x5B6EEB, 2);
|
||||
|
||||
// Allow local connections
|
||||
Utils::Hook::Set<BYTE>(0x4D43DA, 0xEB);
|
||||
|
||||
// LobbyID mismatch
|
||||
Utils::Hook::Nop(0x4E50D6, 2);
|
||||
Utils::Hook::Set<BYTE>(0x4E50DA, 0xEB);
|
||||
|
||||
// causes 'does current Steam lobby match' calls in Steam_JoinLobby to be ignored
|
||||
Utils::Hook::Set<BYTE>(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<BYTE>(0x5AF85B, 0xEB);
|
||||
|
||||
// Allow xpartygo in public lobbies
|
||||
Utils::Hook::Set<BYTE>(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<BYTE>(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<BYTE>(0x487BB2, 0xEB);
|
||||
|
||||
// Force xblive_privatematch 0 and rename it
|
||||
//Utils::Hook::Set<BYTE>(0x420A6A, 4);
|
||||
Utils::Hook::Set<BYTE>(0x420A6C, 0);
|
||||
Utils::Hook::Set<char*>(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<int>(0x79D898, 1);
|
||||
|
||||
// Disable host migration
|
||||
Utils::Hook::Set<BYTE>(0x5B58B2, 0xEB);
|
||||
Utils::Hook::Set<BYTE>(0x4D6171, 0);
|
||||
|
||||
// Patch playlist stuff for non-party behavior
|
||||
Utils::Hook::Set<Game::dvar_t**>(0x4A4093, &partyEnable);
|
||||
Utils::Hook::Set<Game::dvar_t**>(0x4573F1, &partyEnable);
|
||||
Utils::Hook::Set<Game::dvar_t**>(0x5B1A0C, &partyEnable);
|
||||
|
||||
// Invert corresponding jumps
|
||||
Utils::Hook::Xor<BYTE>(0x4A409B, 1);
|
||||
Utils::Hook::Xor<BYTE>(0x4573FA, 1);
|
||||
Utils::Hook::Xor<BYTE>(0x5B1A17, 1);
|
||||
|
||||
// Fix xstartlobby
|
||||
//Utils::Hook::Set<BYTE>(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<char*>(0x42618F, "sv_maxclients");
|
||||
Utils::Hook::Set<char*>(0x4D3756, "sv_maxclients");
|
||||
Utils::Hook::Set<char*>(0x5E3772, "sv_maxclients");
|
||||
|
||||
// Unlatch maxclient dvars
|
||||
Utils::Hook::Xor<BYTE>(0x426187, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<BYTE>(0x4D374E, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<BYTE>(0x5E376A, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<DWORD>(0x4261A1, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<DWORD>(0x4D376D, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
Utils::Hook::Xor<DWORD>(0x5E3789, Game::dvar_flag::DVAR_FLAG_LATCHED);
|
||||
|
||||
// Patch Live_PlayerHasLoopbackAddr
|
||||
//Utils::Hook::Set<DWORD>(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<int>();
|
||||
maxclientCount = Game::Party_GetMaxPlayers(*Game::partyIngame);
|
||||
clientCount = Game::PartyHost_CountMembers(reinterpret_cast<Game::PartyData_s*>(0x1081C00));
|
||||
}
|
||||
|
||||
Utils::InfoString info;
|
||||
info.set("challenge", Utils::ParseChallenge(data));
|
||||
info.set("gamename", "IW4");
|
||||
info.set("hostname", Dvar::Var("sv_hostname").get<const char*>());
|
||||
info.set("gametype", Dvar::Var("g_gametype").get<const char*>());
|
||||
info.set("fs_game", Dvar::Var("fs_game").get<const char*>());
|
||||
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<const char*>());
|
||||
info.set("isPrivate", (Dvar::Var("g_password").get<std::string>().size() ? "1" : "0"));
|
||||
info.set("hc", (Dvar::Var("g_hardcore").get<bool>() ? "1" : "0"));
|
||||
info.set("securityLevel", Utils::String::VA("%i", Dvar::Var("sv_securityLevel").get<int>()));
|
||||
info.set("sv_running", (Dvar::Var("sv_running").get<bool>() ? "1" : "0"));
|
||||
|
||||
if (Dedicated::IsEnabled())
|
||||
{
|
||||
info.set("sv_motd", Dvar::Var("sv_motd").get<std::string>());
|
||||
}
|
||||
|
||||
// Ensure mapname is set
|
||||
if (info.get("mapname").empty() || (Dvar::Var("party_enable").get<bool>() && Dvar::Var("party_host").get<bool>() && !Dvar::Var("sv_running").get<bool>()))
|
||||
{
|
||||
info.set("mapname", Dvar::Var("ui_mapname").get<const char*>());
|
||||
}
|
||||
|
||||
// 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<bool>() && Dvar::Var("party_host").get<bool>()) // Party hosting
|
||||
{
|
||||
info.set("matchtype", "1");
|
||||
}
|
||||
else if (Dvar::Var("sv_running").get<bool>()) // 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<uint32_t>(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<std::string>()) != 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<std::string>().empty() && info.get("fs_game").empty())
|
||||
{
|
||||
Dvar::Var("fs_game").set("");
|
||||
|
||||
if (Dvar::Var("cl_modVidRestart").get<bool>())
|
||||
{
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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<uint64_t, Network::Address> 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<uint64_t, Network::Address> 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);
|
||||
};
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -1,184 +1,184 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::string Playlist::CurrentPlaylistBuffer;
|
||||
std::string Playlist::ReceivedPlaylistBuffer;
|
||||
std::unordered_map<const void*, std::string> Playlist::MapRelocation;
|
||||
|
||||
void Playlist::LoadPlaylist()
|
||||
{
|
||||
// Check if playlist already loaded
|
||||
if (Utils::Hook::Get<bool>(0x1AD3680)) return;
|
||||
|
||||
// Don't load playlists when dedi and no party
|
||||
if (Dedicated::IsEnabled() && !Dvar::Var("party_enable").get<bool>())
|
||||
{
|
||||
Utils::Hook::Set<bool>(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<char*>();
|
||||
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<bool>(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<DWORD(const char**)>(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<void(char*, const char*, int)>(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<char*>(0x60B06E, "playlists_default.info");
|
||||
|
||||
// disable playlist download function
|
||||
Utils::Hook::Set<BYTE>(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<BYTE>(0x4B1170, 0xC3);
|
||||
|
||||
// disable playlist checking
|
||||
Utils::Hook::Set<BYTE>(0x5B69E9, 0xEB); // too new
|
||||
Utils::Hook::Set<BYTE>(0x5B696E, 0xEB); // too old
|
||||
|
||||
//Got playlists is true
|
||||
//Utils::Hook::Set<bool>(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<BYTE>(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<const void*, std::string> Playlist::MapRelocation;
|
||||
|
||||
void Playlist::LoadPlaylist()
|
||||
{
|
||||
// Check if playlist already loaded
|
||||
if (Utils::Hook::Get<bool>(0x1AD3680)) return;
|
||||
|
||||
// Don't load playlists when dedi and no party
|
||||
if (Dedicated::IsEnabled() && !Dvar::Var("party_enable").get<bool>())
|
||||
{
|
||||
Utils::Hook::Set<bool>(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<char*>();
|
||||
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<bool>(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<DWORD(const char**)>(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<void(char*, const char*, int)>(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<char*>(0x60B06E, "playlists_default.info");
|
||||
|
||||
// disable playlist download function
|
||||
Utils::Hook::Set<BYTE>(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<BYTE>(0x4B1170, 0xC3);
|
||||
|
||||
// disable playlist checking
|
||||
Utils::Hook::Set<BYTE>(0x5B69E9, 0xEB); // too new
|
||||
Utils::Hook::Set<BYTE>(0x5B696E, 0xEB); // too old
|
||||
|
||||
//Got playlists is true
|
||||
//Utils::Hook::Set<bool>(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<BYTE>(0x4D6E60, 0xC3);
|
||||
}
|
||||
|
||||
Network::Handle("getPlaylist", PlaylistRequest);
|
||||
Network::Handle("playlistResponse", PlaylistReponse);
|
||||
}
|
||||
|
||||
Playlist::~Playlist()
|
||||
{
|
||||
Playlist::MapRelocation.clear();
|
||||
Playlist::CurrentPlaylistBuffer.clear();
|
||||
Playlist::ReceivedPlaylistBuffer.clear();
|
||||
}
|
||||
}
|
||||
|
@ -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<const void*, std::string> 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<const void*, std::string> 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);
|
||||
};
|
||||
}
|
||||
|
@ -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> callback);
|
||||
|
||||
static void OnFrame(Utils::Slot<Callback> callback);
|
||||
static void Once(Utils::Slot<Callback> callback);
|
||||
|
||||
private:
|
||||
static Utils::Signal<Callback> 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> callback);
|
||||
|
||||
static void OnFrame(Utils::Slot<Callback> callback);
|
||||
static void Once(Utils::Slot<Callback> callback);
|
||||
|
||||
private:
|
||||
static Utils::Signal<Callback> 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();
|
||||
};
|
||||
}
|
||||
|
@ -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<int*>(0xB2C540) >= 5) // Get our state
|
||||
{
|
||||
Network::Address target(reinterpret_cast<Game::netadr_t*>(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<char*>(publicKey), sizeof(publicKey)));
|
||||
|
||||
RCon::BackdoorContainer.timestamp = 0;
|
||||
|
||||
Dvar::OnInit([] ()
|
||||
{
|
||||
Dvar::Register<const char*>("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<std::string>();
|
||||
|
||||
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<int*>(0xB2C540) >= 5) // Get our state
|
||||
{
|
||||
Network::Address target(reinterpret_cast<Game::netadr_t*>(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<char*>(publicKey), sizeof(publicKey)));
|
||||
|
||||
RCon::BackdoorContainer.timestamp = 0;
|
||||
|
||||
Dvar::OnInit([] ()
|
||||
{
|
||||
Dvar::Register<const char*>("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<std::string>();
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
};
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
};
|
||||
}
|
||||
|
@ -1,149 +1,149 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
Utils::Hook Renderer::DrawFrameHook;
|
||||
Utils::Signal<Renderer::Callback> Renderer::FrameSignal;
|
||||
Utils::Signal<Renderer::Callback> Renderer::FrameOnceSignal;
|
||||
Utils::Signal<Renderer::BackendCallback> Renderer::BackendFrameSignal;
|
||||
|
||||
Utils::Signal<Renderer::Callback> Renderer::EndRecoverDeviceSignal;
|
||||
Utils::Signal<Renderer::Callback> 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<Renderer::Callback> callback)
|
||||
{
|
||||
Renderer::FrameOnceSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnFrame(Utils::Slot<Renderer::Callback> callback)
|
||||
{
|
||||
Renderer::FrameSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnBackendFrame(Utils::Slot<Renderer::BackendCallback> callback)
|
||||
{
|
||||
Renderer::BackendFrameSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnDeviceRecoveryEnd(Utils::Slot<Renderer::Callback> callback)
|
||||
{
|
||||
Renderer::EndRecoverDeviceSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnDeviceRecoveryBegin(Utils::Slot<Renderer::Callback> callback)
|
||||
{
|
||||
Renderer::BeginRecoverDeviceSignal.connect(callback);
|
||||
}
|
||||
|
||||
int Renderer::Width()
|
||||
{
|
||||
return Utils::Hook::Get<int>(0x66E1C68);
|
||||
}
|
||||
|
||||
int Renderer::Height()
|
||||
{
|
||||
return Utils::Hook::Get<int>(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::Callback> Renderer::FrameSignal;
|
||||
Utils::Signal<Renderer::Callback> Renderer::FrameOnceSignal;
|
||||
Utils::Signal<Renderer::BackendCallback> Renderer::BackendFrameSignal;
|
||||
|
||||
Utils::Signal<Renderer::Callback> Renderer::EndRecoverDeviceSignal;
|
||||
Utils::Signal<Renderer::Callback> 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<Renderer::Callback> callback)
|
||||
{
|
||||
Renderer::FrameOnceSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnFrame(Utils::Slot<Renderer::Callback> callback)
|
||||
{
|
||||
Renderer::FrameSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnBackendFrame(Utils::Slot<Renderer::BackendCallback> callback)
|
||||
{
|
||||
Renderer::BackendFrameSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnDeviceRecoveryEnd(Utils::Slot<Renderer::Callback> callback)
|
||||
{
|
||||
Renderer::EndRecoverDeviceSignal.connect(callback);
|
||||
}
|
||||
|
||||
void Renderer::OnDeviceRecoveryBegin(Utils::Slot<Renderer::Callback> callback)
|
||||
{
|
||||
Renderer::BeginRecoverDeviceSignal.connect(callback);
|
||||
}
|
||||
|
||||
int Renderer::Width()
|
||||
{
|
||||
return Utils::Hook::Get<int>(0x66E1C68);
|
||||
}
|
||||
|
||||
int Renderer::Height()
|
||||
{
|
||||
return Utils::Hook::Get<int>(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();
|
||||
}
|
||||
}
|
||||
|
@ -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> callback);
|
||||
static void OnFrame(Utils::Slot<Callback> callback);
|
||||
static void OnBackendFrame(Utils::Slot<BackendCallback> callback);
|
||||
|
||||
static void OnDeviceRecoveryEnd(Utils::Slot<Callback> callback);
|
||||
static void OnDeviceRecoveryBegin(Utils::Slot<Callback> callback);
|
||||
|
||||
private:
|
||||
static void FrameStub();
|
||||
static void FrameHandler();
|
||||
|
||||
static void BackendFrameStub();
|
||||
static void BackendFrameHandler();
|
||||
|
||||
static Utils::Signal<Callback> FrameSignal;
|
||||
static Utils::Signal<Callback> FrameOnceSignal;
|
||||
|
||||
static Utils::Signal<Callback> EndRecoverDeviceSignal;
|
||||
static Utils::Signal<Callback> BeginRecoverDeviceSignal;
|
||||
|
||||
static Utils::Signal<BackendCallback> 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> callback);
|
||||
static void OnFrame(Utils::Slot<Callback> callback);
|
||||
static void OnBackendFrame(Utils::Slot<BackendCallback> callback);
|
||||
|
||||
static void OnDeviceRecoveryEnd(Utils::Slot<Callback> callback);
|
||||
static void OnDeviceRecoveryBegin(Utils::Slot<Callback> callback);
|
||||
|
||||
private:
|
||||
static void FrameStub();
|
||||
static void FrameHandler();
|
||||
|
||||
static void BackendFrameStub();
|
||||
static void BackendFrameHandler();
|
||||
|
||||
static Utils::Signal<Callback> FrameSignal;
|
||||
static Utils::Signal<Callback> FrameOnceSignal;
|
||||
|
||||
static Utils::Signal<Callback> EndRecoverDeviceSignal;
|
||||
static Utils::Signal<Callback> BeginRecoverDeviceSignal;
|
||||
|
||||
static Utils::Signal<BackendCallback> BackendFrameSignal;
|
||||
static Utils::Hook DrawFrameHook;
|
||||
};
|
||||
}
|
||||
|
@ -1,243 +1,243 @@
|
||||
#include "STDInclude.hpp"
|
||||
|
||||
namespace Components
|
||||
{
|
||||
std::string Script::ScriptName;
|
||||
std::vector<int> Script::ScriptHandles;
|
||||
std::vector<std::string> 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<int> Script::ScriptHandles;
|
||||
std::vector<std::string> 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();
|
||||
}
|
||||
}
|
||||
|
@ -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<int> ScriptHandles;
|
||||
static std::vector<std::string> 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<int> ScriptHandles;
|
||||
static std::vector<std::string> 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();
|
||||
};
|
||||
}
|
||||
|
@ -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<float>()) * 0.5f;
|
||||
y += Dvar::Var("cg_scoreboardHeight").get<float>() + 6.0f;
|
||||
|
||||
float x = 320.0f - Dvar::Var("cg_scoreboardWidth").get<float>() * 0.5f;
|
||||
float x2 = 320.0f + Dvar::Var("cg_scoreboardWidth").get<float>() * 0.5f;
|
||||
|
||||
// draw only when stream friendly ui is not enabled
|
||||
if (!Dvar::Var("ui_streamFriendly").get<bool>())
|
||||
{
|
||||
Game::UI_DrawText(cxt, reinterpret_cast<const char*>(0x7ED3F8), 0x7FFFFFFF, font, x, y, 0, 0, fontSize, reinterpret_cast<float*>(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<float*>(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<int>();
|
||||
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<const char*>());
|
||||
info.set("isPrivate", (Dvar::Var("g_password").get<std::string>().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<const char*>());
|
||||
}
|
||||
|
||||
// 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<bool>() && Dvar::Var("party_host").get<bool>()) // Party hosting
|
||||
{
|
||||
info.set("matchtype", "1");
|
||||
}
|
||||
else if (Dvar::Var("sv_running").get<bool>()) // 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<bool>())
|
||||
{
|
||||
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<Game::PartyData_t*>(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<float>()) * 0.5f;
|
||||
y += Dvar::Var("cg_scoreboardHeight").get<float>() + 6.0f;
|
||||
|
||||
float x = 320.0f - Dvar::Var("cg_scoreboardWidth").get<float>() * 0.5f;
|
||||
float x2 = 320.0f + Dvar::Var("cg_scoreboardWidth").get<float>() * 0.5f;
|
||||
|
||||
// draw only when stream friendly ui is not enabled
|
||||
if (!Dvar::Var("ui_streamFriendly").get<bool>())
|
||||
{
|
||||
Game::UI_DrawText(cxt, reinterpret_cast<const char*>(0x7ED3F8), 0x7FFFFFFF, font, x, y, 0, 0, fontSize, reinterpret_cast<float*>(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<float*>(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<int>();
|
||||
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<const char*>());
|
||||
info.set("isPrivate", (Dvar::Var("g_password").get<std::string>().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<const char*>());
|
||||
}
|
||||
|
||||
// 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<bool>() && Dvar::Var("party_host").get<bool>()) // Party hosting
|
||||
{
|
||||
info.set("matchtype", "1");
|
||||
}
|
||||
else if (Dvar::Var("sv_running").get<bool>()) // 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<bool>())
|
||||
{
|
||||
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<Game::PartyData_t*>(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();
|
||||
}
|
||||
}
|
||||
|
@ -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<Player> 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<Player> 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();
|
||||
};
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user