From 4696011d9ddb0bd2d00034c5f3a03acd22456f48 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 20 Jan 2024 16:30:59 +0100 Subject: [PATCH 1/5] chore: extend CsvStream to be able to emit const char* allocated with a MemoryManager --- src/ObjCommon/Csv/CsvStream.cpp | 25 +++++++++++++++++++++++-- src/ObjCommon/Csv/CsvStream.h | 22 +++++++++++++++------- 2 files changed, 38 insertions(+), 9 deletions(-) diff --git a/src/ObjCommon/Csv/CsvStream.cpp b/src/ObjCommon/Csv/CsvStream.cpp index 9acc1c762..cd01eed6b 100644 --- a/src/ObjCommon/Csv/CsvStream.cpp +++ b/src/ObjCommon/Csv/CsvStream.cpp @@ -14,6 +14,27 @@ bool CsvInputStream::NextRow(std::vector& out) const if (!out.empty()) out.clear(); + return EmitNextRow( + [&out](std::string value) + { + out.emplace_back(std::move(value)); + }); +} + +bool CsvInputStream::NextRow(std::vector& out, MemoryManager& memory) const +{ + if (!out.empty()) + out.clear(); + + return EmitNextRow( + [&out, &memory](const std::string& value) + { + out.emplace_back(memory.Dup(value.c_str())); + }); +} + +bool CsvInputStream::EmitNextRow(const std::function& cb) const +{ auto c = m_stream.get(); const auto isEof = c == EOF; std::ostringstream col; @@ -21,7 +42,7 @@ bool CsvInputStream::NextRow(std::vector& out) const { if (c == CSV_SEPARATOR) { - out.emplace_back(col.str()); + cb(col.str()); col.clear(); col.str(std::string()); } @@ -46,7 +67,7 @@ bool CsvInputStream::NextRow(std::vector& out) const if (!isEof) { - out.emplace_back(col.str()); + cb(col.str()); } return !isEof; diff --git a/src/ObjCommon/Csv/CsvStream.h b/src/ObjCommon/Csv/CsvStream.h index 29bfb4404..2524ec413 100644 --- a/src/ObjCommon/Csv/CsvStream.h +++ b/src/ObjCommon/Csv/CsvStream.h @@ -1,28 +1,36 @@ #pragma once +#include "Utils/MemoryManager.h" + +#include #include #include #include class CsvInputStream { - std::istream& m_stream; - public: explicit CsvInputStream(std::istream& stream); bool NextRow(std::vector& out) const; + bool NextRow(std::vector& out, MemoryManager& memory) const; + +private: + bool EmitNextRow(const std::function& cb) const; + + std::istream& m_stream; }; class CsvOutputStream { - std::ostream& m_stream; - unsigned m_column_count; - unsigned m_current_column; - bool m_first_row; - public: explicit CsvOutputStream(std::ostream& stream); void WriteColumn(const std::string& value); void NextRow(); + +private: + std::ostream& m_stream; + unsigned m_column_count; + unsigned m_current_column; + bool m_first_row; }; From 8de849dc85af577e96a8f6de8d5b38cc1f65e7ca Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 20 Jan 2024 16:32:11 +0100 Subject: [PATCH 2/5] feat: add iw3 StringTable loading via generic loader --- .../AssetLoaders/AssetLoaderStringTable.cpp | 39 ++++++++ .../IW3/AssetLoaders/AssetLoaderStringTable.h | 17 ++++ .../StringTable/StringTableLoader.h | 89 +++++++++++++++++++ 3 files changed, 145 insertions(+) create mode 100644 src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderStringTable.cpp create mode 100644 src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderStringTable.h create mode 100644 src/ObjLoading/StringTable/StringTableLoader.h diff --git a/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderStringTable.cpp new file mode 100644 index 000000000..895f2b24e --- /dev/null +++ b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderStringTable.cpp @@ -0,0 +1,39 @@ +#include "AssetLoaderStringTable.h" + +#include "Csv/CsvStream.h" +#include "Game/IW3/CommonIW3.h" +#include "ObjLoading.h" +#include "Pool/GlobalAssetPool.h" +#include "StringTable/StringTableLoader.h" + +#include + +using namespace IW3; + +void* AssetLoaderStringTable::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) +{ + auto* stringTable = memory->Create(); + memset(stringTable, 0, sizeof(StringTable)); + stringTable->name = memory->Dup(assetName.c_str()); + return stringTable; +} + +bool AssetLoaderStringTable::CanLoadFromRaw() const +{ + return true; +} + +bool AssetLoaderStringTable::LoadFromRaw( + const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +{ + const auto file = searchPath->Open(assetName); + if (!file.IsOpen()) + return false; + + string_table::StringTableLoaderV1 loader; + auto* stringTable = loader.LoadFromStream(assetName, *memory, *file.m_stream); + + manager->AddAsset(ASSET_TYPE_STRINGTABLE, assetName, stringTable); + + return true; +} diff --git a/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderStringTable.h b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderStringTable.h new file mode 100644 index 000000000..ec7bebb11 --- /dev/null +++ b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderStringTable.h @@ -0,0 +1,17 @@ +#pragma once + +#include "AssetLoading/BasicAssetLoader.h" +#include "Game/IW3/IW3.h" +#include "SearchPath/ISearchPath.h" + +namespace IW3 +{ + class AssetLoaderStringTable final : public BasicAssetLoader + { + public: + _NODISCARD void* CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) override; + _NODISCARD bool CanLoadFromRaw() const override; + bool + LoadFromRaw(const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const override; + }; +} // namespace IW3 diff --git a/src/ObjLoading/StringTable/StringTableLoader.h b/src/ObjLoading/StringTable/StringTableLoader.h new file mode 100644 index 000000000..b685030b2 --- /dev/null +++ b/src/ObjLoading/StringTable/StringTableLoader.h @@ -0,0 +1,89 @@ +#pragma once + +#include "Csv/CsvStream.h" + +#include +#include +#include +#include + +namespace string_table +{ + template class AbstractStringTableLoader + { + protected: + AbstractStringTableLoader() = default; + virtual ~AbstractStringTableLoader() = default; + AbstractStringTableLoader(const AbstractStringTableLoader& other) = default; + AbstractStringTableLoader(AbstractStringTableLoader&& other) noexcept = default; + AbstractStringTableLoader& operator=(const AbstractStringTableLoader& other) = default; + AbstractStringTableLoader& operator=(AbstractStringTableLoader&& other) noexcept = default; + + virtual void SetCellContent(CellType& cell, const char* content) = 0; + + virtual void PostProcessStringTable(StringTableType* stringTable, const unsigned cellCount, MemoryManager& memory) {} + + public: + StringTableType* LoadFromStream(const std::string& assetName, MemoryManager& memory, std::istream& stream) + { + auto* stringTable = memory.Create(); + stringTable->name = memory.Dup(assetName.c_str()); + + std::vector> csvLines; + std::vector currentLine; + auto maxCols = 0u; + const CsvInputStream csv(stream); + + while (csv.NextRow(currentLine)) + { + if (currentLine.size() > maxCols) + maxCols = currentLine.size(); + csvLines.emplace_back(std::move(currentLine)); + currentLine = std::vector(); + } + + stringTable->columnCount = static_cast(maxCols); + stringTable->rowCount = static_cast(csvLines.size()); + const auto cellCount = static_cast(stringTable->rowCount) * static_cast(stringTable->columnCount); + + if (cellCount) + { + stringTable->values = static_cast(memory.Alloc(sizeof(CellType) * cellCount)); + + for (auto row = 0u; row < csvLines.size(); row++) + { + const auto& rowValues = csvLines[row]; + for (auto col = 0u; col < maxCols; col++) + { + auto& cell = stringTable->values[row * maxCols + col]; + if (col >= rowValues.size() || rowValues[col].empty()) + SetCellContent(cell, ""); + else + SetCellContent(cell, memory.Dup(rowValues[col].c_str())); + } + } + } + else + { + stringTable->values = nullptr; + } + + PostProcessStringTable(stringTable, cellCount, memory); + + return stringTable; + } + }; + + // ================================= + // V1: IW3 + // - Cells are const char* + // ================================= + template class StringTableLoaderV1 final : public AbstractStringTableLoader + { + protected: + void SetCellContent(const char*& cell, const char* content) override + { + cell = content; + } + }; +} // namespace string_table From 2a1b64021bc266e3b42a04912134f2f591e6e185 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 20 Jan 2024 16:32:30 +0100 Subject: [PATCH 3/5] chore: use generic loaders for iw4,iw5,t5,t6 instead of duplicating implementations --- .../AssetLoaders/AssetLoaderStringTable.cpp | 46 +------------- .../AssetLoaders/AssetLoaderStringTable.cpp | 46 +------------- .../AssetLoaders/AssetLoaderStringTable.cpp | 62 +------------------ .../AssetLoaders/AssetLoaderStringTable.cpp | 62 +------------------ .../StringTable/StringTableLoader.h | 56 +++++++++++++++++ 5 files changed, 68 insertions(+), 204 deletions(-) diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderStringTable.cpp index 642a60a8a..e7335004c 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderStringTable.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderStringTable.cpp @@ -5,6 +5,7 @@ #include "Game/IW4/IW4.h" #include "ObjLoading.h" #include "Pool/GlobalAssetPool.h" +#include "StringTable/StringTableLoader.h" #include @@ -30,49 +31,8 @@ bool AssetLoaderStringTable::LoadFromRaw( if (!file.IsOpen()) return false; - auto* stringTable = memory->Create(); - stringTable->name = memory->Dup(assetName.c_str()); - - std::vector> csvLines; - std::vector currentLine; - auto maxCols = 0u; - const CsvInputStream csv(*file.m_stream); - - while (csv.NextRow(currentLine)) - { - if (currentLine.size() > maxCols) - maxCols = currentLine.size(); - csvLines.emplace_back(std::move(currentLine)); - currentLine = std::vector(); - } - - stringTable->columnCount = static_cast(maxCols); - stringTable->rowCount = static_cast(csvLines.size()); - const auto cellCount = static_cast(stringTable->rowCount) * static_cast(stringTable->columnCount); - - if (cellCount) - { - stringTable->values = static_cast(memory->Alloc(sizeof(StringTableCell) * cellCount)); - - for (auto row = 0u; row < csvLines.size(); row++) - { - const auto& rowValues = csvLines[row]; - for (auto col = 0u; col < maxCols; col++) - { - auto& cell = stringTable->values[row * maxCols + col]; - if (col >= rowValues.size() || rowValues[col].empty()) - cell.string = ""; - else - cell.string = memory->Dup(rowValues[col].c_str()); - - cell.hash = Common::StringTable_HashString(cell.string); - } - } - } - else - { - stringTable->values = nullptr; - } + string_table::StringTableLoaderV2 loader; + auto* stringTable = loader.LoadFromStream(assetName, *memory, *file.m_stream); manager->AddAsset(ASSET_TYPE_STRINGTABLE, assetName, stringTable); diff --git a/src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderStringTable.cpp index 9c9eb6e24..ece654610 100644 --- a/src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderStringTable.cpp +++ b/src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderStringTable.cpp @@ -5,6 +5,7 @@ #include "Game/IW5/IW5.h" #include "ObjLoading.h" #include "Pool/GlobalAssetPool.h" +#include "StringTable/StringTableLoader.h" #include @@ -30,49 +31,8 @@ bool AssetLoaderStringTable::LoadFromRaw( if (!file.IsOpen()) return false; - auto* stringTable = memory->Create(); - stringTable->name = memory->Dup(assetName.c_str()); - - std::vector> csvLines; - std::vector currentLine; - auto maxCols = 0u; - const CsvInputStream csv(*file.m_stream); - - while (csv.NextRow(currentLine)) - { - if (currentLine.size() > maxCols) - maxCols = currentLine.size(); - csvLines.emplace_back(std::move(currentLine)); - currentLine = std::vector(); - } - - stringTable->columnCount = static_cast(maxCols); - stringTable->rowCount = static_cast(csvLines.size()); - const auto cellCount = static_cast(stringTable->rowCount) * static_cast(stringTable->columnCount); - - if (cellCount) - { - stringTable->values = static_cast(memory->Alloc(sizeof(StringTableCell) * cellCount)); - - for (auto row = 0u; row < csvLines.size(); row++) - { - const auto& rowValues = csvLines[row]; - for (auto col = 0u; col < maxCols; col++) - { - auto& cell = stringTable->values[row * maxCols + col]; - if (col >= rowValues.size() || rowValues[col].empty()) - cell.string = ""; - else - cell.string = memory->Dup(rowValues[col].c_str()); - - cell.hash = Common::StringTable_HashString(cell.string); - } - } - } - else - { - stringTable->values = nullptr; - } + string_table::StringTableLoaderV2 loader; + auto* stringTable = loader.LoadFromStream(assetName, *memory, *file.m_stream); manager->AddAsset(ASSET_TYPE_STRINGTABLE, assetName, stringTable); diff --git a/src/ObjLoading/Game/T5/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/T5/AssetLoaders/AssetLoaderStringTable.cpp index 8261df565..d8785dc76 100644 --- a/src/ObjLoading/Game/T5/AssetLoaders/AssetLoaderStringTable.cpp +++ b/src/ObjLoading/Game/T5/AssetLoaders/AssetLoaderStringTable.cpp @@ -4,6 +4,7 @@ #include "Game/T5/CommonT5.h" #include "Game/T5/T5.h" #include "Pool/GlobalAssetPool.h" +#include "StringTable/StringTableLoader.h" #include @@ -29,65 +30,8 @@ bool AssetLoaderStringTable::LoadFromRaw( if (!file.IsOpen()) return false; - auto* stringTable = memory->Create(); - stringTable->name = memory->Dup(assetName.c_str()); - - std::vector> csvLines; - std::vector currentLine; - auto maxCols = 0u; - const CsvInputStream csv(*file.m_stream); - - while (csv.NextRow(currentLine)) - { - if (currentLine.size() > maxCols) - maxCols = currentLine.size(); - csvLines.emplace_back(std::move(currentLine)); - currentLine = std::vector(); - } - - stringTable->columnCount = static_cast(maxCols); - stringTable->rowCount = static_cast(csvLines.size()); - const auto cellCount = static_cast(stringTable->rowCount) * static_cast(stringTable->columnCount); - - if (cellCount) - { - stringTable->values = static_cast(memory->Alloc(sizeof(StringTableCell) * cellCount)); - stringTable->cellIndex = static_cast(memory->Alloc(sizeof(int16_t) * cellCount)); - - for (auto c = 0u; c < cellCount; c++) - stringTable->cellIndex[c] = static_cast(c); - - for (auto row = 0u; row < csvLines.size(); row++) - { - const auto& rowValues = csvLines[row]; - for (auto col = 0u; col < maxCols; col++) - { - auto& cell = stringTable->values[row * maxCols + col]; - if (col >= rowValues.size() || rowValues[col].empty()) - cell.string = ""; - else - cell.string = memory->Dup(rowValues[col].c_str()); - - cell.hash = Common::Com_HashString(cell.string); - } - } - - std::sort(&stringTable->cellIndex[0], - &stringTable->cellIndex[cellCount - 1], - [stringTable, maxCols](const int16_t a, const int16_t b) - { - auto compareResult = stringTable->values[a].hash - stringTable->values[b].hash; - if (compareResult == 0) - compareResult = a % maxCols - b % maxCols; - return compareResult < 0; - }); - } - - else - { - stringTable->values = nullptr; - stringTable->cellIndex = nullptr; - } + string_table::StringTableLoaderV3 loader; + auto* stringTable = loader.LoadFromStream(assetName, *memory, *file.m_stream); manager->AddAsset(ASSET_TYPE_STRINGTABLE, assetName, stringTable); diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderStringTable.cpp index 150431f1d..e77cc4bb2 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderStringTable.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderStringTable.cpp @@ -4,6 +4,7 @@ #include "Game/T6/CommonT6.h" #include "Game/T6/T6.h" #include "Pool/GlobalAssetPool.h" +#include "StringTable/StringTableLoader.h" #include @@ -29,65 +30,8 @@ bool AssetLoaderStringTable::LoadFromRaw( if (!file.IsOpen()) return false; - auto* stringTable = memory->Create(); - stringTable->name = memory->Dup(assetName.c_str()); - - std::vector> csvLines; - std::vector currentLine; - auto maxCols = 0u; - const CsvInputStream csv(*file.m_stream); - - while (csv.NextRow(currentLine)) - { - if (currentLine.size() > maxCols) - maxCols = currentLine.size(); - csvLines.emplace_back(std::move(currentLine)); - currentLine = std::vector(); - } - - stringTable->columnCount = static_cast(maxCols); - stringTable->rowCount = static_cast(csvLines.size()); - const auto cellCount = static_cast(stringTable->rowCount) * static_cast(stringTable->columnCount); - - if (cellCount) - { - stringTable->values = static_cast(memory->Alloc(sizeof(StringTableCell) * cellCount)); - stringTable->cellIndex = static_cast(memory->Alloc(sizeof(int16_t) * cellCount)); - - for (auto c = 0u; c < cellCount; c++) - stringTable->cellIndex[c] = static_cast(c); - - for (auto row = 0u; row < csvLines.size(); row++) - { - const auto& rowValues = csvLines[row]; - for (auto col = 0u; col < maxCols; col++) - { - auto& cell = stringTable->values[row * maxCols + col]; - if (col >= rowValues.size() || rowValues[col].empty()) - cell.string = ""; - else - cell.string = memory->Dup(rowValues[col].c_str()); - - cell.hash = Common::Com_HashString(cell.string); - } - } - - std::sort(&stringTable->cellIndex[0], - &stringTable->cellIndex[cellCount - 1], - [stringTable, maxCols](const int16_t a, const int16_t b) - { - auto compareResult = stringTable->values[a].hash - stringTable->values[b].hash; - if (compareResult == 0) - compareResult = a % maxCols - b % maxCols; - return compareResult < 0; - }); - } - - else - { - stringTable->values = nullptr; - stringTable->cellIndex = nullptr; - } + string_table::StringTableLoaderV3 loader; + auto* stringTable = loader.LoadFromStream(assetName, *memory, *file.m_stream); manager->AddAsset(ASSET_TYPE_STRINGTABLE, assetName, stringTable); diff --git a/src/ObjLoading/StringTable/StringTableLoader.h b/src/ObjLoading/StringTable/StringTableLoader.h index b685030b2..fae42fc4c 100644 --- a/src/ObjLoading/StringTable/StringTableLoader.h +++ b/src/ObjLoading/StringTable/StringTableLoader.h @@ -86,4 +86,60 @@ namespace string_table cell = content; } }; + + // ================================= + // V2: IW4, IW5 + // - Cells are a struct and have a hash + // ================================= + template + class StringTableLoaderV2 final : public AbstractStringTableLoader> + { + using CellType_t = decltype(*StringTableType::values); + + protected: + void SetCellContent(CellType_t& cell, const char* content) override + { + cell.string = content; + cell.hash = HashFunc(content); + } + }; + + // ================================= + // V3: T5, T6 + // - Cells are a struct and have a hash + // - StringTable has an index array for binary search + // ================================= + template + class StringTableLoaderV3 final : public AbstractStringTableLoader> + { + using CellType_t = decltype(*StringTableType::values); + + protected: + void SetCellContent(CellType_t& cell, const char* content) override + { + cell.string = content; + cell.hash = HashFunc(content); + } + + void PostProcessStringTable(StringTableType* stringTable, const unsigned cellCount, MemoryManager& memory) override + { + if (!cellCount) + { + stringTable->cellIndex = nullptr; + return; + } + + stringTable->cellIndex = static_cast(memory.Alloc(sizeof(int16_t) * cellCount)); + + std::sort(&stringTable->cellIndex[0], + &stringTable->cellIndex[cellCount - 1], + [stringTable](const int16_t a, const int16_t b) + { + auto compareResult = stringTable->values[a].hash - stringTable->values[b].hash; + if (compareResult == 0) + compareResult = (a % stringTable->columnCount) - (b % stringTable->columnCount); + return compareResult < 0; + }); + } + }; } // namespace string_table From ada3a3251e9a2f46e28a922fe0a705a175f36331 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 20 Jan 2024 20:03:59 +0100 Subject: [PATCH 4/5] test: add unit tests for string table asset loaders --- .../AssetLoaderStringTableTest.cpp | 46 +++++++++++++++ .../AssetLoaderStringTableTest.cpp | 53 +++++++++++++++++ .../AssetLoaderStringTableTest.cpp | 52 ++++++++++++++++ .../AssetLoaderStringTableTest.cpp | 59 +++++++++++++++++++ .../AssetLoaderStringTableTest.cpp | 59 +++++++++++++++++++ 5 files changed, 269 insertions(+) create mode 100644 test/ObjLoadingTests/Game/IW3/AssetLoaders/AssetLoaderStringTableTest.cpp create mode 100644 test/ObjLoadingTests/Game/IW4/AssetLoaders/AssetLoaderStringTableTest.cpp create mode 100644 test/ObjLoadingTests/Game/IW5/AssetLoaders/AssetLoaderStringTableTest.cpp create mode 100644 test/ObjLoadingTests/Game/T5/AssetLoaders/AssetLoaderStringTableTest.cpp create mode 100644 test/ObjLoadingTests/Game/T6/AssetLoaders/AssetLoaderStringTableTest.cpp diff --git a/test/ObjLoadingTests/Game/IW3/AssetLoaders/AssetLoaderStringTableTest.cpp b/test/ObjLoadingTests/Game/IW3/AssetLoaders/AssetLoaderStringTableTest.cpp new file mode 100644 index 000000000..1663dc9f9 --- /dev/null +++ b/test/ObjLoadingTests/Game/IW3/AssetLoaders/AssetLoaderStringTableTest.cpp @@ -0,0 +1,46 @@ +#include "Game/IW3/AssetLoaders/AssetLoaderStringTable.h" + +#include "Game/IW3/GameIW3.h" +#include "Mock/MockAssetLoadingManager.h" +#include "Mock/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include + +using namespace IW3; +using namespace std::literals; + +namespace +{ + TEST_CASE("AssetLoaderStringTable(IW3): Can parse string table", "[iw3][stringtable][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("mp/cooltable.csv", + "test,data,lol\n" + "lorem,ipsum"); + + Zone zone("MockZone", 0, &g_GameIW3); + MockAssetLoadingManager assetLoadingManager(&zone, &searchPath); + + AssetLoaderStringTable assetLoader; + MemoryManager memory; + + assetLoader.LoadFromRaw("mp/cooltable.csv", &searchPath, &memory, &assetLoadingManager, &zone); + + auto* assetInfo = reinterpret_cast*>(assetLoadingManager.MockGetAddedAsset("mp/cooltable.csv")); + REQUIRE(assetInfo != nullptr); + + const auto* stringTable = assetInfo->Asset(); + REQUIRE(stringTable->name == "mp/cooltable.csv"s); + REQUIRE(stringTable->columnCount == 3); + REQUIRE(stringTable->rowCount == 2); + + REQUIRE(stringTable->values[0] == "test"s); + REQUIRE(stringTable->values[1] == "data"s); + REQUIRE(stringTable->values[2] == "lol"s); + REQUIRE(stringTable->values[3] == "lorem"s); + REQUIRE(stringTable->values[4] == "ipsum"s); + REQUIRE(stringTable->values[5] == ""s); + } +} // namespace diff --git a/test/ObjLoadingTests/Game/IW4/AssetLoaders/AssetLoaderStringTableTest.cpp b/test/ObjLoadingTests/Game/IW4/AssetLoaders/AssetLoaderStringTableTest.cpp new file mode 100644 index 000000000..47c1cff40 --- /dev/null +++ b/test/ObjLoadingTests/Game/IW4/AssetLoaders/AssetLoaderStringTableTest.cpp @@ -0,0 +1,53 @@ +#include "Game/IW4/AssetLoaders/AssetLoaderStringTable.h" + +#include "Game/IW4/CommonIW4.h" +#include "Game/IW4/GameIW4.h" +#include "Mock/MockAssetLoadingManager.h" +#include "Mock/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include + +using namespace IW4; +using namespace std::literals; + +namespace +{ + TEST_CASE("AssetLoaderStringTable(IW4): Can parse string table", "[iw4][stringtable][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("mp/cooltable.csv", + "test,data,lol\n" + "lorem,ipsum"); + + Zone zone("MockZone", 0, &g_GameIW4); + MockAssetLoadingManager assetLoadingManager(&zone, &searchPath); + + AssetLoaderStringTable assetLoader; + MemoryManager memory; + + assetLoader.LoadFromRaw("mp/cooltable.csv", &searchPath, &memory, &assetLoadingManager, &zone); + + auto* assetInfo = reinterpret_cast*>(assetLoadingManager.MockGetAddedAsset("mp/cooltable.csv")); + REQUIRE(assetInfo != nullptr); + + const auto* stringTable = assetInfo->Asset(); + REQUIRE(stringTable->name == "mp/cooltable.csv"s); + REQUIRE(stringTable->columnCount == 3); + REQUIRE(stringTable->rowCount == 2); + + CHECK(stringTable->values[0].string == "test"s); + CHECK(stringTable->values[0].hash == 0x364492); + CHECK(stringTable->values[1].string == "data"s); + CHECK(stringTable->values[1].hash == 0x2eefaa); + CHECK(stringTable->values[2].string == "lol"s); + CHECK(stringTable->values[2].hash == 0x1a349); + CHECK(stringTable->values[3].string == "lorem"s); + CHECK(stringTable->values[3].hash == 0x6261837); + CHECK(stringTable->values[4].string == "ipsum"s); + CHECK(stringTable->values[4].hash == 0x5fc4bc4); + CHECK(stringTable->values[5].string == ""s); + CHECK(stringTable->values[5].hash == 0x0); + } +} // namespace diff --git a/test/ObjLoadingTests/Game/IW5/AssetLoaders/AssetLoaderStringTableTest.cpp b/test/ObjLoadingTests/Game/IW5/AssetLoaders/AssetLoaderStringTableTest.cpp new file mode 100644 index 000000000..2dc8f1377 --- /dev/null +++ b/test/ObjLoadingTests/Game/IW5/AssetLoaders/AssetLoaderStringTableTest.cpp @@ -0,0 +1,52 @@ +#include "Game/IW5/AssetLoaders/AssetLoaderStringTable.h" + +#include "Game/IW5/GameIW5.h" +#include "Mock/MockAssetLoadingManager.h" +#include "Mock/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include + +using namespace IW5; +using namespace std::literals; + +namespace +{ + TEST_CASE("AssetLoaderStringTable(IW5): Can parse string table", "[iw5][stringtable][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("mp/cooltable.csv", + "test,data,lol\n" + "lorem,ipsum"); + + Zone zone("MockZone", 0, &g_GameIW5); + MockAssetLoadingManager assetLoadingManager(&zone, &searchPath); + + AssetLoaderStringTable assetLoader; + MemoryManager memory; + + assetLoader.LoadFromRaw("mp/cooltable.csv", &searchPath, &memory, &assetLoadingManager, &zone); + + auto* assetInfo = reinterpret_cast*>(assetLoadingManager.MockGetAddedAsset("mp/cooltable.csv")); + REQUIRE(assetInfo != nullptr); + + const auto* stringTable = assetInfo->Asset(); + REQUIRE(stringTable->name == "mp/cooltable.csv"s); + REQUIRE(stringTable->columnCount == 3); + REQUIRE(stringTable->rowCount == 2); + + CHECK(stringTable->values[0].string == "test"s); + CHECK(stringTable->values[0].hash == 0x364492); + CHECK(stringTable->values[1].string == "data"s); + CHECK(stringTable->values[1].hash == 0x2eefaa); + CHECK(stringTable->values[2].string == "lol"s); + CHECK(stringTable->values[2].hash == 0x1a349); + CHECK(stringTable->values[3].string == "lorem"s); + CHECK(stringTable->values[3].hash == 0x6261837); + CHECK(stringTable->values[4].string == "ipsum"s); + CHECK(stringTable->values[4].hash == 0x5fc4bc4); + CHECK(stringTable->values[5].string == ""s); + CHECK(stringTable->values[5].hash == 0x0); + } +} // namespace diff --git a/test/ObjLoadingTests/Game/T5/AssetLoaders/AssetLoaderStringTableTest.cpp b/test/ObjLoadingTests/Game/T5/AssetLoaders/AssetLoaderStringTableTest.cpp new file mode 100644 index 000000000..3a439fd59 --- /dev/null +++ b/test/ObjLoadingTests/Game/T5/AssetLoaders/AssetLoaderStringTableTest.cpp @@ -0,0 +1,59 @@ +#include "Game/T5/AssetLoaders/AssetLoaderStringTable.h" + +#include "Game/T5/GameT5.h" +#include "Mock/MockAssetLoadingManager.h" +#include "Mock/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include + +using namespace T5; +using namespace std::literals; + +namespace +{ + TEST_CASE("AssetLoaderStringTable(T5): Can parse string table", "[t5][stringtable][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("mp/cooltable.csv", + "test,data,lol\n" + "lorem,ipsum"); + + Zone zone("MockZone", 0, &g_GameT5); + MockAssetLoadingManager assetLoadingManager(&zone, &searchPath); + + AssetLoaderStringTable assetLoader; + MemoryManager memory; + + assetLoader.LoadFromRaw("mp/cooltable.csv", &searchPath, &memory, &assetLoadingManager, &zone); + + auto* assetInfo = reinterpret_cast*>(assetLoadingManager.MockGetAddedAsset("mp/cooltable.csv")); + REQUIRE(assetInfo != nullptr); + + const auto* stringTable = assetInfo->Asset(); + REQUIRE(stringTable->name == "mp/cooltable.csv"s); + REQUIRE(stringTable->columnCount == 3); + REQUIRE(stringTable->rowCount == 2); + + CHECK(stringTable->values[0].string == "test"s); + CHECK(stringTable->values[0].hash == 0x7c9e6865); + CHECK(stringTable->values[1].string == "data"s); + CHECK(stringTable->values[1].hash == 0x7c95915f); + CHECK(stringTable->values[2].string == "lol"s); + CHECK(stringTable->values[2].hash == 0xb888d0c); + CHECK(stringTable->values[3].string == "lorem"s); + CHECK(stringTable->values[3].hash == 0xfe02704); + CHECK(stringTable->values[4].string == "ipsum"s); + CHECK(stringTable->values[4].hash == 0xfaa7033); + CHECK(stringTable->values[5].string == ""s); + CHECK(stringTable->values[5].hash == 0x1505); + + REQUIRE(stringTable->cellIndex != nullptr); + CHECK(stringTable->cellIndex[0] == 2); + CHECK(stringTable->cellIndex[1] == 4); + CHECK(stringTable->cellIndex[2] == 3); + CHECK(stringTable->cellIndex[3] == 1); + CHECK(stringTable->cellIndex[4] == 0); + } +} // namespace diff --git a/test/ObjLoadingTests/Game/T6/AssetLoaders/AssetLoaderStringTableTest.cpp b/test/ObjLoadingTests/Game/T6/AssetLoaders/AssetLoaderStringTableTest.cpp new file mode 100644 index 000000000..4ca2edb03 --- /dev/null +++ b/test/ObjLoadingTests/Game/T6/AssetLoaders/AssetLoaderStringTableTest.cpp @@ -0,0 +1,59 @@ +#include "Game/T6/AssetLoaders/AssetLoaderStringTable.h" + +#include "Game/T6/GameT6.h" +#include "Mock/MockAssetLoadingManager.h" +#include "Mock/MockSearchPath.h" +#include "Utils/MemoryManager.h" + +#include +#include + +using namespace T6; +using namespace std::literals; + +namespace +{ + TEST_CASE("AssetLoaderStringTable(T6): Can parse string table", "[t6][stringtable][assetloader]") + { + MockSearchPath searchPath; + searchPath.AddFileData("mp/cooltable.csv", + "test,data,lol\n" + "lorem,ipsum"); + + Zone zone("MockZone", 0, &g_GameT6); + MockAssetLoadingManager assetLoadingManager(&zone, &searchPath); + + AssetLoaderStringTable assetLoader; + MemoryManager memory; + + assetLoader.LoadFromRaw("mp/cooltable.csv", &searchPath, &memory, &assetLoadingManager, &zone); + + auto* assetInfo = reinterpret_cast*>(assetLoadingManager.MockGetAddedAsset("mp/cooltable.csv")); + REQUIRE(assetInfo != nullptr); + + const auto* stringTable = assetInfo->Asset(); + REQUIRE(stringTable->name == "mp/cooltable.csv"s); + REQUIRE(stringTable->columnCount == 3); + REQUIRE(stringTable->rowCount == 2); + + CHECK(stringTable->values[0].string == "test"s); + CHECK(stringTable->values[0].hash == 0x7c9e6865); + CHECK(stringTable->values[1].string == "data"s); + CHECK(stringTable->values[1].hash == 0x7c95915f); + CHECK(stringTable->values[2].string == "lol"s); + CHECK(stringTable->values[2].hash == 0xb888d0c); + CHECK(stringTable->values[3].string == "lorem"s); + CHECK(stringTable->values[3].hash == 0xfe02704); + CHECK(stringTable->values[4].string == "ipsum"s); + CHECK(stringTable->values[4].hash == 0xfaa7033); + CHECK(stringTable->values[5].string == ""s); + CHECK(stringTable->values[5].hash == 0x1505); + + REQUIRE(stringTable->cellIndex != nullptr); + CHECK(stringTable->cellIndex[0] == 2); + CHECK(stringTable->cellIndex[1] == 4); + CHECK(stringTable->cellIndex[2] == 3); + CHECK(stringTable->cellIndex[3] == 1); + CHECK(stringTable->cellIndex[4] == 0); + } +} // namespace From c8d25059b92978971631dccd7488b74284da2fb2 Mon Sep 17 00:00:00 2001 From: Jan Date: Sat, 20 Jan 2024 20:04:22 +0100 Subject: [PATCH 5/5] fix: fix StringTableLoaderV3 not properly initializing cell indices --- src/ObjLoading/StringTable/StringTableLoader.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/ObjLoading/StringTable/StringTableLoader.h b/src/ObjLoading/StringTable/StringTableLoader.h index fae42fc4c..d3e45ae92 100644 --- a/src/ObjLoading/StringTable/StringTableLoader.h +++ b/src/ObjLoading/StringTable/StringTableLoader.h @@ -130,6 +130,8 @@ namespace string_table } stringTable->cellIndex = static_cast(memory.Alloc(sizeof(int16_t) * cellCount)); + for (auto i = 0u; i < cellCount; i++) + stringTable->cellIndex[i] = i; std::sort(&stringTable->cellIndex[0], &stringTable->cellIndex[cellCount - 1],