From 6dc46a4289046e97e44eb17267844512dffc53d6 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Tue, 13 Sep 2016 20:55:10 +0200 Subject: [PATCH 1/5] Begin implementing surface loading for CODO maps --- deps/fmt | 2 +- deps/mongoose | 2 +- deps/protobuf | 2 +- src/Components/Loader.cpp | 1 + src/Components/Loader.hpp | 1 + src/Components/Modules/ModelSurfs.cpp | 124 ++++++++++++++++++++++++++ src/Components/Modules/ModelSurfs.hpp | 25 ++++++ src/Components/Modules/Renderer.cpp | 28 ++++++ src/Components/Modules/Renderer.hpp | 7 ++ src/Game/Functions.cpp | 3 + src/Game/Functions.hpp | 9 ++ 11 files changed, 201 insertions(+), 3 deletions(-) create mode 100644 src/Components/Modules/ModelSurfs.cpp create mode 100644 src/Components/Modules/ModelSurfs.hpp diff --git a/deps/fmt b/deps/fmt index 0d25f6fc..1fb0586b 160000 --- a/deps/fmt +++ b/deps/fmt @@ -1 +1 @@ -Subproject commit 0d25f6fcbbf0a867b939a5501965ee4462b21ee6 +Subproject commit 1fb0586b065c4202e976528a6bdc6384dc56dc04 diff --git a/deps/mongoose b/deps/mongoose index 2a541175..d6dda04f 160000 --- a/deps/mongoose +++ b/deps/mongoose @@ -1 +1 @@ -Subproject commit 2a541175b56a1aeecd2dc8474f981923ef580af6 +Subproject commit d6dda04f4f00016e12618d0b55c972498e6249b5 diff --git a/deps/protobuf b/deps/protobuf index 14e74f6a..86fcd879 160000 --- a/deps/protobuf +++ b/deps/protobuf @@ -1 +1 @@ -Subproject commit 14e74f6a21f2726d25e0e679c59d569f6bc8fe8e +Subproject commit 86fcd879b38505446799b2f2a2929415ddad620a diff --git a/src/Components/Loader.cpp b/src/Components/Loader.cpp index 90e40d33..468c8e7a 100644 --- a/src/Components/Loader.cpp +++ b/src/Components/Loader.cpp @@ -56,6 +56,7 @@ namespace Components Loader::Register(new BitMessage()); #endif Loader::Register(new FileSystem()); + Loader::Register(new ModelSurfs()); Loader::Register(new PlayerName()); Loader::Register(new QuickPatch()); Loader::Register(new ServerInfo()); diff --git a/src/Components/Loader.hpp b/src/Components/Loader.hpp index 00883999..71cd546b 100644 --- a/src/Components/Loader.hpp +++ b/src/Components/Loader.hpp @@ -69,6 +69,7 @@ namespace Components #include "Modules\Singleton.hpp" #include "Modules\BitMessage.hpp" #include "Modules\FileSystem.hpp" +#include "Modules\ModelSurfs.hpp" #include "Modules\PlayerName.hpp" #include "Modules\QuickPatch.hpp" #include "Modules\ServerInfo.hpp" diff --git a/src/Components/Modules/ModelSurfs.cpp b/src/Components/Modules/ModelSurfs.cpp new file mode 100644 index 00000000..abfa6e68 --- /dev/null +++ b/src/Components/Modules/ModelSurfs.cpp @@ -0,0 +1,124 @@ +#include "STDInclude.hpp" + +namespace Components +{ + std::map ModelSurfs::BufferMap; + + IUnknown* ModelSurfs::GetBuffer(void* buffer) + { + return ModelSurfs::BufferMap[buffer]; + } + + void ModelSurfs::SetBuffer(char /*streamHandle*/, void* buffer, IUnknown** bufferOut, int* offsetOut) + { + *offsetOut; + *bufferOut = ModelSurfs::BufferMap[buffer]; + } + + void ModelSurfs::ReleaseModelSurf(Game::XAssetHeader header) + { + Game::XModelSurfs* surfaces = header.surfaces; + + (surfaces); + } + + 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*/) + { + Game::XModelSurfs* surfaces = header.surfaces; + + // TODO: Recreate all buffers here + (surfaces); + }, nullptr, false); + } + + __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::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(0x5BC050, 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()); + } +} diff --git a/src/Components/Modules/ModelSurfs.hpp b/src/Components/Modules/ModelSurfs.hpp new file mode 100644 index 00000000..6b253c0b --- /dev/null +++ b/src/Components/Modules/ModelSurfs.hpp @@ -0,0 +1,25 @@ +namespace Components +{ + class ModelSurfs : public Component + { + public: + ModelSurfs(); + ~ModelSurfs(); + + private: + + static std::map BufferMap; + + 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 GetIndexBufferStub(); + static void GetIndexBufferStub2(); + static void GetVertexBufferStub(); + }; +} diff --git a/src/Components/Modules/Renderer.cpp b/src/Components/Modules/Renderer.cpp index 28439c72..424e72a1 100644 --- a/src/Components/Modules/Renderer.cpp +++ b/src/Components/Modules/Renderer.cpp @@ -7,6 +7,9 @@ namespace Components wink::signal> Renderer::FrameOnceSignal; wink::signal> Renderer::BackendFrameSignal; + wink::signal> Renderer::EndRecoverDeviceSignal; + wink::signal> Renderer::BeginRecoverDeviceSignal; + __declspec(naked) void Renderer::FrameStub() { __asm @@ -62,6 +65,16 @@ namespace Components Renderer::BackendFrameSignal.connect(callback); } + void Renderer::OnDeviceRecoveryEnd(Renderer::Callback* callback) + { + Renderer::EndRecoverDeviceSignal.connect(callback); + } + + void Renderer::OnDeviceRecoveryBegin(Renderer::Callback* callback) + { + Renderer::BeginRecoverDeviceSignal.connect(callback); + } + int Renderer::Width() { return Utils::Hook::Get(0x66E1C68); @@ -106,6 +119,18 @@ namespace Components 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() @@ -114,5 +139,8 @@ namespace Components Renderer::BackendFrameSignal.clear(); Renderer::FrameOnceSignal.clear(); Renderer::FrameSignal.clear(); + + Renderer::EndRecoverDeviceSignal.clear(); + Renderer::BeginRecoverDeviceSignal.clear(); } } diff --git a/src/Components/Modules/Renderer.hpp b/src/Components/Modules/Renderer.hpp index fc3c4ba6..b1ceecf0 100644 --- a/src/Components/Modules/Renderer.hpp +++ b/src/Components/Modules/Renderer.hpp @@ -20,6 +20,9 @@ namespace Components static void OnFrame(Callback* callback); static void OnBackendFrame(BackendCallback* callback); + static void OnDeviceRecoveryEnd(Callback* callback); + static void OnDeviceRecoveryBegin(Callback* callback); + private: static void FrameStub(); static void FrameHandler(); @@ -29,6 +32,10 @@ namespace Components static wink::signal> FrameSignal; static wink::signal> FrameOnceSignal; + + static wink::signal> EndRecoverDeviceSignal; + static wink::signal> BeginRecoverDeviceSignal; + static wink::signal> BackendFrameSignal; static Utils::Hook DrawFrameHook; }; diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 53714476..3bcbee85 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -31,6 +31,8 @@ namespace Game Con_DrawMiniConsole_t Con_DrawMiniConsole = (Con_DrawMiniConsole_t)0x464F30; Con_DrawSolidConsole_t Con_DrawSolidConsole = (Con_DrawSolidConsole_t)0x5A5040; + DB_BeginRecoverLostDevice_t DB_BeginRecoverLostDevice = (DB_BeginRecoverLostDevice_t)0x4BFF90; + DB_EndRecoverLostDevice_t DB_EndRecoverLostDevice = (DB_EndRecoverLostDevice_t)0x46B660; DB_EnumXAssets_t DB_EnumXAssets = (DB_EnumXAssets_t)0x4B76D0; DB_EnumXAssets_Internal_t DB_EnumXAssets_Internal = (DB_EnumXAssets_Internal_t)0x5BB0A0; DB_FindXAssetHeader_t DB_FindXAssetHeader = (DB_FindXAssetHeader_t)0x407930; @@ -40,6 +42,7 @@ namespace Game DB_IsXAssetDefault_t DB_IsXAssetDefault = (DB_IsXAssetDefault_t)0x48E6A0; DB_LoadXAssets_t DB_LoadXAssets = (DB_LoadXAssets_t)0x4E5930; DB_ReadXFileUncompressed_t DB_ReadXFileUncompressed = (DB_ReadXFileUncompressed_t)0x4705E0; + DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers = (DB_ReleaseXAssetHandler_t*)0x799AB8; Dvar_RegisterBool_t Dvar_RegisterBool = (Dvar_RegisterBool_t)0x4CE1A0; Dvar_RegisterFloat_t Dvar_RegisterFloat = (Dvar_RegisterFloat_t)0x648440; diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index a5adfcab..bb3d579f 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -63,6 +63,12 @@ namespace Game typedef void (__cdecl * Con_DrawSolidConsole_t)(); extern Con_DrawSolidConsole_t Con_DrawSolidConsole; + typedef void(__cdecl * DB_BeginRecoverLostDevice_t)(); + extern DB_BeginRecoverLostDevice_t DB_BeginRecoverLostDevice; + + typedef void(__cdecl * DB_EndRecoverLostDevice_t)(); + extern DB_EndRecoverLostDevice_t DB_EndRecoverLostDevice; + typedef void(__cdecl * DB_EnumXAssets_t)(XAssetType type, void(*)(XAssetHeader, void *), void* userdata, bool overrides); extern DB_EnumXAssets_t DB_EnumXAssets; @@ -90,6 +96,9 @@ namespace Game typedef void(__cdecl * DB_ReadXFileUncompressed_t)(void* buffer, int size); extern DB_ReadXFileUncompressed_t DB_ReadXFileUncompressed; + typedef void(__cdecl * DB_ReleaseXAssetHandler_t)(XAssetHeader header); + extern DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers; + typedef dvar_t* (__cdecl * Dvar_RegisterBool_t)(const char* name, bool default, int flags, const char* description); extern Dvar_RegisterBool_t Dvar_RegisterBool; From ab3c75cb5be76f7236fa2a652c7115d24f0f5ee9 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Tue, 13 Sep 2016 21:41:10 +0200 Subject: [PATCH 2/5] More surface loading stuff --- src/Components/Modules/ModelSurfs.cpp | 40 +++++++++++++++++++++++++-- src/Components/Modules/ModelSurfs.hpp | 4 +++ src/Game/Functions.cpp | 1 + src/Game/Functions.hpp | 3 ++ 4 files changed, 45 insertions(+), 3 deletions(-) diff --git a/src/Components/Modules/ModelSurfs.cpp b/src/Components/Modules/ModelSurfs.cpp index abfa6e68..63fd6647 100644 --- a/src/Components/Modules/ModelSurfs.cpp +++ b/src/Components/Modules/ModelSurfs.cpp @@ -15,10 +15,17 @@ namespace Components *bufferOut = ModelSurfs::BufferMap[buffer]; } + // TODO: Implement + bool ModelSurfs::LoadXSurfaces(Game::XModel* model) + { + (model); + return false; + } + + // TODO: Implement void ModelSurfs::ReleaseModelSurf(Game::XAssetHeader header) { Game::XModelSurfs* surfaces = header.surfaces; - (surfaces); } @@ -32,17 +39,24 @@ namespace Components ModelSurfs::BufferMap.clear(); } + // TODO: Implement void ModelSurfs::EndRecover() { Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_XMODELSURFS, [] (Game::XAssetHeader header, void* /*userdata*/) { Game::XModelSurfs* surfaces = header.surfaces; - - // TODO: Recreate all buffers here (surfaces); }, nullptr, false); } + void ModelSurfs::XModelSurfsFixup(Game::XModel* model) + { + if (!ModelSurfs::LoadXSurfaces(model)) + { + Game::DB_XModelSurfsFixup(model); + } + } + __declspec(naked) void ModelSurfs::GetIndexBufferStub() { __asm @@ -82,6 +96,24 @@ namespace Components } } + __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 @@ -112,6 +144,8 @@ namespace Components 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(0x5BC050, ModelSurfs::GetIndexBufferStub, HOOK_JUMP).Install()->Quick(); Utils::Hook(0x558E70, ModelSurfs::GetIndexBufferStub2, HOOK_CALL).Install()->Quick(); Utils::Hook(0x5BC050, ModelSurfs::GetVertexBufferStub, HOOK_JUMP).Install()->Quick(); diff --git a/src/Components/Modules/ModelSurfs.hpp b/src/Components/Modules/ModelSurfs.hpp index 6b253c0b..63cb2f5a 100644 --- a/src/Components/Modules/ModelSurfs.hpp +++ b/src/Components/Modules/ModelSurfs.hpp @@ -18,6 +18,10 @@ namespace Components static IUnknown* GetBuffer(void* buffer); static void SetBuffer(char streamHandle, void* buffer, IUnknown** bufferOut, int* offsetOut); + static bool LoadXSurfaces(Game::XModel* model); + static void XModelSurfsFixup(Game::XModel* model); + + static void GetIndexBaseStub(); static void GetIndexBufferStub(); static void GetIndexBufferStub2(); static void GetVertexBufferStub(); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 3bcbee85..2eb9c8ce 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -43,6 +43,7 @@ namespace Game DB_LoadXAssets_t DB_LoadXAssets = (DB_LoadXAssets_t)0x4E5930; DB_ReadXFileUncompressed_t DB_ReadXFileUncompressed = (DB_ReadXFileUncompressed_t)0x4705E0; DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers = (DB_ReleaseXAssetHandler_t*)0x799AB8; + DB_XModelSurfsFixup_t DB_XModelSurfsFixup = (DB_XModelSurfsFixup_t)0x5BAC50; Dvar_RegisterBool_t Dvar_RegisterBool = (Dvar_RegisterBool_t)0x4CE1A0; Dvar_RegisterFloat_t Dvar_RegisterFloat = (Dvar_RegisterFloat_t)0x648440; diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index bb3d579f..4b4e8bd2 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -99,6 +99,9 @@ namespace Game typedef void(__cdecl * DB_ReleaseXAssetHandler_t)(XAssetHeader header); extern DB_ReleaseXAssetHandler_t* DB_ReleaseXAssetHandlers; + typedef void(__cdecl * DB_XModelSurfsFixup_t)(XModel* model); + extern DB_XModelSurfsFixup_t DB_XModelSurfsFixup; + typedef dvar_t* (__cdecl * Dvar_RegisterBool_t)(const char* name, bool default, int flags, const char* description); extern Dvar_RegisterBool_t Dvar_RegisterBool; From 52487aa2245dbfffa1c148f51d7edeafecf19e66 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 14 Sep 2016 23:01:53 +0200 Subject: [PATCH 3/5] Finish surface loading, but not test ready --- src/Components/Modules/Download.cpp | 2 +- src/Components/Modules/FileSystem.cpp | 68 +++++++--- src/Components/Modules/FileSystem.hpp | 20 +-- src/Components/Modules/Menus.cpp | 4 +- src/Components/Modules/Menus.hpp | 4 +- src/Components/Modules/ModelSurfs.cpp | 174 ++++++++++++++++++++++++-- src/Components/Modules/ModelSurfs.hpp | 6 +- src/Game/Functions.cpp | 34 +++++ src/Game/Functions.hpp | 6 + src/Game/Structs.hpp | 42 ++++++- src/Utils/Memory.cpp | 18 +-- src/Utils/Memory.hpp | 1 + src/Utils/Stream.hpp | 2 +- 13 files changed, 328 insertions(+), 53 deletions(-) diff --git a/src/Components/Modules/Download.cpp b/src/Components/Modules/Download.cpp index 5e819aeb..e4d14444 100644 --- a/src/Components/Modules/Download.cpp +++ b/src/Components/Modules/Download.cpp @@ -522,7 +522,7 @@ namespace Components if (file.Exists()) { - std::string& buffer = file.GetBuffer(); + std::string buffer = file.GetBuffer(); mg_printf(nc, "HTTP/1.1 200 OK\r\n" diff --git a/src/Components/Modules/FileSystem.cpp b/src/Components/Modules/FileSystem.cpp index f67126bb..f11c3c3b 100644 --- a/src/Components/Modules/FileSystem.cpp +++ b/src/Components/Modules/FileSystem.cpp @@ -2,24 +2,64 @@ namespace Components { - void FileSystem::File::Read() + FileSystem::File::File(std::string file) : Name(file), Handle(0) + { + this->Size = Game::FS_FOpenFileRead(this->Name.data(), &this->Handle, 0); + } + + FileSystem::File::~File() { - char* buffer = nullptr; - int size = Game::FS_ReadFile(this->FilePath.data(), &buffer); - - this->Buffer.clear(); - - if (size < 0) + if (this->Exists()) { - if (buffer) - { - Game::FS_FreeFile(buffer); - } + Game::FS_FCloseFile(this->Handle); } - else + } + + bool FileSystem::File::Exists() + { + return (this->Size > 0); + } + + std::string FileSystem::File::GetName() + { + return this->Name; + } + + std::string FileSystem::File::GetBuffer() + { + Utils::Memory::Allocator allocator; + if (!this->Exists()) return std::string(); + + int position = Game::FS_FTell(this->Handle); + this->Seek(0, FS_SEEK_SET); + + char* buffer = allocator.AllocateArray(this->Size); + if (!FileSystem::File::Read(buffer, this->Size)) { - this->Buffer.append(buffer, size); - Game::FS_FreeFile(buffer); + this->Seek(position, FS_SEEK_SET); + return std::string(); + } + + this->Seek(position, FS_SEEK_SET); + + return std::string(buffer, this->Size); + } + + bool FileSystem::File::Read(void* buffer, size_t size) + { + if (!this->Exists() || static_cast(this->Size) < size || Game::FS_Read(buffer, size, this->Handle) != static_cast(size)) + { + return false; + } + + return true; + } + + void FileSystem::File::Seek(int offset, int origin) + { + if (this->Exists()) + { + Game::FS_Seek(this->Handle, offset, origin); } } diff --git a/src/Components/Modules/FileSystem.hpp b/src/Components/Modules/FileSystem.hpp index 9a074268..98adc7dc 100644 --- a/src/Components/Modules/FileSystem.hpp +++ b/src/Components/Modules/FileSystem.hpp @@ -7,18 +7,20 @@ namespace Components class File { public: - File() {}; - File(std::string file) : FilePath(file) { this->Read(); }; + File() : Size(-1), Name(), Handle(0) {}; + File(std::string file); + ~File(); - bool Exists() { return !this->Buffer.empty(); }; - std::string GetName() { return this->FilePath; }; - std::string& GetBuffer() { return this->Buffer; }; + bool Exists(); + std::string GetName(); + std::string GetBuffer(); + bool Read(void* buffer, size_t size); + void Seek(int offset, int origin); private: - std::string FilePath; - std::string Buffer; - - void Read(); + int Handle; + int Size; + std::string Name; }; class FileWriter diff --git a/src/Components/Modules/Menus.cpp b/src/Components/Modules/Menus.cpp index 6259bc30..21a6efe5 100644 --- a/src/Components/Modules/Menus.cpp +++ b/src/Components/Modules/Menus.cpp @@ -25,7 +25,7 @@ namespace Components return i; } - Game::script_t* Menus::LoadMenuScript(std::string name, std::string& buffer) + Game::script_t* Menus::LoadMenuScript(std::string name, std::string buffer) { Game::script_t* script = Game::Script_Alloc(sizeof(Game::script_t) + 1 + buffer.length()); @@ -52,7 +52,7 @@ namespace Components return script; } - int Menus::LoadMenuSource(std::string name, std::string& buffer) + int Menus::LoadMenuSource(std::string name, std::string buffer) { int handle = Menus::ReserveSourceHandle(); if (!Menus::IsValidSourceHandle(handle)) return 0; // No free source slot! diff --git a/src/Components/Modules/Menus.hpp b/src/Components/Modules/Menus.hpp index 01fe54e4..8711d718 100644 --- a/src/Components/Modules/Menus.hpp +++ b/src/Components/Modules/Menus.hpp @@ -30,8 +30,8 @@ namespace Components static std::vector LoadMenu(Game::menuDef_t* menudef); static std::vector LoadMenu(std::string file); - static Game::script_t* LoadMenuScript(std::string name, std::string& buffer); - static int LoadMenuSource(std::string name, std::string& buffer); + static 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); diff --git a/src/Components/Modules/ModelSurfs.cpp b/src/Components/Modules/ModelSurfs.cpp index 63fd6647..7d18fca1 100644 --- a/src/Components/Modules/ModelSurfs.cpp +++ b/src/Components/Modules/ModelSurfs.cpp @@ -3,6 +3,7 @@ namespace Components { std::map ModelSurfs::BufferMap; + std::map ModelSurfs::AllocMap; IUnknown* ModelSurfs::GetBuffer(void* buffer) { @@ -11,22 +12,172 @@ namespace Components void ModelSurfs::SetBuffer(char /*streamHandle*/, void* buffer, IUnknown** bufferOut, int* offsetOut) { - *offsetOut; + *offsetOut = 0; *bufferOut = ModelSurfs::BufferMap[buffer]; } - // TODO: Implement - bool ModelSurfs::LoadXSurfaces(Game::XModel* model) + void ModelSurfs::CreateBuffers(Game::XModelSurfs* surfs) { - (model); - return false; + for (int i = 0; i < surfs->numSurfaces; ++i) + { + Game::XSurface* surface = &surfs->surfaces[i]; + if (surface->streamHandle == 0xFF) + { + IDirect3DVertexBuffer9* vertexBuffer; + IDirect3DIndexBuffer9* indexBuffer; + + Game::Load_VertexBuffer(surface->vertexBuffer, &vertexBuffer, surface->numVertices * 32); + Game::Load_IndexBuffer(surface->indexBuffer, &indexBuffer, surface->numPrimitives * 3); + + ModelSurfs::BufferMap[surface->vertexBuffer] = vertexBuffer; + ModelSurfs::BufferMap[surface->indexBuffer] = indexBuffer; + } + } + } + + Game::XModelSurfs* ModelSurfs::LoadXModelSurfaces(std::string name) + { + Utils::Memory::Allocator allocator; + FileSystem::File model(fmt::sprintf("models/%s", name.data())); + + if (!model.Exists()) + { + Logger::Error("Loading model %s failed!", name.data()); + } + + Game::CModelHeader header; + if (!model.Read(&header, sizeof header)) + { + Logger::Error("Reading header for model %s failed!", name.data()); + } + + if (header.version != 1) + { + Logger::Error("Model %s has an invalid version %d (should be 1)!", name.data(), header.version); + } + + // Allocate section buffers + header.sectionHeader[Game::SECTION_MAIN].buffer = Utils::Memory::Allocate(header.sectionHeader[Game::SECTION_MAIN].size); + header.sectionHeader[Game::SECTION_INDEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_INDEX].size, 16); + header.sectionHeader[Game::SECTION_VERTEX].buffer = Utils::Memory::AllocateAlign(header.sectionHeader[Game::SECTION_VERTEX].size, 16); + header.sectionHeader[Game::SECTION_FIXUP].buffer = allocator.AllocateArray(header.sectionHeader[Game::SECTION_FIXUP].size); + + // Load section data + for (int i = 0; i < ARRAY_SIZE(header.sectionHeader); ++i) + { + model.Seek(header.sectionHeader[i].offset, FS_SEEK_SET); + if (!model.Read(header.sectionHeader[i].buffer, header.sectionHeader[i].size)) + { + Logger::Error("Reading section %d for model %s failed!", i, name.data()); + } + } + + // Fixup sections + unsigned int* fixups = reinterpret_cast(header.sectionHeader[Game::SECTION_FIXUP].buffer); + for (int i = 0; i < 3; ++i) + { + Game::CModelSectionHeader* section = &header.sectionHeader[i]; + for (int j = section->fixupStart; j < section->fixupStart + section->fixupCount; ++j) + { + unsigned int fixup = fixups[i]; + *reinterpret_cast(reinterpret_cast(section->buffer) + (fixup >> 3)) += reinterpret_cast(header.sectionHeader[fixup & 3].buffer); + } + } + + // Store allocation data (not sure if this is correct) + Game::CModelAllocData* allocationData = Utils::Memory::AllocateArray(); + allocationData->mainArray = header.sectionHeader[Game::SECTION_MAIN].buffer; + allocationData->indexBuffer = header.sectionHeader[Game::SECTION_INDEX].buffer; + allocationData->vertexBuffer = header.sectionHeader[Game::SECTION_VERTEX].buffer; + + ModelSurfs::AllocMap[allocationData->vertexBuffer] = allocationData; + *reinterpret_cast(reinterpret_cast(allocationData->mainArray) + 44) = allocationData; + + Assert_Size(Game::XSurface, 64); + Game::XModelSurfs* modelSurfs = reinterpret_cast(allocationData->mainArray); + Game::XSurface* tempSurfaces = allocator.AllocateArray(modelSurfs->numSurfaces); + char* surfaceData = reinterpret_cast(modelSurfs->surfaces); + + for (int i = 0; i < modelSurfs->numSurfaces; ++i) + { + memcpy(&tempSurfaces[i], surfaceData + (i * 84), 12); + memcpy(&tempSurfaces[i].indexBuffer, surfaceData + (i * 84) + 16, 20); + memcpy(&tempSurfaces[i].numCT, surfaceData + (i * 84) + 40, 8); + memcpy(&tempSurfaces[i].pad5, surfaceData + (i * 84) + 52, 24); + tempSurfaces[i].streamHandle = 0xFF; // Fake handle for buffer interception + } + + 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->lods[i].surfaces; + + if (!surfs->surfaces) + { + Game::XModelSurfs* newSurfs = ModelSurfs::LoadXModelSurfaces(surfs->name); + + surfs->surfaces = newSurfs->surfaces; + surfs->numSurfaces = newSurfs->numSurfaces; + + model->lods[i].surfaces = newSurfs; + memcpy(model->lods[i].pad3, newSurfs->pad, 24); + + short numSurfs = static_cast(newSurfs->numSurfaces); + model->lods[i].someCount = numSurfs; + model->lods[i].someTotalCount = surfCount; + surfCount += numSurfs; + } + } + + return changed; } - // TODO: Implement void ModelSurfs::ReleaseModelSurf(Game::XAssetHeader header) { - Game::XModelSurfs* surfaces = header.surfaces; - (surfaces); + for (int i = 0; i < header.surfaces->numSurfaces && header.surfaces->surfaces; ++i) + { + Game::XSurface* surface = &header.surfaces->surfaces[i]; + if (surface->streamHandle == 0xFF) + { + auto buffer = ModelSurfs::BufferMap.find(surface->indexBuffer); + if (buffer != ModelSurfs::BufferMap.end()) + { + buffer->second->Release(); + ModelSurfs::BufferMap.erase(buffer); + } + + buffer = ModelSurfs::BufferMap.find(surface->vertexBuffer); + if (buffer != ModelSurfs::BufferMap.end()) + { + buffer->second->Release(); + ModelSurfs::BufferMap.erase(buffer); + } + + auto allocData = ModelSurfs::AllocMap.find(surface->vertexBuffer); + if (allocData != ModelSurfs::AllocMap.end()) + { + Utils::Memory::Free(allocData->second->indexBuffer); + Utils::Memory::Free(allocData->second->vertexBuffer); + Utils::Memory::Free(allocData->second->mainArray); + Utils::Memory::Free(allocData->second); + + ModelSurfs::AllocMap.erase(allocData); + } + } + } } void ModelSurfs::BeginRecover() @@ -39,19 +190,17 @@ namespace Components ModelSurfs::BufferMap.clear(); } - // TODO: Implement void ModelSurfs::EndRecover() { Game::DB_EnumXAssets_Internal(Game::XAssetType::ASSET_TYPE_XMODELSURFS, [] (Game::XAssetHeader header, void* /*userdata*/) { - Game::XModelSurfs* surfaces = header.surfaces; - (surfaces); + ModelSurfs::CreateBuffers(header.surfaces); }, nullptr, false); } void ModelSurfs::XModelSurfsFixup(Game::XModel* model) { - if (!ModelSurfs::LoadXSurfaces(model)) + if (!ModelSurfs::LoadSurfaces(model)) { Game::DB_XModelSurfsFixup(model); } @@ -154,5 +303,6 @@ namespace Components ModelSurfs::~ModelSurfs() { assert(ModelSurfs::BufferMap.empty()); + assert(ModelSurfs::AllocMap.empty()); } } diff --git a/src/Components/Modules/ModelSurfs.hpp b/src/Components/Modules/ModelSurfs.hpp index 63cb2f5a..7754a195 100644 --- a/src/Components/Modules/ModelSurfs.hpp +++ b/src/Components/Modules/ModelSurfs.hpp @@ -7,8 +7,8 @@ namespace Components ~ModelSurfs(); private: - static std::map BufferMap; + static std::map AllocMap; static void ReleaseModelSurf(Game::XAssetHeader header); @@ -18,7 +18,9 @@ namespace Components static IUnknown* GetBuffer(void* buffer); static void SetBuffer(char streamHandle, void* buffer, IUnknown** bufferOut, int* offsetOut); - static bool LoadXSurfaces(Game::XModel* model); + 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(); diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 2eb9c8ce..59a89795 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -135,6 +135,7 @@ namespace Game PartyHost_GetMemberName_t PartyHost_GetMemberName = (PartyHost_GetMemberName_t)0x44BE90; R_AddCmdDrawStretchPic_t R_AddCmdDrawStretchPic = (R_AddCmdDrawStretchPic_t)0x509770; + R_AllocStaticIndexBuffer_t R_AllocStaticIndexBuffer = (R_AllocStaticIndexBuffer_t)0x51E7A0; R_Cinematic_StartPlayback_Now_t R_Cinematic_StartPlayback_Now = (R_Cinematic_StartPlayback_Now_t)0x51C5B0; R_RegisterFont_t R_RegisterFont = (R_RegisterFont_t)0x505670; R_AddCmdDrawText_t R_AddCmdDrawText = (R_AddCmdDrawText_t)0x509D80; @@ -465,4 +466,37 @@ namespace Game pop esi } } + + void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count) + { + if (Components::Dvar::Var("r_loadForRenderer").Get()) + { + void* buffer = R_AllocStaticIndexBuffer(storeHere, 2 * count); + memcpy(buffer, data, 2 * count); + + if (storeHere && *storeHere) + { + (*storeHere)->Unlock(); + } + } + } + + void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len) + { + DWORD func = 0x5112C0; + + __asm + { + push edi + + mov eax, len + mov edi, where + push data + + call func + + add esp, 4 + pop edi + } + } } diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index 4b4e8bd2..4e84c60a 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -329,6 +329,9 @@ namespace Game typedef void(_cdecl * R_AddCmdDrawStretchPic_t)(float x, float y, float w, float h, float xScale, float yScale, float xay, float yay, const float *color, Game::Material* material); extern R_AddCmdDrawStretchPic_t R_AddCmdDrawStretchPic; + typedef void* (__cdecl * R_AllocStaticIndexBuffer_t)(IDirect3DIndexBuffer9** store, int length); + extern R_AllocStaticIndexBuffer_t R_AllocStaticIndexBuffer; + typedef bool(__cdecl * R_Cinematic_StartPlayback_Now_t)(); extern R_Cinematic_StartPlayback_Now_t R_Cinematic_StartPlayback_Now; @@ -530,4 +533,7 @@ namespace Game void IN_KeyUp(kbutton_t* button); void IN_KeyDown(kbutton_t* button); + + void Load_IndexBuffer(void* data, IDirect3DIndexBuffer9** storeHere, int count); + void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len); } diff --git a/src/Game/Structs.hpp b/src/Game/Structs.hpp index ea949aeb..719c98d2 100644 --- a/src/Game/Structs.hpp +++ b/src/Game/Structs.hpp @@ -818,6 +818,10 @@ namespace Game menuDef_t **menus; }; +#define FS_SEEK_CUR 0 +#define FS_SEEK_END 1 +#define FS_SEEK_SET 2 + enum FsListBehavior_e { FS_LIST_PURE_ONLY = 0x0, @@ -1394,7 +1398,10 @@ namespace Game struct XModelLodInfo { - char pad[4]; // +0 + // I'm not sure if this is correct + short someCount; + short someTotalCount; + short numSurfs; // +4 short pad2;// +6 XModelSurfs* surfaces; // +8 @@ -1525,6 +1532,39 @@ namespace Game PhysCollmap* physCollmap; }; // total size 304 + //static_assert(offsetof(XModel, lods) <= 70, ""); + + struct CModelAllocData + { + void* mainArray; + void* vertexBuffer; + void* indexBuffer; + }; + + struct CModelSectionHeader + { + int size; + int offset; + int fixupStart; + int fixupCount; + void* buffer; + }; + + enum CModelSection + { + SECTION_MAIN = 0, + SECTION_INDEX = 1, + SECTION_VERTEX = 2, + SECTION_FIXUP = 3, + }; + + struct CModelHeader + { + int version; + unsigned int signature; + CModelSectionHeader sectionHeader[4]; + }; + struct DSkelPartBits { int anim[4]; diff --git a/src/Utils/Memory.cpp b/src/Utils/Memory.cpp index 4ed582bb..97f97674 100644 --- a/src/Utils/Memory.cpp +++ b/src/Utils/Memory.cpp @@ -2,17 +2,17 @@ namespace Utils { + void* Memory::AllocateAlign(size_t length, size_t alignment) + { + void* data = _aligned_malloc(length, alignment); + assert(data != nullptr); + return data; + } + void* Memory::Allocate(size_t length) { - void* data = new char[length]; - + void* data = calloc(length, 1); assert(data != nullptr); - - if (data) - { - ZeroMemory(data, length); - } - return data; } @@ -27,7 +27,7 @@ namespace Utils { if (data) { - delete[] data; + free(data); } } diff --git a/src/Utils/Memory.hpp b/src/Utils/Memory.hpp index 5164664a..a4c83bfb 100644 --- a/src/Utils/Memory.hpp +++ b/src/Utils/Memory.hpp @@ -92,6 +92,7 @@ namespace Utils std::map RefMemory; }; + static void* AllocateAlign(size_t length, size_t alignment); static void* Allocate(size_t length); template static T* Allocate() { diff --git a/src/Utils/Stream.hpp b/src/Utils/Stream.hpp index d3a6e7c3..8df32ed1 100644 --- a/src/Utils/Stream.hpp +++ b/src/Utils/Stream.hpp @@ -12,7 +12,7 @@ namespace Utils class Reader { public: - Reader(Utils::Memory::Allocator* allocator, std::string& buffer) : Buffer(buffer), Allocator(allocator), Position(0) {} + Reader(Utils::Memory::Allocator* allocator, std::string buffer) : Buffer(buffer), Allocator(allocator), Position(0) {} std::string ReadString(); const char* ReadCString(); From 7831886e3a324d0d4a7a11b463843a2c0c50ef1c Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 14 Sep 2016 23:06:12 +0200 Subject: [PATCH 4/5] Optimize Load_VertexBuffer (actually a wrapper for R_CreateWorldVertexBuffer) --- src/Game/Functions.cpp | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 59a89795..035e23ec 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -483,19 +483,20 @@ namespace Game void Load_VertexBuffer(void* data, IDirect3DVertexBuffer9** where, int len) { - DWORD func = 0x5112C0; - __asm { push edi + push ebx mov eax, len mov edi, where push data - call func + mov ebx, 5112C0h + call ebx add esp, 4 + pop ebx pop edi } } From 5afea26d5fef189d352f08128bc07e112f391730 Mon Sep 17 00:00:00 2001 From: momo5502 Date: Wed, 14 Sep 2016 23:06:34 +0200 Subject: [PATCH 5/5] Welp. --- src/Game/Functions.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index 035e23ec..eea68b0a 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -494,8 +494,8 @@ namespace Game mov ebx, 5112C0h call ebx - add esp, 4 + pop ebx pop edi }