mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-12-27 12:31:50 +00:00
Merge pull request #619 from Laupetin/fix/asset-references-in-global-assets
fix: asset references in global assets
This commit is contained in:
3
.github/workflows/ci.yaml
vendored
3
.github/workflows/ci.yaml
vendored
@@ -78,6 +78,7 @@ jobs:
|
||||
./ObjLoadingTests
|
||||
./ObjWritingTests
|
||||
./ParserTests
|
||||
./SystemTests
|
||||
./ZoneCodeGeneratorLibTests
|
||||
./ZoneCommonTests
|
||||
|
||||
@@ -138,6 +139,8 @@ jobs:
|
||||
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
|
||||
./ParserTests
|
||||
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
|
||||
./SystemTests
|
||||
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
|
||||
./ZoneCodeGeneratorLibTests
|
||||
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
|
||||
./ZoneCommonTests
|
||||
|
||||
14
premake5.lua
14
premake5.lua
@@ -129,11 +129,13 @@ group ""
|
||||
include "src/Common.lua"
|
||||
include "src/Cryptography.lua"
|
||||
include "src/ImageConverter.lua"
|
||||
include "src/Linker.lua"
|
||||
include "src/LinkerCli.lua"
|
||||
include "src/Linking.lua"
|
||||
include "src/ModMan.lua"
|
||||
include "src/Parser.lua"
|
||||
include "src/RawTemplater.lua"
|
||||
include "src/Unlinker.lua"
|
||||
include "src/UnlinkerCli.lua"
|
||||
include "src/Unlinking.lua"
|
||||
include "src/Utils.lua"
|
||||
include "src/ZoneCode.lua"
|
||||
include "src/ZoneCodeGeneratorLib.lua"
|
||||
@@ -165,6 +167,8 @@ group "Components"
|
||||
ObjImage:project()
|
||||
ObjLoading:project()
|
||||
ObjWriting:project()
|
||||
Linking:project()
|
||||
Unlinking:project()
|
||||
group ""
|
||||
|
||||
-- Tools group: All projects that compile into the final tools
|
||||
@@ -175,8 +179,8 @@ group ""
|
||||
|
||||
-- Tools group: All projects that compile into the final tools
|
||||
group "Tools"
|
||||
Linker:project()
|
||||
Unlinker:project()
|
||||
LinkerCli:project()
|
||||
UnlinkerCli:project()
|
||||
ImageConverter:project()
|
||||
|
||||
if _OPTIONS["modman"] then
|
||||
@@ -199,6 +203,7 @@ include "test/ObjLoadingTests.lua"
|
||||
include "test/ObjWritingTests.lua"
|
||||
include "test/ParserTestUtils.lua"
|
||||
include "test/ParserTests.lua"
|
||||
include "test/SystemTests.lua"
|
||||
include "test/ZoneCodeGeneratorLibTests.lua"
|
||||
include "test/ZoneCommonTests.lua"
|
||||
|
||||
@@ -212,6 +217,7 @@ group "Tests"
|
||||
ObjWritingTests:project()
|
||||
ParserTestUtils:project()
|
||||
ParserTests:project()
|
||||
SystemTests:project()
|
||||
ZoneCodeGeneratorLibTests:project()
|
||||
ZoneCommonTests:project()
|
||||
group ""
|
||||
|
||||
@@ -18,11 +18,11 @@ public:
|
||||
|
||||
template<typename AssetType> struct AssetNameAccessor
|
||||
{
|
||||
public:
|
||||
static_assert(std::is_base_of_v<IAssetBase, AssetType>);
|
||||
// static constexpr bool IS_SINGLETON = false;
|
||||
// using RETURN_TYPE = const char*&;
|
||||
|
||||
// const char*& operator()(AssetType::Type& asset)
|
||||
// static RETURN_TYPE GetAssetName(assetType::Type& asset)
|
||||
// {
|
||||
// throw std::runtime_error("Not implemented");
|
||||
// }
|
||||
@@ -34,8 +34,9 @@ public:
|
||||
public: \
|
||||
static_assert(std::is_base_of_v<IAssetBase, assetType>); \
|
||||
static constexpr bool IS_SINGLETON = false; \
|
||||
using RETURN_TYPE = const char*&; \
|
||||
\
|
||||
const char*& operator()(assetType::Type& asset) \
|
||||
static RETURN_TYPE GetAssetName(assetType::Type& asset) \
|
||||
{ \
|
||||
return asset.nameProperty; \
|
||||
} \
|
||||
@@ -47,10 +48,17 @@ public:
|
||||
public: \
|
||||
static_assert(std::is_base_of_v<IAssetBase, assetType>); \
|
||||
static constexpr bool IS_SINGLETON = true; \
|
||||
using RETURN_TYPE = const char* const&; \
|
||||
\
|
||||
const char* const& operator()(assetType::Type& asset) \
|
||||
static RETURN_TYPE GetAssetName(assetType::Type& asset) \
|
||||
{ \
|
||||
static const char* NAME = singletonName; \
|
||||
return NAME; \
|
||||
} \
|
||||
}
|
||||
|
||||
template<typename AssetType> AssetNameAccessor<AssetType>::RETURN_TYPE AssetName(typename AssetType::Type& asset)
|
||||
{
|
||||
static_assert(std::is_base_of_v<IAssetBase, AssetType>);
|
||||
return AssetNameAccessor<AssetType>::GetAssetName(asset);
|
||||
}
|
||||
|
||||
@@ -1,56 +0,0 @@
|
||||
Linker = {}
|
||||
|
||||
function Linker:include(includes)
|
||||
if includes:handle(self:name()) then
|
||||
includedirs {
|
||||
path.join(ProjectFolder(), "Linker")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function Linker:link(links)
|
||||
|
||||
end
|
||||
|
||||
function Linker:use()
|
||||
dependson(self:name())
|
||||
end
|
||||
|
||||
function Linker:name()
|
||||
return "Linker"
|
||||
end
|
||||
|
||||
function Linker:project()
|
||||
local folder = ProjectFolder()
|
||||
local includes = Includes:create()
|
||||
local links = Links:create()
|
||||
|
||||
project(self:name())
|
||||
targetdir(TargetDirectoryBin)
|
||||
location "%{wks.location}/src/%{prj.name}"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
files {
|
||||
path.join(folder, "Linker/**.h"),
|
||||
path.join(folder, "Linker/**.cpp")
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Utils:include(includes)
|
||||
ZoneLoading:include(includes)
|
||||
ObjCompiling:include(includes)
|
||||
ObjLoading:include(includes)
|
||||
ObjWriting:include(includes)
|
||||
ZoneWriting:include(includes)
|
||||
|
||||
Raw:use()
|
||||
|
||||
links:linkto(Utils)
|
||||
links:linkto(ObjCompiling)
|
||||
links:linkto(ZoneLoading)
|
||||
links:linkto(ZoneWriting)
|
||||
links:linkto(ObjLoading)
|
||||
links:linkto(ObjWriting)
|
||||
links:linkall()
|
||||
end
|
||||
@@ -1,480 +0,0 @@
|
||||
#include "Linker.h"
|
||||
|
||||
#include "LinkerArgs.h"
|
||||
#include "LinkerPaths.h"
|
||||
#include "ObjContainer/SoundBank/SoundBankWriter.h"
|
||||
#include "ObjWriting.h"
|
||||
#include "SearchPath/OutputPathFilesystem.h"
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
#include "Utils/ObjFileStream.h"
|
||||
#include "Zone/AssetList/AssetList.h"
|
||||
#include "Zone/AssetList/AssetListReader.h"
|
||||
#include "Zone/Definition/ZoneDefinitionStream.h"
|
||||
#include "ZoneCreation/ZoneCreationContext.h"
|
||||
#include "ZoneCreation/ZoneCreator.h"
|
||||
#include "ZoneLoading.h"
|
||||
#include "ZoneWriting.h"
|
||||
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
class LinkerSearchPathContext
|
||||
{
|
||||
public:
|
||||
explicit LinkerSearchPathContext(const ILinkerSearchPathBuilder& searchPathBuilder)
|
||||
: m_search_path_builder(searchPathBuilder)
|
||||
{
|
||||
m_independent_search_paths = m_search_path_builder.BuildIndependentSearchPaths();
|
||||
if (m_independent_search_paths)
|
||||
m_search_paths.IncludeSearchPath(m_independent_search_paths.get());
|
||||
}
|
||||
|
||||
[[nodiscard]] ISearchPath& GetSearchPaths()
|
||||
{
|
||||
return m_search_paths;
|
||||
}
|
||||
|
||||
void LoadProjectSpecific(const std::string& projectName)
|
||||
{
|
||||
m_project_specific_search_paths = m_search_path_builder.BuildSearchPathsSpecificToProject(projectName);
|
||||
if (m_project_specific_search_paths)
|
||||
m_search_paths.IncludeSearchPath(m_project_specific_search_paths.get());
|
||||
}
|
||||
|
||||
void UnloadProjectSpecific()
|
||||
{
|
||||
if (!m_project_specific_search_paths)
|
||||
return;
|
||||
|
||||
m_search_paths.RemoveSearchPath(m_project_specific_search_paths.get());
|
||||
m_project_specific_search_paths.reset();
|
||||
}
|
||||
|
||||
void LoadGameSpecific(const std::string& projectName, const GameId game)
|
||||
{
|
||||
m_game_specific_search_paths = m_search_path_builder.BuildSearchPathsSpecificToProjectAndGame(projectName, game);
|
||||
if (m_game_specific_search_paths)
|
||||
m_search_paths.IncludeSearchPath(m_game_specific_search_paths.get());
|
||||
}
|
||||
|
||||
void UnloadGameSpecific()
|
||||
{
|
||||
if (!m_game_specific_search_paths)
|
||||
return;
|
||||
|
||||
m_search_paths.RemoveSearchPath(m_game_specific_search_paths.get());
|
||||
m_game_specific_search_paths.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
const ILinkerSearchPathBuilder& m_search_path_builder;
|
||||
std::unique_ptr<ISearchPath> m_independent_search_paths;
|
||||
std::unique_ptr<ISearchPath> m_project_specific_search_paths;
|
||||
std::unique_ptr<ISearchPath> m_game_specific_search_paths;
|
||||
SearchPaths m_search_paths;
|
||||
};
|
||||
|
||||
class LinkerPathManager
|
||||
{
|
||||
public:
|
||||
explicit LinkerPathManager(const LinkerArgs& args)
|
||||
: m_linker_paths(ILinkerPaths::FromArgs(args)),
|
||||
m_asset_paths(m_linker_paths->AssetSearchPaths()),
|
||||
m_gdt_paths(m_linker_paths->GdtSearchPaths()),
|
||||
m_source_paths(m_linker_paths->SourceSearchPaths())
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<ILinkerPaths> m_linker_paths;
|
||||
LinkerSearchPathContext m_asset_paths;
|
||||
LinkerSearchPathContext m_gdt_paths;
|
||||
LinkerSearchPathContext m_source_paths;
|
||||
};
|
||||
|
||||
class PathProjectContext
|
||||
{
|
||||
public:
|
||||
PathProjectContext(LinkerPathManager& paths, const std::string& projectName)
|
||||
: m_paths(paths)
|
||||
{
|
||||
m_paths.m_asset_paths.LoadProjectSpecific(projectName);
|
||||
m_paths.m_gdt_paths.LoadProjectSpecific(projectName);
|
||||
m_paths.m_source_paths.LoadProjectSpecific(projectName);
|
||||
}
|
||||
|
||||
~PathProjectContext()
|
||||
{
|
||||
m_paths.m_asset_paths.UnloadProjectSpecific();
|
||||
m_paths.m_gdt_paths.UnloadProjectSpecific();
|
||||
m_paths.m_source_paths.UnloadProjectSpecific();
|
||||
}
|
||||
|
||||
PathProjectContext(const PathProjectContext& other) = delete;
|
||||
PathProjectContext(PathProjectContext&& other) noexcept = delete;
|
||||
PathProjectContext& operator=(const PathProjectContext& other) = delete;
|
||||
PathProjectContext& operator=(PathProjectContext&& other) noexcept = delete;
|
||||
|
||||
private:
|
||||
LinkerPathManager& m_paths;
|
||||
};
|
||||
|
||||
class PathGameContext
|
||||
{
|
||||
public:
|
||||
PathGameContext(LinkerPathManager& paths, const std::string& projectName, const GameId game)
|
||||
: m_paths(paths)
|
||||
{
|
||||
m_paths.m_asset_paths.LoadGameSpecific(projectName, game);
|
||||
m_paths.m_gdt_paths.LoadGameSpecific(projectName, game);
|
||||
m_paths.m_source_paths.LoadGameSpecific(projectName, game);
|
||||
}
|
||||
|
||||
~PathGameContext()
|
||||
{
|
||||
m_paths.m_asset_paths.UnloadGameSpecific();
|
||||
m_paths.m_gdt_paths.UnloadGameSpecific();
|
||||
m_paths.m_source_paths.UnloadGameSpecific();
|
||||
}
|
||||
|
||||
PathGameContext(const PathGameContext& other) = delete;
|
||||
PathGameContext(PathGameContext&& other) noexcept = delete;
|
||||
PathGameContext& operator=(const PathGameContext& other) = delete;
|
||||
PathGameContext& operator=(PathGameContext&& other) noexcept = delete;
|
||||
|
||||
private:
|
||||
LinkerPathManager& m_paths;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class LinkerImpl final : public Linker
|
||||
{
|
||||
std::unique_ptr<ZoneDefinition> ReadZoneDefinition(LinkerPathManager& paths, const std::string& targetName, bool logMissing = true) const
|
||||
{
|
||||
auto& sourceSearchPath = paths.m_source_paths.GetSearchPaths();
|
||||
std::unique_ptr<ZoneDefinition> zoneDefinition;
|
||||
{
|
||||
const auto definitionFileName = std::format("{}.zone", targetName);
|
||||
const auto definitionStream = sourceSearchPath.Open(definitionFileName);
|
||||
if (!definitionStream.IsOpen())
|
||||
{
|
||||
if (logMissing)
|
||||
con::error("Could not find zone definition file for target \"{}\".", targetName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ZoneDefinitionInputStream zoneDefinitionInputStream(*definitionStream.m_stream, targetName, definitionFileName, sourceSearchPath);
|
||||
zoneDefinition = zoneDefinitionInputStream.ReadDefinition();
|
||||
}
|
||||
|
||||
if (!zoneDefinition)
|
||||
{
|
||||
con::error("Failed to read zone definition file for target \"{}\".", targetName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return zoneDefinition;
|
||||
}
|
||||
|
||||
bool ReadIgnoreEntries(LinkerPathManager& paths, const std::string& zoneName, const GameId game, AssetList& assetList) const
|
||||
{
|
||||
{
|
||||
AssetListReader assetListReader(paths.m_source_paths.GetSearchPaths(), game);
|
||||
const auto maybeReadAssetList = assetListReader.ReadAssetList(zoneName, false);
|
||||
if (maybeReadAssetList)
|
||||
{
|
||||
assetList.m_entries.reserve(assetList.m_entries.size() + maybeReadAssetList->m_entries.size());
|
||||
for (auto& entry : maybeReadAssetList->m_entries)
|
||||
assetList.m_entries.emplace_back(std::move(entry));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const auto zoneDefinition = ReadZoneDefinition(paths, zoneName, false);
|
||||
|
||||
if (zoneDefinition)
|
||||
{
|
||||
assetList.m_entries.reserve(assetList.m_entries.size() + zoneDefinition->m_assets.size());
|
||||
for (const auto& entry : zoneDefinition->m_assets)
|
||||
assetList.m_entries.emplace_back(entry.m_asset_type, entry.m_asset_name, entry.m_is_reference);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProcessZoneDefinitionIgnores(LinkerPathManager& paths, const std::string& targetName, ZoneCreationContext& context) const
|
||||
{
|
||||
if (context.m_definition->m_ignores.empty())
|
||||
return true;
|
||||
|
||||
for (const auto& ignore : context.m_definition->m_ignores)
|
||||
{
|
||||
if (ignore == targetName)
|
||||
continue;
|
||||
|
||||
if (!ReadIgnoreEntries(paths, ignore, context.m_definition->m_game, context.m_ignored_assets))
|
||||
{
|
||||
con::error("Failed to read asset listing for ignoring assets of project \"{}\".", ignore);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool LoadGdtFilesFromZoneDefinition(std::vector<std::unique_ptr<Gdt>>& gdtList, const ZoneDefinition& zoneDefinition, ISearchPath* gdtSearchPath)
|
||||
{
|
||||
for (const auto& gdtName : zoneDefinition.m_gdts)
|
||||
{
|
||||
const auto gdtFile = gdtSearchPath->Open(std::format("{}.gdt", gdtName));
|
||||
if (!gdtFile.IsOpen())
|
||||
{
|
||||
con::error("Failed to open file for gdt \"{}\"", gdtName);
|
||||
return false;
|
||||
}
|
||||
|
||||
GdtReader gdtReader(*gdtFile.m_stream);
|
||||
auto gdt = std::make_unique<Gdt>();
|
||||
if (!gdtReader.Read(*gdt))
|
||||
{
|
||||
con::error("Failed to read gdt file \"{}\"", gdtName);
|
||||
return false;
|
||||
}
|
||||
|
||||
gdtList.emplace_back(std::move(gdt));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<Zone> CreateZoneForDefinition(
|
||||
LinkerPathManager& paths, const fs::path& outDir, const fs::path& cacheDir, const std::string& targetName, ZoneDefinition& zoneDefinition) const
|
||||
{
|
||||
ZoneCreationContext context(&zoneDefinition, &paths.m_asset_paths.GetSearchPaths(), outDir, cacheDir);
|
||||
if (!ProcessZoneDefinitionIgnores(paths, targetName, context))
|
||||
return nullptr;
|
||||
if (!LoadGdtFilesFromZoneDefinition(context.m_gdt_files, zoneDefinition, &paths.m_gdt_paths.GetSearchPaths()))
|
||||
return nullptr;
|
||||
|
||||
return zone_creator::CreateZoneForDefinition(zoneDefinition.m_game, context);
|
||||
}
|
||||
|
||||
static bool WriteZoneToFile(IOutputPath& outPath, const Zone& zone)
|
||||
{
|
||||
const auto stream = outPath.Open(std::format("{}.ff", zone.m_name));
|
||||
if (!stream)
|
||||
{
|
||||
con::error("Failed to open file for zone: {}", zone.m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
con::info("Building zone \"{}\"", zone.m_name);
|
||||
|
||||
if (!ZoneWriting::WriteZone(*stream, zone))
|
||||
{
|
||||
con::error("Writing zone failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
con::info("Created zone \"{}\"", zone.m_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuildFastFile(LinkerPathManager& paths, const std::string& projectName, const std::string& targetName, ZoneDefinition& zoneDefinition) const
|
||||
{
|
||||
const fs::path outDir(paths.m_linker_paths->BuildOutputFolderPath(projectName, zoneDefinition.m_game));
|
||||
|
||||
OutputPathFilesystem outputPath(outDir);
|
||||
|
||||
const fs::path cacheDir(paths.m_linker_paths->BuildCacheFolderPath(projectName, zoneDefinition.m_game));
|
||||
SoundBankWriter::OutputPath = outDir;
|
||||
|
||||
const auto zone = CreateZoneForDefinition(paths, outDir, cacheDir, targetName, zoneDefinition);
|
||||
auto result = zone != nullptr;
|
||||
if (zone)
|
||||
result = WriteZoneToFile(outputPath, *zone);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BuildProject(LinkerPathManager& paths, const std::string& projectName, const std::string& targetName) const
|
||||
{
|
||||
std::deque<std::string> targetsToBuild;
|
||||
std::unordered_set<std::string> alreadyBuiltTargets;
|
||||
|
||||
targetsToBuild.emplace_back(targetName);
|
||||
|
||||
while (!targetsToBuild.empty())
|
||||
{
|
||||
const auto currentTarget = std::move(targetsToBuild.front());
|
||||
targetsToBuild.pop_front();
|
||||
alreadyBuiltTargets.emplace(currentTarget);
|
||||
|
||||
PathProjectContext projectContext(paths, projectName);
|
||||
|
||||
const auto zoneDefinition = ReadZoneDefinition(paths, targetName);
|
||||
if (!zoneDefinition)
|
||||
return false;
|
||||
|
||||
PathGameContext gameContext(paths, projectName, zoneDefinition->m_game);
|
||||
|
||||
if (!zoneDefinition->m_assets.empty())
|
||||
{
|
||||
if (!BuildFastFile(paths, projectName, targetName, *zoneDefinition))
|
||||
return false;
|
||||
|
||||
for (const auto& referencedTarget : zoneDefinition->m_targets_to_build)
|
||||
{
|
||||
if (alreadyBuiltTargets.find(referencedTarget) == alreadyBuiltTargets.end())
|
||||
{
|
||||
targetsToBuild.emplace_back(referencedTarget);
|
||||
con::info("Building referenced target \"{}\"", referencedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadZones()
|
||||
{
|
||||
for (const auto& zonePath : m_args.m_zones_to_load)
|
||||
{
|
||||
if (!fs::is_regular_file(zonePath))
|
||||
{
|
||||
con::error("Could not find zone file to load \"{}\".", zonePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zoneDirectory = fs::path(zonePath).remove_filename();
|
||||
if (zoneDirectory.empty())
|
||||
zoneDirectory = fs::current_path();
|
||||
auto absoluteZoneDirectory = absolute(zoneDirectory).string();
|
||||
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
|
||||
con::debug("Loaded zone \"{}\"", zone->m_name);
|
||||
|
||||
m_loaded_zones.emplace_back(std::move(zone));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnloadZones()
|
||||
{
|
||||
for (auto i = m_loaded_zones.rbegin(); i != m_loaded_zones.rend(); ++i)
|
||||
{
|
||||
auto& loadedZone = *i;
|
||||
std::string zoneName = loadedZone->m_name;
|
||||
|
||||
loadedZone.reset();
|
||||
|
||||
con::debug("Unloaded zone \"{}\"", zoneName);
|
||||
}
|
||||
m_loaded_zones.clear();
|
||||
}
|
||||
|
||||
static bool GetProjectAndTargetFromProjectSpecifier(const std::string& projectSpecifier, std::string& projectName, std::string& targetName)
|
||||
{
|
||||
const auto targetNameSeparatorIndex = projectSpecifier.find_first_of('/');
|
||||
if (targetNameSeparatorIndex == std::string::npos)
|
||||
{
|
||||
projectName = projectSpecifier;
|
||||
targetName = projectSpecifier;
|
||||
}
|
||||
else if (projectSpecifier.find_first_of('/', targetNameSeparatorIndex + 1) != std::string::npos)
|
||||
{
|
||||
con::error("Project specifier cannot have more than one target name: \"{}\"", projectSpecifier);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
projectName = projectSpecifier.substr(0, targetNameSeparatorIndex);
|
||||
targetName = projectSpecifier.substr(targetNameSeparatorIndex + 1);
|
||||
}
|
||||
|
||||
if (projectName.empty())
|
||||
{
|
||||
con::error("Project name cannot be empty: \"{}\"", projectSpecifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetName.empty())
|
||||
{
|
||||
con::error("Target name cannot be empty: \"{}\"", projectSpecifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
bool Start(const int argc, const char** argv) override
|
||||
{
|
||||
con::init();
|
||||
|
||||
auto shouldContinue = true;
|
||||
if (!m_args.ParseArgs(argc, argv, shouldContinue))
|
||||
return false;
|
||||
|
||||
if (!shouldContinue)
|
||||
return true;
|
||||
|
||||
LinkerPathManager paths(m_args);
|
||||
|
||||
if (!LoadZones())
|
||||
return false;
|
||||
|
||||
auto result = true;
|
||||
for (const auto& projectSpecifier : m_args.m_project_specifiers_to_build)
|
||||
{
|
||||
std::string projectName;
|
||||
std::string targetName;
|
||||
if (!GetProjectAndTargetFromProjectSpecifier(projectSpecifier, projectName, targetName))
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!BuildProject(paths, projectName, targetName))
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UnloadZones();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
LinkerArgs m_args;
|
||||
std::vector<std::unique_ptr<Zone>> m_loaded_zones;
|
||||
};
|
||||
|
||||
std::unique_ptr<Linker> Linker::Create()
|
||||
{
|
||||
return std::make_unique<LinkerImpl>();
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
#include "Linker.h"
|
||||
|
||||
int main(const int argc, const char** argv)
|
||||
{
|
||||
const auto linker = Linker::Create();
|
||||
|
||||
return linker->Start(argc, argv) ? 0 : 1;
|
||||
}
|
||||
49
src/LinkerCli.lua
Normal file
49
src/LinkerCli.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
LinkerCli = {}
|
||||
|
||||
function LinkerCli:include(includes)
|
||||
if includes:handle(self:name()) then
|
||||
includedirs {
|
||||
path.join(ProjectFolder(), "LinkerCli")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function LinkerCli:link(links)
|
||||
|
||||
end
|
||||
|
||||
function LinkerCli:use()
|
||||
dependson(self:name())
|
||||
end
|
||||
|
||||
function LinkerCli:name()
|
||||
return "LinkerCli"
|
||||
end
|
||||
|
||||
function LinkerCli:project()
|
||||
local folder = ProjectFolder()
|
||||
local includes = Includes:create()
|
||||
local links = Links:create()
|
||||
|
||||
project(self:name())
|
||||
targetdir(TargetDirectoryBin)
|
||||
targetname "Linker"
|
||||
location "%{wks.location}/src/%{prj.name}"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
files {
|
||||
path.join(folder, "LinkerCli/**.h"),
|
||||
path.join(folder, "LinkerCli/**.cpp")
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Utils:include(includes)
|
||||
Linking:include(includes)
|
||||
|
||||
Raw:use()
|
||||
|
||||
links:linkto(Utils)
|
||||
links:linkto(Linking)
|
||||
links:linkall()
|
||||
end
|
||||
19
src/LinkerCli/main.cpp
Normal file
19
src/LinkerCli/main.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "Linker.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
|
||||
int main(const int argc, const char** argv)
|
||||
{
|
||||
con::init();
|
||||
|
||||
LinkerArgs args;
|
||||
auto shouldContinue = true;
|
||||
if (!args.ParseArgs(argc, argv, shouldContinue))
|
||||
return 1;
|
||||
|
||||
if (!shouldContinue)
|
||||
return 0;
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
|
||||
return linker->Start() ? 0 : 1;
|
||||
}
|
||||
61
src/Linking.lua
Normal file
61
src/Linking.lua
Normal file
@@ -0,0 +1,61 @@
|
||||
Linking = {}
|
||||
|
||||
function Linking:include(includes)
|
||||
if includes:handle(self:name()) then
|
||||
ZoneCommon:include(includes)
|
||||
includedirs {
|
||||
path.join(ProjectFolder(), "Linking")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function Linking:link(links)
|
||||
links:add(self:name())
|
||||
links:linkto(Utils)
|
||||
links:linkto(ObjCompiling)
|
||||
links:linkto(ZoneLoading)
|
||||
links:linkto(ZoneWriting)
|
||||
links:linkto(ObjLoading)
|
||||
links:linkto(ObjWriting)
|
||||
end
|
||||
|
||||
function Linking:use()
|
||||
|
||||
end
|
||||
|
||||
function Linking:name()
|
||||
return "Linking"
|
||||
end
|
||||
|
||||
function Linking:project()
|
||||
local folder = ProjectFolder()
|
||||
local includes = Includes:create()
|
||||
|
||||
project(self:name())
|
||||
targetdir(TargetDirectoryLib)
|
||||
location "%{wks.location}/src/%{prj.name}"
|
||||
kind "StaticLib"
|
||||
language "C++"
|
||||
|
||||
files {
|
||||
path.join(folder, "Linking/**.h"),
|
||||
path.join(folder, "Linking/**.cpp")
|
||||
}
|
||||
|
||||
vpaths {
|
||||
["*"] = {
|
||||
path.join(folder, "Linking")
|
||||
}
|
||||
}
|
||||
|
||||
ObjCommon:use()
|
||||
useSourceTemplating("Linking")
|
||||
|
||||
self:include(includes)
|
||||
Utils:include(includes)
|
||||
ZoneLoading:include(includes)
|
||||
ObjCompiling:include(includes)
|
||||
ObjLoading:include(includes)
|
||||
ObjWriting:include(includes)
|
||||
ZoneWriting:include(includes)
|
||||
end
|
||||
476
src/Linking/Linker.cpp
Normal file
476
src/Linking/Linker.cpp
Normal file
@@ -0,0 +1,476 @@
|
||||
#include "Linker.h"
|
||||
|
||||
#include "LinkerArgs.h"
|
||||
#include "LinkerPaths.h"
|
||||
#include "ObjContainer/SoundBank/SoundBankWriter.h"
|
||||
#include "ObjWriting.h"
|
||||
#include "SearchPath/OutputPathFilesystem.h"
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
#include "Utils/ObjFileStream.h"
|
||||
#include "Zone/AssetList/AssetList.h"
|
||||
#include "Zone/AssetList/AssetListReader.h"
|
||||
#include "Zone/Definition/ZoneDefinitionStream.h"
|
||||
#include "ZoneCreation/ZoneCreationContext.h"
|
||||
#include "ZoneCreation/ZoneCreator.h"
|
||||
#include "ZoneLoading.h"
|
||||
#include "ZoneWriting.h"
|
||||
|
||||
#include <deque>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <unordered_set>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
class LinkerSearchPathContext
|
||||
{
|
||||
public:
|
||||
explicit LinkerSearchPathContext(const ILinkerSearchPathBuilder& searchPathBuilder)
|
||||
: m_search_path_builder(searchPathBuilder)
|
||||
{
|
||||
m_independent_search_paths = m_search_path_builder.BuildIndependentSearchPaths();
|
||||
if (m_independent_search_paths)
|
||||
m_search_paths.IncludeSearchPath(m_independent_search_paths.get());
|
||||
}
|
||||
|
||||
[[nodiscard]] ISearchPath& GetSearchPaths()
|
||||
{
|
||||
return m_search_paths;
|
||||
}
|
||||
|
||||
void LoadProjectSpecific(const std::string& projectName)
|
||||
{
|
||||
m_project_specific_search_paths = m_search_path_builder.BuildSearchPathsSpecificToProject(projectName);
|
||||
if (m_project_specific_search_paths)
|
||||
m_search_paths.IncludeSearchPath(m_project_specific_search_paths.get());
|
||||
}
|
||||
|
||||
void UnloadProjectSpecific()
|
||||
{
|
||||
if (!m_project_specific_search_paths)
|
||||
return;
|
||||
|
||||
m_search_paths.RemoveSearchPath(m_project_specific_search_paths.get());
|
||||
m_project_specific_search_paths.reset();
|
||||
}
|
||||
|
||||
void LoadGameSpecific(const std::string& projectName, const GameId game)
|
||||
{
|
||||
m_game_specific_search_paths = m_search_path_builder.BuildSearchPathsSpecificToProjectAndGame(projectName, game);
|
||||
if (m_game_specific_search_paths)
|
||||
m_search_paths.IncludeSearchPath(m_game_specific_search_paths.get());
|
||||
}
|
||||
|
||||
void UnloadGameSpecific()
|
||||
{
|
||||
if (!m_game_specific_search_paths)
|
||||
return;
|
||||
|
||||
m_search_paths.RemoveSearchPath(m_game_specific_search_paths.get());
|
||||
m_game_specific_search_paths.reset();
|
||||
}
|
||||
|
||||
private:
|
||||
const ILinkerSearchPathBuilder& m_search_path_builder;
|
||||
std::unique_ptr<ISearchPath> m_independent_search_paths;
|
||||
std::unique_ptr<ISearchPath> m_project_specific_search_paths;
|
||||
std::unique_ptr<ISearchPath> m_game_specific_search_paths;
|
||||
SearchPaths m_search_paths;
|
||||
};
|
||||
|
||||
class LinkerPathManager
|
||||
{
|
||||
public:
|
||||
explicit LinkerPathManager(const LinkerArgs& args)
|
||||
: m_linker_paths(ILinkerPaths::FromArgs(args)),
|
||||
m_asset_paths(m_linker_paths->AssetSearchPaths()),
|
||||
m_gdt_paths(m_linker_paths->GdtSearchPaths()),
|
||||
m_source_paths(m_linker_paths->SourceSearchPaths())
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<ILinkerPaths> m_linker_paths;
|
||||
LinkerSearchPathContext m_asset_paths;
|
||||
LinkerSearchPathContext m_gdt_paths;
|
||||
LinkerSearchPathContext m_source_paths;
|
||||
};
|
||||
|
||||
class PathProjectContext
|
||||
{
|
||||
public:
|
||||
PathProjectContext(LinkerPathManager& paths, const std::string& projectName)
|
||||
: m_paths(paths)
|
||||
{
|
||||
m_paths.m_asset_paths.LoadProjectSpecific(projectName);
|
||||
m_paths.m_gdt_paths.LoadProjectSpecific(projectName);
|
||||
m_paths.m_source_paths.LoadProjectSpecific(projectName);
|
||||
}
|
||||
|
||||
~PathProjectContext()
|
||||
{
|
||||
m_paths.m_asset_paths.UnloadProjectSpecific();
|
||||
m_paths.m_gdt_paths.UnloadProjectSpecific();
|
||||
m_paths.m_source_paths.UnloadProjectSpecific();
|
||||
}
|
||||
|
||||
PathProjectContext(const PathProjectContext& other) = delete;
|
||||
PathProjectContext(PathProjectContext&& other) noexcept = delete;
|
||||
PathProjectContext& operator=(const PathProjectContext& other) = delete;
|
||||
PathProjectContext& operator=(PathProjectContext&& other) noexcept = delete;
|
||||
|
||||
private:
|
||||
LinkerPathManager& m_paths;
|
||||
};
|
||||
|
||||
class PathGameContext
|
||||
{
|
||||
public:
|
||||
PathGameContext(LinkerPathManager& paths, const std::string& projectName, const GameId game)
|
||||
: m_paths(paths)
|
||||
{
|
||||
m_paths.m_asset_paths.LoadGameSpecific(projectName, game);
|
||||
m_paths.m_gdt_paths.LoadGameSpecific(projectName, game);
|
||||
m_paths.m_source_paths.LoadGameSpecific(projectName, game);
|
||||
}
|
||||
|
||||
~PathGameContext()
|
||||
{
|
||||
m_paths.m_asset_paths.UnloadGameSpecific();
|
||||
m_paths.m_gdt_paths.UnloadGameSpecific();
|
||||
m_paths.m_source_paths.UnloadGameSpecific();
|
||||
}
|
||||
|
||||
PathGameContext(const PathGameContext& other) = delete;
|
||||
PathGameContext(PathGameContext&& other) noexcept = delete;
|
||||
PathGameContext& operator=(const PathGameContext& other) = delete;
|
||||
PathGameContext& operator=(PathGameContext&& other) noexcept = delete;
|
||||
|
||||
private:
|
||||
LinkerPathManager& m_paths;
|
||||
};
|
||||
|
||||
class LinkerImpl final : public Linker
|
||||
{
|
||||
public:
|
||||
LinkerImpl(LinkerArgs args)
|
||||
: m_args(std::move(args))
|
||||
{
|
||||
}
|
||||
|
||||
bool Start() override
|
||||
{
|
||||
LinkerPathManager paths(m_args);
|
||||
|
||||
if (!LoadZones())
|
||||
return false;
|
||||
|
||||
auto result = true;
|
||||
for (const auto& projectSpecifier : m_args.m_project_specifiers_to_build)
|
||||
{
|
||||
std::string projectName;
|
||||
std::string targetName;
|
||||
if (!GetProjectAndTargetFromProjectSpecifier(projectSpecifier, projectName, targetName))
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
|
||||
if (!BuildProject(paths, projectName, targetName))
|
||||
{
|
||||
result = false;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
UnloadZones();
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<ZoneDefinition> ReadZoneDefinition(LinkerPathManager& paths, const std::string& targetName, bool logMissing = true) const
|
||||
{
|
||||
auto& sourceSearchPath = paths.m_source_paths.GetSearchPaths();
|
||||
std::unique_ptr<ZoneDefinition> zoneDefinition;
|
||||
{
|
||||
const auto definitionFileName = std::format("{}.zone", targetName);
|
||||
const auto definitionStream = sourceSearchPath.Open(definitionFileName);
|
||||
if (!definitionStream.IsOpen())
|
||||
{
|
||||
if (logMissing)
|
||||
con::error("Could not find zone definition file for target \"{}\".", targetName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
ZoneDefinitionInputStream zoneDefinitionInputStream(*definitionStream.m_stream, targetName, definitionFileName, sourceSearchPath);
|
||||
zoneDefinition = zoneDefinitionInputStream.ReadDefinition();
|
||||
}
|
||||
|
||||
if (!zoneDefinition)
|
||||
{
|
||||
con::error("Failed to read zone definition file for target \"{}\".", targetName);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return zoneDefinition;
|
||||
}
|
||||
|
||||
bool ReadIgnoreEntries(LinkerPathManager& paths, const std::string& zoneName, const GameId game, AssetList& assetList) const
|
||||
{
|
||||
{
|
||||
AssetListReader assetListReader(paths.m_source_paths.GetSearchPaths(), game);
|
||||
const auto maybeReadAssetList = assetListReader.ReadAssetList(zoneName, false);
|
||||
if (maybeReadAssetList)
|
||||
{
|
||||
assetList.m_entries.reserve(assetList.m_entries.size() + maybeReadAssetList->m_entries.size());
|
||||
for (auto& entry : maybeReadAssetList->m_entries)
|
||||
assetList.m_entries.emplace_back(std::move(entry));
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
{
|
||||
const auto zoneDefinition = ReadZoneDefinition(paths, zoneName, false);
|
||||
|
||||
if (zoneDefinition)
|
||||
{
|
||||
assetList.m_entries.reserve(assetList.m_entries.size() + zoneDefinition->m_assets.size());
|
||||
for (const auto& entry : zoneDefinition->m_assets)
|
||||
assetList.m_entries.emplace_back(entry.m_asset_type, entry.m_asset_name, entry.m_is_reference);
|
||||
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ProcessZoneDefinitionIgnores(LinkerPathManager& paths, const std::string& targetName, ZoneCreationContext& context) const
|
||||
{
|
||||
if (context.m_definition->m_ignores.empty())
|
||||
return true;
|
||||
|
||||
for (const auto& ignore : context.m_definition->m_ignores)
|
||||
{
|
||||
if (ignore == targetName)
|
||||
continue;
|
||||
|
||||
if (!ReadIgnoreEntries(paths, ignore, context.m_definition->m_game, context.m_ignored_assets))
|
||||
{
|
||||
con::error("Failed to read asset listing for ignoring assets of project \"{}\".", ignore);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool LoadGdtFilesFromZoneDefinition(std::vector<std::unique_ptr<Gdt>>& gdtList, const ZoneDefinition& zoneDefinition, ISearchPath* gdtSearchPath)
|
||||
{
|
||||
for (const auto& gdtName : zoneDefinition.m_gdts)
|
||||
{
|
||||
const auto gdtFile = gdtSearchPath->Open(std::format("{}.gdt", gdtName));
|
||||
if (!gdtFile.IsOpen())
|
||||
{
|
||||
con::error("Failed to open file for gdt \"{}\"", gdtName);
|
||||
return false;
|
||||
}
|
||||
|
||||
GdtReader gdtReader(*gdtFile.m_stream);
|
||||
auto gdt = std::make_unique<Gdt>();
|
||||
if (!gdtReader.Read(*gdt))
|
||||
{
|
||||
con::error("Failed to read gdt file \"{}\"", gdtName);
|
||||
return false;
|
||||
}
|
||||
|
||||
gdtList.emplace_back(std::move(gdt));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<Zone> CreateZoneForDefinition(
|
||||
LinkerPathManager& paths, const fs::path& outDir, const fs::path& cacheDir, const std::string& targetName, ZoneDefinition& zoneDefinition) const
|
||||
{
|
||||
ZoneCreationContext context(&zoneDefinition, &paths.m_asset_paths.GetSearchPaths(), outDir, cacheDir);
|
||||
if (!ProcessZoneDefinitionIgnores(paths, targetName, context))
|
||||
return nullptr;
|
||||
if (!LoadGdtFilesFromZoneDefinition(context.m_gdt_files, zoneDefinition, &paths.m_gdt_paths.GetSearchPaths()))
|
||||
return nullptr;
|
||||
|
||||
return zone_creator::CreateZoneForDefinition(zoneDefinition.m_game, context);
|
||||
}
|
||||
|
||||
static bool WriteZoneToFile(IOutputPath& outPath, const Zone& zone)
|
||||
{
|
||||
const auto stream = outPath.Open(std::format("{}.ff", zone.m_name));
|
||||
if (!stream)
|
||||
{
|
||||
con::error("Failed to open file for zone: {}", zone.m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
con::info("Building zone \"{}\"", zone.m_name);
|
||||
|
||||
if (!ZoneWriting::WriteZone(*stream, zone))
|
||||
{
|
||||
con::error("Writing zone failed.");
|
||||
return false;
|
||||
}
|
||||
|
||||
con::info("Created zone \"{}\"", zone.m_name);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool BuildFastFile(LinkerPathManager& paths, const std::string& projectName, const std::string& targetName, ZoneDefinition& zoneDefinition) const
|
||||
{
|
||||
const fs::path outDir(paths.m_linker_paths->BuildOutputFolderPath(projectName, zoneDefinition.m_game));
|
||||
|
||||
OutputPathFilesystem outputPath(outDir);
|
||||
|
||||
const fs::path cacheDir(paths.m_linker_paths->BuildCacheFolderPath(projectName, zoneDefinition.m_game));
|
||||
SoundBankWriter::OutputPath = outDir;
|
||||
|
||||
const auto zone = CreateZoneForDefinition(paths, outDir, cacheDir, targetName, zoneDefinition);
|
||||
auto result = zone != nullptr;
|
||||
if (zone)
|
||||
result = WriteZoneToFile(outputPath, *zone);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
bool BuildProject(LinkerPathManager& paths, const std::string& projectName, const std::string& targetName) const
|
||||
{
|
||||
std::deque<std::string> targetsToBuild;
|
||||
std::unordered_set<std::string> alreadyBuiltTargets;
|
||||
|
||||
targetsToBuild.emplace_back(targetName);
|
||||
|
||||
while (!targetsToBuild.empty())
|
||||
{
|
||||
const auto currentTarget = std::move(targetsToBuild.front());
|
||||
targetsToBuild.pop_front();
|
||||
alreadyBuiltTargets.emplace(currentTarget);
|
||||
|
||||
PathProjectContext projectContext(paths, projectName);
|
||||
|
||||
const auto zoneDefinition = ReadZoneDefinition(paths, targetName);
|
||||
if (!zoneDefinition)
|
||||
return false;
|
||||
|
||||
PathGameContext gameContext(paths, projectName, zoneDefinition->m_game);
|
||||
|
||||
if (!zoneDefinition->m_assets.empty())
|
||||
{
|
||||
if (!BuildFastFile(paths, projectName, targetName, *zoneDefinition))
|
||||
return false;
|
||||
|
||||
for (const auto& referencedTarget : zoneDefinition->m_targets_to_build)
|
||||
{
|
||||
if (alreadyBuiltTargets.find(referencedTarget) == alreadyBuiltTargets.end())
|
||||
{
|
||||
targetsToBuild.emplace_back(referencedTarget);
|
||||
con::info("Building referenced target \"{}\"", referencedTarget);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadZones()
|
||||
{
|
||||
for (const auto& zonePath : m_args.m_zones_to_load)
|
||||
{
|
||||
if (!fs::is_regular_file(zonePath))
|
||||
{
|
||||
con::error("Could not find zone file to load \"{}\".", zonePath);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zoneDirectory = fs::path(zonePath).remove_filename();
|
||||
if (zoneDirectory.empty())
|
||||
zoneDirectory = fs::current_path();
|
||||
auto absoluteZoneDirectory = absolute(zoneDirectory).string();
|
||||
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
|
||||
con::debug("Loaded zone \"{}\"", zone->m_name);
|
||||
|
||||
m_loaded_zones.emplace_back(std::move(zone));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnloadZones()
|
||||
{
|
||||
for (auto i = m_loaded_zones.rbegin(); i != m_loaded_zones.rend(); ++i)
|
||||
{
|
||||
auto& loadedZone = *i;
|
||||
std::string zoneName = loadedZone->m_name;
|
||||
|
||||
loadedZone.reset();
|
||||
|
||||
con::debug("Unloaded zone \"{}\"", zoneName);
|
||||
}
|
||||
m_loaded_zones.clear();
|
||||
}
|
||||
|
||||
static bool GetProjectAndTargetFromProjectSpecifier(const std::string& projectSpecifier, std::string& projectName, std::string& targetName)
|
||||
{
|
||||
const auto targetNameSeparatorIndex = projectSpecifier.find_first_of('/');
|
||||
if (targetNameSeparatorIndex == std::string::npos)
|
||||
{
|
||||
projectName = projectSpecifier;
|
||||
targetName = projectSpecifier;
|
||||
}
|
||||
else if (projectSpecifier.find_first_of('/', targetNameSeparatorIndex + 1) != std::string::npos)
|
||||
{
|
||||
con::error("Project specifier cannot have more than one target name: \"{}\"", projectSpecifier);
|
||||
return false;
|
||||
}
|
||||
else
|
||||
{
|
||||
projectName = projectSpecifier.substr(0, targetNameSeparatorIndex);
|
||||
targetName = projectSpecifier.substr(targetNameSeparatorIndex + 1);
|
||||
}
|
||||
|
||||
if (projectName.empty())
|
||||
{
|
||||
con::error("Project name cannot be empty: \"{}\"", projectSpecifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (targetName.empty())
|
||||
{
|
||||
con::error("Target name cannot be empty: \"{}\"", projectSpecifier);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
LinkerArgs m_args;
|
||||
std::vector<std::unique_ptr<Zone>> m_loaded_zones;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Linker> Linker::Create(LinkerArgs args)
|
||||
{
|
||||
return std::make_unique<LinkerImpl>(std::move(args));
|
||||
}
|
||||
@@ -1,16 +1,15 @@
|
||||
#pragma once
|
||||
|
||||
#include "LinkerArgs.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Linker
|
||||
{
|
||||
public:
|
||||
Linker() = default;
|
||||
virtual ~Linker() = default;
|
||||
|
||||
Linker(const Linker& other) = delete;
|
||||
Linker(Linker&& other) noexcept = delete;
|
||||
Linker& operator=(const Linker& other) = delete;
|
||||
Linker& operator=(Linker&& other) noexcept = delete;
|
||||
static std::unique_ptr<Linker> Create(LinkerArgs args);
|
||||
|
||||
/**
|
||||
* \brief Starts the Linker application logic.
|
||||
@@ -18,7 +17,5 @@ public:
|
||||
* \param argv The command line arguments.
|
||||
* \return \c true if the application was successful or \c false if an error occurred.
|
||||
*/
|
||||
virtual bool Start(int argc, const char** argv) = 0;
|
||||
|
||||
static std::unique_ptr<Linker> Create();
|
||||
virtual bool Start() = 0;
|
||||
};
|
||||
@@ -104,7 +104,7 @@ XAssetInfoGeneric* AssetCreationContext::LoadDefaultAssetDependency(const asset_
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
XAssetInfoGeneric* AssetCreationContext::LoadDependencyGeneric(const asset_type_t assetType, const std::string& assetName)
|
||||
XAssetInfoGeneric* AssetCreationContext::LoadDependencyGeneric(const asset_type_t assetType, const std::string& assetName, bool required)
|
||||
{
|
||||
auto* alreadyLoadedAsset = m_zone.m_pools->GetAssetOrAssetReference(assetType, assetName);
|
||||
if (alreadyLoadedAsset)
|
||||
@@ -131,7 +131,7 @@ XAssetInfoGeneric* AssetCreationContext::LoadDependencyGeneric(const asset_type_
|
||||
|
||||
con::error("Could not load asset \"{}\" of type \"{}\"", assetName, *m_zone.m_pools->GetAssetTypeName(assetType));
|
||||
}
|
||||
else
|
||||
else if (required)
|
||||
{
|
||||
con::error("Missing asset \"{}\" of type \"{}\"", assetName, *m_zone.m_pools->GetAssetTypeName(assetType));
|
||||
}
|
||||
|
||||
@@ -54,7 +54,7 @@ public:
|
||||
return static_cast<XAssetInfo<typename AssetType::Type>*>(LoadDependencyGeneric(AssetType::EnumEntry, assetName));
|
||||
}
|
||||
|
||||
XAssetInfoGeneric* LoadDependencyGeneric(asset_type_t assetType, const std::string& assetName);
|
||||
XAssetInfoGeneric* LoadDependencyGeneric(asset_type_t assetType, const std::string& assetName, bool required = true);
|
||||
|
||||
template<typename AssetType> IndirectAssetReference LoadIndirectAssetReference(const std::string& assetName)
|
||||
{
|
||||
|
||||
61
src/ObjLoading/Asset/GlobalAssetPoolsLoader.cpp
Normal file
61
src/ObjLoading/Asset/GlobalAssetPoolsLoader.cpp
Normal file
@@ -0,0 +1,61 @@
|
||||
#include "GlobalAssetPoolsLoader.h"
|
||||
|
||||
GlobalAssetPoolsRegistrationPreparation::GlobalAssetPoolsRegistrationPreparation(GenericAssetRegistration& registration,
|
||||
Zone& zone,
|
||||
Zone& foreignZone,
|
||||
AssetCreationContext& context)
|
||||
: m_registration(registration),
|
||||
m_zone(zone),
|
||||
m_foreign_zone(foreignZone),
|
||||
m_context(context),
|
||||
m_failure(false)
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<XAssetInfoGeneric*> GlobalAssetPoolsRegistrationPreparation::Visit_Dependency(const asset_type_t assetType, const char* assetName)
|
||||
{
|
||||
if (assetName && assetName[0] == ',')
|
||||
{
|
||||
/*
|
||||
Try to load the actual asset when the asset from the global asset pools just references one.
|
||||
If that fails, keep the reference to not destroy previous existing behaviour of just being able to use assets from the global pools
|
||||
without ignores.
|
||||
*/
|
||||
auto* nonReferenceAssetName = &assetName[1];
|
||||
|
||||
auto* assetDependency = m_context.LoadDependencyGeneric(assetType, nonReferenceAssetName, false);
|
||||
if (assetDependency)
|
||||
{
|
||||
m_registration.AddDependency(assetDependency);
|
||||
return std::nullopt;
|
||||
}
|
||||
}
|
||||
|
||||
auto* newDependency = m_context.LoadDependencyGeneric(assetType, assetName);
|
||||
if (newDependency)
|
||||
{
|
||||
m_registration.AddDependency(newDependency);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
m_failure = true;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<scr_string_t> GlobalAssetPoolsRegistrationPreparation::Visit_ScriptString(scr_string_t scriptString)
|
||||
{
|
||||
// Make sure any used script string is available in the created zone
|
||||
m_zone.m_script_strings.AddOrGetScriptString(m_foreign_zone.m_script_strings.CValue(scriptString));
|
||||
m_registration.AddScriptString(scriptString);
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void GlobalAssetPoolsRegistrationPreparation::Visit_IndirectAssetRef(const asset_type_t assetType, const char* assetName)
|
||||
{
|
||||
m_registration.AddIndirectAssetReference(m_context.LoadIndirectAssetReferenceGeneric(assetType, assetName));
|
||||
}
|
||||
|
||||
bool GlobalAssetPoolsRegistrationPreparation::Failed() const
|
||||
{
|
||||
return m_failure;
|
||||
}
|
||||
@@ -1,13 +1,36 @@
|
||||
#pragma once
|
||||
|
||||
#include "Asset/IAssetCreator.h"
|
||||
#include "Marking/AssetVisitor.h"
|
||||
#include "Marking/BaseAssetMarker.h"
|
||||
#include "Pool/GlobalAssetPool.h"
|
||||
|
||||
class GlobalAssetPoolsRegistrationPreparation : public AssetVisitor
|
||||
{
|
||||
public:
|
||||
GlobalAssetPoolsRegistrationPreparation(GenericAssetRegistration& registration, Zone& zone, Zone& foreignZone, AssetCreationContext& context);
|
||||
|
||||
std::optional<XAssetInfoGeneric*> Visit_Dependency(asset_type_t assetType, const char* assetName) override;
|
||||
std::optional<scr_string_t> Visit_ScriptString(scr_string_t scriptString) override;
|
||||
void Visit_IndirectAssetRef(asset_type_t assetType, const char* assetName) override;
|
||||
|
||||
[[nodiscard]] bool Failed() const;
|
||||
|
||||
private:
|
||||
GenericAssetRegistration& m_registration;
|
||||
Zone& m_zone;
|
||||
Zone& m_foreign_zone;
|
||||
AssetCreationContext& m_context;
|
||||
|
||||
bool m_failure;
|
||||
};
|
||||
|
||||
template<typename AssetType> class GlobalAssetPoolsLoader : public AssetCreator<AssetType>
|
||||
{
|
||||
public:
|
||||
static_assert(std::is_base_of_v<IAssetBase, AssetType>);
|
||||
|
||||
GlobalAssetPoolsLoader(Zone& zone)
|
||||
explicit GlobalAssetPoolsLoader(Zone& zone)
|
||||
: m_zone(zone)
|
||||
{
|
||||
}
|
||||
@@ -21,26 +44,12 @@ public:
|
||||
|
||||
AssetRegistration<AssetType> registration(assetName, existingAsset->Asset());
|
||||
|
||||
for (const auto* dependency : existingAsset->m_dependencies)
|
||||
{
|
||||
auto* newDependency = context.LoadDependencyGeneric(dependency->m_type, dependency->m_name);
|
||||
if (newDependency)
|
||||
registration.AddDependency(newDependency);
|
||||
else
|
||||
return AssetCreationResult::Failure();
|
||||
}
|
||||
|
||||
for (const auto& indirectAssetReference : existingAsset->m_indirect_asset_references)
|
||||
registration.AddIndirectAssetReference(context.LoadIndirectAssetReferenceGeneric(indirectAssetReference.m_type, indirectAssetReference.m_name));
|
||||
|
||||
// Make sure any used script string is available in the created zone
|
||||
// The replacement of the scr_string_t values will be done upon writing
|
||||
for (const auto scrString : existingAsset->m_used_script_strings)
|
||||
m_zone.m_script_strings.AddOrGetScriptString(existingAsset->m_zone->m_script_strings.CValue(scrString));
|
||||
GlobalAssetPoolsRegistrationPreparation registrationPreparation(registration, m_zone, *existingAsset->m_zone, context);
|
||||
AssetMarker<AssetType> marker(registrationPreparation);
|
||||
marker.Mark(existingAsset->Asset());
|
||||
|
||||
auto* newAsset = context.AddAsset(std::move(registration));
|
||||
|
||||
// Make sure we remember this asset came from another zone
|
||||
// Make sure we remember this asset came from a different zone
|
||||
newAsset->m_zone = existingAsset->m_zone;
|
||||
|
||||
return AssetCreationResult::Success(newAsset);
|
||||
|
||||
@@ -42,7 +42,7 @@ public:
|
||||
AssetCreationResult CreateDefaultAsset(const std::string& assetName, AssetCreationContext& context) const override
|
||||
{
|
||||
auto* asset = m_memory.Alloc<typename AssetType::Type>();
|
||||
AssetNameAccessor<AssetType>{}(*asset) = m_memory.Dup(assetName.c_str());
|
||||
AssetName<AssetType>(*asset) = m_memory.Dup(assetName.c_str());
|
||||
|
||||
return AssetCreationResult::Success(context.AddAsset<AssetType>(assetName, asset));
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ObjLoaderIW3.h"
|
||||
|
||||
#include "Asset/GlobalAssetPoolsLoader.h"
|
||||
#include "Game/IW3/AssetMarkerIW3.h"
|
||||
#include "Game/IW3/GameIW3.h"
|
||||
#include "Game/IW3/IW3.h"
|
||||
#include "Game/IW3/XModel/LoaderXModelIW3.h"
|
||||
@@ -65,7 +66,7 @@ namespace
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSound>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSoundCurve>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetLoadedSound>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMap>>(zone));
|
||||
// collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMap>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMapPvs>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetComWorld>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetGameWorldSp>>(zone));
|
||||
@@ -78,7 +79,7 @@ namespace
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetMenu>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetLocalize>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetWeapon>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSoundDriverGlobals>>(zone));
|
||||
// collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSoundDriverGlobals>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetFx>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetImpactFx>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetRawFile>>(zone));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ObjLoaderIW4.h"
|
||||
|
||||
#include "Asset/GlobalAssetPoolsLoader.h"
|
||||
#include "Game/IW4/AssetMarkerIW4.h"
|
||||
#include "Game/IW4/GameIW4.h"
|
||||
#include "Game/IW4/IW4.h"
|
||||
#include "Game/IW4/XModel/LoaderXModelIW4.h"
|
||||
@@ -79,7 +80,7 @@ namespace
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetPhysPreset>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetPhysCollMap>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetXAnim>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetXModelSurfs>>(zone));
|
||||
// collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetXModelSurfs>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetXModel>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetMaterial>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetPixelShader>>(zone));
|
||||
@@ -90,7 +91,7 @@ namespace
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSound>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSoundCurve>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetLoadedSound>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMapSp>>(zone));
|
||||
// collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMapSp>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMapMp>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetComWorld>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetGameWorldSp>>(zone));
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ObjLoaderIW5.h"
|
||||
|
||||
#include "Asset/GlobalAssetPoolsLoader.h"
|
||||
#include "Game/IW5/AssetMarkerIW5.h"
|
||||
#include "Game/IW5/GameIW5.h"
|
||||
#include "Game/IW5/IW5.h"
|
||||
#include "Game/IW5/XModel/LoaderXModelIW5.h"
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include "ObjLoaderT5.h"
|
||||
|
||||
#include "Asset/GlobalAssetPoolsLoader.h"
|
||||
#include "Game/T5/AssetMarkerT5.h"
|
||||
#include "Game/T5/GameT5.h"
|
||||
#include "Game/T5/T5.h"
|
||||
#include "Game/T5/XModel/LoaderXModelT5.h"
|
||||
@@ -71,7 +72,7 @@ namespace
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetImage>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSoundBank>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSoundPatch>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMap>>(zone));
|
||||
// collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMap>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMapPvs>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetComWorld>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetGameWorldSp>>(zone));
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Asset/GlobalAssetPoolsLoader.h"
|
||||
#include "FontIcon/CsvLoaderFontIconT6.h"
|
||||
#include "FontIcon/JsonLoaderFontIconT6.h"
|
||||
#include "Game/T6/AssetMarkerT6.h"
|
||||
#include "Game/T6/CommonT6.h"
|
||||
#include "Game/T6/GameAssetPoolT6.h"
|
||||
#include "Game/T6/GameT6.h"
|
||||
@@ -340,7 +341,7 @@ namespace T6
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetImage>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSoundBank>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetSoundPatch>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMap>>(zone));
|
||||
// collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMap>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetClipMapPvs>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetComWorld>>(zone));
|
||||
collection.AddAssetCreator(std::make_unique<GlobalAssetPoolsLoader<AssetGameWorldSp>>(zone));
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
Unlinker = {}
|
||||
|
||||
function Unlinker:include(includes)
|
||||
if includes:handle(self:name()) then
|
||||
includedirs {
|
||||
path.join(ProjectFolder(), "Unlinker")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function Unlinker:link(links)
|
||||
|
||||
end
|
||||
|
||||
function Unlinker:use()
|
||||
dependson(self:name())
|
||||
end
|
||||
|
||||
function Unlinker:name()
|
||||
return "Unlinker"
|
||||
end
|
||||
|
||||
function Unlinker:project()
|
||||
local folder = ProjectFolder()
|
||||
local includes = Includes:create()
|
||||
local links = Links:create()
|
||||
|
||||
project(self:name())
|
||||
targetdir(TargetDirectoryBin)
|
||||
location "%{wks.location}/src/%{prj.name}"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
files {
|
||||
path.join(folder, "Unlinker/**.h"),
|
||||
path.join(folder, "Unlinker/**.cpp")
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Utils:include(includes)
|
||||
ZoneLoading:include(includes)
|
||||
ObjLoading:include(includes)
|
||||
ObjWriting:include(includes)
|
||||
|
||||
Raw:use()
|
||||
|
||||
links:linkto(Utils)
|
||||
links:linkto(ZoneLoading)
|
||||
links:linkto(ObjLoading)
|
||||
links:linkto(ObjWriting)
|
||||
links:linkall()
|
||||
end
|
||||
@@ -1,342 +0,0 @@
|
||||
#include "Unlinker.h"
|
||||
|
||||
#include "ContentLister/ContentPrinter.h"
|
||||
#include "IObjLoader.h"
|
||||
#include "IObjWriter.h"
|
||||
#include "ObjWriting.h"
|
||||
#include "SearchPath/IWD.h"
|
||||
#include "SearchPath/OutputPathFilesystem.h"
|
||||
#include "UnlinkerArgs.h"
|
||||
#include "UnlinkerPaths.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
#include "Utils/ObjFileStream.h"
|
||||
#include "Zone/Definition/ZoneDefWriter.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
class Unlinker::Impl
|
||||
{
|
||||
public:
|
||||
/**
|
||||
* \copydoc Unlinker::Start
|
||||
*/
|
||||
bool Start(const int argc, const char** argv)
|
||||
{
|
||||
con::init();
|
||||
|
||||
auto shouldContinue = true;
|
||||
if (!m_args.ParseArgs(argc, argv, shouldContinue))
|
||||
return false;
|
||||
|
||||
if (!shouldContinue)
|
||||
return true;
|
||||
|
||||
UnlinkerPaths paths;
|
||||
if (!paths.LoadUserPaths(m_args))
|
||||
return false;
|
||||
|
||||
if (!LoadZones(paths))
|
||||
return false;
|
||||
|
||||
const auto result = UnlinkZones(paths);
|
||||
|
||||
UnloadZones();
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool ShouldLoadObj() const
|
||||
{
|
||||
return m_args.m_task != UnlinkerArgs::ProcessingTask::LIST && !m_args.m_skip_obj;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool WriteZoneDefinitionFile(const Zone& zone, const fs::path& zoneDefinitionFileFolder) const
|
||||
{
|
||||
auto zoneDefinitionFilePath(zoneDefinitionFileFolder);
|
||||
zoneDefinitionFilePath.append(zone.m_name);
|
||||
zoneDefinitionFilePath.replace_extension(".zone");
|
||||
|
||||
std::ofstream zoneDefinitionFile(zoneDefinitionFilePath, std::fstream::out | std::fstream::binary);
|
||||
if (!zoneDefinitionFile.is_open())
|
||||
{
|
||||
con::error("Failed to open file for zone definition file of zone \"{}\".", zone.m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* zoneDefWriter = IZoneDefWriter::GetZoneDefWriterForGame(zone.m_game_id);
|
||||
zoneDefWriter->WriteZoneDef(zoneDefinitionFile, zone, m_args.m_use_gdt);
|
||||
|
||||
zoneDefinitionFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool OpenGdtFile(const Zone& zone, const fs::path& outputFolder, std::ofstream& stream)
|
||||
{
|
||||
auto gdtFilePath(outputFolder);
|
||||
gdtFilePath.append("source_data");
|
||||
|
||||
fs::create_directories(gdtFilePath);
|
||||
|
||||
gdtFilePath.append(zone.m_name);
|
||||
gdtFilePath.replace_extension(".gdt");
|
||||
|
||||
stream = std::ofstream(gdtFilePath, std::fstream::out | std::fstream::binary);
|
||||
if (!stream.is_open())
|
||||
{
|
||||
con::error("Failed to open file for zone definition file of zone \"{}\".", zone.m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateAssetIncludesAndExcludes(const AssetDumpingContext& context) const
|
||||
{
|
||||
const auto assetTypeCount = context.m_zone.m_pools->GetAssetTypeCount();
|
||||
|
||||
ObjWriting::Configuration.AssetTypesToHandleBitfield = std::vector<bool>(assetTypeCount);
|
||||
|
||||
std::vector<bool> handledSpecifiedAssets(m_args.m_specified_asset_types.size());
|
||||
for (auto i = 0u; i < assetTypeCount; i++)
|
||||
{
|
||||
const auto assetTypeName = std::string(*context.m_zone.m_pools->GetAssetTypeName(i));
|
||||
|
||||
const auto foundSpecifiedEntry = m_args.m_specified_asset_type_map.find(assetTypeName);
|
||||
if (foundSpecifiedEntry != m_args.m_specified_asset_type_map.end())
|
||||
{
|
||||
ObjWriting::Configuration.AssetTypesToHandleBitfield[i] = m_args.m_asset_type_handling == UnlinkerArgs::AssetTypeHandling::INCLUDE;
|
||||
assert(foundSpecifiedEntry->second < handledSpecifiedAssets.size());
|
||||
handledSpecifiedAssets[foundSpecifiedEntry->second] = true;
|
||||
}
|
||||
else
|
||||
ObjWriting::Configuration.AssetTypesToHandleBitfield[i] = m_args.m_asset_type_handling == UnlinkerArgs::AssetTypeHandling::EXCLUDE;
|
||||
}
|
||||
|
||||
auto anySpecifiedValueInvalid = false;
|
||||
for (auto i = 0u; i < handledSpecifiedAssets.size(); i++)
|
||||
{
|
||||
if (!handledSpecifiedAssets[i])
|
||||
{
|
||||
con::error("Unknown asset type \"{}\"", m_args.m_specified_asset_types[i]);
|
||||
anySpecifiedValueInvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (anySpecifiedValueInvalid)
|
||||
{
|
||||
con::error("Valid asset types are:");
|
||||
|
||||
auto first = true;
|
||||
std::ostringstream ss;
|
||||
for (auto i = 0u; i < assetTypeCount; i++)
|
||||
{
|
||||
const auto assetTypeName = std::string(*context.m_zone.m_pools->GetAssetTypeName(i));
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ss << ", ";
|
||||
ss << assetTypeName;
|
||||
}
|
||||
con::error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Performs the tasks specified by the command line arguments on the specified zone.
|
||||
* \param searchPath The search path for obj data.
|
||||
* \param zone The zone to handle.
|
||||
* \return \c true if handling the zone was successful, otherwise \c false
|
||||
*/
|
||||
bool HandleZone(ISearchPath& searchPath, Zone& zone) const
|
||||
{
|
||||
if (m_args.m_task == UnlinkerArgs::ProcessingTask::LIST)
|
||||
{
|
||||
const ContentPrinter printer(zone);
|
||||
printer.PrintContent();
|
||||
}
|
||||
else if (m_args.m_task == UnlinkerArgs::ProcessingTask::DUMP)
|
||||
{
|
||||
const auto outputFolderPathStr = m_args.GetOutputFolderPathForZone(zone);
|
||||
const fs::path outputFolderPath(outputFolderPathStr);
|
||||
fs::create_directories(outputFolderPath);
|
||||
|
||||
fs::path zoneDefinitionFileFolder(outputFolderPath);
|
||||
zoneDefinitionFileFolder.append("zone_source");
|
||||
fs::create_directories(zoneDefinitionFileFolder);
|
||||
|
||||
if (!WriteZoneDefinitionFile(zone, zoneDefinitionFileFolder))
|
||||
return false;
|
||||
|
||||
OutputPathFilesystem outputFolderOutputPath(outputFolderPath);
|
||||
AssetDumpingContext context(zone, outputFolderPathStr, outputFolderOutputPath, searchPath, std::nullopt);
|
||||
|
||||
std::ofstream gdtStream;
|
||||
if (m_args.m_use_gdt)
|
||||
{
|
||||
if (!OpenGdtFile(zone, outputFolderPath, gdtStream))
|
||||
return false;
|
||||
auto gdt = std::make_unique<GdtOutputStream>(gdtStream);
|
||||
gdt->BeginStream();
|
||||
|
||||
const auto* game = IGame::GetGameById(zone.m_game_id);
|
||||
gdt->WriteVersion(GdtVersion(game->GetShortName(), 1));
|
||||
|
||||
context.m_gdt = std::move(gdt);
|
||||
}
|
||||
|
||||
UpdateAssetIncludesAndExcludes(context);
|
||||
|
||||
const auto* objWriter = IObjWriter::GetObjWriterForGame(zone.m_game_id);
|
||||
|
||||
auto result = objWriter->DumpZone(context);
|
||||
|
||||
if (m_args.m_use_gdt)
|
||||
{
|
||||
context.m_gdt->EndStream();
|
||||
gdtStream.close();
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
con::error("Dumping zone failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadZones(UnlinkerPaths& paths)
|
||||
{
|
||||
for (const auto& zonePath : m_args.m_zones_to_load)
|
||||
{
|
||||
if (!fs::is_regular_file(zonePath))
|
||||
{
|
||||
con::error("Could not find file \"{}\".", zonePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto absoluteZoneDirectory = absolute(std::filesystem::path(zonePath).remove_filename()).string();
|
||||
|
||||
auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
|
||||
con::debug("Loaded zone \"{}\"", zone->m_name);
|
||||
|
||||
if (ShouldLoadObj())
|
||||
{
|
||||
const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game_id);
|
||||
objLoader->LoadReferencedContainersForZone(*searchPathsForZone, *zone);
|
||||
}
|
||||
|
||||
m_loaded_zones.emplace_back(std::move(zone));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnloadZones()
|
||||
{
|
||||
for (auto i = m_loaded_zones.rbegin(); i != m_loaded_zones.rend(); ++i)
|
||||
{
|
||||
auto& loadedZone = *i;
|
||||
|
||||
// Copy zone name since we deallocate before logging
|
||||
const auto zoneName = loadedZone->m_name;
|
||||
|
||||
if (ShouldLoadObj())
|
||||
{
|
||||
const auto* objLoader = IObjLoader::GetObjLoaderForGame(loadedZone->m_game_id);
|
||||
objLoader->UnloadContainersOfZone(*loadedZone);
|
||||
}
|
||||
|
||||
loadedZone.reset();
|
||||
|
||||
con::debug("Unloaded zone \"{}\"", zoneName);
|
||||
}
|
||||
m_loaded_zones.clear();
|
||||
}
|
||||
|
||||
bool UnlinkZones(UnlinkerPaths& paths) const
|
||||
{
|
||||
for (const auto& zonePath : m_args.m_zones_to_unlink)
|
||||
{
|
||||
if (!fs::is_regular_file(zonePath))
|
||||
{
|
||||
con::error("Could not find file \"{}\".", zonePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto zoneDirectory = fs::path(zonePath).remove_filename();
|
||||
if (zoneDirectory.empty())
|
||||
zoneDirectory = fs::current_path();
|
||||
auto absoluteZoneDirectory = absolute(zoneDirectory).string();
|
||||
|
||||
auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
|
||||
con::debug("Loaded zone \"{}\"", zone->m_name);
|
||||
|
||||
const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game_id);
|
||||
if (ShouldLoadObj())
|
||||
objLoader->LoadReferencedContainersForZone(*searchPathsForZone, *zone);
|
||||
|
||||
if (!HandleZone(*searchPathsForZone, *zone))
|
||||
return false;
|
||||
|
||||
if (ShouldLoadObj())
|
||||
objLoader->UnloadContainersOfZone(*zone);
|
||||
|
||||
// Copy zone name for using it after freeing the zone
|
||||
std::string zoneName = zone->m_name;
|
||||
zone.reset();
|
||||
con::debug("Unloaded zone \"{}\"", zoneName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UnlinkerArgs m_args;
|
||||
std::vector<std::unique_ptr<Zone>> m_loaded_zones;
|
||||
};
|
||||
|
||||
Unlinker::Unlinker()
|
||||
{
|
||||
m_impl = new Impl();
|
||||
}
|
||||
|
||||
Unlinker::~Unlinker()
|
||||
{
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
|
||||
bool Unlinker::Start(const int argc, const char** argv) const
|
||||
{
|
||||
return m_impl->Start(argc, argv);
|
||||
}
|
||||
@@ -1,24 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
class Unlinker
|
||||
{
|
||||
class Impl;
|
||||
Impl* m_impl;
|
||||
|
||||
public:
|
||||
Unlinker();
|
||||
~Unlinker();
|
||||
|
||||
Unlinker(const Unlinker& other) = delete;
|
||||
Unlinker(Unlinker&& other) noexcept = delete;
|
||||
Unlinker& operator=(const Unlinker& other) = delete;
|
||||
Unlinker& operator=(Unlinker&& other) noexcept = delete;
|
||||
|
||||
/**
|
||||
* \brief Starts the Unlinker application logic.
|
||||
* \param argc The amount of command line arguments specified.
|
||||
* \param argv The command line arguments.
|
||||
* \return \c true if the application was successful or \c false if an error occurred.
|
||||
*/
|
||||
bool Start(int argc, const char** argv) const;
|
||||
};
|
||||
@@ -1,8 +0,0 @@
|
||||
#include "Unlinker.h"
|
||||
|
||||
int main(const int argc, const char** argv)
|
||||
{
|
||||
Unlinker unlinker;
|
||||
|
||||
return unlinker.Start(argc, argv) ? 0 : 1;
|
||||
}
|
||||
49
src/UnlinkerCli.lua
Normal file
49
src/UnlinkerCli.lua
Normal file
@@ -0,0 +1,49 @@
|
||||
UnlinkerCli = {}
|
||||
|
||||
function UnlinkerCli:include(includes)
|
||||
if includes:handle(self:name()) then
|
||||
includedirs {
|
||||
path.join(ProjectFolder(), "UnlinkerCli")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function UnlinkerCli:link(links)
|
||||
|
||||
end
|
||||
|
||||
function UnlinkerCli:use()
|
||||
dependson(self:name())
|
||||
end
|
||||
|
||||
function UnlinkerCli:name()
|
||||
return "UnlinkerCli"
|
||||
end
|
||||
|
||||
function UnlinkerCli:project()
|
||||
local folder = ProjectFolder()
|
||||
local includes = Includes:create()
|
||||
local links = Links:create()
|
||||
|
||||
project(self:name())
|
||||
targetdir(TargetDirectoryBin)
|
||||
targetname "Unlinker"
|
||||
location "%{wks.location}/src/%{prj.name}"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
files {
|
||||
path.join(folder, "UnlinkerCli/**.h"),
|
||||
path.join(folder, "UnlinkerCli/**.cpp")
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Utils:include(includes)
|
||||
Unlinking:include(includes)
|
||||
|
||||
Raw:use()
|
||||
|
||||
links:linkto(Utils)
|
||||
links:linkto(Unlinking)
|
||||
links:linkall()
|
||||
end
|
||||
19
src/UnlinkerCli/main.cpp
Normal file
19
src/UnlinkerCli/main.cpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#include "Unlinker.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
|
||||
int main(const int argc, const char** argv)
|
||||
{
|
||||
con::init();
|
||||
|
||||
UnlinkerArgs args;
|
||||
auto shouldContinue = true;
|
||||
if (!args.ParseArgs(argc, argv, shouldContinue))
|
||||
return 1;
|
||||
|
||||
if (!shouldContinue)
|
||||
return 0;
|
||||
|
||||
const auto unlinker = Unlinker::Create(std::move(args));
|
||||
|
||||
return unlinker->Start() ? 0 : 1;
|
||||
}
|
||||
57
src/Unlinking.lua
Normal file
57
src/Unlinking.lua
Normal file
@@ -0,0 +1,57 @@
|
||||
Unlinking = {}
|
||||
|
||||
function Unlinking:include(includes)
|
||||
if includes:handle(self:name()) then
|
||||
ZoneCommon:include(includes)
|
||||
includedirs {
|
||||
path.join(ProjectFolder(), "Unlinking"),
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function Unlinking:link(links)
|
||||
links:add(self:name())
|
||||
links:linkto(Utils)
|
||||
links:linkto(ZoneLoading)
|
||||
links:linkto(ObjLoading)
|
||||
links:linkto(ObjWriting)
|
||||
end
|
||||
|
||||
function Unlinking:use()
|
||||
|
||||
end
|
||||
|
||||
function Unlinking:name()
|
||||
return "Unlinking"
|
||||
end
|
||||
|
||||
function Unlinking:project()
|
||||
local folder = ProjectFolder()
|
||||
local includes = Includes:create()
|
||||
|
||||
project(self:name())
|
||||
targetdir(TargetDirectoryLib)
|
||||
location "%{wks.location}/src/%{prj.name}"
|
||||
kind "StaticLib"
|
||||
language "C++"
|
||||
|
||||
files {
|
||||
path.join(folder, "Unlinking/**.h"),
|
||||
path.join(folder, "Unlinking/**.cpp")
|
||||
}
|
||||
|
||||
vpaths {
|
||||
["*"] = {
|
||||
path.join(folder, "Unlinking")
|
||||
}
|
||||
}
|
||||
|
||||
ObjCommon:use()
|
||||
useSourceTemplating("Unlinking")
|
||||
|
||||
self:include(includes)
|
||||
Utils:include(includes)
|
||||
ZoneLoading:include(includes)
|
||||
ObjLoading:include(includes)
|
||||
ObjWriting:include(includes)
|
||||
end
|
||||
327
src/Unlinking/Unlinker.cpp
Normal file
327
src/Unlinking/Unlinker.cpp
Normal file
@@ -0,0 +1,327 @@
|
||||
#include "Unlinker.h"
|
||||
|
||||
#include "ContentLister/ContentPrinter.h"
|
||||
#include "IObjLoader.h"
|
||||
#include "IObjWriter.h"
|
||||
#include "ObjWriting.h"
|
||||
#include "SearchPath/IWD.h"
|
||||
#include "SearchPath/OutputPathFilesystem.h"
|
||||
#include "UnlinkerArgs.h"
|
||||
#include "UnlinkerPaths.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/Logging/Log.h"
|
||||
#include "Utils/ObjFileStream.h"
|
||||
#include "Zone/Definition/ZoneDefWriter.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <regex>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
class UnlinkerImpl : public Unlinker
|
||||
{
|
||||
public:
|
||||
UnlinkerImpl(UnlinkerArgs args)
|
||||
: m_args(std::move(args))
|
||||
{
|
||||
}
|
||||
|
||||
bool Start() override
|
||||
{
|
||||
UnlinkerPaths paths;
|
||||
if (!paths.LoadUserPaths(m_args))
|
||||
return false;
|
||||
|
||||
if (!LoadZones(paths))
|
||||
return false;
|
||||
|
||||
const auto result = UnlinkZones(paths);
|
||||
|
||||
UnloadZones();
|
||||
return result;
|
||||
}
|
||||
|
||||
private:
|
||||
[[nodiscard]] bool ShouldLoadObj() const
|
||||
{
|
||||
return m_args.m_task != UnlinkerArgs::ProcessingTask::LIST && !m_args.m_skip_obj;
|
||||
}
|
||||
|
||||
[[nodiscard]] bool WriteZoneDefinitionFile(const Zone& zone, const fs::path& zoneDefinitionFileFolder) const
|
||||
{
|
||||
auto zoneDefinitionFilePath(zoneDefinitionFileFolder);
|
||||
zoneDefinitionFilePath.append(zone.m_name);
|
||||
zoneDefinitionFilePath.replace_extension(".zone");
|
||||
|
||||
std::ofstream zoneDefinitionFile(zoneDefinitionFilePath, std::fstream::out | std::fstream::binary);
|
||||
if (!zoneDefinitionFile.is_open())
|
||||
{
|
||||
con::error("Failed to open file for zone definition file of zone \"{}\".", zone.m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
const auto* zoneDefWriter = IZoneDefWriter::GetZoneDefWriterForGame(zone.m_game_id);
|
||||
zoneDefWriter->WriteZoneDef(zoneDefinitionFile, zone, m_args.m_use_gdt);
|
||||
|
||||
zoneDefinitionFile.close();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool OpenGdtFile(const Zone& zone, const fs::path& outputFolder, std::ofstream& stream)
|
||||
{
|
||||
auto gdtFilePath(outputFolder);
|
||||
gdtFilePath.append("source_data");
|
||||
|
||||
fs::create_directories(gdtFilePath);
|
||||
|
||||
gdtFilePath.append(zone.m_name);
|
||||
gdtFilePath.replace_extension(".gdt");
|
||||
|
||||
stream = std::ofstream(gdtFilePath, std::fstream::out | std::fstream::binary);
|
||||
if (!stream.is_open())
|
||||
{
|
||||
con::error("Failed to open file for zone definition file of zone \"{}\".", zone.m_name);
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UpdateAssetIncludesAndExcludes(const AssetDumpingContext& context) const
|
||||
{
|
||||
const auto assetTypeCount = context.m_zone.m_pools->GetAssetTypeCount();
|
||||
|
||||
ObjWriting::Configuration.AssetTypesToHandleBitfield = std::vector<bool>(assetTypeCount);
|
||||
|
||||
std::vector<bool> handledSpecifiedAssets(m_args.m_specified_asset_types.size());
|
||||
for (auto i = 0u; i < assetTypeCount; i++)
|
||||
{
|
||||
const auto assetTypeName = std::string(*context.m_zone.m_pools->GetAssetTypeName(i));
|
||||
|
||||
const auto foundSpecifiedEntry = m_args.m_specified_asset_type_map.find(assetTypeName);
|
||||
if (foundSpecifiedEntry != m_args.m_specified_asset_type_map.end())
|
||||
{
|
||||
ObjWriting::Configuration.AssetTypesToHandleBitfield[i] = m_args.m_asset_type_handling == UnlinkerArgs::AssetTypeHandling::INCLUDE;
|
||||
assert(foundSpecifiedEntry->second < handledSpecifiedAssets.size());
|
||||
handledSpecifiedAssets[foundSpecifiedEntry->second] = true;
|
||||
}
|
||||
else
|
||||
ObjWriting::Configuration.AssetTypesToHandleBitfield[i] = m_args.m_asset_type_handling == UnlinkerArgs::AssetTypeHandling::EXCLUDE;
|
||||
}
|
||||
|
||||
auto anySpecifiedValueInvalid = false;
|
||||
for (auto i = 0u; i < handledSpecifiedAssets.size(); i++)
|
||||
{
|
||||
if (!handledSpecifiedAssets[i])
|
||||
{
|
||||
con::error("Unknown asset type \"{}\"", m_args.m_specified_asset_types[i]);
|
||||
anySpecifiedValueInvalid = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (anySpecifiedValueInvalid)
|
||||
{
|
||||
con::error("Valid asset types are:");
|
||||
|
||||
auto first = true;
|
||||
std::ostringstream ss;
|
||||
for (auto i = 0u; i < assetTypeCount; i++)
|
||||
{
|
||||
const auto assetTypeName = std::string(*context.m_zone.m_pools->GetAssetTypeName(i));
|
||||
|
||||
if (first)
|
||||
first = false;
|
||||
else
|
||||
ss << ", ";
|
||||
ss << assetTypeName;
|
||||
}
|
||||
con::error(ss.str());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Performs the tasks specified by the command line arguments on the specified zone.
|
||||
* \param searchPath The search path for obj data.
|
||||
* \param zone The zone to handle.
|
||||
* \return \c true if handling the zone was successful, otherwise \c false
|
||||
*/
|
||||
bool HandleZone(ISearchPath& searchPath, Zone& zone) const
|
||||
{
|
||||
if (m_args.m_task == UnlinkerArgs::ProcessingTask::LIST)
|
||||
{
|
||||
const ContentPrinter printer(zone);
|
||||
printer.PrintContent();
|
||||
}
|
||||
else if (m_args.m_task == UnlinkerArgs::ProcessingTask::DUMP)
|
||||
{
|
||||
const auto outputFolderPathStr = m_args.GetOutputFolderPathForZone(zone);
|
||||
const fs::path outputFolderPath(outputFolderPathStr);
|
||||
fs::create_directories(outputFolderPath);
|
||||
|
||||
fs::path zoneDefinitionFileFolder(outputFolderPath);
|
||||
zoneDefinitionFileFolder.append("zone_source");
|
||||
fs::create_directories(zoneDefinitionFileFolder);
|
||||
|
||||
if (!WriteZoneDefinitionFile(zone, zoneDefinitionFileFolder))
|
||||
return false;
|
||||
|
||||
OutputPathFilesystem outputFolderOutputPath(outputFolderPath);
|
||||
AssetDumpingContext context(zone, outputFolderPathStr, outputFolderOutputPath, searchPath, std::nullopt);
|
||||
|
||||
std::ofstream gdtStream;
|
||||
if (m_args.m_use_gdt)
|
||||
{
|
||||
if (!OpenGdtFile(zone, outputFolderPath, gdtStream))
|
||||
return false;
|
||||
auto gdt = std::make_unique<GdtOutputStream>(gdtStream);
|
||||
gdt->BeginStream();
|
||||
|
||||
const auto* game = IGame::GetGameById(zone.m_game_id);
|
||||
gdt->WriteVersion(GdtVersion(game->GetShortName(), 1));
|
||||
|
||||
context.m_gdt = std::move(gdt);
|
||||
}
|
||||
|
||||
UpdateAssetIncludesAndExcludes(context);
|
||||
|
||||
const auto* objWriter = IObjWriter::GetObjWriterForGame(zone.m_game_id);
|
||||
|
||||
auto result = objWriter->DumpZone(context);
|
||||
|
||||
if (m_args.m_use_gdt)
|
||||
{
|
||||
context.m_gdt->EndStream();
|
||||
gdtStream.close();
|
||||
}
|
||||
|
||||
if (!result)
|
||||
{
|
||||
con::error("Dumping zone failed!");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadZones(UnlinkerPaths& paths)
|
||||
{
|
||||
for (const auto& zonePath : m_args.m_zones_to_load)
|
||||
{
|
||||
if (!fs::is_regular_file(zonePath))
|
||||
{
|
||||
con::error("Could not find file \"{}\".", zonePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto absoluteZoneDirectory = absolute(std::filesystem::path(zonePath).remove_filename()).string();
|
||||
|
||||
auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
|
||||
con::debug("Loaded zone \"{}\"", zone->m_name);
|
||||
|
||||
if (ShouldLoadObj())
|
||||
{
|
||||
const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game_id);
|
||||
objLoader->LoadReferencedContainersForZone(*searchPathsForZone, *zone);
|
||||
}
|
||||
|
||||
m_loaded_zones.emplace_back(std::move(zone));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void UnloadZones()
|
||||
{
|
||||
for (auto i = m_loaded_zones.rbegin(); i != m_loaded_zones.rend(); ++i)
|
||||
{
|
||||
auto& loadedZone = *i;
|
||||
|
||||
// Copy zone name since we deallocate before logging
|
||||
const auto zoneName = loadedZone->m_name;
|
||||
|
||||
if (ShouldLoadObj())
|
||||
{
|
||||
const auto* objLoader = IObjLoader::GetObjLoaderForGame(loadedZone->m_game_id);
|
||||
objLoader->UnloadContainersOfZone(*loadedZone);
|
||||
}
|
||||
|
||||
loadedZone.reset();
|
||||
|
||||
con::debug("Unloaded zone \"{}\"", zoneName);
|
||||
}
|
||||
m_loaded_zones.clear();
|
||||
}
|
||||
|
||||
bool UnlinkZones(UnlinkerPaths& paths) const
|
||||
{
|
||||
for (const auto& zonePath : m_args.m_zones_to_unlink)
|
||||
{
|
||||
if (!fs::is_regular_file(zonePath))
|
||||
{
|
||||
con::error("Could not find file \"{}\".", zonePath);
|
||||
continue;
|
||||
}
|
||||
|
||||
auto zoneDirectory = fs::path(zonePath).remove_filename();
|
||||
if (zoneDirectory.empty())
|
||||
zoneDirectory = fs::current_path();
|
||||
auto absoluteZoneDirectory = absolute(zoneDirectory).string();
|
||||
|
||||
auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
|
||||
con::debug("Loaded zone \"{}\"", zone->m_name);
|
||||
|
||||
const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game_id);
|
||||
if (ShouldLoadObj())
|
||||
objLoader->LoadReferencedContainersForZone(*searchPathsForZone, *zone);
|
||||
|
||||
if (!HandleZone(*searchPathsForZone, *zone))
|
||||
return false;
|
||||
|
||||
if (ShouldLoadObj())
|
||||
objLoader->UnloadContainersOfZone(*zone);
|
||||
|
||||
// Copy zone name for using it after freeing the zone
|
||||
std::string zoneName = zone->m_name;
|
||||
zone.reset();
|
||||
con::debug("Unloaded zone \"{}\"", zoneName);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
UnlinkerArgs m_args;
|
||||
std::vector<std::unique_ptr<Zone>> m_loaded_zones;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<Unlinker> Unlinker::Create(UnlinkerArgs args)
|
||||
{
|
||||
return std::make_unique<UnlinkerImpl>(std::move(args));
|
||||
}
|
||||
19
src/Unlinking/Unlinker.h
Normal file
19
src/Unlinking/Unlinker.h
Normal file
@@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
|
||||
#include "UnlinkerArgs.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class Unlinker
|
||||
{
|
||||
public:
|
||||
virtual ~Unlinker() = default;
|
||||
|
||||
static std::unique_ptr<Unlinker> Create(UnlinkerArgs args);
|
||||
|
||||
/**
|
||||
* \brief Starts the Unlinker application logic.
|
||||
* \return \c true if the application was successful or \c false if an error occurred.
|
||||
*/
|
||||
virtual bool Start() = 0;
|
||||
};
|
||||
@@ -225,6 +225,20 @@ function ZoneCode:allTestFiles()
|
||||
return result
|
||||
end
|
||||
|
||||
function ZoneCode:allMarkFiles()
|
||||
result = {}
|
||||
|
||||
for game, assets in pairs(self.Assets) do
|
||||
for i, assetName in ipairs(assets) do
|
||||
local assetNameLower = string.lower(assetName)
|
||||
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
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
function ZoneCode:allLoadFiles()
|
||||
result = {}
|
||||
|
||||
@@ -233,8 +247,6 @@ 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
|
||||
|
||||
|
||||
@@ -145,7 +145,9 @@ namespace
|
||||
LINE("")
|
||||
LINEF("#include \"{0}_load_db.h\"", Lower(m_env.m_asset->m_definition->m_name))
|
||||
LINE("")
|
||||
LINEF("#include \"{0}_mark_db.h\"", Lower(m_env.m_asset->m_definition->m_name))
|
||||
LINEF("#include \"Game/{0}/AssetMarker{0}.h\"", m_env.m_game)
|
||||
LINE("")
|
||||
LINE("#include \"Loading/AssetInfoCollector.h\"")
|
||||
|
||||
if (!m_env.m_referenced_assets.empty())
|
||||
{
|
||||
@@ -236,11 +238,6 @@ namespace
|
||||
return std::format("Loader_{0}", asset->m_definition->m_name);
|
||||
}
|
||||
|
||||
static std::string MarkerClassName(const StructureInformation* asset)
|
||||
{
|
||||
return std::format("Marker_{0}", asset->m_definition->m_name);
|
||||
}
|
||||
|
||||
static std::string VariableDecl(const DataDefinition* def)
|
||||
{
|
||||
return std::format("{0}* var{1};", def->GetFullName(), MakeSafeTypeName(def));
|
||||
@@ -2155,14 +2152,16 @@ namespace
|
||||
|
||||
LINE("assert(pAsset != nullptr);")
|
||||
LINE("")
|
||||
LINEF("{0} marker(m_zone);", MarkerClassName(m_env.m_asset))
|
||||
LINE("AssetInfoCollector assetInfo(m_zone);")
|
||||
LINEF("AssetMarker<{0}> marker(assetInfo);", m_env.m_asset->m_asset_name)
|
||||
LINE("marker.Mark(*pAsset);")
|
||||
LINE("")
|
||||
LINEF("auto* reallocatedAsset = m_zone.Memory().Alloc<{0}>();", info->m_definition->GetFullName())
|
||||
LINEF("std::memcpy(reallocatedAsset, *pAsset, sizeof({0}));", info->m_definition->GetFullName())
|
||||
LINE("")
|
||||
LINEF("m_asset_info = reinterpret_cast<XAssetInfo<{0}>*>(LinkAsset(AssetNameAccessor<{1}>()(**pAsset), reallocatedAsset, marker.GetDependencies(), "
|
||||
"marker.GetUsedScriptStrings(), marker.GetIndirectAssetReferences()));",
|
||||
LINEF("m_asset_info = reinterpret_cast<XAssetInfo<{0}>*>(LinkAsset(AssetName<{1}>(**pAsset), reallocatedAsset, "
|
||||
"assetInfo.GetDependencies(), "
|
||||
"assetInfo.GetUsedScriptStrings(), assetInfo.GetIndirectAssetReferences()));",
|
||||
info->m_definition->GetFullName(),
|
||||
info->m_asset_name)
|
||||
LINE("*pAsset = m_asset_info->Asset();")
|
||||
@@ -2186,7 +2185,7 @@ namespace
|
||||
LINE("")
|
||||
LINE("if (m_asset_info == nullptr && *pAsset != nullptr)")
|
||||
m_intendation++;
|
||||
LINEF("m_asset_info = reinterpret_cast<XAssetInfo<{0}>*>(GetAssetInfo(AssetNameAccessor<{1}>()(**pAsset)));",
|
||||
LINEF("m_asset_info = reinterpret_cast<XAssetInfo<{0}>*>(GetAssetInfo(AssetName<{1}>(**pAsset)));",
|
||||
m_env.m_asset->m_definition->GetFullName(),
|
||||
m_env.m_asset->m_asset_name)
|
||||
m_intendation--;
|
||||
|
||||
@@ -32,14 +32,15 @@ namespace
|
||||
LINE("#pragma once")
|
||||
LINE("")
|
||||
LINEF("#include \"Game/{0}/{0}.h\"", m_env.m_game)
|
||||
LINE("#include \"Loading/AssetMarker.h\"")
|
||||
LINE("#include \"Marking/BaseAssetMarker.h\"")
|
||||
LINE("#include \"Marking/AssetVisitor.h\"")
|
||||
LINE("")
|
||||
LINE("#include <string>")
|
||||
LINE("")
|
||||
LINEF("namespace {0}", m_env.m_game)
|
||||
LINE("{")
|
||||
m_intendation++;
|
||||
LINEF("class {0} final : public AssetMarker", MarkerClassName(m_env.m_asset))
|
||||
LINEF("class {0} final : public BaseAssetMarker", MarkerClassName(m_env.m_asset))
|
||||
LINE("{")
|
||||
m_intendation++;
|
||||
|
||||
@@ -48,7 +49,6 @@ namespace
|
||||
m_intendation++;
|
||||
PrintHeaderConstructor();
|
||||
PrintHeaderMainMarkMethodDeclaration(m_env.m_asset);
|
||||
PrintHeaderGetAssetInfoMethodDeclaration(m_env.m_asset);
|
||||
LINE("")
|
||||
|
||||
m_intendation--;
|
||||
@@ -107,6 +107,8 @@ namespace
|
||||
LINE("};")
|
||||
m_intendation--;
|
||||
LINE("}")
|
||||
LINE("")
|
||||
LINEF("DEFINE_MARKER_CLASS_FOR_ASSET({0}::{1}, {0}::{2})", m_env.m_game, m_env.m_asset->m_asset_name, MarkerClassName(m_env.m_asset))
|
||||
}
|
||||
|
||||
void Source()
|
||||
@@ -136,8 +138,6 @@ namespace
|
||||
PrintConstructorMethod();
|
||||
LINE("")
|
||||
PrintMainMarkMethod();
|
||||
LINE("")
|
||||
PrintGetAssetInfoMethod();
|
||||
|
||||
for (const auto* type : m_env.m_used_types)
|
||||
{
|
||||
@@ -210,14 +210,9 @@ namespace
|
||||
LINEF("void Mark_{0}();", MakeSafeTypeName(info->m_definition))
|
||||
}
|
||||
|
||||
void PrintHeaderGetAssetInfoMethodDeclaration(const StructureInformation* info) const
|
||||
{
|
||||
LINEF("XAssetInfo<{0}>* GetAssetInfo({0}* pAsset) const;", info->m_definition->GetFullName())
|
||||
}
|
||||
|
||||
void PrintHeaderConstructor() const
|
||||
{
|
||||
LINEF("{0}(Zone& zone);", MarkerClassName(m_env.m_asset))
|
||||
LINEF("explicit {0}(AssetVisitor& visitor);", MarkerClassName(m_env.m_asset))
|
||||
}
|
||||
|
||||
void PrintHeaderMainMarkMethodDeclaration(const StructureInformation* info) const
|
||||
@@ -237,10 +232,10 @@ namespace
|
||||
|
||||
void PrintConstructorMethod()
|
||||
{
|
||||
LINEF("{0}::{0}(Zone& zone)", MarkerClassName(m_env.m_asset))
|
||||
LINEF("{0}::{0}(AssetVisitor& visitor)", MarkerClassName(m_env.m_asset))
|
||||
|
||||
m_intendation++;
|
||||
LINEF(": AssetMarker({0}::EnumEntry, zone)", m_env.m_asset->m_asset_name)
|
||||
LINE(": BaseAssetMarker(visitor)")
|
||||
m_intendation--;
|
||||
|
||||
LINE("{")
|
||||
@@ -286,7 +281,7 @@ namespace
|
||||
|
||||
if (info && StructureComputations(info).IsAsset())
|
||||
{
|
||||
LINEF("AddDependency({0}(m_zone).GetAssetInfo(*{1}));", MarkerClassName(info), MakeTypePtrVarName(def))
|
||||
LINEF("Mark_Dependency<{0}>(*{1});", info->m_asset_name, MakeTypePtrVarName(def))
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -413,7 +408,7 @@ namespace
|
||||
{
|
||||
if (loadType == MemberLoadType::SINGLE_POINTER)
|
||||
{
|
||||
LINEF("AddDependency({0}(m_zone).GetAssetInfo({1}));", MarkerClassName(member->m_type), MakeMemberAccess(info, member, modifier))
|
||||
LINEF("Mark_Dependency<{0}>({1});", member->m_type->m_asset_name, MakeMemberAccess(info, member, modifier))
|
||||
}
|
||||
else if (loadType == MemberLoadType::POINTER_ARRAY)
|
||||
{
|
||||
@@ -752,20 +747,6 @@ namespace
|
||||
LINE("}")
|
||||
}
|
||||
|
||||
void PrintGetAssetInfoMethod()
|
||||
{
|
||||
LINEF("XAssetInfo<{0}>* {1}::GetAssetInfo({0}* pAsset) const", m_env.m_asset->m_definition->GetFullName(), MarkerClassName(m_env.m_asset))
|
||||
LINE("{")
|
||||
m_intendation++;
|
||||
|
||||
LINEF("return reinterpret_cast<XAssetInfo<{0}>*>(GetAssetInfoByName(AssetNameAccessor<{1}>()(*pAsset)));",
|
||||
m_env.m_asset->m_definition->GetFullName(),
|
||||
m_env.m_asset->m_asset_name)
|
||||
|
||||
m_intendation--;
|
||||
LINE("}")
|
||||
}
|
||||
|
||||
void PrintMainMarkMethod()
|
||||
{
|
||||
LINEF("void {0}::Mark({1}* pAsset)", MarkerClassName(m_env.m_asset), m_env.m_asset->m_definition->GetFullName())
|
||||
|
||||
@@ -261,7 +261,7 @@ namespace
|
||||
"{0}::{0}({1}* asset, const Zone& zone, IZoneOutputStream& stream)", WriterClassName(m_env.m_asset), m_env.m_asset->m_definition->GetFullName())
|
||||
|
||||
m_intendation++;
|
||||
LINEF(": AssetWriter(zone.m_pools->GetAssetOrAssetReference({0}::EnumEntry, AssetNameAccessor<{0}>()(*asset)), zone, stream)",
|
||||
LINEF(": AssetWriter(zone.m_pools->GetAssetOrAssetReference({0}::EnumEntry, NonReferenceAssetName(AssetName<{0}>(*asset))), zone, stream)",
|
||||
m_env.m_asset->m_asset_name)
|
||||
m_intendation--;
|
||||
|
||||
|
||||
@@ -10,6 +10,7 @@ function ZoneCommon:include(includes)
|
||||
ObjCommon:include(includes)
|
||||
Parser:include(includes)
|
||||
Cryptography:include(includes)
|
||||
ZoneCode:include(includes)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -21,6 +22,7 @@ function ZoneCommon:link(links)
|
||||
links:linkto(Parser)
|
||||
links:linkto(Utils)
|
||||
links:linkto(lzx)
|
||||
ZoneCode:use()
|
||||
end
|
||||
|
||||
function ZoneCommon:use()
|
||||
@@ -43,9 +45,20 @@ function ZoneCommon:project()
|
||||
|
||||
files {
|
||||
path.join(folder, "ZoneCommon/**.h"),
|
||||
path.join(folder, "ZoneCommon/**.cpp")
|
||||
path.join(folder, "ZoneCommon/**.cpp"),
|
||||
ZoneCode:allMarkFiles()
|
||||
}
|
||||
|
||||
vpaths {
|
||||
["*"] = {
|
||||
path.join(folder, "ZoneCommon"),
|
||||
path.join(BuildFolder(), "src/ZoneCode")
|
||||
}
|
||||
}
|
||||
|
||||
lzx:include(includes)
|
||||
self:include(includes)
|
||||
lzx:include(includes)
|
||||
ZoneCode:include(includes)
|
||||
|
||||
ZoneCode:use()
|
||||
end
|
||||
|
||||
27
src/ZoneCommon/Game/IW3/AssetMarkerIW3.h
Normal file
27
src/ZoneCommon/Game/IW3/AssetMarkerIW3.h
Normal file
@@ -0,0 +1,27 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/IW3/XAssets/clipmap_t/clipmap_t_mark_db.h"
|
||||
#include "Game/IW3/XAssets/comworld/comworld_mark_db.h"
|
||||
#include "Game/IW3/XAssets/font_s/font_s_mark_db.h"
|
||||
#include "Game/IW3/XAssets/fxeffectdef/fxeffectdef_mark_db.h"
|
||||
#include "Game/IW3/XAssets/fximpacttable/fximpacttable_mark_db.h"
|
||||
#include "Game/IW3/XAssets/gameworldmp/gameworldmp_mark_db.h"
|
||||
#include "Game/IW3/XAssets/gameworldsp/gameworldsp_mark_db.h"
|
||||
#include "Game/IW3/XAssets/gfximage/gfximage_mark_db.h"
|
||||
#include "Game/IW3/XAssets/gfxlightdef/gfxlightdef_mark_db.h"
|
||||
#include "Game/IW3/XAssets/gfxworld/gfxworld_mark_db.h"
|
||||
#include "Game/IW3/XAssets/loadedsound/loadedsound_mark_db.h"
|
||||
#include "Game/IW3/XAssets/localizeentry/localizeentry_mark_db.h"
|
||||
#include "Game/IW3/XAssets/mapents/mapents_mark_db.h"
|
||||
#include "Game/IW3/XAssets/material/material_mark_db.h"
|
||||
#include "Game/IW3/XAssets/materialtechniqueset/materialtechniqueset_mark_db.h"
|
||||
#include "Game/IW3/XAssets/menudef_t/menudef_t_mark_db.h"
|
||||
#include "Game/IW3/XAssets/menulist/menulist_mark_db.h"
|
||||
#include "Game/IW3/XAssets/physpreset/physpreset_mark_db.h"
|
||||
#include "Game/IW3/XAssets/rawfile/rawfile_mark_db.h"
|
||||
#include "Game/IW3/XAssets/snd_alias_list_t/snd_alias_list_t_mark_db.h"
|
||||
#include "Game/IW3/XAssets/sndcurve/sndcurve_mark_db.h"
|
||||
#include "Game/IW3/XAssets/stringtable/stringtable_mark_db.h"
|
||||
#include "Game/IW3/XAssets/weapondef/weapondef_mark_db.h"
|
||||
#include "Game/IW3/XAssets/xanimparts/xanimparts_mark_db.h"
|
||||
#include "Game/IW3/XAssets/xmodel/xmodel_mark_db.h"
|
||||
37
src/ZoneCommon/Game/IW4/AssetMarkerIW4.h
Normal file
37
src/ZoneCommon/Game/IW4/AssetMarkerIW4.h
Normal file
@@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/IW4/XAssets/addonmapents/addonmapents_mark_db.h"
|
||||
#include "Game/IW4/XAssets/clipmap_t/clipmap_t_mark_db.h"
|
||||
#include "Game/IW4/XAssets/comworld/comworld_mark_db.h"
|
||||
#include "Game/IW4/XAssets/font_s/font_s_mark_db.h"
|
||||
#include "Game/IW4/XAssets/fxeffectdef/fxeffectdef_mark_db.h"
|
||||
#include "Game/IW4/XAssets/fximpacttable/fximpacttable_mark_db.h"
|
||||
#include "Game/IW4/XAssets/fxworld/fxworld_mark_db.h"
|
||||
#include "Game/IW4/XAssets/gameworldmp/gameworldmp_mark_db.h"
|
||||
#include "Game/IW4/XAssets/gameworldsp/gameworldsp_mark_db.h"
|
||||
#include "Game/IW4/XAssets/gfximage/gfximage_mark_db.h"
|
||||
#include "Game/IW4/XAssets/gfxlightdef/gfxlightdef_mark_db.h"
|
||||
#include "Game/IW4/XAssets/gfxworld/gfxworld_mark_db.h"
|
||||
#include "Game/IW4/XAssets/leaderboarddef/leaderboarddef_mark_db.h"
|
||||
#include "Game/IW4/XAssets/loadedsound/loadedsound_mark_db.h"
|
||||
#include "Game/IW4/XAssets/localizeentry/localizeentry_mark_db.h"
|
||||
#include "Game/IW4/XAssets/mapents/mapents_mark_db.h"
|
||||
#include "Game/IW4/XAssets/material/material_mark_db.h"
|
||||
#include "Game/IW4/XAssets/materialpixelshader/materialpixelshader_mark_db.h"
|
||||
#include "Game/IW4/XAssets/materialtechniqueset/materialtechniqueset_mark_db.h"
|
||||
#include "Game/IW4/XAssets/materialvertexdeclaration/materialvertexdeclaration_mark_db.h"
|
||||
#include "Game/IW4/XAssets/materialvertexshader/materialvertexshader_mark_db.h"
|
||||
#include "Game/IW4/XAssets/menudef_t/menudef_t_mark_db.h"
|
||||
#include "Game/IW4/XAssets/menulist/menulist_mark_db.h"
|
||||
#include "Game/IW4/XAssets/physcollmap/physcollmap_mark_db.h"
|
||||
#include "Game/IW4/XAssets/physpreset/physpreset_mark_db.h"
|
||||
#include "Game/IW4/XAssets/rawfile/rawfile_mark_db.h"
|
||||
#include "Game/IW4/XAssets/snd_alias_list_t/snd_alias_list_t_mark_db.h"
|
||||
#include "Game/IW4/XAssets/sndcurve/sndcurve_mark_db.h"
|
||||
#include "Game/IW4/XAssets/stringtable/stringtable_mark_db.h"
|
||||
#include "Game/IW4/XAssets/structureddatadefset/structureddatadefset_mark_db.h"
|
||||
#include "Game/IW4/XAssets/tracerdef/tracerdef_mark_db.h"
|
||||
#include "Game/IW4/XAssets/vehicledef/vehicledef_mark_db.h"
|
||||
#include "Game/IW4/XAssets/weaponcompletedef/weaponcompletedef_mark_db.h"
|
||||
#include "Game/IW4/XAssets/xanimparts/xanimparts_mark_db.h"
|
||||
#include "Game/IW4/XAssets/xmodel/xmodel_mark_db.h"
|
||||
42
src/ZoneCommon/Game/IW5/AssetMarkerIW5.h
Normal file
42
src/ZoneCommon/Game/IW5/AssetMarkerIW5.h
Normal file
@@ -0,0 +1,42 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/IW5/XAssets/addonmapents/addonmapents_mark_db.h"
|
||||
#include "Game/IW5/XAssets/clipmap_t/clipmap_t_mark_db.h"
|
||||
#include "Game/IW5/XAssets/comworld/comworld_mark_db.h"
|
||||
#include "Game/IW5/XAssets/font_s/font_s_mark_db.h"
|
||||
#include "Game/IW5/XAssets/fxeffectdef/fxeffectdef_mark_db.h"
|
||||
#include "Game/IW5/XAssets/fximpacttable/fximpacttable_mark_db.h"
|
||||
#include "Game/IW5/XAssets/fxworld/fxworld_mark_db.h"
|
||||
#include "Game/IW5/XAssets/gfximage/gfximage_mark_db.h"
|
||||
#include "Game/IW5/XAssets/gfxlightdef/gfxlightdef_mark_db.h"
|
||||
#include "Game/IW5/XAssets/gfxworld/gfxworld_mark_db.h"
|
||||
#include "Game/IW5/XAssets/glassworld/glassworld_mark_db.h"
|
||||
#include "Game/IW5/XAssets/leaderboarddef/leaderboarddef_mark_db.h"
|
||||
#include "Game/IW5/XAssets/loadedsound/loadedsound_mark_db.h"
|
||||
#include "Game/IW5/XAssets/localizeentry/localizeentry_mark_db.h"
|
||||
#include "Game/IW5/XAssets/mapents/mapents_mark_db.h"
|
||||
#include "Game/IW5/XAssets/material/material_mark_db.h"
|
||||
#include "Game/IW5/XAssets/materialpixelshader/materialpixelshader_mark_db.h"
|
||||
#include "Game/IW5/XAssets/materialtechniqueset/materialtechniqueset_mark_db.h"
|
||||
#include "Game/IW5/XAssets/materialvertexdeclaration/materialvertexdeclaration_mark_db.h"
|
||||
#include "Game/IW5/XAssets/materialvertexshader/materialvertexshader_mark_db.h"
|
||||
#include "Game/IW5/XAssets/menudef_t/menudef_t_mark_db.h"
|
||||
#include "Game/IW5/XAssets/menulist/menulist_mark_db.h"
|
||||
#include "Game/IW5/XAssets/pathdata/pathdata_mark_db.h"
|
||||
#include "Game/IW5/XAssets/physcollmap/physcollmap_mark_db.h"
|
||||
#include "Game/IW5/XAssets/physpreset/physpreset_mark_db.h"
|
||||
#include "Game/IW5/XAssets/rawfile/rawfile_mark_db.h"
|
||||
#include "Game/IW5/XAssets/scriptfile/scriptfile_mark_db.h"
|
||||
#include "Game/IW5/XAssets/snd_alias_list_t/snd_alias_list_t_mark_db.h"
|
||||
#include "Game/IW5/XAssets/sndcurve/sndcurve_mark_db.h"
|
||||
#include "Game/IW5/XAssets/stringtable/stringtable_mark_db.h"
|
||||
#include "Game/IW5/XAssets/structureddatadefset/structureddatadefset_mark_db.h"
|
||||
#include "Game/IW5/XAssets/surfacefxtable/surfacefxtable_mark_db.h"
|
||||
#include "Game/IW5/XAssets/tracerdef/tracerdef_mark_db.h"
|
||||
#include "Game/IW5/XAssets/vehicledef/vehicledef_mark_db.h"
|
||||
#include "Game/IW5/XAssets/vehicletrack/vehicletrack_mark_db.h"
|
||||
#include "Game/IW5/XAssets/weaponattachment/weaponattachment_mark_db.h"
|
||||
#include "Game/IW5/XAssets/weaponcompletedef/weaponcompletedef_mark_db.h"
|
||||
#include "Game/IW5/XAssets/xanimparts/xanimparts_mark_db.h"
|
||||
#include "Game/IW5/XAssets/xmodel/xmodel_mark_db.h"
|
||||
#include "Game/IW5/XAssets/xmodelsurfs/xmodelsurfs_mark_db.h"
|
||||
34
src/ZoneCommon/Game/T5/AssetMarkerT5.h
Normal file
34
src/ZoneCommon/Game/T5/AssetMarkerT5.h
Normal file
@@ -0,0 +1,34 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/T5/XAssets/clipmap_t/clipmap_t_mark_db.h"
|
||||
#include "Game/T5/XAssets/comworld/comworld_mark_db.h"
|
||||
#include "Game/T5/XAssets/ddlroot_t/ddlroot_t_mark_db.h"
|
||||
#include "Game/T5/XAssets/destructibledef/destructibledef_mark_db.h"
|
||||
#include "Game/T5/XAssets/emblemset/emblemset_mark_db.h"
|
||||
#include "Game/T5/XAssets/font_s/font_s_mark_db.h"
|
||||
#include "Game/T5/XAssets/fxeffectdef/fxeffectdef_mark_db.h"
|
||||
#include "Game/T5/XAssets/fximpacttable/fximpacttable_mark_db.h"
|
||||
#include "Game/T5/XAssets/gameworldmp/gameworldmp_mark_db.h"
|
||||
#include "Game/T5/XAssets/gameworldsp/gameworldsp_mark_db.h"
|
||||
#include "Game/T5/XAssets/gfximage/gfximage_mark_db.h"
|
||||
#include "Game/T5/XAssets/gfxlightdef/gfxlightdef_mark_db.h"
|
||||
#include "Game/T5/XAssets/gfxworld/gfxworld_mark_db.h"
|
||||
#include "Game/T5/XAssets/glasses/glasses_mark_db.h"
|
||||
#include "Game/T5/XAssets/localizeentry/localizeentry_mark_db.h"
|
||||
#include "Game/T5/XAssets/mapents/mapents_mark_db.h"
|
||||
#include "Game/T5/XAssets/material/material_mark_db.h"
|
||||
#include "Game/T5/XAssets/materialtechniqueset/materialtechniqueset_mark_db.h"
|
||||
#include "Game/T5/XAssets/menudef_t/menudef_t_mark_db.h"
|
||||
#include "Game/T5/XAssets/menulist/menulist_mark_db.h"
|
||||
#include "Game/T5/XAssets/packindex/packindex_mark_db.h"
|
||||
#include "Game/T5/XAssets/physconstraints/physconstraints_mark_db.h"
|
||||
#include "Game/T5/XAssets/physpreset/physpreset_mark_db.h"
|
||||
#include "Game/T5/XAssets/rawfile/rawfile_mark_db.h"
|
||||
#include "Game/T5/XAssets/sndbank/sndbank_mark_db.h"
|
||||
#include "Game/T5/XAssets/snddriverglobals/snddriverglobals_mark_db.h"
|
||||
#include "Game/T5/XAssets/sndpatch/sndpatch_mark_db.h"
|
||||
#include "Game/T5/XAssets/stringtable/stringtable_mark_db.h"
|
||||
#include "Game/T5/XAssets/weaponvariantdef/weaponvariantdef_mark_db.h"
|
||||
#include "Game/T5/XAssets/xanimparts/xanimparts_mark_db.h"
|
||||
#include "Game/T5/XAssets/xglobals/xglobals_mark_db.h"
|
||||
#include "Game/T5/XAssets/xmodel/xmodel_mark_db.h"
|
||||
50
src/ZoneCommon/Game/T6/AssetMarkerT6.h
Normal file
50
src/ZoneCommon/Game/T6/AssetMarkerT6.h
Normal file
@@ -0,0 +1,50 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/T6/XAssets/addonmapents/addonmapents_mark_db.h"
|
||||
#include "Game/T6/XAssets/clipmap_t/clipmap_t_mark_db.h"
|
||||
#include "Game/T6/XAssets/comworld/comworld_mark_db.h"
|
||||
#include "Game/T6/XAssets/ddlroot_t/ddlroot_t_mark_db.h"
|
||||
#include "Game/T6/XAssets/destructibledef/destructibledef_mark_db.h"
|
||||
#include "Game/T6/XAssets/emblemset/emblemset_mark_db.h"
|
||||
#include "Game/T6/XAssets/font_s/font_s_mark_db.h"
|
||||
#include "Game/T6/XAssets/fonticon/fonticon_mark_db.h"
|
||||
#include "Game/T6/XAssets/footstepfxtabledef/footstepfxtabledef_mark_db.h"
|
||||
#include "Game/T6/XAssets/footsteptabledef/footsteptabledef_mark_db.h"
|
||||
#include "Game/T6/XAssets/fxeffectdef/fxeffectdef_mark_db.h"
|
||||
#include "Game/T6/XAssets/fximpacttable/fximpacttable_mark_db.h"
|
||||
#include "Game/T6/XAssets/gameworldmp/gameworldmp_mark_db.h"
|
||||
#include "Game/T6/XAssets/gameworldsp/gameworldsp_mark_db.h"
|
||||
#include "Game/T6/XAssets/gfximage/gfximage_mark_db.h"
|
||||
#include "Game/T6/XAssets/gfxlightdef/gfxlightdef_mark_db.h"
|
||||
#include "Game/T6/XAssets/gfxworld/gfxworld_mark_db.h"
|
||||
#include "Game/T6/XAssets/glasses/glasses_mark_db.h"
|
||||
#include "Game/T6/XAssets/keyvaluepairs/keyvaluepairs_mark_db.h"
|
||||
#include "Game/T6/XAssets/leaderboarddef/leaderboarddef_mark_db.h"
|
||||
#include "Game/T6/XAssets/localizeentry/localizeentry_mark_db.h"
|
||||
#include "Game/T6/XAssets/mapents/mapents_mark_db.h"
|
||||
#include "Game/T6/XAssets/material/material_mark_db.h"
|
||||
#include "Game/T6/XAssets/materialtechniqueset/materialtechniqueset_mark_db.h"
|
||||
#include "Game/T6/XAssets/memoryblock/memoryblock_mark_db.h"
|
||||
#include "Game/T6/XAssets/menudef_t/menudef_t_mark_db.h"
|
||||
#include "Game/T6/XAssets/menulist/menulist_mark_db.h"
|
||||
#include "Game/T6/XAssets/physconstraints/physconstraints_mark_db.h"
|
||||
#include "Game/T6/XAssets/physpreset/physpreset_mark_db.h"
|
||||
#include "Game/T6/XAssets/qdb/qdb_mark_db.h"
|
||||
#include "Game/T6/XAssets/rawfile/rawfile_mark_db.h"
|
||||
#include "Game/T6/XAssets/scriptparsetree/scriptparsetree_mark_db.h"
|
||||
#include "Game/T6/XAssets/skinnedvertsdef/skinnedvertsdef_mark_db.h"
|
||||
#include "Game/T6/XAssets/slug/slug_mark_db.h"
|
||||
#include "Game/T6/XAssets/sndbank/sndbank_mark_db.h"
|
||||
#include "Game/T6/XAssets/snddriverglobals/snddriverglobals_mark_db.h"
|
||||
#include "Game/T6/XAssets/sndpatch/sndpatch_mark_db.h"
|
||||
#include "Game/T6/XAssets/stringtable/stringtable_mark_db.h"
|
||||
#include "Game/T6/XAssets/tracerdef/tracerdef_mark_db.h"
|
||||
#include "Game/T6/XAssets/vehicledef/vehicledef_mark_db.h"
|
||||
#include "Game/T6/XAssets/weaponattachment/weaponattachment_mark_db.h"
|
||||
#include "Game/T6/XAssets/weaponattachmentunique/weaponattachmentunique_mark_db.h"
|
||||
#include "Game/T6/XAssets/weaponcamo/weaponcamo_mark_db.h"
|
||||
#include "Game/T6/XAssets/weaponvariantdef/weaponvariantdef_mark_db.h"
|
||||
#include "Game/T6/XAssets/xanimparts/xanimparts_mark_db.h"
|
||||
#include "Game/T6/XAssets/xglobals/xglobals_mark_db.h"
|
||||
#include "Game/T6/XAssets/xmodel/xmodel_mark_db.h"
|
||||
#include "Game/T6/XAssets/zbarrierdef/zbarrierdef_mark_db.h"
|
||||
29
src/ZoneCommon/Marking/AssetVisitor.h
Normal file
29
src/ZoneCommon/Marking/AssetVisitor.h
Normal file
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include "Pool/XAssetInfo.h"
|
||||
#include "Zone/ZoneTypes.h"
|
||||
|
||||
#include <optional>
|
||||
|
||||
class AssetVisitor
|
||||
{
|
||||
public:
|
||||
virtual ~AssetVisitor() = default;
|
||||
|
||||
virtual std::optional<XAssetInfoGeneric*> Visit_Dependency(asset_type_t assetType, const char* assetName)
|
||||
{
|
||||
// Do nothing by default
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
virtual std::optional<scr_string_t> Visit_ScriptString(scr_string_t scriptString)
|
||||
{
|
||||
// Do nothing by default
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
virtual void Visit_IndirectAssetRef(asset_type_t assetType, const char* assetName)
|
||||
{
|
||||
// Do nothing by default
|
||||
}
|
||||
};
|
||||
40
src/ZoneCommon/Marking/BaseAssetMarker.cpp
Normal file
40
src/ZoneCommon/Marking/BaseAssetMarker.cpp
Normal file
@@ -0,0 +1,40 @@
|
||||
#include "BaseAssetMarker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
BaseAssetMarker::BaseAssetMarker(AssetVisitor& visitor)
|
||||
: m_visitor(visitor)
|
||||
{
|
||||
}
|
||||
|
||||
void BaseAssetMarker::Mark_ScriptString(scr_string_t& scriptString) const
|
||||
{
|
||||
const auto result = m_visitor.Visit_ScriptString(scriptString);
|
||||
if (result.has_value())
|
||||
scriptString = *result;
|
||||
}
|
||||
|
||||
void BaseAssetMarker::MarkArray_ScriptString(scr_string_t* scriptStringArray, const size_t count) const
|
||||
{
|
||||
assert(scriptStringArray != nullptr);
|
||||
|
||||
for (size_t index = 0; index < count; index++)
|
||||
Mark_ScriptString(scriptStringArray[index]);
|
||||
}
|
||||
|
||||
void BaseAssetMarker::Mark_IndirectAssetRef(const asset_type_t assetType, const char* assetName) const
|
||||
{
|
||||
if (!assetName)
|
||||
return;
|
||||
|
||||
m_visitor.Visit_IndirectAssetRef(assetType, assetName);
|
||||
}
|
||||
|
||||
void BaseAssetMarker::MarkArray_IndirectAssetRef(const asset_type_t assetType, const char** assetNames, const size_t count) const
|
||||
{
|
||||
assert(assetNames != nullptr);
|
||||
|
||||
for (size_t index = 0; index < count; index++)
|
||||
Mark_IndirectAssetRef(assetType, assetNames[index]);
|
||||
}
|
||||
45
src/ZoneCommon/Marking/BaseAssetMarker.h
Normal file
45
src/ZoneCommon/Marking/BaseAssetMarker.h
Normal file
@@ -0,0 +1,45 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/IAsset.h"
|
||||
#include "Marking/AssetVisitor.h"
|
||||
#include "Zone/ZoneTypes.h"
|
||||
|
||||
#include <type_traits>
|
||||
|
||||
class BaseAssetMarker
|
||||
{
|
||||
protected:
|
||||
explicit BaseAssetMarker(AssetVisitor& visitor);
|
||||
|
||||
template<typename AssetType> void Mark_Dependency(std::add_lvalue_reference_t<std::add_pointer_t<typename AssetType::Type>> asset)
|
||||
{
|
||||
static_assert(std::is_base_of_v<IAssetBase, AssetType>);
|
||||
|
||||
const auto result = m_visitor.Visit_Dependency(AssetType::EnumEntry, AssetName<AssetType>(*asset));
|
||||
if (result.has_value())
|
||||
asset = static_cast<std::add_pointer_t<typename AssetType::Type>>((*result)->m_ptr);
|
||||
}
|
||||
|
||||
void Mark_ScriptString(scr_string_t& scriptString) const;
|
||||
void MarkArray_ScriptString(scr_string_t* scriptStringArray, size_t count) const;
|
||||
|
||||
void Mark_IndirectAssetRef(asset_type_t assetType, const char* assetName) const;
|
||||
void MarkArray_IndirectAssetRef(asset_type_t assetType, const char** assetNames, size_t count) const;
|
||||
|
||||
AssetVisitor& m_visitor;
|
||||
};
|
||||
|
||||
template<typename AssetType> struct AssetMarkerWrapper
|
||||
{
|
||||
static_assert(std::is_base_of_v<IAssetBase, AssetType>);
|
||||
// using WrapperClass = WrapperClass;
|
||||
};
|
||||
|
||||
template<typename AssetType> using AssetMarker = AssetMarkerWrapper<AssetType>::WrapperClass;
|
||||
|
||||
#define DEFINE_MARKER_CLASS_FOR_ASSET(asset, markerClass) \
|
||||
template<> struct AssetMarkerWrapper<asset> \
|
||||
{ \
|
||||
static_assert(std::is_base_of_v<IAssetBase, asset>); \
|
||||
using WrapperClass = markerClass; \
|
||||
};
|
||||
85
src/ZoneLoading/Loading/AssetInfoCollector.cpp
Normal file
85
src/ZoneLoading/Loading/AssetInfoCollector.cpp
Normal file
@@ -0,0 +1,85 @@
|
||||
#include "AssetInfoCollector.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
AssetInfoCollector::AssetInfoCollector(Zone& zone)
|
||||
: m_zone(zone)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<XAssetInfoGeneric*> AssetInfoCollector::GetDependencies() const
|
||||
{
|
||||
std::vector<XAssetInfoGeneric*> dependencies;
|
||||
if (!m_dependencies.empty())
|
||||
{
|
||||
dependencies.reserve(m_dependencies.size());
|
||||
for (auto dependency : m_dependencies)
|
||||
dependencies.push_back(dependency);
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
std::vector<scr_string_t> AssetInfoCollector::GetUsedScriptStrings() const
|
||||
{
|
||||
std::vector<scr_string_t> 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::ranges::sort(usedScriptStrings);
|
||||
}
|
||||
|
||||
return usedScriptStrings;
|
||||
}
|
||||
|
||||
std::vector<IndirectAssetReference> AssetInfoCollector::GetIndirectAssetReferences() const
|
||||
{
|
||||
std::vector<IndirectAssetReference> assetReferences;
|
||||
if (!m_indirect_asset_references.empty())
|
||||
{
|
||||
assetReferences.reserve(m_indirect_asset_references.size());
|
||||
for (const auto& assetReference : m_indirect_asset_references)
|
||||
assetReferences.emplace_back(assetReference);
|
||||
}
|
||||
|
||||
return assetReferences;
|
||||
}
|
||||
|
||||
std::optional<XAssetInfoGeneric*> AssetInfoCollector::Visit_Dependency(const asset_type_t assetType, const char* assetName)
|
||||
{
|
||||
auto* assetInfo = m_zone.m_pools->GetAsset(assetType, assetName);
|
||||
if (assetInfo == nullptr)
|
||||
return std::nullopt;
|
||||
|
||||
const auto existingEntry = m_dependencies.find(assetInfo);
|
||||
if (existingEntry != m_dependencies.end())
|
||||
return std::nullopt;
|
||||
|
||||
m_dependencies.emplace(assetInfo);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::optional<scr_string_t> AssetInfoCollector::Visit_ScriptString(scr_string_t scriptString)
|
||||
{
|
||||
assert(scriptString < m_zone.m_script_strings.Count());
|
||||
|
||||
if (scriptString >= m_zone.m_script_strings.Count())
|
||||
return std::nullopt;
|
||||
|
||||
m_used_script_strings.emplace(scriptString);
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void AssetInfoCollector::Visit_IndirectAssetRef(asset_type_t assetType, const char* assetName)
|
||||
{
|
||||
if (!assetName || !assetName[0])
|
||||
return;
|
||||
|
||||
m_indirect_asset_references.emplace(assetType, assetName);
|
||||
}
|
||||
31
src/ZoneLoading/Loading/AssetInfoCollector.h
Normal file
31
src/ZoneLoading/Loading/AssetInfoCollector.h
Normal file
@@ -0,0 +1,31 @@
|
||||
#pragma once
|
||||
|
||||
#include "Marking/AssetVisitor.h"
|
||||
#include "Pool/XAssetInfo.h"
|
||||
#include "Zone/ZoneTypes.h"
|
||||
|
||||
#include <optional>
|
||||
#include <unordered_set>
|
||||
#include <vector>
|
||||
|
||||
class AssetInfoCollector : public AssetVisitor
|
||||
{
|
||||
public:
|
||||
explicit AssetInfoCollector(Zone& zone);
|
||||
~AssetInfoCollector() override = default;
|
||||
|
||||
[[nodiscard]] std::vector<XAssetInfoGeneric*> GetDependencies() const;
|
||||
[[nodiscard]] std::vector<scr_string_t> GetUsedScriptStrings() const;
|
||||
[[nodiscard]] std::vector<IndirectAssetReference> GetIndirectAssetReferences() const;
|
||||
|
||||
std::optional<XAssetInfoGeneric*> Visit_Dependency(asset_type_t assetType, const char* assetName) override;
|
||||
std::optional<scr_string_t> Visit_ScriptString(scr_string_t scriptString) override;
|
||||
void Visit_IndirectAssetRef(asset_type_t assetType, const char* assetName) override;
|
||||
|
||||
private:
|
||||
std::unordered_set<XAssetInfoGeneric*> m_dependencies;
|
||||
std::unordered_set<scr_string_t> m_used_script_strings;
|
||||
std::unordered_set<IndirectAssetReference> m_indirect_asset_references;
|
||||
|
||||
Zone& m_zone;
|
||||
};
|
||||
@@ -1,102 +0,0 @@
|
||||
#include "AssetMarker.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <cassert>
|
||||
|
||||
AssetMarker::AssetMarker(const asset_type_t assetType, Zone& zone)
|
||||
: m_zone(zone),
|
||||
m_asset_type(assetType)
|
||||
{
|
||||
}
|
||||
|
||||
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]);
|
||||
}
|
||||
|
||||
void AssetMarker::Mark_IndirectAssetRef(asset_type_t type, const char* assetRefName)
|
||||
{
|
||||
if (!assetRefName || !assetRefName[0])
|
||||
return;
|
||||
|
||||
m_indirect_asset_references.emplace(type, assetRefName);
|
||||
}
|
||||
|
||||
void AssetMarker::MarkArray_IndirectAssetRef(const asset_type_t type, const char** assetRefNames, const size_t count)
|
||||
{
|
||||
assert(assetRefNames != nullptr);
|
||||
|
||||
for (size_t index = 0; index < count; index++)
|
||||
Mark_IndirectAssetRef(type, assetRefNames[index]);
|
||||
}
|
||||
|
||||
XAssetInfoGeneric* AssetMarker::GetAssetInfoByName(const std::string& name) const
|
||||
{
|
||||
return m_zone.m_pools->GetAsset(m_asset_type, name);
|
||||
}
|
||||
|
||||
std::vector<XAssetInfoGeneric*> AssetMarker::GetDependencies() const
|
||||
{
|
||||
std::vector<XAssetInfoGeneric*> dependencies;
|
||||
if (!m_dependencies.empty())
|
||||
{
|
||||
dependencies.reserve(m_dependencies.size());
|
||||
for (auto dependency : m_dependencies)
|
||||
dependencies.push_back(dependency);
|
||||
}
|
||||
|
||||
return dependencies;
|
||||
}
|
||||
|
||||
std::vector<scr_string_t> AssetMarker::GetUsedScriptStrings() const
|
||||
{
|
||||
std::vector<scr_string_t> 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::ranges::sort(usedScriptStrings);
|
||||
}
|
||||
|
||||
return usedScriptStrings;
|
||||
}
|
||||
|
||||
std::vector<IndirectAssetReference> AssetMarker::GetIndirectAssetReferences() const
|
||||
{
|
||||
std::vector<IndirectAssetReference> assetReferences;
|
||||
if (!m_indirect_asset_references.empty())
|
||||
{
|
||||
assetReferences.reserve(m_indirect_asset_references.size());
|
||||
for (const auto& assetReference : m_indirect_asset_references)
|
||||
assetReferences.emplace_back(assetReference);
|
||||
}
|
||||
|
||||
return assetReferences;
|
||||
}
|
||||
@@ -1,37 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ContentLoaderBase.h"
|
||||
#include "Pool/XAssetInfo.h"
|
||||
#include "Zone/ZoneTypes.h"
|
||||
|
||||
#include <unordered_set>
|
||||
|
||||
class AssetMarker
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] std::vector<XAssetInfoGeneric*> GetDependencies() const;
|
||||
[[nodiscard]] std::vector<scr_string_t> GetUsedScriptStrings() const;
|
||||
[[nodiscard]] std::vector<IndirectAssetReference> GetIndirectAssetReferences() const;
|
||||
|
||||
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);
|
||||
|
||||
void Mark_IndirectAssetRef(asset_type_t type, const char* assetRefName);
|
||||
void MarkArray_IndirectAssetRef(asset_type_t type, const char** assetRefNames, size_t count);
|
||||
|
||||
[[nodiscard]] XAssetInfoGeneric* GetAssetInfoByName(const std::string& name) const;
|
||||
|
||||
Zone& m_zone;
|
||||
|
||||
private:
|
||||
asset_type_t m_asset_type;
|
||||
|
||||
std::unordered_set<XAssetInfoGeneric*> m_dependencies;
|
||||
std::unordered_set<scr_string_t> m_used_script_strings;
|
||||
std::unordered_set<IndirectAssetReference> m_indirect_asset_references;
|
||||
};
|
||||
@@ -10,6 +10,14 @@ AssetWriter::AssetWriter(XAssetInfoGeneric* asset, const Zone& zone, IZoneOutput
|
||||
{
|
||||
}
|
||||
|
||||
const char* AssetWriter::NonReferenceAssetName(const char* assetName)
|
||||
{
|
||||
if (assetName && assetName[0] == ',')
|
||||
return &assetName[1];
|
||||
|
||||
return assetName;
|
||||
}
|
||||
|
||||
scr_string_t AssetWriter::UseScriptString(const scr_string_t scrString) const
|
||||
{
|
||||
assert(scrString < m_asset->m_zone->m_script_strings.Count());
|
||||
@@ -17,6 +25,7 @@ scr_string_t AssetWriter::UseScriptString(const scr_string_t scrString) const
|
||||
if (m_asset->m_zone == &m_zone)
|
||||
return scrString;
|
||||
|
||||
// The asset comes from a different zone, we need to translate it
|
||||
const auto strValue = m_asset->m_zone->m_script_strings.CValue(scrString);
|
||||
return m_zone.m_script_strings.GetScriptString(strValue);
|
||||
}
|
||||
|
||||
@@ -11,7 +11,8 @@ class AssetWriter : public ContentWriterBase
|
||||
protected:
|
||||
AssetWriter(XAssetInfoGeneric* asset, const Zone& zone, IZoneOutputStream& stream);
|
||||
|
||||
_NODISCARD scr_string_t UseScriptString(scr_string_t scrString) const;
|
||||
[[nodiscard]] static const char* NonReferenceAssetName(const char* assetName);
|
||||
[[nodiscard]] scr_string_t UseScriptString(scr_string_t scrString) const;
|
||||
void WriteScriptStringArray(bool atStreamStart, size_t count);
|
||||
|
||||
XAssetInfoGeneric* m_asset;
|
||||
|
||||
@@ -11,6 +11,7 @@ end
|
||||
function Catch2Common:link(links)
|
||||
links:add(self:name())
|
||||
links:linkto(catch2)
|
||||
links:linkto(Utils)
|
||||
end
|
||||
|
||||
function Catch2Common:use()
|
||||
@@ -45,7 +46,9 @@ function Catch2Common:project()
|
||||
|
||||
self:include(includes)
|
||||
catch2:include(includes)
|
||||
Utils:include(includes)
|
||||
|
||||
links:linkto(catch2)
|
||||
links:linkto(Utils)
|
||||
links:linkall()
|
||||
end
|
||||
|
||||
@@ -24,4 +24,13 @@ namespace oat::paths
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::filesystem::path GetTempDirectory(const std::string& subDir)
|
||||
{
|
||||
auto result = fs::current_path() / "build" / ".tmp" / subDir;
|
||||
if (!fs::is_directory(result))
|
||||
fs::create_directories(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace oat::paths
|
||||
|
||||
@@ -1,10 +1,12 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
namespace oat::paths
|
||||
{
|
||||
std::filesystem::path GetSourceDirectory();
|
||||
std::filesystem::path GetTestDirectory();
|
||||
std::filesystem::path GetTempDirectory();
|
||||
std::filesystem::path GetTempDirectory(const std::string& subDir);
|
||||
} // namespace oat::paths
|
||||
|
||||
@@ -1,12 +1,28 @@
|
||||
#include "Utils/Logging/Log.h"
|
||||
|
||||
#include <catch2/catch_session.hpp>
|
||||
#include <cstdlib>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool ShouldClearTempFolder()
|
||||
{
|
||||
auto* envVar = std::getenv("OAT_KEEP_TMP_DIR");
|
||||
|
||||
return !envVar || !envVar[0] || (envVar[0] == '0' && !envVar[1]);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
int main(const int argc, char* argv[])
|
||||
{
|
||||
con::init();
|
||||
con::set_log_level(con::LogLevel::DEBUG);
|
||||
|
||||
const fs::path absoluteBinDir(fs::canonical(argv[0]).parent_path());
|
||||
|
||||
const auto expectedLibDir = absoluteBinDir.parent_path().parent_path();
|
||||
@@ -15,24 +31,24 @@ int main(const int argc, char* argv[])
|
||||
|
||||
if (absoluteBinDir.filename() != "tests" || expectedLibDir.filename() != "lib" || expectedBuildDir.filename() != "build")
|
||||
{
|
||||
std::cerr << std::format("Expected test binary to be in the folder it was compiled into (build/lib/?/tests) but was {}\n", absoluteBinDir.string());
|
||||
std::cerr << "Please do not move test executable out of compilation folder\n";
|
||||
con::error("Expected test binary to be in the folder it was compiled into (build/lib/?/tests) but was {}", absoluteBinDir.string());
|
||||
con::error("Please do not move test executable out of compilation folder");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto expectedSrcDir = expectedRootDir / "src";
|
||||
if (!fs::is_directory(expectedSrcDir))
|
||||
{
|
||||
std::cerr << std::format("Expected source directory to exist in {}, but it did not\n", expectedSrcDir.string());
|
||||
std::cerr << "Please do not move test executable out of compilation folder\n";
|
||||
con::error("Expected source directory to exist in {}, but it did not", expectedSrcDir.string());
|
||||
con::error("Please do not move test executable out of compilation folder");
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto expectedTestDir = expectedRootDir / "test";
|
||||
if (!fs::is_directory(expectedTestDir))
|
||||
{
|
||||
std::cerr << std::format("Expected test directory to exist in {}, but it did not\n", expectedTestDir.string());
|
||||
std::cerr << "Please do not move test executable out of compilation folder\n";
|
||||
con::error("Expected test directory to exist in {}, but it did not", expectedTestDir.string());
|
||||
con::error("Please do not move test executable out of compilation folder");
|
||||
return 1;
|
||||
}
|
||||
|
||||
@@ -41,8 +57,16 @@ int main(const int argc, char* argv[])
|
||||
const auto result = Catch::Session().run(argc, argv);
|
||||
|
||||
const auto tempDir = expectedBuildDir / ".tmp";
|
||||
if (ShouldClearTempFolder())
|
||||
{
|
||||
con::info("Clearing tmp folder. Define env var OAT_KEEP_TMP_DIR=1 to keep it.");
|
||||
if (fs::is_directory(tempDir))
|
||||
fs::remove_all(tempDir);
|
||||
}
|
||||
else
|
||||
{
|
||||
con::info("Kept tmp dir {} on disk.", tempDir.string());
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
64
test/SystemTests.lua
Normal file
64
test/SystemTests.lua
Normal file
@@ -0,0 +1,64 @@
|
||||
SystemTests = {}
|
||||
|
||||
function SystemTests:include(includes)
|
||||
if includes:handle(self:name()) then
|
||||
includedirs {
|
||||
path.join(TestFolder(), "SystemTests")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function SystemTests:link(links)
|
||||
|
||||
end
|
||||
|
||||
function SystemTests:use()
|
||||
|
||||
end
|
||||
|
||||
function SystemTests:name()
|
||||
return "SystemTests"
|
||||
end
|
||||
|
||||
function SystemTests:project()
|
||||
local folder = TestFolder()
|
||||
local includes = Includes:create()
|
||||
local links = Links:create()
|
||||
|
||||
project(self:name())
|
||||
targetdir(TargetDirectoryTest)
|
||||
location "%{wks.location}/test/%{prj.name}"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
files {
|
||||
path.join(folder, "SystemTests/**.h"),
|
||||
path.join(folder, "SystemTests/**.cpp")
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Catch2Common:include(includes)
|
||||
Utils:include(includes)
|
||||
ZoneLoading:include(includes)
|
||||
ZoneWriting:include(includes)
|
||||
ObjLoading:include(includes)
|
||||
ObjCompiling:include(includes)
|
||||
ObjWriting:include(includes)
|
||||
Linking:include(includes)
|
||||
Unlinking:include(includes)
|
||||
catch2:include(includes)
|
||||
|
||||
Raw:use()
|
||||
|
||||
links:linkto(Utils)
|
||||
links:linkto(ZoneLoading)
|
||||
links:linkto(ZoneWriting)
|
||||
links:linkto(ObjLoading)
|
||||
links:linkto(ObjCompiling)
|
||||
links:linkto(ObjWriting)
|
||||
links:linkto(catch2)
|
||||
links:linkto(Catch2Common)
|
||||
links:linkto(Linking)
|
||||
links:linkto(Unlinking)
|
||||
links:linkall()
|
||||
end
|
||||
1
test/SystemTests/Game/IW3/Simple/SimpleZone.txt
Normal file
1
test/SystemTests/Game/IW3/Simple/SimpleZone.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
4
test/SystemTests/Game/IW3/Simple/SimpleZoneIW3.zone
Normal file
4
test/SystemTests/Game/IW3/Simple/SimpleZoneIW3.zone
Normal file
@@ -0,0 +1,4 @@
|
||||
>game,IW3
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
65
test/SystemTests/Game/IW3/SimpleZoneIW3.cpp
Normal file
65
test/SystemTests/Game/IW3/SimpleZoneIW3.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "Game/IW3/GameAssetPoolIW3.h"
|
||||
#include "Linker.h"
|
||||
#include "OatTestPaths.h"
|
||||
#include "SystemTestsPaths.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
TEST_CASE("Simple Zone(IW3)", "[iw3][system][simple]")
|
||||
{
|
||||
const auto assetSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW3/Simple").string();
|
||||
const auto sourceSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW3/Simple").string();
|
||||
const auto outputPath = oat::paths::GetTempDirectory("SimpleZoneIW3").string();
|
||||
|
||||
const char* argStrings[]{
|
||||
"SystemTests", // bin
|
||||
"--verbose",
|
||||
"--asset-search-path",
|
||||
assetSearchPath.c_str(),
|
||||
"--source-search-path",
|
||||
sourceSearchPath.c_str(),
|
||||
"--output-folder",
|
||||
outputPath.c_str(),
|
||||
"SimpleZoneIW3",
|
||||
};
|
||||
|
||||
LinkerArgs args;
|
||||
|
||||
bool shouldContinue = true;
|
||||
const auto couldParseArgs = args.ParseArgs(std::extent_v<decltype(argStrings)>, argStrings, shouldContinue);
|
||||
|
||||
REQUIRE(couldParseArgs);
|
||||
REQUIRE(shouldContinue);
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
const auto linkerResult = linker->Start();
|
||||
|
||||
REQUIRE(linkerResult);
|
||||
|
||||
// x64 for now produces invalid zones, don't try to load them yet
|
||||
#ifdef ARCH_x86
|
||||
const auto expectedZonePath = (fs::path(outputPath) / "SimpleZoneIW3.ff").string();
|
||||
auto maybeZone = ZoneLoading::LoadZone(expectedZonePath, std::nullopt);
|
||||
REQUIRE(maybeZone);
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
auto pools = dynamic_cast<GameAssetPoolIW3*>(zone->m_pools.get());
|
||||
|
||||
REQUIRE(zone->m_game_id == GameId::IW3);
|
||||
REQUIRE(zone->m_platform == GamePlatform::PC);
|
||||
REQUIRE(zone->m_name == "SimpleZoneIW3");
|
||||
REQUIRE(pools->GetTotalAssetCount() == 1);
|
||||
REQUIRE(pools->m_raw_file->GetAsset("SimpleZone.txt"));
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
1
test/SystemTests/Game/IW4/Simple/SimpleZone.txt
Normal file
1
test/SystemTests/Game/IW4/Simple/SimpleZone.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
4
test/SystemTests/Game/IW4/Simple/SimpleZoneIW4.zone
Normal file
4
test/SystemTests/Game/IW4/Simple/SimpleZoneIW4.zone
Normal file
@@ -0,0 +1,4 @@
|
||||
>game,IW4
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
65
test/SystemTests/Game/IW4/SimpleZoneIW4.cpp
Normal file
65
test/SystemTests/Game/IW4/SimpleZoneIW4.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "Game/IW4/GameAssetPoolIW4.h"
|
||||
#include "Linker.h"
|
||||
#include "OatTestPaths.h"
|
||||
#include "SystemTestsPaths.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
TEST_CASE("Simple Zone(IW4)", "[iw4][system][simple]")
|
||||
{
|
||||
const auto assetSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW4/Simple").string();
|
||||
const auto sourceSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW4/Simple").string();
|
||||
const auto outputPath = oat::paths::GetTempDirectory("SimpleZoneIW4").string();
|
||||
|
||||
const char* argStrings[]{
|
||||
"SystemTests", // bin
|
||||
"--verbose",
|
||||
"--asset-search-path",
|
||||
assetSearchPath.c_str(),
|
||||
"--source-search-path",
|
||||
sourceSearchPath.c_str(),
|
||||
"--output-folder",
|
||||
outputPath.c_str(),
|
||||
"SimpleZoneIW4",
|
||||
};
|
||||
|
||||
LinkerArgs args;
|
||||
|
||||
bool shouldContinue = true;
|
||||
const auto couldParseArgs = args.ParseArgs(std::extent_v<decltype(argStrings)>, argStrings, shouldContinue);
|
||||
|
||||
REQUIRE(couldParseArgs);
|
||||
REQUIRE(shouldContinue);
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
const auto linkerResult = linker->Start();
|
||||
|
||||
REQUIRE(linkerResult);
|
||||
|
||||
// x64 for now produces invalid zones, don't try to load them yet
|
||||
#ifdef ARCH_x86
|
||||
const auto expectedZonePath = (fs::path(outputPath) / "SimpleZoneIW4.ff").string();
|
||||
auto maybeZone = ZoneLoading::LoadZone(expectedZonePath, std::nullopt);
|
||||
REQUIRE(maybeZone);
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
auto pools = dynamic_cast<GameAssetPoolIW4*>(zone->m_pools.get());
|
||||
|
||||
REQUIRE(zone->m_game_id == GameId::IW4);
|
||||
REQUIRE(zone->m_platform == GamePlatform::PC);
|
||||
REQUIRE(zone->m_name == "SimpleZoneIW4");
|
||||
REQUIRE(pools->GetTotalAssetCount() == 1);
|
||||
REQUIRE(pools->m_raw_file->GetAsset("SimpleZone.txt"));
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
1
test/SystemTests/Game/IW5/Simple/SimpleZone.txt
Normal file
1
test/SystemTests/Game/IW5/Simple/SimpleZone.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
4
test/SystemTests/Game/IW5/Simple/SimpleZoneIW5.zone
Normal file
4
test/SystemTests/Game/IW5/Simple/SimpleZoneIW5.zone
Normal file
@@ -0,0 +1,4 @@
|
||||
>game,IW5
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
65
test/SystemTests/Game/IW5/SimpleZoneIW5.cpp
Normal file
65
test/SystemTests/Game/IW5/SimpleZoneIW5.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "Game/IW5/GameAssetPoolIW5.h"
|
||||
#include "Linker.h"
|
||||
#include "OatTestPaths.h"
|
||||
#include "SystemTestsPaths.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
TEST_CASE("Simple Zone(IW5)", "[iw5][system][simple]")
|
||||
{
|
||||
const auto assetSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW5/Simple").string();
|
||||
const auto sourceSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/IW5/Simple").string();
|
||||
const auto outputPath = oat::paths::GetTempDirectory("SimpleZoneIW5").string();
|
||||
|
||||
const char* argStrings[]{
|
||||
"SystemTests", // bin
|
||||
"--verbose",
|
||||
"--asset-search-path",
|
||||
assetSearchPath.c_str(),
|
||||
"--source-search-path",
|
||||
sourceSearchPath.c_str(),
|
||||
"--output-folder",
|
||||
outputPath.c_str(),
|
||||
"SimpleZoneIW5",
|
||||
};
|
||||
|
||||
LinkerArgs args;
|
||||
|
||||
bool shouldContinue = true;
|
||||
const auto couldParseArgs = args.ParseArgs(std::extent_v<decltype(argStrings)>, argStrings, shouldContinue);
|
||||
|
||||
REQUIRE(couldParseArgs);
|
||||
REQUIRE(shouldContinue);
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
const auto linkerResult = linker->Start();
|
||||
|
||||
REQUIRE(linkerResult);
|
||||
|
||||
// x64 for now produces invalid zones, don't try to load them yet
|
||||
#ifdef ARCH_x86
|
||||
const auto expectedZonePath = (fs::path(outputPath) / "SimpleZoneIW5.ff").string();
|
||||
auto maybeZone = ZoneLoading::LoadZone(expectedZonePath, std::nullopt);
|
||||
REQUIRE(maybeZone);
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
auto pools = dynamic_cast<GameAssetPoolIW5*>(zone->m_pools.get());
|
||||
|
||||
REQUIRE(zone->m_game_id == GameId::IW5);
|
||||
REQUIRE(zone->m_platform == GamePlatform::PC);
|
||||
REQUIRE(zone->m_name == "SimpleZoneIW5");
|
||||
REQUIRE(pools->GetTotalAssetCount() == 1);
|
||||
REQUIRE(pools->m_raw_file->GetAsset("SimpleZone.txt"));
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
1
test/SystemTests/Game/T5/Simple/SimpleZone.txt
Normal file
1
test/SystemTests/Game/T5/Simple/SimpleZone.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
4
test/SystemTests/Game/T5/Simple/SimpleZoneT5.zone
Normal file
4
test/SystemTests/Game/T5/Simple/SimpleZoneT5.zone
Normal file
@@ -0,0 +1,4 @@
|
||||
>game,T5
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
65
test/SystemTests/Game/T5/SimpleZoneT5.cpp
Normal file
65
test/SystemTests/Game/T5/SimpleZoneT5.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "Game/T5/GameAssetPoolT5.h"
|
||||
#include "Linker.h"
|
||||
#include "OatTestPaths.h"
|
||||
#include "SystemTestsPaths.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
TEST_CASE("Simple Zone(T5)", "[t5][system][simple]")
|
||||
{
|
||||
const auto assetSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/T5/Simple").string();
|
||||
const auto sourceSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/T5/Simple").string();
|
||||
const auto outputPath = oat::paths::GetTempDirectory("SimpleZoneT5").string();
|
||||
|
||||
const char* argStrings[]{
|
||||
"SystemTests", // bin
|
||||
"--verbose",
|
||||
"--asset-search-path",
|
||||
assetSearchPath.c_str(),
|
||||
"--source-search-path",
|
||||
sourceSearchPath.c_str(),
|
||||
"--output-folder",
|
||||
outputPath.c_str(),
|
||||
"SimpleZoneT5",
|
||||
};
|
||||
|
||||
LinkerArgs args;
|
||||
|
||||
bool shouldContinue = true;
|
||||
const auto couldParseArgs = args.ParseArgs(std::extent_v<decltype(argStrings)>, argStrings, shouldContinue);
|
||||
|
||||
REQUIRE(couldParseArgs);
|
||||
REQUIRE(shouldContinue);
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
const auto linkerResult = linker->Start();
|
||||
|
||||
REQUIRE(linkerResult);
|
||||
|
||||
// x64 for now produces invalid zones, don't try to load them yet
|
||||
#ifdef ARCH_x86
|
||||
const auto expectedZonePath = (fs::path(outputPath) / "SimpleZoneT5.ff").string();
|
||||
auto maybeZone = ZoneLoading::LoadZone(expectedZonePath, std::nullopt);
|
||||
REQUIRE(maybeZone);
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
auto pools = dynamic_cast<GameAssetPoolT5*>(zone->m_pools.get());
|
||||
|
||||
REQUIRE(zone->m_game_id == GameId::T5);
|
||||
REQUIRE(zone->m_platform == GamePlatform::PC);
|
||||
REQUIRE(zone->m_name == "SimpleZoneT5");
|
||||
REQUIRE(pools->GetTotalAssetCount() == 1);
|
||||
REQUIRE(pools->m_raw_file->GetAsset("SimpleZone.txt"));
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
@@ -0,0 +1,4 @@
|
||||
>game,T6
|
||||
|
||||
techniqueset,trivial_floatz_2992w610
|
||||
material,test
|
||||
@@ -0,0 +1,4 @@
|
||||
>game,T6
|
||||
|
||||
ignore,ZoneWithTechsetT6
|
||||
material,test
|
||||
Binary file not shown.
@@ -0,0 +1,3 @@
|
||||
>game,T6
|
||||
|
||||
techniqueset,trivial_floatz_2992w610
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"$schema": "http://openassettools.dev/schema/material.v1.json",
|
||||
"_game": "t6",
|
||||
"_type": "material",
|
||||
"_version": 1,
|
||||
"cameraRegion": "none",
|
||||
"constants": [],
|
||||
"contents": 1,
|
||||
"gameFlags": [],
|
||||
"layeredSurfaceTypes": 536870912,
|
||||
"sortKey": 4,
|
||||
"stateBits": [
|
||||
{
|
||||
"alphaTest": "disabled",
|
||||
"blendOpAlpha": "disabled",
|
||||
"blendOpRgb": "disabled",
|
||||
"colorWriteAlpha": true,
|
||||
"colorWriteRgb": true,
|
||||
"cullFace": "back",
|
||||
"depthTest": "disabled",
|
||||
"depthWrite": false,
|
||||
"dstBlendAlpha": "zero",
|
||||
"dstBlendRgb": "zero",
|
||||
"polygonOffset": "offset0",
|
||||
"polymodeLine": false,
|
||||
"srcBlendAlpha": "one",
|
||||
"srcBlendRgb": "one"
|
||||
}
|
||||
],
|
||||
"stateBitsEntry": [
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"stateFlags": 0,
|
||||
"surfaceFlags": 0,
|
||||
"surfaceTypeBits": 0,
|
||||
"techniqueSet": "trivial_floatz_2992w610",
|
||||
"textureAtlas": {
|
||||
"columns": 1,
|
||||
"rows": 1
|
||||
},
|
||||
"textures": []
|
||||
}
|
||||
130
test/SystemTests/Game/T6/ExtendAndDereferenceT6.cpp
Normal file
130
test/SystemTests/Game/T6/ExtendAndDereferenceT6.cpp
Normal file
@@ -0,0 +1,130 @@
|
||||
/*
|
||||
This makes sure that when reusing assets from an existing zone, asset dependencies that are references can be overwritten to be non-references.
|
||||
|
||||
In this case:
|
||||
|
||||
- The zone `ZoneWithTechsetT6` contains the techniqueset `trivial_floatz_2992w610`
|
||||
- The zone `ZoneWithMaterialT6` contains the material `test` and with a reference to its techniqueset `,trivial_floatz_2992w610`
|
||||
- The final zone `CombinedZoneT6` is built while loading both of the other fastfile and is expected to contain both the material `test` with a reference to a
|
||||
(actual asset, not a reference) techniqueset `trivial_floatz_2992w610`
|
||||
*/
|
||||
|
||||
#include "Game/T6/GameAssetPoolT6.h"
|
||||
#include "Linker.h"
|
||||
#include "OatTestPaths.h"
|
||||
#include "SystemTestsPaths.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
void BuildZoneWithMaterial(const fs::path& testDir, const fs::path& outputPath)
|
||||
{
|
||||
const auto testDirStr = testDir.string();
|
||||
const auto outputPathStr = outputPath.string();
|
||||
|
||||
const char* argStrings[]{
|
||||
"SystemTests", // bin
|
||||
"--verbose",
|
||||
"--asset-search-path",
|
||||
testDirStr.c_str(),
|
||||
"--source-search-path",
|
||||
testDirStr.c_str(),
|
||||
"--output-folder",
|
||||
outputPathStr.c_str(),
|
||||
"ZoneWithMaterialT6",
|
||||
};
|
||||
|
||||
LinkerArgs args;
|
||||
|
||||
bool shouldContinue = true;
|
||||
const auto couldParseArgs = args.ParseArgs(std::extent_v<decltype(argStrings)>, argStrings, shouldContinue);
|
||||
|
||||
REQUIRE(couldParseArgs);
|
||||
REQUIRE(shouldContinue);
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
const auto linkerResult = linker->Start();
|
||||
|
||||
REQUIRE(linkerResult);
|
||||
}
|
||||
|
||||
void BuildCombinedZone(const fs::path& testDir, const fs::path& outputPath)
|
||||
{
|
||||
const auto testDirStr = testDir.string();
|
||||
const auto outputPathStr = outputPath.string();
|
||||
|
||||
const auto zoneWithTechsetPathStr = (testDir / "ZoneWithTechsetT6.ff").string();
|
||||
const auto zoneWithMaterialPathStr = (outputPath / "ZoneWithMaterialT6.ff").string();
|
||||
|
||||
const char* argStrings[]{
|
||||
"SystemTests", // bin
|
||||
"--verbose",
|
||||
"--load",
|
||||
zoneWithTechsetPathStr.c_str(),
|
||||
"--load",
|
||||
zoneWithMaterialPathStr.c_str(),
|
||||
"--source-search-path",
|
||||
testDirStr.c_str(),
|
||||
"--output-folder",
|
||||
outputPathStr.c_str(),
|
||||
"CombinedZoneT6",
|
||||
};
|
||||
|
||||
LinkerArgs args;
|
||||
|
||||
bool shouldContinue = true;
|
||||
const auto couldParseArgs = args.ParseArgs(std::extent_v<decltype(argStrings)>, argStrings, shouldContinue);
|
||||
|
||||
REQUIRE(couldParseArgs);
|
||||
REQUIRE(shouldContinue);
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
const auto linkerResult = linker->Start();
|
||||
|
||||
REQUIRE(linkerResult);
|
||||
}
|
||||
|
||||
void CheckCombinedZoneContent(const fs::path& outputPath)
|
||||
{
|
||||
const auto expectedZonePath = (fs::path(outputPath) / "CombinedZoneT6.ff").string();
|
||||
auto maybeZone = ZoneLoading::LoadZone(expectedZonePath, std::nullopt);
|
||||
REQUIRE(maybeZone);
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
auto pools = dynamic_cast<GameAssetPoolT6*>(zone->m_pools.get());
|
||||
|
||||
REQUIRE(zone->m_game_id == GameId::T6);
|
||||
REQUIRE(zone->m_platform == GamePlatform::PC);
|
||||
REQUIRE(zone->m_name == "CombinedZoneT6");
|
||||
REQUIRE(pools->GetTotalAssetCount() == 2);
|
||||
REQUIRE(pools->m_technique_set->GetAsset("trivial_floatz_2992w610"));
|
||||
|
||||
const auto* material = pools->m_material->GetAsset("test");
|
||||
REQUIRE(material);
|
||||
REQUIRE(material->Asset()->techniqueSet);
|
||||
REQUIRE(material->Asset()->techniqueSet->name == "trivial_floatz_2992w610"s);
|
||||
REQUIRE(material->Asset()->techniqueSet->techniques[T6::TECHNIQUE_UNLIT]);
|
||||
}
|
||||
|
||||
// x64 for now produces invalid zones, don't try to load them yet
|
||||
#ifdef ARCH_x86
|
||||
TEST_CASE("Extend and dereference(T6)", "[t6][system][simple]")
|
||||
{
|
||||
const auto testDir = oat::paths::GetSystemTestsDirectory() / "Game/T6/ExtendAndDereference";
|
||||
const auto outputPath = oat::paths::GetTempDirectory("ExtendAndDereferenceT6");
|
||||
|
||||
BuildZoneWithMaterial(testDir, outputPath);
|
||||
BuildCombinedZone(testDir, outputPath);
|
||||
CheckCombinedZoneContent(outputPath);
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
@@ -0,0 +1,6 @@
|
||||
>game,T6
|
||||
|
||||
material,,Suzanne
|
||||
material,,Suzanne2
|
||||
xmodel,Suzanne1
|
||||
xmodel,Suzanne2
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "http://openassettools.dev/schema/xmodel.v1.json",
|
||||
"_game": "t6",
|
||||
"_type": "xmodel",
|
||||
"_version": 2,
|
||||
"flags": 2621440,
|
||||
"lightingOriginOffset": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.5
|
||||
},
|
||||
"lightingOriginRange": 0.5,
|
||||
"lods": [
|
||||
{
|
||||
"distance": 11447.6904296875,
|
||||
"file": "model_export/Suzanne1.gltf"
|
||||
}
|
||||
],
|
||||
"type": "rigid"
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
{
|
||||
"$schema": "http://openassettools.dev/schema/xmodel.v1.json",
|
||||
"_game": "t6",
|
||||
"_type": "xmodel",
|
||||
"_version": 2,
|
||||
"flags": 2621440,
|
||||
"lightingOriginOffset": {
|
||||
"x": 0.0,
|
||||
"y": 0.0,
|
||||
"z": 0.5
|
||||
},
|
||||
"lightingOriginRange": 0.5,
|
||||
"lods": [
|
||||
{
|
||||
"distance": 11447.6904296875,
|
||||
"file": "model_export/Suzanne2.gltf"
|
||||
}
|
||||
],
|
||||
"type": "rigid"
|
||||
}
|
||||
@@ -0,0 +1,4 @@
|
||||
>game,T6
|
||||
|
||||
ignore,Ignored
|
||||
xmodel,Suzanne2
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"$schema": "http://openassettools.dev/schema/material.v1.json",
|
||||
"_game": "t6",
|
||||
"_type": "material",
|
||||
"_version": 1,
|
||||
"cameraRegion": "none",
|
||||
"constants": [],
|
||||
"contents": 1,
|
||||
"gameFlags": [],
|
||||
"layeredSurfaceTypes": 536870912,
|
||||
"sortKey": 4,
|
||||
"stateBits": [
|
||||
{
|
||||
"alphaTest": "disabled",
|
||||
"blendOpAlpha": "disabled",
|
||||
"blendOpRgb": "disabled",
|
||||
"colorWriteAlpha": true,
|
||||
"colorWriteRgb": true,
|
||||
"cullFace": "back",
|
||||
"depthTest": "disabled",
|
||||
"depthWrite": false,
|
||||
"dstBlendAlpha": "zero",
|
||||
"dstBlendRgb": "zero",
|
||||
"polygonOffset": "offset0",
|
||||
"polymodeLine": false,
|
||||
"srcBlendAlpha": "one",
|
||||
"srcBlendRgb": "one"
|
||||
}
|
||||
],
|
||||
"stateBitsEntry": [
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"stateFlags": 0,
|
||||
"surfaceFlags": 0,
|
||||
"surfaceTypeBits": 0,
|
||||
"techniqueSet": "trivial_floatz_2992w610",
|
||||
"textureAtlas": {
|
||||
"columns": 1,
|
||||
"rows": 1
|
||||
},
|
||||
"textures": []
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
{
|
||||
"$schema": "http://openassettools.dev/schema/material.v1.json",
|
||||
"_game": "t6",
|
||||
"_type": "material",
|
||||
"_version": 1,
|
||||
"cameraRegion": "none",
|
||||
"constants": [],
|
||||
"contents": 1,
|
||||
"gameFlags": [],
|
||||
"layeredSurfaceTypes": 536870912,
|
||||
"sortKey": 4,
|
||||
"stateBits": [
|
||||
{
|
||||
"alphaTest": "disabled",
|
||||
"blendOpAlpha": "disabled",
|
||||
"blendOpRgb": "disabled",
|
||||
"colorWriteAlpha": true,
|
||||
"colorWriteRgb": true,
|
||||
"cullFace": "back",
|
||||
"depthTest": "disabled",
|
||||
"depthWrite": false,
|
||||
"dstBlendAlpha": "zero",
|
||||
"dstBlendRgb": "zero",
|
||||
"polygonOffset": "offset0",
|
||||
"polymodeLine": false,
|
||||
"srcBlendAlpha": "one",
|
||||
"srcBlendRgb": "one"
|
||||
}
|
||||
],
|
||||
"stateBitsEntry": [
|
||||
-1,
|
||||
-1,
|
||||
0,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1,
|
||||
-1
|
||||
],
|
||||
"stateFlags": 0,
|
||||
"surfaceFlags": 0,
|
||||
"surfaceTypeBits": 0,
|
||||
"techniqueSet": "trivial_floatz_2992w610",
|
||||
"textureAtlas": {
|
||||
"columns": 1,
|
||||
"rows": 1
|
||||
},
|
||||
"textures": []
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
techniqueset,trivial_floatz_2992w610
|
||||
|
137
test/SystemTests/Game/T6/ReuseGlobalAssetPoolsAssetsT6.cpp
Normal file
137
test/SystemTests/Game/T6/ReuseGlobalAssetPoolsAssetsT6.cpp
Normal file
@@ -0,0 +1,137 @@
|
||||
/*
|
||||
This makes sure that when reusing assets from an existing zone, asset dependencies that are references can be overwritten to be non-references.
|
||||
|
||||
In this case:
|
||||
|
||||
- The zone `ZoneWithTechsetT6` contains the techniqueset `trivial_floatz_2992w610`
|
||||
- The zone `ZoneWithMaterialT6` contains the material `test` and with a reference to its techniqueset `,trivial_floatz_2992w610`
|
||||
- The final zone `CombinedZoneT6` is built while loading both of the other fastfile and is expected to contain both the material `test` with a reference to a
|
||||
(actual asset, not a reference) techniqueset `trivial_floatz_2992w610`
|
||||
*/
|
||||
|
||||
#include "Game/T6/GameAssetPoolT6.h"
|
||||
#include "Linker.h"
|
||||
#include "OatTestPaths.h"
|
||||
#include "SystemTestsPaths.h"
|
||||
#include "Unlinker.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
void BuildBaseZone(const fs::path& testDir, const fs::path& outputPath)
|
||||
{
|
||||
const auto testDirStr = testDir.string();
|
||||
const auto assetDirStr = (testDir / "BaseZone").string();
|
||||
const auto outputPathStr = outputPath.string();
|
||||
|
||||
const char* argStrings[]{
|
||||
"SystemTests", // bin
|
||||
"--verbose",
|
||||
"--add-asset-search-path",
|
||||
assetDirStr.c_str(),
|
||||
"--source-search-path",
|
||||
testDirStr.c_str(),
|
||||
"--output-folder",
|
||||
outputPathStr.c_str(),
|
||||
"BaseZone",
|
||||
};
|
||||
|
||||
LinkerArgs args;
|
||||
|
||||
bool shouldContinue = true;
|
||||
const auto couldParseArgs = args.ParseArgs(std::extent_v<decltype(argStrings)>, argStrings, shouldContinue);
|
||||
|
||||
REQUIRE(couldParseArgs);
|
||||
REQUIRE(shouldContinue);
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
const auto linkerResult = linker->Start();
|
||||
|
||||
REQUIRE(linkerResult);
|
||||
}
|
||||
|
||||
void BuildTestZone(const fs::path& testDir, const fs::path& outputPath)
|
||||
{
|
||||
const auto testDirStr = testDir.string();
|
||||
const auto assetDirStr = (testDir / "TestZone").string();
|
||||
const auto outputPathStr = outputPath.string();
|
||||
|
||||
const auto baseZonePathStr = (outputPath / "BaseZone.ff").string();
|
||||
|
||||
const char* argStrings[]{
|
||||
"SystemTests", // bin
|
||||
"--verbose",
|
||||
"--load",
|
||||
baseZonePathStr.c_str(),
|
||||
"--add-asset-search-path",
|
||||
assetDirStr.c_str(),
|
||||
"--source-search-path",
|
||||
testDirStr.c_str(),
|
||||
"--output-folder",
|
||||
outputPathStr.c_str(),
|
||||
"TestZone",
|
||||
"TestZone", // build twice to ensure the second build uses a global asset that the first build already used
|
||||
};
|
||||
|
||||
LinkerArgs args;
|
||||
|
||||
bool shouldContinue = true;
|
||||
const auto couldParseArgs = args.ParseArgs(std::extent_v<decltype(argStrings)>, argStrings, shouldContinue);
|
||||
|
||||
REQUIRE(couldParseArgs);
|
||||
REQUIRE(shouldContinue);
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
const auto linkerResult = linker->Start();
|
||||
|
||||
REQUIRE(linkerResult);
|
||||
}
|
||||
|
||||
void CheckTestZoneContent(const fs::path& outputPath)
|
||||
{
|
||||
const auto expectedZonePathStr = (outputPath / "TestZone.ff").string();
|
||||
auto maybeZone = ZoneLoading::LoadZone(expectedZonePathStr, std::nullopt);
|
||||
REQUIRE(maybeZone);
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
auto pools = dynamic_cast<GameAssetPoolT6*>(zone->m_pools.get());
|
||||
|
||||
REQUIRE(zone->m_game_id == GameId::T6);
|
||||
REQUIRE(zone->m_platform == GamePlatform::PC);
|
||||
REQUIRE(zone->m_name == "TestZone");
|
||||
REQUIRE(pools->GetTotalAssetCount() == 3);
|
||||
REQUIRE(pools->m_material->GetAsset("Suzanne2"));
|
||||
REQUIRE(pools->m_technique_set->GetAsset(",trivial_floatz_2992w610"));
|
||||
|
||||
const auto* xmodel = pools->m_xmodel->GetAsset("Suzanne2");
|
||||
REQUIRE(xmodel);
|
||||
REQUIRE(xmodel->Asset()->boneNames);
|
||||
REQUIRE(xmodel->Asset()->numRootBones == 1);
|
||||
REQUIRE(xmodel->Asset()->numBones == 3);
|
||||
REQUIRE(zone->m_script_strings.Value(xmodel->Asset()->boneNames[0]) == "Root2");
|
||||
REQUIRE(zone->m_script_strings.Value(xmodel->Asset()->boneNames[1]) == "EarLeft2");
|
||||
REQUIRE(zone->m_script_strings.Value(xmodel->Asset()->boneNames[2]) == "EarRight2");
|
||||
}
|
||||
|
||||
// x64 for now produces invalid zones, don't try to load them yet
|
||||
#ifdef ARCH_x86
|
||||
TEST_CASE("Reuse assets from global asset pool(T6)", "[t6][system][simple]")
|
||||
{
|
||||
const auto testDir = oat::paths::GetSystemTestsDirectory() / "Game/T6/ReuseGlobalAssetPoolsAssets";
|
||||
const auto outputPath = oat::paths::GetTempDirectory("ReuseGlobalAssetPoolsAssetsT6");
|
||||
|
||||
BuildBaseZone(testDir, outputPath);
|
||||
BuildTestZone(testDir, outputPath);
|
||||
CheckTestZoneContent(outputPath);
|
||||
}
|
||||
#endif
|
||||
} // namespace
|
||||
1
test/SystemTests/Game/T6/Simple/SimpleZone.txt
Normal file
1
test/SystemTests/Game/T6/Simple/SimpleZone.txt
Normal file
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
4
test/SystemTests/Game/T6/Simple/SimpleZoneT6.zone
Normal file
4
test/SystemTests/Game/T6/Simple/SimpleZoneT6.zone
Normal file
@@ -0,0 +1,4 @@
|
||||
>game,T6
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
65
test/SystemTests/Game/T6/SimpleZoneT6.cpp
Normal file
65
test/SystemTests/Game/T6/SimpleZoneT6.cpp
Normal file
@@ -0,0 +1,65 @@
|
||||
#include "Game/T6/GameAssetPoolT6.h"
|
||||
#include "Linker.h"
|
||||
#include "OatTestPaths.h"
|
||||
#include "SystemTestsPaths.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
using namespace std::literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
TEST_CASE("Simple Zone(T6)", "[t6][system][simple]")
|
||||
{
|
||||
const auto assetSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/T6/Simple").string();
|
||||
const auto sourceSearchPath = (oat::paths::GetSystemTestsDirectory() / "Game/T6/Simple").string();
|
||||
const auto outputPath = oat::paths::GetTempDirectory("SimpleZoneT6").string();
|
||||
|
||||
const char* argStrings[]{
|
||||
"SystemTests", // bin
|
||||
"--verbose",
|
||||
"--asset-search-path",
|
||||
assetSearchPath.c_str(),
|
||||
"--source-search-path",
|
||||
sourceSearchPath.c_str(),
|
||||
"--output-folder",
|
||||
outputPath.c_str(),
|
||||
"SimpleZoneT6",
|
||||
};
|
||||
|
||||
LinkerArgs args;
|
||||
|
||||
bool shouldContinue = true;
|
||||
const auto couldParseArgs = args.ParseArgs(std::extent_v<decltype(argStrings)>, argStrings, shouldContinue);
|
||||
|
||||
REQUIRE(couldParseArgs);
|
||||
REQUIRE(shouldContinue);
|
||||
|
||||
const auto linker = Linker::Create(std::move(args));
|
||||
const auto linkerResult = linker->Start();
|
||||
|
||||
REQUIRE(linkerResult);
|
||||
|
||||
// x64 for now produces invalid zones, don't try to load them yet
|
||||
#ifdef ARCH_x86
|
||||
const auto expectedZonePath = (fs::path(outputPath) / "SimpleZoneT6.ff").string();
|
||||
auto maybeZone = ZoneLoading::LoadZone(expectedZonePath, std::nullopt);
|
||||
REQUIRE(maybeZone);
|
||||
|
||||
auto zone = std::move(*maybeZone);
|
||||
auto pools = dynamic_cast<GameAssetPoolT6*>(zone->m_pools.get());
|
||||
|
||||
REQUIRE(zone->m_game_id == GameId::T6);
|
||||
REQUIRE(zone->m_platform == GamePlatform::PC);
|
||||
REQUIRE(zone->m_name == "SimpleZoneT6");
|
||||
REQUIRE(pools->GetTotalAssetCount() == 1);
|
||||
REQUIRE(pools->m_raw_file->GetAsset("SimpleZone.txt"));
|
||||
#endif
|
||||
}
|
||||
} // namespace
|
||||
15
test/SystemTests/SystemTestsPaths.cpp
Normal file
15
test/SystemTests/SystemTestsPaths.cpp
Normal file
@@ -0,0 +1,15 @@
|
||||
#include "SystemTestsPaths.h"
|
||||
|
||||
#include "OatTestPaths.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace oat::paths
|
||||
{
|
||||
std::filesystem::path GetSystemTestsDirectory()
|
||||
{
|
||||
return GetTestDirectory() / "SystemTests";
|
||||
}
|
||||
} // namespace oat::paths
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user