diff --git a/src/Components/Modules/AssetHandler.cpp b/src/Components/Modules/AssetHandler.cpp index 5fb1739d..94561e8e 100644 --- a/src/Components/Modules/AssetHandler.cpp +++ b/src/Components/Modules/AssetHandler.cpp @@ -181,6 +181,23 @@ namespace Components } } + Game::XAssetHeader AssetHandler::FindAssetForZone(Game::XAssetType type, std::string filename, ZoneBuilder::Zone* builder) + { + Game::XAssetHeader header = { 0 }; + + if (AssetHandler::AssetInterfaces.find(type) != AssetHandler::AssetInterfaces.end()) + { + AssetHandler::AssetInterfaces[type]->Load(&header, filename, builder); + } + + if (!header.data) + { + header = Game::DB_FindXAssetHeader(type, filename.data()); + } + + return header; + } + Game::XAssetHeader AssetHandler::FindOriginalAsset(Game::XAssetType type, const char* filename) { Game::XAssetHeader header = { 0 }; diff --git a/src/Components/Modules/AssetHandler.hpp b/src/Components/Modules/AssetHandler.hpp index dd6b348d..0661c808 100644 --- a/src/Components/Modules/AssetHandler.hpp +++ b/src/Components/Modules/AssetHandler.hpp @@ -10,7 +10,7 @@ namespace Components 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) { /*ErrorTypeNotSupported(this);*/ }; + virtual void Load(Game::XAssetHeader* header, std::string name, ZoneBuilder::Zone* builder) { /*ErrorTypeNotSupported(this);*/ }; }; typedef Game::XAssetHeader(*Callback)(Game::XAssetType, const char*); @@ -29,6 +29,7 @@ namespace Components 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); private: static bool BypassState; diff --git a/src/Components/Modules/AssetInterfaces/IGfxImage.cpp b/src/Components/Modules/AssetInterfaces/IGfxImage.cpp index d3aff469..2b671c21 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxImage.cpp +++ b/src/Components/Modules/AssetInterfaces/IGfxImage.cpp @@ -2,6 +2,33 @@ 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) return; // TODO: Check for default? + + image = builder->GetAllocator()->AllocateArray(); + if (!image) + { + Components::Logger::Error("Failed to allocate GfxImage structure!"); + return; + } + + image->semantic = 0; + image->category = 3; + image->cardMemory = 0; + + Game::Image_LoadFromFileWithReader(image, (Game::Reader_t)0x46CBF0); + + // Free our image when done building zone + builder->GetAllocator()->Reference(image, [] (void*data) + { + Game::Image_Release((Game::GfxImage*)data); + }); + + header->image = image; + } + void IGfxImage::Save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { Assert_AssetStruct(Game::GfxImage, 32); diff --git a/src/Components/Modules/AssetInterfaces/IGfxImage.hpp b/src/Components/Modules/AssetInterfaces/IGfxImage.hpp index 58e63bc2..d7747bf6 100644 --- a/src/Components/Modules/AssetInterfaces/IGfxImage.hpp +++ b/src/Components/Modules/AssetInterfaces/IGfxImage.hpp @@ -5,5 +5,6 @@ namespace Assets virtual Game::XAssetType GetType() override { return Game::XAssetType::ASSET_TYPE_IMAGE; }; virtual void Save(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) override; + virtual void Load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) override; }; } \ No newline at end of file diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.cpp b/src/Components/Modules/AssetInterfaces/IMaterial.cpp index a5f21f56..eefc4ab9 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.cpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.cpp @@ -2,6 +2,104 @@ namespace Assets { + void IMaterial::Load(Game::XAssetHeader* header, std::string name, Components::ZoneBuilder::Zone* builder) + { + Components::FileSystem::File materialInfo(Utils::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()->AllocateArray(); + + if (!material) + { + Components::Logger::Error("Failed to allocate material structure!"); + return; + } + + // Copy base material to our structure + memcpy(material, baseMaterial, sizeof(Game::Material)); + material->name = builder->GetAllocator()->DuplicateString(name); + + // Load referenced textures + auto textures = infoData["textures"]; + if (textures.is_array()) + { + std::vector textureList; + + for (auto texture : textures.array_items()) + { + if (!texture.is_array()) continue; + if (textureList.size() >= 0xFF) break; + + auto textureInfo = texture.array_items(); + if (textureInfo.size() < 2) continue; + + auto map = textureInfo[0]; + auto image = textureInfo[1]; + if(!map.is_string() || !image.is_string()) continue; + + Game::MaterialTextureDef textureDef; + + textureDef.semantic = 0; // No water image + textureDef.nameEnd = map.string_value().data()[map.string_value().size() - 1]; + textureDef.nameStart = map.string_value().data()[0]; + 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; + + textureList.push_back(textureDef); + } + + if (textureList.size()) + { + Game::MaterialTextureDef* textureTable = builder->GetAllocator()->AllocateArray(textureList.size()); + + if (!textureTable) + { + Components::Logger::Error("Failed to allocate texture table!"); + return; + } + + memcpy(textureTable, textureList.data(), sizeof(Game::MaterialTextureDef) * textureList.size()); + + material->textureTable = textureTable; + } + else + { + material->textureTable = 0; + } + + material->textureCount = (char)textureList.size() & 0xFF; + } + + header->material = material; + } + void IMaterial::Mark(Game::XAssetHeader header, Components::ZoneBuilder::Zone* builder) { Game::Material* asset = header.material; diff --git a/src/Components/Modules/AssetInterfaces/IMaterial.hpp b/src/Components/Modules/AssetInterfaces/IMaterial.hpp index 83c82c42..7cf194f3 100644 --- a/src/Components/Modules/AssetInterfaces/IMaterial.hpp +++ b/src/Components/Modules/AssetInterfaces/IMaterial.hpp @@ -6,5 +6,6 @@ namespace Assets 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; }; } \ No newline at end of file diff --git a/src/Components/Modules/ZoneBuilder.cpp b/src/Components/Modules/ZoneBuilder.cpp index 094d669c..c6e7155c 100644 --- a/src/Components/Modules/ZoneBuilder.cpp +++ b/src/Components/Modules/ZoneBuilder.cpp @@ -27,6 +27,11 @@ namespace Components return &Buffer; } + Utils::Memory::Allocator* ZoneBuilder::Zone::GetAllocator() + { + return &MemAllocator; + } + void ZoneBuilder::Zone::Zone::Build() { ZoneBuilder::Zone::LoadFastFiles(); @@ -118,7 +123,7 @@ namespace Components return false; } - Game::XAssetHeader assetHeader = Game::DB_FindXAssetHeader(type, name.data()); + Game::XAssetHeader assetHeader = AssetHandler::FindAssetForZone(type, name, this); if (!assetHeader.data) { diff --git a/src/Components/Modules/ZoneBuilder.hpp b/src/Components/Modules/ZoneBuilder.hpp index ebb9510a..53f8ca43 100644 --- a/src/Components/Modules/ZoneBuilder.hpp +++ b/src/Components/Modules/ZoneBuilder.hpp @@ -15,6 +15,7 @@ namespace Components void Build(); Utils::Stream* GetBuffer(); + Utils::Memory::Allocator* GetAllocator(); bool HasPointer(const void* pointer); void StorePointer(const void* pointer); @@ -56,6 +57,8 @@ namespace Components std::string ZoneName; Utils::CSV DataMap; + Utils::Memory::Allocator MemAllocator; + std::vector LoadedAssets; std::vector ScriptStrings; std::map ScriptStringMap; diff --git a/src/Game/Functions.cpp b/src/Game/Functions.cpp index df8d5d94..d02e7a6b 100644 --- a/src/Game/Functions.cpp +++ b/src/Game/Functions.cpp @@ -62,6 +62,9 @@ namespace Game FS_Restart_t FS_Restart = (FS_Restart_t)0x461A50; FS_BuildPathToFile_t FS_BuildPathToFile = (FS_BuildPathToFile_t)0x4702C0; + Image_LoadFromFileWithReader_t Image_LoadFromFileWithReader = (Image_LoadFromFileWithReader_t)0x53ABF0; + Image_Release_t Image_Release = (Image_Release_t)0x51F010; + Menus_CloseAll_t Menus_CloseAll = (Menus_CloseAll_t)0x4BA5B0; Menus_OpenByName_t Menus_OpenByName = (Menus_OpenByName_t)0x4CCE60; Menus_FindByName_t Menus_FindByName = (Menus_FindByName_t)0x487240; @@ -280,4 +283,17 @@ namespace Game SetConsole("com_errorTitle", title.data()); Cbuf_AddText(0, "openmenu error_popmenu_lobby"); } + + unsigned int R_HashString(const char* string) + { + unsigned int hash = 0; + + while (*string) + { + hash = (*string | 0x20) ^ (33 * hash); + string++; + } + + return hash; + } } diff --git a/src/Game/Functions.hpp b/src/Game/Functions.hpp index b73895a5..b1fb3932 100644 --- a/src/Game/Functions.hpp +++ b/src/Game/Functions.hpp @@ -145,6 +145,13 @@ namespace Game typedef int(__cdecl * FS_BuildPathToFile_t)(const char*, const char*, const char*, char**); extern FS_BuildPathToFile_t FS_BuildPathToFile; + typedef int(__cdecl * Reader_t)(char const*, int *); + typedef bool(__cdecl * Image_LoadFromFileWithReader_t)(GfxImage* image, Reader_t reader); + extern Image_LoadFromFileWithReader_t Image_LoadFromFileWithReader; + + typedef void(__cdecl * Image_Release_t)(GfxImage* image); + extern Image_Release_t Image_Release; + typedef void(__cdecl * Menus_CloseAll_t)(UiContext *dc); extern Menus_CloseAll_t Menus_CloseAll; @@ -356,4 +363,6 @@ namespace Game XAssetType DB_GetXAssetNameType(const char* name); void MessageBox(std::string message, std::string title); + + unsigned int R_HashString(const char* string); } diff --git a/src/Utils/Memory.hpp b/src/Utils/Memory.hpp index 1e2da977..ec753262 100644 --- a/src/Utils/Memory.hpp +++ b/src/Utils/Memory.hpp @@ -3,8 +3,66 @@ namespace Utils class Memory { public: + class Allocator + { + public: + typedef void(*FreeCallback)(void*); + + Allocator() + { + this->Pool.clear(); + this->RefMemory.clear(); + } + ~Allocator() + { + for (auto i = this->RefMemory.begin(); i != this->RefMemory.end(); i++) + { + if (i->first && i->second) + { + i->second(i->first); + } + } + + this->RefMemory.clear(); + + for (auto data : this->Pool) + { + Memory::Free(data); + } + + this->Pool.clear(); + } + + void Reference(void* memory, FreeCallback callback) + { + this->RefMemory[memory] = callback; + } + + void* Allocate(size_t length) + { + void* data = Memory::Allocate(length); + this->Pool.push_back(data); + return data; + } + template T* AllocateArray(size_t count = 1) + { + return (T*)this->Allocate(count * sizeof(T)); + } + + char* DuplicateString(std::string string) + { + char* data = Memory::DuplicateString(string); + this->Pool.push_back(data); + return data; + } + + private: + std::vector Pool; + std::map RefMemory; + }; + static void* Allocate(size_t length); - template static T* AllocateArray(size_t count) + template static T* AllocateArray(size_t count = 1) { return (T*)Allocate(count * sizeof(T)); }