diff --git a/premake5.lua b/premake5.lua index 0152b930..6d9b19f7 100644 --- a/premake5.lua +++ b/premake5.lua @@ -3,6 +3,7 @@ include "tools/scripts/including.lua" include "tools/scripts/linking.lua" include "tools/scripts/options.lua" include "tools/scripts/platform.lua" +include "tools/scripts/version.lua" -- ================== -- Workspace @@ -63,6 +64,13 @@ workspace "OpenAssetTools" "__STDC_WANT_LIB_EXT1__=1", "_CRT_SECURE_NO_WARNINGS" } + + -- Write the current version to a header + -- This is better than adding it as macro here since changing a global macro would cause a full rebuild + WriteVersionHeader() + includedirs { + GetVersionHeaderFolder() + } filter "options:debug-structureddatadef" defines { "STRUCTUREDDATADEF_DEBUG" } diff --git a/src/Linker/Linker.cpp b/src/Linker/Linker.cpp index 613aee22..01a094e8 100644 --- a/src/Linker/Linker.cpp +++ b/src/Linker/Linker.cpp @@ -616,9 +616,13 @@ public: bool Start(const int argc, const char** argv) override { - if (!m_args.ParseArgs(argc, argv)) + auto shouldContinue = true; + if (!m_args.ParseArgs(argc, argv, shouldContinue)) return false; + if (!shouldContinue) + return true; + if (!m_search_paths.BuildProjectIndependentSearchPaths()) return false; diff --git a/src/Linker/LinkerArgs.cpp b/src/Linker/LinkerArgs.cpp index a9b2d318..1e6c927c 100644 --- a/src/Linker/LinkerArgs.cpp +++ b/src/Linker/LinkerArgs.cpp @@ -1,11 +1,13 @@ #include "LinkerArgs.h" +#include "GitVersion.h" #include "ObjLoading.h" #include "ObjWriting.h" #include "Utils/Arguments/UsageInformation.h" #include "Utils/FileUtils.h" #include +#include #include #include @@ -19,6 +21,12 @@ const CommandLineOption* const OPTION_HELP = .WithDescription("Displays usage information.") .Build(); +const CommandLineOption* const OPTION_VERSION = + CommandLineOption::Builder::Create() + .WithLongName("version") + .WithDescription("Prints the application version.") + .Build(); + const CommandLineOption* const OPTION_VERBOSE = CommandLineOption::Builder::Create() .WithShortName("v") @@ -88,6 +96,7 @@ const CommandLineOption* const OPTION_MENU_NO_OPTIMIZATION = const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ OPTION_HELP, + OPTION_VERSION, OPTION_VERBOSE, OPTION_BASE_FOLDER, OPTION_OUTPUT_FOLDER, @@ -100,7 +109,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ }; LinkerArgs::LinkerArgs() - : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent::value), + : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v), m_base_pattern(R"(\?base\?)"), m_game_pattern(R"(\?game\?)"), m_project_pattern(R"(\?project\?)"), @@ -125,6 +134,11 @@ void LinkerArgs::PrintUsage() usage.Print(); } +void LinkerArgs::PrintVersion() +{ + std::cout << "OpenAssetTools Linker " << std::string(GIT_VERSION) << "\n"; +} + void LinkerArgs::SetVerbose(const bool isVerbose) { m_verbose = isVerbose; @@ -198,8 +212,9 @@ std::set LinkerArgs::GetSearchPathsForProject(const std::set& 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 29bfb440..2524ec41 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; }; diff --git a/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderLocalizeEntry.cpp b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderLocalizeEntry.cpp new file mode 100644 index 00000000..a6ab6caa --- /dev/null +++ b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderLocalizeEntry.cpp @@ -0,0 +1,36 @@ +#include "AssetLoaderLocalizeEntry.h" + +#include "Localize/LocalizeCommonAssetLoader.h" + +using namespace IW3; + +XAssetInfoGeneric* AssetLoaderLocalizeEntry::LoadFromGlobalAssetPools(const std::string& assetName) const +{ + return nullptr; +} + +void* AssetLoaderLocalizeEntry::CreateEmptyAsset(const std::string& assetName, MemoryManager* memory) +{ + return nullptr; +} + +bool AssetLoaderLocalizeEntry::CanLoadFromRaw() const +{ + return true; +} + +bool AssetLoaderLocalizeEntry::LoadFromRaw( + const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const +{ + const LocalizeCommonAssetLoader commonLoader( + [memory, manager](const CommonLocalizeEntry& entry) + { + auto* localizeEntry = memory->Create(); + localizeEntry->name = memory->Dup(entry.m_key.c_str()); + localizeEntry->value = memory->Dup(entry.m_value.c_str()); + + manager->AddAsset(ASSET_TYPE_LOCALIZE_ENTRY, entry.m_key, localizeEntry); + }); + + return commonLoader.LoadLocalizeAsset(assetName, searchPath, manager, zone); +} diff --git a/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderLocalizeEntry.h b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderLocalizeEntry.h new file mode 100644 index 00000000..24af4893 --- /dev/null +++ b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderLocalizeEntry.h @@ -0,0 +1,19 @@ +#pragma once + +#include "AssetLoading/BasicAssetLoader.h" +#include "AssetLoading/IAssetLoadingManager.h" +#include "Game/IW3/IW3.h" +#include "SearchPath/ISearchPath.h" + +namespace IW3 +{ + class AssetLoaderLocalizeEntry final : public BasicAssetLoader + { + public: + _NODISCARD XAssetInfoGeneric* LoadFromGlobalAssetPools(const std::string& assetName) const override; + _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/Game/IW3/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/IW3/AssetLoaders/AssetLoaderStringTable.cpp new file mode 100644 index 00000000..895f2b24 --- /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 00000000..ec7bebb1 --- /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/Game/IW3/ObjLoaderIW3.cpp b/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp index ba76c278..2bf52739 100644 --- a/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp +++ b/src/ObjLoading/Game/IW3/ObjLoaderIW3.cpp @@ -1,6 +1,7 @@ #include "ObjLoaderIW3.h" #include "AssetLoaders/AssetLoaderGfxImage.h" +#include "AssetLoaders/AssetLoaderLocalizeEntry.h" #include "AssetLoaders/AssetLoaderRawFile.h" #include "AssetLoading/AssetLoadingManager.h" #include "Game/IW3/GameAssetPoolIW3.h" @@ -43,7 +44,7 @@ ObjLoader::ObjLoader() REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_FONT, Font_s)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_MENULIST, MenuList)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_MENU, menuDef_t)) - REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_LOCALIZE_ENTRY, LocalizeEntry)) + REGISTER_ASSET_LOADER(AssetLoaderLocalizeEntry) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_WEAPON, WeaponDef)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_SNDDRIVER_GLOBALS, SndDriverGlobals)) REGISTER_ASSET_LOADER(BASIC_LOADER(ASSET_TYPE_FX, FxEffectDef)) diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderLocalizeEntry.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderLocalizeEntry.cpp index a4fd689c..ea83282c 100644 --- a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderLocalizeEntry.cpp +++ b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderLocalizeEntry.cpp @@ -22,7 +22,7 @@ bool AssetLoaderLocalizeEntry::CanLoadFromRaw() const bool AssetLoaderLocalizeEntry::LoadFromRaw( const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const { - LocalizeCommonAssetLoader commonLoader( + const LocalizeCommonAssetLoader commonLoader( [memory, manager](const CommonLocalizeEntry& entry) { auto* localizeEntry = memory->Create(); diff --git a/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/IW4/AssetLoaders/AssetLoaderStringTable.cpp index 642a60a8..e7335004 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/AssetLoaderLocalizeEntry.cpp b/src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderLocalizeEntry.cpp index f8c47383..ac6d39e1 100644 --- a/src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderLocalizeEntry.cpp +++ b/src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderLocalizeEntry.cpp @@ -22,7 +22,7 @@ bool AssetLoaderLocalizeEntry::CanLoadFromRaw() const bool AssetLoaderLocalizeEntry::LoadFromRaw( const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const { - LocalizeCommonAssetLoader commonLoader( + const LocalizeCommonAssetLoader commonLoader( [memory, manager](const CommonLocalizeEntry& entry) { auto* localizeEntry = memory->Create(); diff --git a/src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/IW5/AssetLoaders/AssetLoaderStringTable.cpp index 9c9eb6e2..ece65461 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/AssetLoaderLocalizeEntry.cpp b/src/ObjLoading/Game/T5/AssetLoaders/AssetLoaderLocalizeEntry.cpp index 6152f877..091d217f 100644 --- a/src/ObjLoading/Game/T5/AssetLoaders/AssetLoaderLocalizeEntry.cpp +++ b/src/ObjLoading/Game/T5/AssetLoaders/AssetLoaderLocalizeEntry.cpp @@ -22,7 +22,7 @@ bool AssetLoaderLocalizeEntry::CanLoadFromRaw() const bool AssetLoaderLocalizeEntry::LoadFromRaw( const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const { - LocalizeCommonAssetLoader commonLoader( + const LocalizeCommonAssetLoader commonLoader( [memory, manager](const CommonLocalizeEntry& entry) { auto* localizeEntry = memory->Create(); diff --git a/src/ObjLoading/Game/T5/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/T5/AssetLoaders/AssetLoaderStringTable.cpp index 8261df56..d8785dc7 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/AssetLoaderLocalizeEntry.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderLocalizeEntry.cpp index 2aaa13e5..6dbb3bab 100644 --- a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderLocalizeEntry.cpp +++ b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderLocalizeEntry.cpp @@ -22,7 +22,7 @@ bool AssetLoaderLocalizeEntry::CanLoadFromRaw() const bool AssetLoaderLocalizeEntry::LoadFromRaw( const std::string& assetName, ISearchPath* searchPath, MemoryManager* memory, IAssetLoadingManager* manager, Zone* zone) const { - LocalizeCommonAssetLoader commonLoader( + const LocalizeCommonAssetLoader commonLoader( [memory, manager](const CommonLocalizeEntry& entry) { auto* localizeEntry = memory->Create(); diff --git a/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderStringTable.cpp b/src/ObjLoading/Game/T6/AssetLoaders/AssetLoaderStringTable.cpp index 150431f1..e77cc4bb 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 new file mode 100644 index 00000000..d3e45ae9 --- /dev/null +++ b/src/ObjLoading/StringTable/StringTableLoader.h @@ -0,0 +1,147 @@ +#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; + } + }; + + // ================================= + // 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)); + for (auto i = 0u; i < cellCount; i++) + stringTable->cellIndex[i] = i; + + 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 diff --git a/src/RawTemplater/RawTemplater.cpp b/src/RawTemplater/RawTemplater.cpp index 27af12d5..902e199b 100644 --- a/src/RawTemplater/RawTemplater.cpp +++ b/src/RawTemplater/RawTemplater.cpp @@ -45,9 +45,13 @@ public: int Run(const int argc, const char** argv) { - if (!m_args.Parse(argc, argv)) + auto shouldContinue = true; + if (!m_args.ParseArgs(argc, argv, shouldContinue)) return 1; + if (!shouldContinue) + return 0; + if (!m_args.m_build_log_file.empty()) { fs::path p = fs::path(m_args.m_build_log_file).parent_path(); diff --git a/src/RawTemplater/RawTemplaterArguments.cpp b/src/RawTemplater/RawTemplaterArguments.cpp index 9174d71f..d3879b10 100644 --- a/src/RawTemplater/RawTemplaterArguments.cpp +++ b/src/RawTemplater/RawTemplaterArguments.cpp @@ -1,11 +1,18 @@ #include "RawTemplaterArguments.h" +#include "GitVersion.h" #include "Utils/Arguments/CommandLineOption.h" #include "Utils/Arguments/UsageInformation.h" +#include +#include + const CommandLineOption* const OPTION_HELP = CommandLineOption::Builder::Create().WithShortName("?").WithLongName("help").WithDescription("Displays usage information.").Build(); +const CommandLineOption* const OPTION_VERSION = + CommandLineOption::Builder::Create().WithLongName("version").WithDescription("Prints the application version.").Build(); + const CommandLineOption* const OPTION_VERBOSE = CommandLineOption::Builder::Create().WithShortName("v").WithLongName("verbose").WithDescription("Outputs a lot more and more detailed messages.").Build(); @@ -30,7 +37,14 @@ const CommandLineOption* const OPTION_DEFINE = CommandLineOption::Builder::Creat .Reusable() .Build(); -const CommandLineOption* const COMMAND_LINE_OPTIONS[]{OPTION_HELP, OPTION_VERBOSE, OPTION_OUTPUT_FOLDER, OPTION_BUILD_LOG, OPTION_DEFINE}; +const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ + OPTION_HELP, + OPTION_VERSION, + OPTION_VERBOSE, + OPTION_OUTPUT_FOLDER, + OPTION_BUILD_LOG, + OPTION_DEFINE, +}; RawTemplaterArguments::RawTemplaterArguments() : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v), @@ -50,8 +64,14 @@ void RawTemplaterArguments::PrintUsage() usage.Print(); } -bool RawTemplaterArguments::Parse(const int argc, const char** argv) +void RawTemplaterArguments::PrintVersion() { + std::cout << "OpenAssetTools RawTemplater " << std::string(GIT_VERSION) << "\n"; +} + +bool RawTemplaterArguments::ParseArgs(const int argc, const char** argv, bool& shouldContinue) +{ + shouldContinue = true; if (!m_argument_parser.ParseArguments(argc - 1, &argv[1])) { PrintUsage(); @@ -62,7 +82,16 @@ bool RawTemplaterArguments::Parse(const int argc, const char** argv) if (m_argument_parser.IsOptionSpecified(OPTION_HELP)) { PrintUsage(); - return false; + shouldContinue = false; + return true; + } + + // Check if the user wants to see the version + if (m_argument_parser.IsOptionSpecified(OPTION_VERSION)) + { + PrintVersion(); + shouldContinue = false; + return true; } m_input_files = m_argument_parser.GetArguments(); diff --git a/src/RawTemplater/RawTemplaterArguments.h b/src/RawTemplater/RawTemplaterArguments.h index 463ccbc6..dadb1f88 100644 --- a/src/RawTemplater/RawTemplaterArguments.h +++ b/src/RawTemplater/RawTemplaterArguments.h @@ -14,6 +14,7 @@ class RawTemplaterArguments * \brief Prints a command line usage help text for the RawTemplater tool to stdout. */ static void PrintUsage(); + static void PrintVersion(); public: bool m_verbose; @@ -27,5 +28,5 @@ public: RawTemplaterArguments(); - bool Parse(int argc, const char** argv); + bool ParseArgs(int argc, const char** argv, bool& shouldContinue); }; diff --git a/src/Unlinker/Unlinker.cpp b/src/Unlinker/Unlinker.cpp index 2abcf354..f26cc12d 100644 --- a/src/Unlinker/Unlinker.cpp +++ b/src/Unlinker/Unlinker.cpp @@ -429,9 +429,13 @@ public: */ bool Start(const int argc, const char** argv) { - if (!m_args.ParseArgs(argc, argv)) + auto shouldContinue = true; + if (!m_args.ParseArgs(argc, argv, shouldContinue)) return false; + if (!shouldContinue) + return true; + if (!BuildSearchPaths()) return false; diff --git a/src/Unlinker/UnlinkerArgs.cpp b/src/Unlinker/UnlinkerArgs.cpp index 5c7bfaa0..29276c66 100644 --- a/src/Unlinker/UnlinkerArgs.cpp +++ b/src/Unlinker/UnlinkerArgs.cpp @@ -1,10 +1,12 @@ #include "UnlinkerArgs.h" +#include "GitVersion.h" #include "ObjLoading.h" #include "ObjWriting.h" #include "Utils/Arguments/UsageInformation.h" #include "Utils/FileUtils.h" +#include #include #include @@ -16,6 +18,12 @@ const CommandLineOption* const OPTION_HELP = .WithDescription("Displays usage information.") .Build(); +const CommandLineOption* const OPTION_VERSION = + CommandLineOption::Builder::Create() + .WithLongName("version") + .WithDescription("Prints the application version.") + .Build(); + const CommandLineOption* const OPTION_VERBOSE = CommandLineOption::Builder::Create() .WithShortName("v") @@ -113,6 +121,7 @@ const CommandLineOption* const OPTION_LEGACY_MENUS = const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ OPTION_HELP, + OPTION_VERSION, OPTION_VERBOSE, OPTION_MINIMAL_ZONE_FILE, OPTION_LOAD, @@ -155,6 +164,11 @@ void UnlinkerArgs::PrintUsage() usage.Print(); } +void UnlinkerArgs::PrintVersion() +{ + std::cout << "OpenAssetTools Unlinker " << std::string(GIT_VERSION) << "\n"; +} + void UnlinkerArgs::SetVerbose(const bool isVerbose) { m_verbose = isVerbose; @@ -237,8 +251,9 @@ void UnlinkerArgs::ParseCommaSeparatedAssetTypeString(const std::string& input) AddSpecifiedAssetType(std::string(lowerInput, currentPos, lowerInput.size() - currentPos)); } -bool UnlinkerArgs::ParseArgs(const int argc, const char** argv) +bool UnlinkerArgs::ParseArgs(const int argc, const char** argv, bool& shouldContinue) { + shouldContinue = true; if (!m_argument_parser.ParseArguments(argc - 1, &argv[1])) { PrintUsage(); @@ -249,7 +264,16 @@ bool UnlinkerArgs::ParseArgs(const int argc, const char** argv) if (m_argument_parser.IsOptionSpecified(OPTION_HELP)) { PrintUsage(); - return false; + shouldContinue = false; + return true; + } + + // Check if the user wants to see the version + if (m_argument_parser.IsOptionSpecified(OPTION_VERSION)) + { + PrintVersion(); + shouldContinue = false; + return true; } m_zones_to_unlink = m_argument_parser.GetArguments(); diff --git a/src/Unlinker/UnlinkerArgs.h b/src/Unlinker/UnlinkerArgs.h index d26dca94..35872566 100644 --- a/src/Unlinker/UnlinkerArgs.h +++ b/src/Unlinker/UnlinkerArgs.h @@ -21,6 +21,7 @@ private: * \brief Prints a command line usage help text for the Unlinker tool to stdout. */ static void PrintUsage(); + static void PrintVersion(); void SetVerbose(bool isVerbose); bool SetImageDumpingMode(); @@ -60,7 +61,7 @@ public: bool m_verbose; UnlinkerArgs(); - bool ParseArgs(int argc, const char** argv); + bool ParseArgs(int argc, const char** argv, bool& shouldContinue); /** * \brief Converts the output path specified by command line arguments to a path applies for the specified zone. diff --git a/src/ZoneCode.lua b/src/ZoneCode.lua index df12b90c..dd1e07d6 100644 --- a/src/ZoneCode.lua +++ b/src/ZoneCode.lua @@ -203,6 +203,8 @@ function ZoneCode:outputForAssets(assetList) buildoutputs { "%{wks.location}/src/ZoneCode/Game/%{file.basename}/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_load_db.cpp", "%{wks.location}/src/ZoneCode/Game/%{file.basename}/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_load_db.h", + "%{wks.location}/src/ZoneCode/Game/%{file.basename}/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_mark_db.cpp", + "%{wks.location}/src/ZoneCode/Game/%{file.basename}/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_mark_db.h", "%{wks.location}/src/ZoneCode/Game/%{file.basename}/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_write_db.cpp", "%{wks.location}/src/ZoneCode/Game/%{file.basename}/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_write_db.h", "%{wks.location}/src/ZoneCode/Game/%{file.basename}/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_struct_test.cpp", @@ -231,6 +233,8 @@ function ZoneCode:allLoadFiles() local assetNameLower = string.lower(assetName) table.insert(result, "%{wks.location}/src/ZoneCode/Game/" .. game .. "/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_load_db.cpp") table.insert(result, "%{wks.location}/src/ZoneCode/Game/" .. game .. "/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_load_db.h") + table.insert(result, "%{wks.location}/src/ZoneCode/Game/" .. game .. "/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_mark_db.cpp") + table.insert(result, "%{wks.location}/src/ZoneCode/Game/" .. game .. "/XAssets/" .. assetNameLower .. "/" .. assetNameLower .. "_mark_db.h") end end @@ -302,6 +306,7 @@ function ZoneCode:project() .. ' -c "' .. path.join(path.getabsolute(ProjectFolder()), 'ZoneCode/Game/%{file.basename}/%{file.basename}_Commands.txt') .. '"' .. ' -o "%{wks.location}/src/ZoneCode/Game/%{file.basename}/XAssets"' .. ' -g "*" ZoneLoad' + .. ' -g "*" ZoneMark' .. ' -g "*" ZoneWrite' .. ' -g "*" AssetStructTests' } diff --git a/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.cpp b/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.cpp index cda4cba4..75ae23e9 100644 --- a/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.cpp +++ b/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.cpp @@ -4,6 +4,7 @@ StructureInformation::StructureInformation(DefinitionWithMembers* definition) : m_definition(definition), m_asset_enum_entry(nullptr), m_is_leaf(false), + m_requires_marking(false), m_non_embedded_reference_exists(false), m_single_pointer_reference_exists(false), m_array_pointer_reference_exists(false), diff --git a/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.h b/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.h index cc82aeba..64f448f5 100644 --- a/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.h +++ b/src/ZoneCodeGeneratorLib/Domain/Information/StructureInformation.h @@ -19,6 +19,7 @@ public: std::vector> m_ordered_members; bool m_is_leaf; + bool m_requires_marking; bool m_non_embedded_reference_exists; bool m_single_pointer_reference_exists; diff --git a/src/ZoneCodeGeneratorLib/Generating/CodeGenerator.cpp b/src/ZoneCodeGeneratorLib/Generating/CodeGenerator.cpp index 575aa631..abf6dc52 100644 --- a/src/ZoneCodeGeneratorLib/Generating/CodeGenerator.cpp +++ b/src/ZoneCodeGeneratorLib/Generating/CodeGenerator.cpp @@ -3,6 +3,7 @@ #include "Domain/Computations/StructureComputations.h" #include "Templates/AssetStructTestsTemplate.h" #include "Templates/ZoneLoadTemplate.h" +#include "Templates/ZoneMarkTemplate.h" #include "Templates/ZoneWriteTemplate.h" #include @@ -20,6 +21,7 @@ CodeGenerator::CodeGenerator(const ZoneCodeGeneratorArguments* args) void CodeGenerator::SetupTemplates() { m_template_mapping["zoneload"] = std::make_unique(); + m_template_mapping["zonemark"] = std::make_unique(); m_template_mapping["zonewrite"] = std::make_unique(); m_template_mapping["assetstructtests"] = std::make_unique(); } diff --git a/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneLoadTemplate.cpp b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneLoadTemplate.cpp index 927f8273..7866f580 100644 --- a/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneLoadTemplate.cpp +++ b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneLoadTemplate.cpp @@ -27,6 +27,13 @@ class ZoneLoadTemplate::Internal final : BaseTemplate return str.str(); } + static std::string MarkerClassName(StructureInformation* asset) + { + std::ostringstream str; + str << "Marker_" << asset->m_definition->m_name; + return str.str(); + } + static std::string VariableDecl(const DataDefinition* def) { std::ostringstream str; @@ -155,7 +162,7 @@ class ZoneLoadTemplate::Internal final : BaseTemplate if (info && StructureComputations(info).IsAsset()) { LINE(LoaderClassName(info) << " loader(m_zone, m_stream);") - LINE("AddDependency(loader.Load(" << MakeTypePtrVarName(def) << "));") + LINE("loader.Load(" << MakeTypePtrVarName(def) << ");") } else { @@ -249,32 +256,6 @@ class ZoneLoadTemplate::Internal final : BaseTemplate LINE("}") } - void LoadMember_ScriptString(StructureInformation* info, - MemberInformation* member, - const DeclarationModifierComputations& modifier, - const MemberLoadType loadType) const - { - if (loadType == MemberLoadType::ARRAY_POINTER) - { - LINE("varScriptString = " << MakeMemberAccess(info, member, modifier) << ";") - LINE("LoadScriptStringArray(true, " << MakeEvaluation(modifier.GetArrayPointerCountEvaluation()) << ");") - } - else if (loadType == MemberLoadType::EMBEDDED_ARRAY) - { - LINE("varScriptString = " << MakeMemberAccess(info, member, modifier) << ";") - LINE("LoadScriptStringArray(false, " << MakeArrayCount(dynamic_cast(modifier.GetDeclarationModifier())) << ");") - } - else if (loadType == MemberLoadType::EMBEDDED) - { - LINE(MakeMemberAccess(info, member, modifier) << " = UseScriptString(" << MakeMemberAccess(info, member, modifier) << ");") - } - else - { - assert(false); - LINE("#error unsupported loadType " << static_cast(loadType) << " for scripstring") - } - } - void LoadMember_Asset(StructureInformation* info, MemberInformation* member, const DeclarationModifierComputations& modifier, @@ -283,7 +264,7 @@ class ZoneLoadTemplate::Internal final : BaseTemplate if (loadType == MemberLoadType::SINGLE_POINTER) { LINE(LoaderClassName(member->m_type) << " loader(m_zone, m_stream);") - LINE("AddDependency(loader.Load(&" << MakeMemberAccess(info, member, modifier) << "));") + LINE("loader.Load(&" << MakeMemberAccess(info, member, modifier) << ");") } else if (loadType == MemberLoadType::POINTER_ARRAY) { @@ -483,10 +464,6 @@ class ZoneLoadTemplate::Internal final : BaseTemplate { LoadMember_String(info, member, modifier, loadType); } - else if (member->m_is_script_string) - { - LoadMember_ScriptString(info, member, modifier, loadType); - } else if (member->m_type && StructureComputations(member->m_type).IsAsset()) { LoadMember_Asset(info, member, modifier, loadType); @@ -671,12 +648,6 @@ class ZoneLoadTemplate::Internal final : BaseTemplate LINE(MakeMemberAccess(info, member, modifier) << " = m_stream->ConvertOffsetToPointer(" << MakeMemberAccess(info, member, modifier) << ");") - if (member->m_is_script_string && loadType == MemberLoadType::ARRAY_POINTER) - { - LINE("MarkScriptStringArrayAsUsed(" << MakeMemberAccess(info, member, modifier) << ", " - << MakeEvaluation(modifier.GetArrayPointerCountEvaluation()) << ");") - } - m_intendation--; LINE("}") } @@ -894,7 +865,7 @@ class ZoneLoadTemplate::Internal final : BaseTemplate if (computations.ShouldIgnore()) return; - if (member->m_is_string || member->m_is_script_string || computations.ContainsNonEmbeddedReference() || member->m_type && !member->m_type->m_is_leaf + if (member->m_is_string || computations.ContainsNonEmbeddedReference() || member->m_type && !member->m_type->m_is_leaf || computations.IsAfterPartialLoad()) { if (info->m_definition->GetType() == DataDefinitionType::UNION) @@ -1099,7 +1070,12 @@ class ZoneLoadTemplate::Internal final : BaseTemplate m_intendation++; LINE("assert(pAsset != nullptr);") - LINE("m_asset_info = reinterpret_castm_definition->GetFullName() << ">*>(LinkAsset(GetAssetName(*pAsset), *pAsset));") + LINE("") + LINE(MarkerClassName(m_env.m_asset) << " marker(m_zone);") + LINE("marker.Mark(*pAsset);") + LINE("") + LINE("m_asset_info = reinterpret_castm_definition->GetFullName() << ">*>(LinkAsset(GetAssetName(*pAsset), *pAsset, marker.GetUsedScriptStrings(), marker.GetDependencies()));") LINE("*pAsset = m_asset_info->Asset();") m_intendation--; @@ -1272,6 +1248,7 @@ public: LINE("// ====================================================================") LINE("") LINE("#include \"" << Lower(m_env.m_asset->m_definition->m_name) << "_load_db.h\"") + LINE("#include \"" << Lower(m_env.m_asset->m_definition->m_name) << "_mark_db.h\"") LINE("#include ") LINE("") diff --git a/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneMarkTemplate.cpp b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneMarkTemplate.cpp new file mode 100644 index 00000000..45eacaa6 --- /dev/null +++ b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneMarkTemplate.cpp @@ -0,0 +1,823 @@ +#include "ZoneMarkTemplate.h" + +#include "Domain/Computations/MemberComputations.h" +#include "Domain/Computations/StructureComputations.h" +#include "Internal/BaseTemplate.h" +#include "Utils/StringUtils.h" + +#include +#include +#include + +class ZoneMarkTemplate::Internal final : BaseTemplate +{ + enum class MemberLoadType + { + ARRAY_POINTER, + DYNAMIC_ARRAY, + EMBEDDED, + EMBEDDED_ARRAY, + POINTER_ARRAY, + SINGLE_POINTER + }; + + static std::string MarkerClassName(StructureInformation* asset) + { + std::ostringstream str; + str << "Marker_" << asset->m_definition->m_name; + return str.str(); + } + + static std::string VariableDecl(const DataDefinition* def) + { + std::ostringstream str; + str << def->GetFullName() << "* var" << MakeSafeTypeName(def) << ";"; + return str.str(); + } + + static std::string PointerVariableDecl(const DataDefinition* def) + { + std::ostringstream str; + str << def->GetFullName() << "** var" << MakeSafeTypeName(def) << "Ptr;"; + return str.str(); + } + + void PrintHeaderPtrArrayMarkMethodDeclaration(const DataDefinition* def) const + { + LINE("void MarkPtrArray_" << MakeSafeTypeName(def) << "(size_t count);") + } + + void PrintHeaderArrayMarkMethodDeclaration(const DataDefinition* def) const + { + LINE("void MarkArray_" << MakeSafeTypeName(def) << "(size_t count);") + } + + void PrintHeaderMarkMethodDeclaration(const StructureInformation* info) const + { + LINE("void Mark_" << MakeSafeTypeName(info->m_definition) << "();") + } + + void PrintHeaderGetAssetInfoMethodDeclaration(const StructureInformation* info) const + { + LINE("XAssetInfo<" << info->m_definition->GetFullName() << ">* GetAssetInfo(" << info->m_definition->GetFullName() << "* pAsset) const;") + } + + void PrintHeaderGetNameMethodDeclaration(const StructureInformation* info) const + { + LINE("static std::string GetAssetName(" << info->m_definition->GetFullName() << "* pAsset);") + } + + void PrintHeaderConstructor() const + { + LINE(MarkerClassName(m_env.m_asset) << "(Zone* zone);") + } + + void PrintHeaderMainMarkMethodDeclaration(const StructureInformation* info) const + { + LINE("void Mark(" << info->m_definition->GetFullName() << "* pAsset);") + } + + void PrintVariableInitialization(const DataDefinition* def) const + { + LINE("var" << def->m_name << " = nullptr;") + } + + void PrintPointerVariableInitialization(const DataDefinition* def) const + { + LINE("var" << def->m_name << "Ptr = nullptr;") + } + + void PrintConstructorMethod() + { + LINE(MarkerClassName(m_env.m_asset) << "::" << MarkerClassName(m_env.m_asset) << "(Zone* zone)") + + m_intendation++; + LINE(": AssetMarker(" << m_env.m_asset->m_asset_enum_entry->m_name << ", zone)") + m_intendation--; + + LINE("{") + m_intendation++; + + PrintVariableInitialization(m_env.m_asset->m_definition); + PrintPointerVariableInitialization(m_env.m_asset->m_definition); + LINE("") + + for (const auto* type : m_env.m_used_types) + { + if (type->m_info && !type->m_info->m_definition->m_anonymous && !type->m_info->m_is_leaf && !StructureComputations(type->m_info).IsAsset()) + { + PrintVariableInitialization(type->m_type); + } + } + for (const auto* type : m_env.m_used_types) + { + if (type->m_info && type->m_pointer_array_reference_exists && !type->m_is_context_asset) + { + PrintPointerVariableInitialization(type->m_type); + } + } + + m_intendation--; + LINE("}") + } + + void PrintMarkPtrArrayMethod_Loading(const DataDefinition* def, const StructureInformation* info) const + { + if (info && !info->m_is_leaf) + { + LINE(MakeTypeVarName(info->m_definition) << " = *" << MakeTypePtrVarName(def) << ";") + LINE("Mark_" << MakeSafeTypeName(def) << "();") + } + } + + void PrintMarkPtrArrayMethod_PointerCheck(const DataDefinition* def, StructureInformation* info, const bool reusable) + { + LINE("if (*" << MakeTypePtrVarName(def) << ")") + LINE("{") + m_intendation++; + + if (info && StructureComputations(info).IsAsset()) + { + LINE("AddDependency(" << MarkerClassName(info) << "(m_zone).GetAssetInfo(*" << MakeTypePtrVarName(def) << "));") + } + else + { + PrintMarkPtrArrayMethod_Loading(def, info); + } + + m_intendation--; + LINE("}") + } + + void PrintMarkPtrArrayMethod(const DataDefinition* def, StructureInformation* info, const bool reusable) + { + LINE("void " << MarkerClassName(m_env.m_asset) << "::MarkPtrArray_" << MakeSafeTypeName(def) << "(const size_t count)") + LINE("{") + m_intendation++; + + LINE("assert(" << MakeTypePtrVarName(def) << " != nullptr);") + LINE("") + + LINE(def->GetFullName() << "** var = " << MakeTypePtrVarName(def) << ";") + LINE("for(size_t index = 0; index < count; index++)") + LINE("{") + m_intendation++; + + LINE(MakeTypePtrVarName(def) << " = var;") + PrintMarkPtrArrayMethod_PointerCheck(def, info, reusable); + LINE("") + LINE("var++;") + + m_intendation--; + LINE("}") + m_intendation--; + LINE("}") + } + + void PrintMarkArrayMethod(const DataDefinition* def, const StructureInformation* info) + { + LINE("void " << MarkerClassName(m_env.m_asset) << "::MarkArray_" << MakeSafeTypeName(def) << "(const size_t count)") + LINE("{") + m_intendation++; + + LINE("assert(" << MakeTypeVarName(def) << " != nullptr);") + LINE("") + + LINE(def->GetFullName() << "* var = " << MakeTypeVarName(def) << ";") + LINE("for(size_t index = 0; index < count; index++)") + LINE("{") + m_intendation++; + + LINE(MakeTypeVarName(info->m_definition) << " = var;") + LINE("Mark_" << info->m_definition->m_name << "();") + LINE("var++;") + + m_intendation--; + LINE("}") + + m_intendation--; + LINE("}") + } + + void MarkMember_ScriptString(StructureInformation* info, + MemberInformation* member, + const DeclarationModifierComputations& modifier, + const MemberLoadType loadType) const + { + if (loadType == MemberLoadType::ARRAY_POINTER) + { + LINE("MarkArray_ScriptString(" << MakeMemberAccess(info, member, modifier) << ", " << MakeEvaluation(modifier.GetArrayPointerCountEvaluation()) + << ");") + } + else if (loadType == MemberLoadType::EMBEDDED_ARRAY) + { + LINE("MarkArray_ScriptString(" << MakeMemberAccess(info, member, modifier) << ", " + << MakeArrayCount(dynamic_cast(modifier.GetDeclarationModifier())) << ");") + } + else if (loadType == MemberLoadType::EMBEDDED) + { + LINE("Mark_ScriptString(" << MakeMemberAccess(info, member, modifier) << ");") + } + else + { + assert(false); + LINE("#error unsupported loadType " << static_cast(loadType) << " for scriptstring") + } + } + + void MarkMember_Asset(StructureInformation* info, + MemberInformation* member, + const DeclarationModifierComputations& modifier, + const MemberLoadType loadType) const + { + if (loadType == MemberLoadType::SINGLE_POINTER) + { + LINE("AddDependency(" << MarkerClassName(member->m_type) << "(m_zone).GetAssetInfo(" << MakeMemberAccess(info, member, modifier) << "));") + } + else if (loadType == MemberLoadType::POINTER_ARRAY) + { + MarkMember_PointerArray(info, member, modifier); + } + else + { + assert(false); + LINE("#error unsupported loadType " << static_cast(loadType) << " for asset") + } + } + + void MarkMember_ArrayPointer(StructureInformation* info, MemberInformation* member, const DeclarationModifierComputations& modifier) const + { + LINE(MakeTypeVarName(member->m_member->m_type_declaration->m_type) << " = " << MakeMemberAccess(info, member, modifier) << ";") + LINE("MarkArray_" << MakeSafeTypeName(member->m_member->m_type_declaration->m_type) << "(" << MakeEvaluation(modifier.GetArrayPointerCountEvaluation()) + << ");") + } + + void MarkMember_PointerArray(StructureInformation* info, MemberInformation* member, const DeclarationModifierComputations& modifier) const + { + LINE(MakeTypePtrVarName(member->m_member->m_type_declaration->m_type) << " = " << MakeMemberAccess(info, member, modifier) << ";") + if (modifier.IsArray()) + { + LINE("MarkPtrArray_" << MakeSafeTypeName(member->m_member->m_type_declaration->m_type) << "(" << modifier.GetArraySize() << ");") + } + else + { + LINE("MarkPtrArray_" << MakeSafeTypeName(member->m_member->m_type_declaration->m_type) << "(" + << MakeEvaluation(modifier.GetPointerArrayCountEvaluation()) << ");") + } + } + + void MarkMember_EmbeddedArray(StructureInformation* info, MemberInformation* member, const DeclarationModifierComputations& modifier) const + { + const MemberComputations computations(member); + std::string arraySizeStr; + + if (modifier.HasDynamicArrayCount()) + arraySizeStr = MakeEvaluation(modifier.GetDynamicArrayCountEvaluation()); + else + arraySizeStr = std::to_string(modifier.GetArraySize()); + + LINE(MakeTypeVarName(member->m_member->m_type_declaration->m_type) << " = " << MakeMemberAccess(info, member, modifier) << ";") + LINE("MarkArray_" << MakeSafeTypeName(member->m_member->m_type_declaration->m_type) << "(" << arraySizeStr << ");") + } + + void MarkMember_DynamicArray(StructureInformation* info, MemberInformation* member, const DeclarationModifierComputations& modifier) const + { + LINE(MakeTypeVarName(member->m_member->m_type_declaration->m_type) << " = " << MakeMemberAccess(info, member, modifier) << ";") + LINE("MarkArray_" << MakeSafeTypeName(member->m_member->m_type_declaration->m_type) << "(" << MakeEvaluation(modifier.GetDynamicArraySizeEvaluation()) + << ");") + } + + void MarkMember_Embedded(StructureInformation* info, MemberInformation* member, const DeclarationModifierComputations& modifier) const + { + LINE(MakeTypeVarName(member->m_member->m_type_declaration->m_type) << " = &" << MakeMemberAccess(info, member, modifier) << ";") + LINE("Mark_" << MakeSafeTypeName(member->m_member->m_type_declaration->m_type) << "();") + } + + void MarkMember_SinglePointer(StructureInformation* info, MemberInformation* member, const DeclarationModifierComputations& modifier) const + { + LINE(MakeTypeVarName(member->m_member->m_type_declaration->m_type) << " = " << MakeMemberAccess(info, member, modifier) << ";") + LINE("Mark_" << MakeSafeTypeName(member->m_type->m_definition) << "();") + } + + void MarkMember_TypeCheck(StructureInformation* info, + MemberInformation* member, + const DeclarationModifierComputations& modifier, + const MemberLoadType loadType) const + { + if (member->m_is_script_string) + { + MarkMember_ScriptString(info, member, modifier, loadType); + } + else if (member->m_type && StructureComputations(member->m_type).IsAsset()) + { + MarkMember_Asset(info, member, modifier, loadType); + } + else + { + switch (loadType) + { + case MemberLoadType::ARRAY_POINTER: + MarkMember_ArrayPointer(info, member, modifier); + break; + + case MemberLoadType::SINGLE_POINTER: + MarkMember_SinglePointer(info, member, modifier); + break; + + case MemberLoadType::EMBEDDED: + MarkMember_Embedded(info, member, modifier); + break; + + case MemberLoadType::POINTER_ARRAY: + MarkMember_PointerArray(info, member, modifier); + break; + + case MemberLoadType::DYNAMIC_ARRAY: + MarkMember_DynamicArray(info, member, modifier); + break; + + case MemberLoadType::EMBEDDED_ARRAY: + MarkMember_EmbeddedArray(info, member, modifier); + break; + + default: + LINE("// t=" << static_cast(loadType)) + break; + } + } + } + + static bool MarkMember_ShouldMakePointerCheck(StructureInformation* info, + MemberInformation* member, + const DeclarationModifierComputations& modifier, + MemberLoadType loadType) + { + if (loadType != MemberLoadType::ARRAY_POINTER && loadType != MemberLoadType::POINTER_ARRAY && loadType != MemberLoadType::SINGLE_POINTER) + { + return false; + } + + if (loadType == MemberLoadType::POINTER_ARRAY) + { + return !modifier.IsArray(); + } + + if (member->m_is_string) + { + return false; + } + + return true; + } + + void MarkMember_PointerCheck(StructureInformation* info, + MemberInformation* member, + const DeclarationModifierComputations& modifier, + const MemberLoadType loadType) + { + if (MarkMember_ShouldMakePointerCheck(info, member, modifier, loadType)) + { + LINE("if (" << MakeMemberAccess(info, member, modifier) << ")") + LINE("{") + m_intendation++; + + MarkMember_TypeCheck(info, member, modifier, loadType); + + m_intendation--; + LINE("}") + } + else + { + MarkMember_TypeCheck(info, member, modifier, loadType); + } + } + + void MarkMember_ReferenceArray(StructureInformation* info, MemberInformation* member, const DeclarationModifierComputations& modifier) + { + auto first = true; + for (const auto& entry : modifier.GetArrayEntries()) + { + if (first) + { + first = false; + } + else + { + LINE("") + } + + MarkMember_Reference(info, member, entry); + } + } + + void MarkMember_Reference(StructureInformation* info, MemberInformation* member, const DeclarationModifierComputations& modifier) + { + if (modifier.IsDynamicArray()) + { + MarkMember_PointerCheck(info, member, modifier, MemberLoadType::DYNAMIC_ARRAY); + } + else if (modifier.IsSinglePointer()) + { + MarkMember_PointerCheck(info, member, modifier, MemberLoadType::SINGLE_POINTER); + } + else if (modifier.IsArrayPointer()) + { + MarkMember_PointerCheck(info, member, modifier, MemberLoadType::ARRAY_POINTER); + } + else if (modifier.IsPointerArray()) + { + MarkMember_PointerCheck(info, member, modifier, MemberLoadType::POINTER_ARRAY); + } + else if (modifier.IsArray() && modifier.GetNextDeclarationModifier() == nullptr) + { + MarkMember_PointerCheck(info, member, modifier, MemberLoadType::EMBEDDED_ARRAY); + } + else if (modifier.GetDeclarationModifier() == nullptr) + { + MarkMember_PointerCheck(info, member, modifier, MemberLoadType::EMBEDDED); + } + else if (modifier.IsArray()) + { + MarkMember_ReferenceArray(info, member, modifier); + } + else + { + assert(false); + LINE("#error MarkMemberReference failed @ " << member->m_member->m_name) + } + } + + void MarkMember_Condition_Struct(StructureInformation* info, MemberInformation* member) + { + LINE("") + if (member->m_condition) + { + LINE("if (" << MakeEvaluation(member->m_condition.get()) << ")") + LINE("{") + m_intendation++; + + MarkMember_Reference(info, member, DeclarationModifierComputations(member)); + + m_intendation--; + LINE("}") + } + else + { + MarkMember_Reference(info, member, DeclarationModifierComputations(member)); + } + } + + void MarkMember_Condition_Union(StructureInformation* info, MemberInformation* member) + { + const MemberComputations computations(member); + + if (computations.IsFirstMember()) + { + LINE("") + if (member->m_condition) + { + LINE("if (" << MakeEvaluation(member->m_condition.get()) << ")") + LINE("{") + m_intendation++; + + MarkMember_Reference(info, member, DeclarationModifierComputations(member)); + + m_intendation--; + LINE("}") + } + else + { + MarkMember_Reference(info, member, DeclarationModifierComputations(member)); + } + } + else if (computations.IsLastMember()) + { + if (member->m_condition) + { + LINE("else if (" << MakeEvaluation(member->m_condition.get()) << ")") + LINE("{") + m_intendation++; + + MarkMember_Reference(info, member, DeclarationModifierComputations(member)); + + m_intendation--; + LINE("}") + } + else + { + LINE("else") + LINE("{") + m_intendation++; + + MarkMember_Reference(info, member, DeclarationModifierComputations(member)); + + m_intendation--; + LINE("}") + } + } + else + { + if (member->m_condition) + { + LINE("else if (" << MakeEvaluation(member->m_condition.get()) << ")") + LINE("{") + m_intendation++; + + MarkMember_Reference(info, member, DeclarationModifierComputations(member)); + + m_intendation--; + LINE("}") + } + else + { + LINE("#error Middle member of union must have condition (" << member->m_member->m_name << ")") + } + } + } + + void PrintMarkMemberIfNeedsTreatment(StructureInformation* info, MemberInformation* member) + { + const MemberComputations computations(member); + if (computations.ShouldIgnore() || computations.IsInRuntimeBlock()) + return; + + if (member->m_is_script_string || member->m_type && (member->m_type->m_requires_marking || StructureComputations(member->m_type).IsAsset())) + { + if (info->m_definition->GetType() == DataDefinitionType::UNION) + MarkMember_Condition_Union(info, member); + else + MarkMember_Condition_Struct(info, member); + } + } + + void PrintMarkMethod(StructureInformation* info) + { + const StructureComputations computations(info); + LINE("void " << MarkerClassName(m_env.m_asset) << "::Mark_" << info->m_definition->m_name << "()") + LINE("{") + m_intendation++; + + LINE("assert(" << MakeTypeVarName(info->m_definition) << " != nullptr);") + + for (const auto& member : info->m_ordered_members) + { + PrintMarkMemberIfNeedsTreatment(info, member.get()); + } + + m_intendation--; + LINE("}") + } + + void PrintGetNameMethod() + { + LINE("std::string " << MarkerClassName(m_env.m_asset) << "::GetAssetName(" << m_env.m_asset->m_definition->GetFullName() << "* pAsset)") + LINE("{") + m_intendation++; + + if (!m_env.m_asset->m_name_chain.empty()) + { + LINE_START("return pAsset") + + auto first = true; + for (auto* member : m_env.m_asset->m_name_chain) + { + if (first) + { + first = false; + LINE_MIDDLE("->" << member->m_member->m_name) + } + else + { + LINE_MIDDLE("." << member->m_member->m_name) + } + } + LINE_END(";") + } + else + { + LINE("return \"" << m_env.m_asset->m_definition->m_name << "\";") + } + + m_intendation--; + LINE("}") + } + + void PrintGetAssetInfoMethod() + { + LINE("XAssetInfo<" << m_env.m_asset->m_definition->GetFullName() << ">* " << MarkerClassName(m_env.m_asset) << "::GetAssetInfo(" + << m_env.m_asset->m_definition->GetFullName() << "* pAsset) const") + LINE("{") + m_intendation++; + + LINE("return reinterpret_castm_definition->GetFullName() << ">*>(GetAssetInfoByName(GetAssetName(pAsset)));") + + m_intendation--; + LINE("}") + } + + void PrintMainMarkMethod() + { + LINE("void " << MarkerClassName(m_env.m_asset) << "::Mark(" << m_env.m_asset->m_definition->GetFullName() << "* pAsset)") + LINE("{") + m_intendation++; + + LINE("assert(pAsset != nullptr);") + LINE("") + LINE(MakeTypeVarName(m_env.m_asset->m_definition) << " = pAsset;") + LINE("Mark_" << MakeSafeTypeName(m_env.m_asset->m_definition) << "();") + + m_intendation--; + LINE("}") + } + +public: + Internal(std::ostream& stream, RenderingContext* context) + : BaseTemplate(stream, context) + { + } + + void Header() + { + LINE("// ====================================================================") + LINE("// This file has been generated by ZoneCodeGenerator.") + LINE("// Do not modify. ") + LINE("// Any changes will be discarded when regenerating.") + LINE("// ====================================================================") + LINE("") + LINE("#pragma once") + LINE("") + LINE("#include \"Loading/AssetMarker.h\"") + LINE("#include \"Game/" << m_env.m_game << "/" << m_env.m_game << ".h\"") + LINE("#include ") + LINE("") + LINE("namespace " << m_env.m_game) + LINE("{") + m_intendation++; + LINE("class " << MarkerClassName(m_env.m_asset) << " final : public AssetMarker") + LINE("{") + m_intendation++; + + LINE(VariableDecl(m_env.m_asset->m_definition)) + LINE(PointerVariableDecl(m_env.m_asset->m_definition)) + LINE("") + + m_intendation--; + LINE("public:") + m_intendation++; + + // Variable Declarations: type varType; + for (auto* type : m_env.m_used_types) + { + if (type->m_info && !type->m_info->m_definition->m_anonymous && !type->m_info->m_is_leaf && !StructureComputations(type->m_info).IsAsset()) + { + LINE(VariableDecl(type->m_type)) + } + } + for (auto* type : m_env.m_used_types) + { + if (type->m_pointer_array_reference_exists && !type->m_is_context_asset) + { + LINE(PointerVariableDecl(type->m_type)) + } + } + + LINE("") + + // Method Declarations + for (auto* type : m_env.m_used_types) + { + if (type->m_pointer_array_reference_exists && type->m_info->m_requires_marking) + { + PrintHeaderPtrArrayMarkMethodDeclaration(type->m_type); + } + } + for (auto* type : m_env.m_used_types) + { + if (type->m_array_reference_exists && type->m_info && !type->m_info->m_is_leaf && type->m_info->m_requires_marking + && type->m_non_runtime_reference_exists) + { + PrintHeaderArrayMarkMethodDeclaration(type->m_type); + } + } + for (const auto* type : m_env.m_used_structures) + { + if (type->m_non_runtime_reference_exists && !type->m_info->m_is_leaf && type->m_info->m_requires_marking + && !StructureComputations(type->m_info).IsAsset()) + { + PrintHeaderMarkMethodDeclaration(type->m_info); + } + } + PrintHeaderMarkMethodDeclaration(m_env.m_asset); + LINE("") + PrintHeaderGetNameMethodDeclaration(m_env.m_asset); + PrintHeaderGetAssetInfoMethodDeclaration(m_env.m_asset); + LINE("") + PrintHeaderConstructor(); + PrintHeaderMainMarkMethodDeclaration(m_env.m_asset); + + m_intendation--; + LINE("};") + m_intendation--; + LINE("}") + } + + void Source() + { + LINE("// ====================================================================") + LINE("// This file has been generated by ZoneCodeGenerator.") + LINE("// Do not modify. ") + LINE("// Any changes will be discarded when regenerating.") + LINE("// ====================================================================") + LINE("") + LINE("#include \"" << Lower(m_env.m_asset->m_definition->m_name) << "_mark_db.h\"") + LINE("#include ") + LINE("") + + if (!m_env.m_referenced_assets.empty()) + { + LINE("// Referenced Assets:") + for (const auto* type : m_env.m_referenced_assets) + { + LINE("#include \"../" << Lower(type->m_type->m_name) << "/" << Lower(type->m_type->m_name) << "_mark_db.h\"") + } + LINE("") + } + LINE("using namespace " << m_env.m_game << ";") + LINE("") + PrintConstructorMethod(); + + for (const auto* type : m_env.m_used_types) + { + if (type->m_pointer_array_reference_exists && type->m_info->m_requires_marking) + { + LINE("") + PrintMarkPtrArrayMethod(type->m_type, type->m_info, type->m_pointer_array_reference_is_reusable); + } + } + for (const auto* type : m_env.m_used_types) + { + if (type->m_array_reference_exists && type->m_info && !type->m_info->m_is_leaf && type->m_info->m_requires_marking + && type->m_non_runtime_reference_exists) + { + LINE("") + PrintMarkArrayMethod(type->m_type, type->m_info); + } + } + for (const auto* type : m_env.m_used_structures) + { + if (type->m_non_runtime_reference_exists && !type->m_info->m_is_leaf && type->m_info->m_requires_marking + && !StructureComputations(type->m_info).IsAsset()) + { + LINE("") + PrintMarkMethod(type->m_info); + } + } + LINE("") + PrintMarkMethod(m_env.m_asset); + LINE("") + PrintMainMarkMethod(); + LINE("") + PrintGetNameMethod(); + PrintGetAssetInfoMethod(); + } +}; + +std::vector ZoneMarkTemplate::GetFilesToRender(RenderingContext* context) +{ + std::vector files; + + auto assetName = context->m_asset->m_definition->m_name; + utils::MakeStringLowerCase(assetName); + + { + std::ostringstream str; + str << assetName << '/' << assetName << "_mark_db.h"; + files.emplace_back(str.str(), TAG_HEADER); + } + + { + std::ostringstream str; + str << assetName << '/' << assetName << "_mark_db.cpp"; + files.emplace_back(str.str(), TAG_SOURCE); + } + + return files; +} + +void ZoneMarkTemplate::RenderFile(std::ostream& stream, const int fileTag, RenderingContext* context) +{ + Internal internal(stream, context); + + if (fileTag == TAG_HEADER) + { + internal.Header(); + } + else if (fileTag == TAG_SOURCE) + { + internal.Source(); + } + else + { + std::cout << "Unknown tag for ZoneMarkTemplate: " << fileTag << "\n"; + } +} diff --git a/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneMarkTemplate.h b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneMarkTemplate.h new file mode 100644 index 00000000..95513e07 --- /dev/null +++ b/src/ZoneCodeGeneratorLib/Generating/Templates/ZoneMarkTemplate.h @@ -0,0 +1,14 @@ +#pragma once +#include "Generating/ICodeTemplate.h" + +class ZoneMarkTemplate final : public ICodeTemplate +{ + static constexpr int TAG_HEADER = 1; + static constexpr int TAG_SOURCE = 2; + + class Internal; + +public: + std::vector GetFilesToRender(RenderingContext* context) override; + void RenderFile(std::ostream& stream, int fileTag, RenderingContext* context) override; +}; diff --git a/src/ZoneCodeGeneratorLib/Parsing/Commands/CommandsFileReader.cpp b/src/ZoneCodeGeneratorLib/Parsing/Commands/CommandsFileReader.cpp index fd9d8c62..40c1fbdd 100644 --- a/src/ZoneCodeGeneratorLib/Parsing/Commands/CommandsFileReader.cpp +++ b/src/ZoneCodeGeneratorLib/Parsing/Commands/CommandsFileReader.cpp @@ -8,6 +8,7 @@ #include "Parsing/Impl/ParserFilesystemStream.h" #include "Parsing/PostProcessing/CalculateSizeAndAlignPostProcessor.h" #include "Parsing/PostProcessing/LeafsPostProcessor.h" +#include "Parsing/PostProcessing/MarkingRequiredPostProcessor.h" #include "Parsing/PostProcessing/MemberLeafsPostProcessor.h" #include "Parsing/PostProcessing/UnionsPostProcessor.h" #include "Parsing/PostProcessing/UsagesPostProcessor.h" @@ -58,6 +59,7 @@ void CommandsFileReader::SetupPostProcessors() m_post_processors.emplace_back(std::make_unique()); m_post_processors.emplace_back(std::make_unique()); m_post_processors.emplace_back(std::make_unique()); + m_post_processors.emplace_back(std::make_unique()); m_post_processors.emplace_back(std::make_unique()); m_post_processors.emplace_back(std::make_unique()); } diff --git a/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/MarkingRequiredPostProcessor.cpp b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/MarkingRequiredPostProcessor.cpp new file mode 100644 index 00000000..439b41ff --- /dev/null +++ b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/MarkingRequiredPostProcessor.cpp @@ -0,0 +1,76 @@ +#include "MarkingRequiredPostProcessor.h" + +#include "Domain/Computations/MemberComputations.h" +#include "Domain/Computations/StructureComputations.h" +#include "Domain/Definition/PointerDeclarationModifier.h" + +#include + +bool MarkingRequiredPostProcessor::CalculateRequiresMarking(std::unordered_set& visitedStructures, StructureInformation* info) +{ + if (visitedStructures.find(info) != visitedStructures.end()) + return info->m_requires_marking; + + visitedStructures.emplace(info); + + if (info->m_asset_enum_entry) + { + info->m_requires_marking = true; + return true; + } + + for (const auto& member : info->m_ordered_members) + { + // If there is a condition to this member, and it always evaluates to false: Skip this member + if (member->m_condition && member->m_condition->IsStatic() && member->m_condition->EvaluateNumeric() == 0) + continue; + + // Skip if it has a pointer evaluation that always resolves to 0 + auto skip = false; + for (const auto& modifier : member->m_member->m_type_declaration->m_declaration_modifiers) + { + if (modifier->GetType() == DeclarationModifierType::POINTER) + { + const auto* pointer = dynamic_cast(modifier.get()); + const auto* countEvaluation = pointer->GetCountEvaluation(); + + if (countEvaluation->IsStatic() && countEvaluation->EvaluateNumeric() == 0) + { + skip = true; + break; + } + } + } + if (skip) + continue; + + // Any ScriptStrings or Strings need to be processed. + if (member->m_is_script_string || member->m_type && member->m_type->m_asset_enum_entry) + { + info->m_requires_marking = true; + return true; + } + + if (member->m_type != nullptr && member->m_type != info && CalculateRequiresMarking(visitedStructures, member->m_type)) + { + info->m_requires_marking = true; + return true; + } + } + + info->m_requires_marking = false; + return false; +} + +bool MarkingRequiredPostProcessor::PostProcess(IDataRepository* repository) +{ + const auto& allInfos = repository->GetAllStructureInformation(); + + std::unordered_set visitedStructures; + for (const auto& info : allInfos) + { + CalculateRequiresMarking(visitedStructures, info); + } + + return true; +} diff --git a/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/MarkingRequiredPostProcessor.h b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/MarkingRequiredPostProcessor.h new file mode 100644 index 00000000..ad7f8165 --- /dev/null +++ b/src/ZoneCodeGeneratorLib/Parsing/PostProcessing/MarkingRequiredPostProcessor.h @@ -0,0 +1,13 @@ +#pragma once + +#include "IPostProcessor.h" + +#include + +class MarkingRequiredPostProcessor final : public IPostProcessor +{ + static bool CalculateRequiresMarking(std::unordered_set& visitedStructures, StructureInformation* info); + +public: + bool PostProcess(IDataRepository* repository) override; +}; diff --git a/src/ZoneCodeGeneratorLib/ZoneCodeGenerator.cpp b/src/ZoneCodeGeneratorLib/ZoneCodeGenerator.cpp index b6b7a594..cdfcc209 100644 --- a/src/ZoneCodeGeneratorLib/ZoneCodeGenerator.cpp +++ b/src/ZoneCodeGeneratorLib/ZoneCodeGenerator.cpp @@ -64,9 +64,13 @@ public: int Run(const int argc, const char** argv) { - if (!m_args.Parse(argc, argv)) + auto shouldContinue = true; + if (!m_args.ParseArgs(argc, argv, shouldContinue)) return 1; + if (!shouldContinue) + return 0; + if (!ReadHeaderData() || !ReadCommandsData()) return 1; diff --git a/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.cpp b/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.cpp index 9f30103b..2d699577 100644 --- a/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.cpp +++ b/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.cpp @@ -1,5 +1,6 @@ #include "ZoneCodeGeneratorArguments.h" +#include "GitVersion.h" #include "Utils/Arguments/CommandLineOption.h" #include "Utils/Arguments/UsageInformation.h" @@ -9,6 +10,9 @@ const CommandLineOption* const OPTION_HELP = CommandLineOption::Builder::Create().WithShortName("?").WithLongName("help").WithDescription("Displays usage information.").Build(); +const CommandLineOption* const OPTION_VERSION = + CommandLineOption::Builder::Create().WithLongName("version").WithDescription("Prints the application version.").Build(); + const CommandLineOption* const OPTION_VERBOSE = CommandLineOption::Builder::Create().WithShortName("v").WithLongName("verbose").WithDescription("Outputs a lot more and more detailed messages.").Build(); @@ -70,7 +74,15 @@ const CommandLineOption* const OPTION_GENERATE = .Build(); const CommandLineOption* const COMMAND_LINE_OPTIONS[]{ - OPTION_HELP, OPTION_VERBOSE, OPTION_HEADER, OPTION_COMMANDS_FILE, OPTION_OUTPUT_FOLDER, OPTION_PRINT, OPTION_GENERATE}; + OPTION_HELP, + OPTION_VERSION, + OPTION_VERBOSE, + OPTION_HEADER, + OPTION_COMMANDS_FILE, + OPTION_OUTPUT_FOLDER, + OPTION_PRINT, + OPTION_GENERATE, +}; ZoneCodeGeneratorArguments::GenerationTask::GenerationTask() : m_all_assets(false) @@ -91,7 +103,7 @@ ZoneCodeGeneratorArguments::GenerationTask::GenerationTask(std::string assetName } ZoneCodeGeneratorArguments::ZoneCodeGeneratorArguments() - : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent::value), + : m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v), m_task_flags(0) { m_verbose = false; @@ -109,8 +121,14 @@ void ZoneCodeGeneratorArguments::PrintUsage() usage.Print(); } -bool ZoneCodeGeneratorArguments::Parse(const int argc, const char** argv) +void ZoneCodeGeneratorArguments::PrintVersion() { + std::cout << "OpenAssetTools ZoneCodeGenerator " << std::string(GIT_VERSION) << "\n"; +} + +bool ZoneCodeGeneratorArguments::ParseArgs(const int argc, const char** argv, bool& shouldContinue) +{ + shouldContinue = true; if (!m_argument_parser.ParseArguments(argc - 1, &argv[1])) { PrintUsage(); @@ -121,7 +139,16 @@ bool ZoneCodeGeneratorArguments::Parse(const int argc, const char** argv) if (m_argument_parser.IsOptionSpecified(OPTION_HELP)) { PrintUsage(); - return false; + shouldContinue = false; + return true; + } + + // Check if the user wants to see the version + if (m_argument_parser.IsOptionSpecified(OPTION_VERSION)) + { + PrintVersion(); + shouldContinue = false; + return true; } // -v; --verbose diff --git a/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.h b/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.h index 1344d2af..351efc2e 100644 --- a/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.h +++ b/src/ZoneCodeGeneratorLib/ZoneCodeGeneratorArguments.h @@ -13,6 +13,7 @@ class ZoneCodeGeneratorArguments * \brief Prints a command line usage help text for the Unlinker tool to stdout. */ static void PrintUsage(); + static void PrintVersion(); public: static constexpr unsigned FLAG_TASK_GENERATE = 1 << 0; @@ -40,8 +41,7 @@ public: std::vector m_generation_tasks; ZoneCodeGeneratorArguments(); - - bool Parse(int argc, const char** argv); + bool ParseArgs(int argc, const char** argv, bool& shouldContinue); _NODISCARD bool ShouldGenerate() const; _NODISCARD bool ShouldPrint() const; diff --git a/src/ZoneLoading/Loading/AssetLoader.cpp b/src/ZoneLoading/Loading/AssetLoader.cpp index 8a903c91..c56cfe23 100644 --- a/src/ZoneLoading/Loading/AssetLoader.cpp +++ b/src/ZoneLoading/Loading/AssetLoader.cpp @@ -10,73 +10,10 @@ AssetLoader::AssetLoader(const asset_type_t assetType, Zone* zone, IZoneInputStr { } -void AssetLoader::AddDependency(XAssetInfoGeneric* assetInfo) +XAssetInfoGeneric* + AssetLoader::LinkAsset(std::string name, void* asset, std::vector scriptStrings, std::vector dependencies) const { - if (assetInfo == nullptr) - return; - - const auto existingEntry = std::find(m_dependencies.begin(), m_dependencies.end(), assetInfo); - if (existingEntry != m_dependencies.end()) - { - return; - } - - m_dependencies.push_back(assetInfo); -} - -scr_string_t AssetLoader::UseScriptString(const scr_string_t scrString) -{ - assert(scrString < m_zone->m_script_strings.Count()); - - if (scrString >= m_zone->m_script_strings.Count()) - return 0u; - - m_used_script_strings.emplace(scrString); - return scrString; -} - -void AssetLoader::LoadScriptStringArray(const bool atStreamStart, const size_t count) -{ - assert(varScriptString != nullptr); - - if (atStreamStart) - m_stream->Load(varScriptString, count); - - auto* ptr = varScriptString; - for (size_t index = 0; index < count; index++) - { - *ptr = UseScriptString(*ptr); - ptr++; - } -} - -void AssetLoader::MarkScriptStringArrayAsUsed(const scr_string_t* scrStringArray, const size_t count) -{ - for (size_t index = 0; index < count; index++) - { - const auto scrString = scrStringArray[index]; - if (scrString >= m_zone->m_script_strings.Count()) - continue; - - m_used_script_strings.emplace(scrString); - } -} - -XAssetInfoGeneric* AssetLoader::LinkAsset(std::string name, void* asset) -{ - std::vector usedScriptStrings; - if (!m_used_script_strings.empty()) - { - for (auto scrString : m_used_script_strings) - { - usedScriptStrings.push_back(scrString); - } - - std::sort(usedScriptStrings.begin(), usedScriptStrings.end()); - m_used_script_strings.clear(); - } - - return m_zone->m_pools->AddAsset(m_asset_type, std::move(name), asset, std::move(m_dependencies), std::move(usedScriptStrings)); + return m_zone->m_pools->AddAsset(m_asset_type, std::move(name), asset, std::move(dependencies), std::move(scriptStrings)); } XAssetInfoGeneric* AssetLoader::GetAssetInfo(std::string name) const diff --git a/src/ZoneLoading/Loading/AssetLoader.h b/src/ZoneLoading/Loading/AssetLoader.h index 55d1bc84..47246188 100644 --- a/src/ZoneLoading/Loading/AssetLoader.h +++ b/src/ZoneLoading/Loading/AssetLoader.h @@ -11,21 +11,12 @@ class AssetLoader : public ContentLoaderBase { asset_type_t m_asset_type; - std::vector m_dependencies; - std::unordered_set m_used_script_strings; - protected: scr_string_t* varScriptString; AssetLoader(asset_type_t assetType, Zone* zone, IZoneInputStream* stream); - void AddDependency(XAssetInfoGeneric* assetInfo); + XAssetInfoGeneric* LinkAsset(std::string name, void* asset, std::vector scriptStrings, std::vector dependencies) const; - scr_string_t UseScriptString(scr_string_t scrString); - void LoadScriptStringArray(bool atStreamStart, size_t count); - void MarkScriptStringArrayAsUsed(const scr_string_t* scrStringArray, size_t count); - - XAssetInfoGeneric* LinkAsset(std::string name, void* asset); - - XAssetInfoGeneric* GetAssetInfo(std::string name) const; + _NODISCARD XAssetInfoGeneric* GetAssetInfo(std::string name) const; }; diff --git a/src/ZoneLoading/Loading/AssetMarker.cpp b/src/ZoneLoading/Loading/AssetMarker.cpp new file mode 100644 index 00000000..ecf14dd9 --- /dev/null +++ b/src/ZoneLoading/Loading/AssetMarker.cpp @@ -0,0 +1,73 @@ +#include "AssetMarker.h" + +#include +#include + +AssetMarker::AssetMarker(const asset_type_t assetType, Zone* zone) + : m_asset_type(assetType), + m_zone(zone) +{ +} + +void AssetMarker::AddDependency(XAssetInfoGeneric* assetInfo) +{ + if (assetInfo == nullptr) + return; + + const auto existingEntry = m_dependencies.find(assetInfo); + if (existingEntry != m_dependencies.end()) + return; + + m_dependencies.emplace(assetInfo); +} + +void AssetMarker::Mark_ScriptString(const scr_string_t scrString) +{ + assert(scrString < m_zone->m_script_strings.Count()); + + if (scrString >= m_zone->m_script_strings.Count()) + return; + + m_used_script_strings.emplace(scrString); +} + +void AssetMarker::MarkArray_ScriptString(const scr_string_t* scrStringArray, const size_t count) +{ + assert(scrStringArray != nullptr); + + for (size_t index = 0; index < count; index++) + Mark_ScriptString(scrStringArray[index]); +} + +XAssetInfoGeneric* AssetMarker::GetAssetInfoByName(std::string name) const +{ + return m_zone->m_pools->GetAsset(m_asset_type, std::move(name)); +} + +std::vector AssetMarker::GetDependencies() const +{ + std::vector dependencies; + if (!m_dependencies.empty()) + { + dependencies.reserve(m_dependencies.size()); + for (auto dependency : m_dependencies) + dependencies.push_back(dependency); + } + + return dependencies; +} + +std::vector AssetMarker::GetUsedScriptStrings() const +{ + std::vector usedScriptStrings; + if (!m_used_script_strings.empty()) + { + usedScriptStrings.reserve(m_used_script_strings.size()); + for (auto scrString : m_used_script_strings) + usedScriptStrings.push_back(scrString); + + std::sort(usedScriptStrings.begin(), usedScriptStrings.end()); + } + + return usedScriptStrings; +} diff --git a/src/ZoneLoading/Loading/AssetMarker.h b/src/ZoneLoading/Loading/AssetMarker.h new file mode 100644 index 00000000..cb3837bd --- /dev/null +++ b/src/ZoneLoading/Loading/AssetMarker.h @@ -0,0 +1,32 @@ +#pragma once + +#include "ContentLoaderBase.h" +#include "Pool/XAssetInfo.h" +#include "Utils/ClassUtils.h" +#include "Zone/ZoneTypes.h" + +#include + +class AssetMarker +{ + asset_type_t m_asset_type; + + std::unordered_set m_dependencies; + std::unordered_set m_used_script_strings; + +protected: + AssetMarker(asset_type_t assetType, Zone* zone); + + void AddDependency(XAssetInfoGeneric* assetInfo); + + void Mark_ScriptString(scr_string_t scrString); + void MarkArray_ScriptString(const scr_string_t* scrStringArray, size_t count); + + _NODISCARD XAssetInfoGeneric* GetAssetInfoByName(std::string name) const; + + Zone* m_zone; + +public: + _NODISCARD std::vector GetDependencies() const; + _NODISCARD std::vector GetUsedScriptStrings() const; +}; diff --git a/test/ObjLoadingTests/Game/IW3/AssetLoaders/AssetLoaderStringTableTest.cpp b/test/ObjLoadingTests/Game/IW3/AssetLoaders/AssetLoaderStringTableTest.cpp new file mode 100644 index 00000000..1663dc9f --- /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 00000000..47c1cff4 --- /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 00000000..2dc8f137 --- /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 00000000..3a439fd5 --- /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 00000000..4ca2edb0 --- /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 diff --git a/thirdparty/catch2 b/thirdparty/catch2 index 863c662c..79205da6 160000 --- a/thirdparty/catch2 +++ b/thirdparty/catch2 @@ -1 +1 @@ -Subproject commit 863c662c0eff026300f4d729a7054e90d6d12cdd +Subproject commit 79205da6a67f5ad3d7874cf1c6423283dfeab199 diff --git a/thirdparty/json b/thirdparty/json index a259ecc5..7efe8754 160000 --- a/thirdparty/json +++ b/thirdparty/json @@ -1 +1 @@ -Subproject commit a259ecc51e1951e12f757ce17db958e9881e9c6c +Subproject commit 7efe875495a3ed7d805ddbb01af0c7725f50c88b diff --git a/thirdparty/zlib b/thirdparty/zlib index 643e17b7..7af6320a 160000 --- a/thirdparty/zlib +++ b/thirdparty/zlib @@ -1 +1 @@ -Subproject commit 643e17b7498d12ab8d15565662880579692f769d +Subproject commit 7af6320ad78b390de42f414fabdc64dc6d67a5ea diff --git a/tools/scripts/version.lua b/tools/scripts/version.lua new file mode 100644 index 00000000..dc0087f9 --- /dev/null +++ b/tools/scripts/version.lua @@ -0,0 +1,36 @@ +local BuildSubFolderFolder = "premake" +local HeaderFileName = "GitVersion.h" + +function GetGitVersion() + result, errorCode = os.outputof("git describe --tags") + + if errorCode == 0 then + return result + end + + return "Unknown" +end + +function GetVersionHeaderFolder() + return path.join(BuildFolder(), BuildSubFolderFolder) +end + +function WriteVersionHeader() + local folder = GetVersionHeaderFolder() + local file = path.join(folder, HeaderFileName) + local content = string.format([[ +#pragma once + +#define GIT_VERSION "%s" + ]], GetGitVersion()) + + if os.isdir(folder) ~= True then + os.mkdir(folder) + end + + local ok, err = os.writefile_ifnotequal(content, file) + + if ok == -1 then + error("Could not create version file: " .. err) + end +end