From 55f48c9bc23f69151806a679569e6d4f37d3df27 Mon Sep 17 00:00:00 2001 From: Jan Date: Thu, 25 Mar 2021 11:14:51 +0100 Subject: [PATCH] Add InfoString loading --- src/ObjCommon/InfoString/InfoString.cpp | 84 ++++++- src/ObjCommon/InfoString/InfoString.h | 5 +- .../InfoStringToStructConverter.cpp | 11 - .../InfoString/InfoStringToStructConverter.h | 13 +- .../InfoStringToStructConverter.cpp | 223 +++++++++++++++++- .../InfoString/InfoStringToStructConverter.h | 15 +- .../InfoStringToStructConverterBase.cpp | 156 +++++++++++- .../InfoStringToStructConverterBase.h | 34 ++- .../InfoStringFromStructConverterBase.cpp | 3 - .../InfoStringFromStructConverterBase.h | 2 +- 10 files changed, 499 insertions(+), 47 deletions(-) diff --git a/src/ObjCommon/InfoString/InfoString.cpp b/src/ObjCommon/InfoString/InfoString.cpp index 0b39f0bd..789e599a 100644 --- a/src/ObjCommon/InfoString/InfoString.cpp +++ b/src/ObjCommon/InfoString/InfoString.cpp @@ -96,10 +96,90 @@ void InfoString::ToGdtProperties(const std::string& prefix, GdtEntry& gdtEntry) gdtEntry.m_properties["configstringFileType"] = prefix; } -void InfoString::FromString() +class InfoStringInputStream { + std::istream& m_stream; + +public: + explicit InfoStringInputStream(std::istream& stream) + : m_stream(stream) + { + } + + bool NextField(std::string& value) const + { + std::ostringstream str; + + auto c = m_stream.get(); + if (c == EOF) + return false; + + while (c != EOF && c != '\\') + { + str << static_cast(c); + c = m_stream.get(); + } + + value = str.str(); + return true; + } +}; + +bool InfoString::FromStream(std::istream& stream) +{ + const InfoStringInputStream infoStream(stream); + + std::string key; + while (infoStream.NextField(key)) + { + std::string value; + if (!infoStream.NextField(value)) + return false; + + const auto existingEntry = m_values.find(key); + if (existingEntry == m_values.end()) + { + m_keys_by_insertion.push_back(key); + m_values.emplace(std::make_pair(key, value)); + } + else + { + existingEntry->second = value; + } + } + + return true; } -void InfoString::FromString(const std::string& prefix) +bool InfoString::FromStream(const std::string& prefix, std::istream& stream) { + const InfoStringInputStream infoStream(stream); + + std::string readPrefix; + if (!infoStream.NextField(readPrefix)) + return false; + + if (prefix != readPrefix) + return false; + + std::string key; + while(infoStream.NextField(key)) + { + std::string value; + if (!infoStream.NextField(value)) + return false; + + const auto existingEntry = m_values.find(key); + if(existingEntry == m_values.end()) + { + m_keys_by_insertion.push_back(key); + m_values.emplace(std::make_pair(key, value)); + } + else + { + existingEntry->second = value; + } + } + + return true; } diff --git a/src/ObjCommon/InfoString/InfoString.h b/src/ObjCommon/InfoString/InfoString.h index 86ac1690..6aa8368b 100644 --- a/src/ObjCommon/InfoString/InfoString.h +++ b/src/ObjCommon/InfoString/InfoString.h @@ -1,4 +1,5 @@ #pragma once +#include #include #include #include @@ -23,6 +24,6 @@ public: _NODISCARD std::string ToString(const std::string& prefix) const; void ToGdtProperties(const std::string& prefix, GdtEntry& gdtEntry) const; - void FromString(); - void FromString(const std::string& prefix); + bool FromStream(std::istream& stream); + bool FromStream(const std::string& prefix, std::istream& stream); }; \ No newline at end of file diff --git a/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.cpp b/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.cpp index edd1f053..ff2adce4 100644 --- a/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.cpp +++ b/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.cpp @@ -2,14 +2,3 @@ using namespace IW4; -void InfoStringToStructConverter::FillStructure() -{ -} - -InfoStringToStructConverter::InfoStringToStructConverter(const InfoString& infoString, void* structure, - const cspField_t* fields, const size_t fieldCount) - : InfoStringToStructConverterBase(infoString, structure), - m_fields(fields), - m_field_count(fieldCount) -{ -} \ No newline at end of file diff --git a/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.h b/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.h index eb51a804..1a8d0bc3 100644 --- a/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.h +++ b/src/ObjLoading/Game/IW4/InfoString/InfoStringToStructConverter.h @@ -1,4 +1,5 @@ #pragma once +#include "AssetLoading/IAssetLoadingManager.h" #include "InfoString/InfoStringToStructConverterBase.h" #include "Game/IW4/IW4.h" @@ -6,13 +7,19 @@ namespace IW4 { class InfoStringToStructConverter : public InfoStringToStructConverterBase { + protected: + IAssetLoadingManager* m_loading_manager; const cspField_t* m_fields; size_t m_field_count; - protected: - void FillStructure() override; + static bool GetHashValue(const std::string& value, unsigned int& hash); + + virtual bool ConvertExtensionField(const cspField_t& field, const std::string& value) = 0; + bool ConvertBaseField(const cspField_t& field, const std::string& value); public: - InfoStringToStructConverter(const InfoString& infoString, void* structure, const cspField_t* fields, size_t fieldCount); + InfoStringToStructConverter(const InfoString& infoString, void* structure, ZoneScriptStrings& zoneScriptStrings, MemoryManager* memory, IAssetLoadingManager* manager, const cspField_t* fields, + size_t fieldCount); + bool Convert() override; }; } \ No newline at end of file diff --git a/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.cpp b/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.cpp index 8a1afdcb..c296cdaf 100644 --- a/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.cpp +++ b/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.cpp @@ -1,17 +1,226 @@ #include "InfoStringToStructConverter.h" #include +#include + +#include "Game/T6/CommonT6.h" using namespace T6; -void InfoStringToStructConverter::FillStructure() +InfoStringToStructConverter::InfoStringToStructConverter(const InfoString& infoString, void* structure, ZoneScriptStrings& zoneScriptStrings, MemoryManager* memory, IAssetLoadingManager* manager, + const cspField_t* fields, const size_t fieldCount) + : InfoStringToStructConverterBase(infoString, structure, zoneScriptStrings, memory), + m_loading_manager(manager), + m_fields(fields), + m_field_count(fieldCount) { } -InfoStringToStructConverter::InfoStringToStructConverter(const InfoString& infoString, void* structure, - const cspField_t* fields, const size_t fieldCount) - : InfoStringToStructConverterBase(infoString, structure), - m_fields(fields), - m_field_count(fieldCount) +bool InfoStringToStructConverter::GetHashValue(const std::string& value, unsigned& hash) { -} \ No newline at end of file + if (!value.empty() && value[0] == '@') + { + char* endPtr; + hash = strtoul(&value[1], &endPtr, 16); + return endPtr == &value[value.size()]; + } + + hash = CommonT6::Com_HashString(value.c_str()); + return true; +} + +bool InfoStringToStructConverter::ConvertBaseField(const cspField_t& field, const std::string& value) +{ + switch (static_cast(field.iFieldType)) + { + case CSPFT_STRING: + return ConvertString(value, field.iOffset); + + case CSPFT_STRING_MAX_STRING_CHARS: + return ConvertStringBuffer(value, field.iOffset, 1024); + + case CSPFT_STRING_MAX_QPATH: + return ConvertStringBuffer(value, field.iOffset, 64); + + case CSPFT_STRING_MAX_OSPATH: + return ConvertStringBuffer(value, field.iOffset, 256); + + case CSPFT_INT: + return ConvertInt(value, field.iOffset); + + case CSPFT_UINT: + return ConvertUint(value, field.iOffset); + + case CSPFT_BOOL: + return ConvertBool(value, field.iOffset); + + case CSPFT_QBOOLEAN: + return ConvertQBoolean(value, field.iOffset); + + case CSPFT_FLOAT: + return ConvertFloat(value, field.iOffset); + + case CSPFT_MILLISECONDS: + return ConvertMilliseconds(value, field.iOffset); + + case CSPFT_FX: + { + if (value.empty()) + { + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = nullptr; + return true; + } + + auto* fx = m_loading_manager->LoadDependency(ASSET_TYPE_FX, value); + + if (fx == nullptr) + { + std::cout << "Failed to load fx asset \"" << value << "\"" << std::endl; + return false; + } + + m_dependencies.emplace(fx); + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = fx->m_ptr; + + return true; + } + + case CSPFT_XMODEL: + { + if (value.empty()) + { + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = nullptr; + return true; + } + + auto* xmodel = m_loading_manager->LoadDependency(ASSET_TYPE_XMODEL, value); + + if (xmodel == nullptr) + { + std::cout << "Failed to load xmodel asset \"" << value << "\"" << std::endl; + return false; + } + + m_dependencies.emplace(xmodel); + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = xmodel->m_ptr; + + return true; + } + + case CSPFT_MATERIAL: + case CSPFT_MATERIAL_STREAM: + { + if (value.empty()) + { + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = nullptr; + return true; + } + + auto* material = m_loading_manager->LoadDependency(ASSET_TYPE_MATERIAL, value); + + if (material == nullptr) + { + std::cout << "Failed to load material asset \"" << value << "\"" << std::endl; + return false; + } + + m_dependencies.emplace(material); + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = material->m_ptr; + + return true; + } + + case CSPFT_PHYS_PRESET: + { + if (value.empty()) + { + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = nullptr; + return true; + } + + auto* physPreset = m_loading_manager->LoadDependency(ASSET_TYPE_PHYSPRESET, value); + + if (physPreset == nullptr) + { + std::cout << "Failed to load physpreset asset \"" << value << "\"" << std::endl; + return false; + } + + m_dependencies.emplace(physPreset); + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = physPreset->m_ptr; + + return true; + } + + case CSPFT_SCRIPT_STRING: + return ConvertScriptString(value, field.iOffset); + + case CSPFT_TRACER: + { + if (value.empty()) + { + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = nullptr; + return true; + } + + auto* tracer = m_loading_manager->LoadDependency(ASSET_TYPE_TRACER, value); + + if (tracer == nullptr) + { + std::cout << "Failed to load tracer asset \"" << value << "\"" << std::endl; + return false; + } + + m_dependencies.emplace(tracer); + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = tracer->m_ptr; + + return true; + } + + case CSPFT_SOUND_ALIAS_ID: + { + unsigned int soundAliasHash; + if (!GetHashValue(value, soundAliasHash)) + { + std::cout << "Failed to parse value \"" << value << "\" as hash" << std::endl; + return false; + } + + *reinterpret_cast(reinterpret_cast(m_structure) + field.iOffset) = soundAliasHash; + return true; + } + + case CSPFT_NUM_BASE_FIELD_TYPES: + default: + assert(false); + return false; + } +} + +bool InfoStringToStructConverter::Convert() +{ + for (auto fieldIndex = 0u; fieldIndex < m_field_count; fieldIndex++) + { + const auto& field = m_fields[fieldIndex]; + assert(field.iFieldType >= 0); + + auto foundValue = false; + const auto& value = m_info_string.GetValueForKey(std::string(field.szName), &foundValue); + + if (foundValue) + { + if (field.iFieldType < CSPFT_NUM_BASE_FIELD_TYPES) + { + if (!ConvertBaseField(field, value)) + return false; + } + else + { + if (!ConvertExtensionField(field, value)) + return false; + } + } + } + + return true; +} diff --git a/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.h b/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.h index 3afc1201..9e9ecc6d 100644 --- a/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.h +++ b/src/ObjLoading/Game/T6/InfoString/InfoStringToStructConverter.h @@ -1,4 +1,5 @@ #pragma once +#include "AssetLoading/IAssetLoadingManager.h" #include "InfoString/InfoStringToStructConverterBase.h" #include "Game/T6/T6.h" @@ -6,13 +7,19 @@ namespace T6 { class InfoStringToStructConverter : public InfoStringToStructConverterBase { + protected: + IAssetLoadingManager* m_loading_manager; const cspField_t* m_fields; size_t m_field_count; - protected: - void FillStructure() override; + static bool GetHashValue(const std::string& value, unsigned int& hash); + + virtual bool ConvertExtensionField(const cspField_t& field, const std::string& value) = 0; + bool ConvertBaseField(const cspField_t& field, const std::string& value); public: - InfoStringToStructConverter(const InfoString& infoString, void* structure, const cspField_t* fields, size_t fieldCount); + InfoStringToStructConverter(const InfoString& infoString, void* structure, ZoneScriptStrings& zoneScriptStrings, MemoryManager* memory, IAssetLoadingManager* manager, const cspField_t* fields, + size_t fieldCount); + bool Convert() override; }; -} \ No newline at end of file +} diff --git a/src/ObjLoading/InfoString/InfoStringToStructConverterBase.cpp b/src/ObjLoading/InfoString/InfoStringToStructConverterBase.cpp index e34e6f30..2f81d2d1 100644 --- a/src/ObjLoading/InfoString/InfoStringToStructConverterBase.cpp +++ b/src/ObjLoading/InfoString/InfoStringToStructConverterBase.cpp @@ -1,15 +1,155 @@ #include "InfoStringToStructConverterBase.h" -InfoStringToStructConverterBase::InfoStringToStructConverterBase(const InfoString& infoString, void* structure) +#include +#include + +InfoStringToStructConverterBase::InfoStringToStructConverterBase(const InfoString& infoString, void* structure, ZoneScriptStrings& zoneScriptStrings, MemoryManager* memory) : m_info_string(infoString), - m_structure(structure) + m_zone_script_strings(zoneScriptStrings), + m_memory(memory), + m_structure(structure) { } -InfoStringToStructConverterBase::~InfoStringToStructConverterBase() -= default; - -void InfoStringToStructConverterBase::Convert() +bool InfoStringToStructConverterBase::ConvertString(const std::string& value, const size_t offset) { - FillStructure(); -} \ No newline at end of file + *reinterpret_cast(reinterpret_cast(m_structure) + offset) = m_memory->Dup(value.c_str()); + return true; +} + +bool InfoStringToStructConverterBase::ConvertStringBuffer(const std::string& value, const size_t offset, const size_t bufferSize) +{ + strncpy(reinterpret_cast(reinterpret_cast(m_structure) + offset), value.c_str(), bufferSize); + return true; +} + +bool InfoStringToStructConverterBase::ConvertInt(const std::string& value, const size_t offset) +{ + char* endPtr; + *reinterpret_cast(reinterpret_cast(m_structure) + offset) = strtol(value.c_str(), &endPtr, 0); + + if(endPtr != &value[value.size()]) + { + std::cout << "Failed to parse value \"" << value << "\" as int" << std::endl; + return false; + } + + return true; +} + +bool InfoStringToStructConverterBase::ConvertUint(const std::string& value, const size_t offset) +{ + char* endPtr; + *reinterpret_cast(reinterpret_cast(m_structure) + offset) = strtoul(value.c_str(), &endPtr, 0); + + if (endPtr != &value[value.size()]) + { + std::cout << "Failed to parse value \"" << value << "\" as uint" << std::endl; + return false; + } + + return true; +} + +bool InfoStringToStructConverterBase::ConvertBool(const std::string& value, const size_t offset) +{ + char* endPtr; + const auto intValue = strtol(value.c_str(), &endPtr, 0); + + *reinterpret_cast(reinterpret_cast(m_structure) + offset) = intValue != 0; + if (endPtr != &value[value.size()]) + { + std::cout << "Failed to parse value \"" << value << "\" as bool" << std::endl; + return false; + } + + return true; +} + +bool InfoStringToStructConverterBase::ConvertQBoolean(const std::string& value, const size_t offset) +{ + char* endPtr; + const auto intValue = strtol(value.c_str(), &endPtr, 0); + + *reinterpret_cast(reinterpret_cast(m_structure) + offset) = intValue != 0 ? 1 : 0; + if (endPtr != &value[value.size()]) + { + std::cout << "Failed to parse value \"" << value << "\" as qboolean" << std::endl; + return false; + } + + return true; +} + +bool InfoStringToStructConverterBase::ConvertFloat(const std::string& value, const size_t offset) +{ + char* endPtr; + *reinterpret_cast(reinterpret_cast(m_structure) + offset) = strtof(value.c_str(), &endPtr); + + if (endPtr != &value[value.size()]) + { + std::cout << "Failed to parse value \"" << value << "\" as float" << std::endl; + return false; + } + + return true; +} + +bool InfoStringToStructConverterBase::ConvertMilliseconds(const std::string& value, const size_t offset) +{ + char* endPtr; + *reinterpret_cast(reinterpret_cast(m_structure) + offset) = static_cast(strtof(value.c_str(), &endPtr) * 1000.0f); + + if (endPtr != &value[value.size()]) + { + std::cout << "Failed to parse value \"" << value << "\" as milliseconds" << std::endl; + return false; + } + + return true; +} + +bool InfoStringToStructConverterBase::ConvertScriptString(const std::string& value, const size_t offset) +{ + auto scrStrValue = m_zone_script_strings.AddOrGetScriptString(value); + m_used_script_string_list.emplace(scrStrValue); + *reinterpret_cast(reinterpret_cast(m_structure) + offset) = scrStrValue; + + return true; +} + +bool InfoStringToStructConverterBase::ConvertEnumInt(const std::string& value, const size_t offset, const char** enumValues, const size_t enumSize) +{ + for(auto i = 0u; i < enumSize; i++) + { + if(value == enumValues[i]) + { + *reinterpret_cast(reinterpret_cast(m_structure) + offset) = static_cast(i); + return true; + } + } + + return false; +} + +std::vector InfoStringToStructConverterBase::GetUsedScriptStrings() const +{ + std::vector scrStringList; + for(auto scrStr : m_used_script_string_list) + { + scrStringList.push_back(scrStr); + } + + return scrStringList; +} + +std::vector InfoStringToStructConverterBase::GetDependencies() const +{ + std::vector dependencyList; + for (auto* dependency : m_dependencies) + { + dependencyList.push_back(dependency); + } + + return dependencyList; +} diff --git a/src/ObjLoading/InfoString/InfoStringToStructConverterBase.h b/src/ObjLoading/InfoString/InfoStringToStructConverterBase.h index 40c5c0a4..8aa536a5 100644 --- a/src/ObjLoading/InfoString/InfoStringToStructConverterBase.h +++ b/src/ObjLoading/InfoString/InfoStringToStructConverterBase.h @@ -1,22 +1,44 @@ #pragma once +#include +#include + +#include "Utils/ClassUtils.h" #include "InfoString/InfoString.h" +#include "Pool/XAssetInfo.h" +#include "Utils/MemoryManager.h" +#include "Zone/ZoneScriptStrings.h" class InfoStringToStructConverterBase { protected: const InfoString& m_info_string; + ZoneScriptStrings& m_zone_script_strings; + std::unordered_set m_used_script_string_list; + std::unordered_set m_dependencies; + MemoryManager* m_memory; void* m_structure; - - virtual void FillStructure() = 0; + + bool ConvertString(const std::string& value, size_t offset); + bool ConvertStringBuffer(const std::string& value, size_t offset, size_t bufferSize); + bool ConvertInt(const std::string& value, size_t offset); + bool ConvertUint(const std::string& value, size_t offset); + bool ConvertBool(const std::string& value, size_t offset); + bool ConvertQBoolean(const std::string& value, size_t offset); + bool ConvertFloat(const std::string& value, size_t offset); + bool ConvertMilliseconds(const std::string& value, size_t offset); + bool ConvertScriptString(const std::string& value, size_t offset); + bool ConvertEnumInt(const std::string& value, size_t offset, const char** enumValues, size_t enumSize); public: - InfoStringToStructConverterBase(const InfoString& infoString, void* structure); - virtual ~InfoStringToStructConverterBase(); + InfoStringToStructConverterBase(const InfoString& infoString, void* structure, ZoneScriptStrings& zoneScriptStrings, MemoryManager* memory); + virtual ~InfoStringToStructConverterBase() = default; InfoStringToStructConverterBase(const InfoStringToStructConverterBase& other) = delete; InfoStringToStructConverterBase(InfoStringToStructConverterBase&& other) noexcept = delete; InfoStringToStructConverterBase& operator=(const InfoStringToStructConverterBase& other) = delete; InfoStringToStructConverterBase& operator=(InfoStringToStructConverterBase&& other) noexcept = delete; - void Convert(); -}; \ No newline at end of file + virtual bool Convert() = 0; + _NODISCARD std::vector GetUsedScriptStrings() const; + _NODISCARD std::vector GetDependencies() const; +}; diff --git a/src/ObjWriting/InfoString/InfoStringFromStructConverterBase.cpp b/src/ObjWriting/InfoString/InfoStringFromStructConverterBase.cpp index 111e552c..4c394f17 100644 --- a/src/ObjWriting/InfoString/InfoStringFromStructConverterBase.cpp +++ b/src/ObjWriting/InfoString/InfoStringFromStructConverterBase.cpp @@ -19,9 +19,6 @@ InfoStringFromStructConverterBase::InfoStringFromStructConverterBase(const void* { } -InfoStringFromStructConverterBase::~InfoStringFromStructConverterBase() -= default; - InfoString InfoStringFromStructConverterBase::Convert() { FillInfoString(); diff --git a/src/ObjWriting/InfoString/InfoStringFromStructConverterBase.h b/src/ObjWriting/InfoString/InfoStringFromStructConverterBase.h index a1032375..f918083e 100644 --- a/src/ObjWriting/InfoString/InfoStringFromStructConverterBase.h +++ b/src/ObjWriting/InfoString/InfoStringFromStructConverterBase.h @@ -28,7 +28,7 @@ protected: public: explicit InfoStringFromStructConverterBase(const void* structure); InfoStringFromStructConverterBase(const void* structure, std::function scriptStringValueCallback); - virtual ~InfoStringFromStructConverterBase(); + virtual ~InfoStringFromStructConverterBase() = default; InfoStringFromStructConverterBase(const InfoStringFromStructConverterBase& other) = delete; InfoStringFromStructConverterBase(InfoStringFromStructConverterBase&& other) noexcept = delete; InfoStringFromStructConverterBase& operator=(const InfoStringFromStructConverterBase& other) = delete;