mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-10-04 11:25:34 +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:
101 files changed
+3095
-1207
No files matched your search
@@ -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
|
||||
|
||||
+10
-4
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
};
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
@@ -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)
|
||||
{
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
File renamed without changes.
File renamed without changes.
@@ -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));
|
||||
}
|
||||
@@ -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;
|
||||
};
|
||||
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
+14
-2
@@ -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--;
|
||||
|
||||
|
||||
+15
-2
@@ -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
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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"
|
||||
@@ -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
|
||||
}
|
||||
};
|
||||
@@ -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]);
|
||||
}
|
||||
@@ -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; \
|
||||
};
|
||||
@@ -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);
|
||||
}
|
||||
@@ -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 (fs::is_directory(tempDir))
|
||||
fs::remove_all(tempDir);
|
||||
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;
|
||||
}
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
@@ -0,0 +1,4 @@
|
||||
>game,IW3
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
@@ -0,0 +1,4 @@
|
||||
>game,IW4
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
@@ -0,0 +1,4 @@
|
||||
>game,IW5
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
@@ -0,0 +1,4 @@
|
||||
>game,T5
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
@@ -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": []
|
||||
}
|
||||
@@ -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
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "Khronos glTF Blender I/O v5.0.21",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"name": "Scene",
|
||||
"nodes": [4]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"name": "EarLeft",
|
||||
"rotation": [
|
||||
1.5454309831852697e-8, 1.5454309831852697e-8, -0.7071067690849304,
|
||||
0.7071067690849304
|
||||
],
|
||||
"scale": [1, 0.9999999403953552, 1],
|
||||
"translation": [0, 23.455289840698242, 0]
|
||||
},
|
||||
{
|
||||
"name": "EarRight",
|
||||
"rotation": [
|
||||
1.5454309831852697e-8, -1.5454313384566376e-8, 0.7071067690849304,
|
||||
0.7071067690849304
|
||||
],
|
||||
"translation": [0, 23.455289840698242, 0]
|
||||
},
|
||||
{
|
||||
"children": [0, 1],
|
||||
"name": "Root",
|
||||
"rotation": [-2.980232594040899e-8, 0, 0, 1],
|
||||
"scale": [1, 0.9999999403953552, 0.9999999403953552]
|
||||
},
|
||||
{
|
||||
"mesh": 0,
|
||||
"name": "Suzanne",
|
||||
"skin": 0
|
||||
},
|
||||
{
|
||||
"children": [3, 2],
|
||||
"name": "Armature",
|
||||
"translation": [0, -23.480762481689453, 0]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"doubleSided": true,
|
||||
"name": "Suzanne",
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929, 0.800000011920929, 0.800000011920929, 1
|
||||
],
|
||||
"metallicFactor": 0,
|
||||
"roughnessFactor": 0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"name": "Suzanne",
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": 0,
|
||||
"NORMAL": 1,
|
||||
"TEXCOORD_0": 2,
|
||||
"JOINTS_0": 3,
|
||||
"WEIGHTS_0": 4
|
||||
},
|
||||
"indices": 5,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"skins": [
|
||||
{
|
||||
"inverseBindMatrices": 6,
|
||||
"joints": [2, 0, 1],
|
||||
"name": "Armature"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 2865,
|
||||
"max": [34.1796875, 24.609375, 21.2890625],
|
||||
"min": [-34.1796875, -24.609375, -21.2890625],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"componentType": 5126,
|
||||
"count": 2865,
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 2,
|
||||
"componentType": 5126,
|
||||
"count": 2865,
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 3,
|
||||
"componentType": 5121,
|
||||
"count": 2865,
|
||||
"type": "VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView": 4,
|
||||
"componentType": 5126,
|
||||
"count": 2865,
|
||||
"type": "VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView": 5,
|
||||
"componentType": 5123,
|
||||
"count": 2901,
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 6,
|
||||
"componentType": 5126,
|
||||
"count": 3,
|
||||
"type": "MAT4"
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 34380,
|
||||
"byteOffset": 0,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 34380,
|
||||
"byteOffset": 34380,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 22920,
|
||||
"byteOffset": 68760,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 11460,
|
||||
"byteOffset": 91680,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 45840,
|
||||
"byteOffset": 103140,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 5802,
|
||||
"byteOffset": 148980,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 192,
|
||||
"byteOffset": 154784
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 154976,
|
||||
"uri": "data:application/octet-stream;base64,AAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAMBaQQAArz8AQGdBAMBaQQAArz8AQGdBAMBaQQAArz8AQGdBAMBawQAArz8AQGdBAMBawQAArz8AQGdBAMBawQAArz8AQGdBAKAMQQAAFr8A4HZBAKAMQQAAFr8A4HZBAKAMQQAAFr8A4HZBAKAMwQAAFr8A4HZBAKAMwQAAFr8A4HZBAKAMwQAAFr8A4HZBAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAMDaQABAg0AAYJ9BAMDaQABAg0AAYJ9BAMDaQABAg0AAYJ9BAMDaQABAg0AAYJ9BAMDaQABAg0AAYJ9BAMDawABAg0AAYJ9BAMDawABAg0AAYJ9BAMDawABAg0AAYJ9BAMDawABAg0AAYJ9BAMDawABAg0AAYJ9BAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAAB6QAAArz8AsIFBAAB6QAAArz8AsIFBAAB6QAAArz8AsIFBAAB6QAAArz8AsIFBAAB6wAAArz8AsIFBAAB6wAAArz8AsIFBAAB6wAAArz8AsIFBAAB6wAAArz8AsIFBAAD6PwDAwUAAQINBAAD6PwDAwUAAQINBAAD6PwDAwUAAQINBAAD6vwDAwUAAQINBAAD6vwDAwUAAQINBAAD6vwDAwUAAQINBAABhQADAwUAAcJRBAABhQADAwUAAcJRBAABhQADAwUAAcJRBAABhQADAwUAAcJRBAABhQADAwUAAcJRBAABhwADAwUAAcJRBAABhwADAwUAAcJRBAABhwADAwUAAcJRBAABhwADAwUAAcJRBAABhwADAwUAAcJRBAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDaQABAA0EAYJ9BAMDaQABAA0EAYJ9BAMDaQABAA0EAYJ9BAMDaQABAA0EAYJ9BAMDaQABAA0EAYJ9BAMDawABAA0EAYJ9BAMDawABAA0EAYJ9BAMDawABAA0EAYJ9BAMDawABAA0EAYJ9BAMDawABAA0EAYJ9BAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAAB6QAAAL0EAsIFBAAB6QAAAL0EAsIFBAAB6QAAAL0EAsIFBAAB6wAAAL0EAsIFBAAB6wAAAL0EAsIFBAAB6wAAAL0EAsIFBAKAMQQBATkEA4HZBAKAMQQBATkEA4HZBAKAMQQBATkEA4HZBAKAMwQBATkEA4HZBAKAMwQBATkEA4HZBAKAMwQBATkEA4HZBAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAMBaQQAAL0EAQGdBAMBaQQAAL0EAQGdBAMBaQQAAL0EAQGdBAMBawQAAL0EAQGdBAMBawQAAL0EAQGdBAMBawQAAL0EAQGdBAAB6QQDAwUAAAGFBAAB6QQDAwUAAAGFBAAB6wQDAwUAAAGFBAAB6wQDAwUAAAGFBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBACAyQQBgBkEAQJxBACAyQQBgBkEAQJxBACAyQQBgBkEAQJxBACAyQQBgBkEAQJxBACAyQQBgBkEAQJxBACAywQBgBkEAQJxBACAywQBgBkEAQJxBACAywQBgBkEAQJxBACAywQBgBkEAQJxBACAywQBgBkEAQJxBAKAMQQAAFkEA8KBBAKAMQQAAFkEA8KBBAKAMQQAAFkEA8KBBAKAMQQAAFkEA8KBBAKAMQQAAFkEA8KBBAKAMwQAAFkEA8KBBAKAMwQAAFkEA8KBBAKAMwQAAFkEA8KBBAKAMwQAAFkEA8KBBAKAMwQAAFkEA8KBBAIDUQABgBkEAEKRBAIDUQABgBkEAEKRBAIDUQABgBkEAEKRBAIDUQABgBkEAEKRBAIDUQABgBkEAEKRBAIDUwABgBkEAEKRBAIDUwABgBkEAEKRBAIDUwABgBkEAEKRBAIDUwABgBkEAEKRBAIDUwABgBkEAEKRBAEC1QADAwUAAEKRBAEC1QADAwUAAEKRBAEC1QADAwUAAEKRBAEC1QADAwUAAEKRBAEC1wADAwUAAEKRBAEC1wADAwUAAEKRBAEC1wADAwUAAEKRBAEC1wADAwUAAEKRBAIDUQAAAekAAEKRBAIDUQAAAekAAEKRBAIDUQAAAekAAEKRBAIDUQAAAekAAEKRBAIDUQAAAekAAEKRBAIDUwAAAekAAEKRBAIDUwAAAekAAEKRBAIDUwAAAekAAEKRBAIDUwAAAekAAEKRBAIDUwAAAekAAEKRBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMwQDAwUAAoKVBAKAMLine truncated
|
||||
}
|
||||
]
|
||||
}
|
||||
+181
@@ -0,0 +1,181 @@
|
||||
{
|
||||
"asset": {
|
||||
"generator": "Khronos glTF Blender I/O v5.0.21",
|
||||
"version": "2.0"
|
||||
},
|
||||
"scene": 0,
|
||||
"scenes": [
|
||||
{
|
||||
"name": "Scene",
|
||||
"nodes": [4]
|
||||
}
|
||||
],
|
||||
"nodes": [
|
||||
{
|
||||
"name": "EarLeft2",
|
||||
"rotation": [
|
||||
1.5454309831852697e-8, 1.5454309831852697e-8, -0.7071067690849304,
|
||||
0.7071067690849304
|
||||
],
|
||||
"scale": [1, 0.9999999403953552, 1],
|
||||
"translation": [0, 23.455289840698242, 0]
|
||||
},
|
||||
{
|
||||
"name": "EarRight2",
|
||||
"rotation": [
|
||||
1.5454309831852697e-8, -1.5454313384566376e-8, 0.7071067690849304,
|
||||
0.7071067690849304
|
||||
],
|
||||
"translation": [0, 23.455289840698242, 0]
|
||||
},
|
||||
{
|
||||
"children": [0, 1],
|
||||
"name": "Root2",
|
||||
"rotation": [-2.980232594040899e-8, 0, 0, 1],
|
||||
"scale": [1, 0.9999999403953552, 0.9999999403953552]
|
||||
},
|
||||
{
|
||||
"mesh": 0,
|
||||
"name": "Suzanne",
|
||||
"skin": 0
|
||||
},
|
||||
{
|
||||
"children": [3, 2],
|
||||
"name": "Armature",
|
||||
"translation": [0, -23.480762481689453, 0]
|
||||
}
|
||||
],
|
||||
"materials": [
|
||||
{
|
||||
"doubleSided": true,
|
||||
"name": "Suzanne2",
|
||||
"pbrMetallicRoughness": {
|
||||
"baseColorFactor": [
|
||||
0.800000011920929, 0.800000011920929, 0.800000011920929, 1
|
||||
],
|
||||
"metallicFactor": 0,
|
||||
"roughnessFactor": 0.5
|
||||
}
|
||||
}
|
||||
],
|
||||
"meshes": [
|
||||
{
|
||||
"name": "Suzanne",
|
||||
"primitives": [
|
||||
{
|
||||
"attributes": {
|
||||
"POSITION": 0,
|
||||
"NORMAL": 1,
|
||||
"TEXCOORD_0": 2,
|
||||
"JOINTS_0": 3,
|
||||
"WEIGHTS_0": 4
|
||||
},
|
||||
"indices": 5,
|
||||
"material": 0
|
||||
}
|
||||
]
|
||||
}
|
||||
],
|
||||
"skins": [
|
||||
{
|
||||
"inverseBindMatrices": 6,
|
||||
"joints": [2, 0, 1],
|
||||
"name": "Armature"
|
||||
}
|
||||
],
|
||||
"accessors": [
|
||||
{
|
||||
"bufferView": 0,
|
||||
"componentType": 5126,
|
||||
"count": 2865,
|
||||
"max": [34.1796875, 24.609375, 21.2890625],
|
||||
"min": [-34.1796875, -24.609375, -21.2890625],
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 1,
|
||||
"componentType": 5126,
|
||||
"count": 2865,
|
||||
"type": "VEC3"
|
||||
},
|
||||
{
|
||||
"bufferView": 2,
|
||||
"componentType": 5126,
|
||||
"count": 2865,
|
||||
"type": "VEC2"
|
||||
},
|
||||
{
|
||||
"bufferView": 3,
|
||||
"componentType": 5121,
|
||||
"count": 2865,
|
||||
"type": "VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView": 4,
|
||||
"componentType": 5126,
|
||||
"count": 2865,
|
||||
"type": "VEC4"
|
||||
},
|
||||
{
|
||||
"bufferView": 5,
|
||||
"componentType": 5123,
|
||||
"count": 2901,
|
||||
"type": "SCALAR"
|
||||
},
|
||||
{
|
||||
"bufferView": 6,
|
||||
"componentType": 5126,
|
||||
"count": 3,
|
||||
"type": "MAT4"
|
||||
}
|
||||
],
|
||||
"bufferViews": [
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 34380,
|
||||
"byteOffset": 0,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 34380,
|
||||
"byteOffset": 34380,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 22920,
|
||||
"byteOffset": 68760,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 11460,
|
||||
"byteOffset": 91680,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 45840,
|
||||
"byteOffset": 103140,
|
||||
"target": 34962
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 5802,
|
||||
"byteOffset": 148980,
|
||||
"target": 34963
|
||||
},
|
||||
{
|
||||
"buffer": 0,
|
||||
"byteLength": 192,
|
||||
"byteOffset": 154784
|
||||
}
|
||||
],
|
||||
"buffers": [
|
||||
{
|
||||
"byteLength": 154976,
|
||||
"uri": "data:application/octet-stream;base64,AAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvQQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAAAvwQBAg0AAIJlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIQQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAABIwQAAFkAAgIlBAMBaQQAArz8AQGdBAMBaQQAArz8AQGdBAMBaQQAArz8AQGdBAMBawQAArz8AQGdBAMBawQAArz8AQGdBAMBawQAArz8AQGdBAKAMQQAAFr8A4HZBAKAMQQAAFr8A4HZBAKAMQQAAFr8A4HZBAKAMwQAAFr8A4HZBAKAMwQAAFr8A4HZBAKAMwQAAFr8A4HZBAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMQQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMwQAASD8AwI9BAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMQQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAKAMwQCAVEAAQJxBAMDaQABAg0AAYJ9BAMDaQABAg0AAYJ9BAMDaQABAg0AAYJ9BAMDaQABAg0AAYJ9BAMDaQABAg0AAYJ9BAMDawABAg0AAYJ9BAMDawABAg0AAYJ9BAMDawABAg0AAYJ9BAMDawABAg0AAYJ9BAMDawABAg0AAYJ9BAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiQAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAICiwAAAFkAAcJRBAAB6QAAArz8AsIFBAAB6QAAArz8AsIFBAAB6QAAArz8AsIFBAAB6QAAArz8AsIFBAAB6wAAArz8AsIFBAAB6wAAArz8AsIFBAAB6wAAArz8AsIFBAAB6wAAArz8AsIFBAAD6PwDAwUAAQINBAAD6PwDAwUAAQINBAAD6PwDAwUAAQINBAAD6vwDAwUAAQINBAAD6vwDAwUAAQINBAAD6vwDAwUAAQINBAABhQADAwUAAcJRBAABhQADAwUAAcJRBAABhQADAwUAAcJRBAABhQADAwUAAcJRBAABhQADAwUAAcJRBAABhwADAwUAAcJRBAABhwADAwUAAcJRBAABhwADAwUAAcJRBAABhwADAwUAAcJRBAABhwADAwUAAcJRBAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBQADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDBwADAwUAAYJ9BAMDaQABAA0EAYJ9BAMDaQABAA0EAYJ9BAMDaQABAA0EAYJ9BAMDaQABAA0EAYJ9BAMDaQABAA0EAYJ9BAMDawABAA0EAYJ9BAMDawABAA0EAYJ9BAMDawABAA0EAYJ9BAMDawABAA0EAYJ9BAMDawABAA0EAYJ9BAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiQABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAICiwABAHEEAcJRBAAB6QAAAL0EAsIFBAAB6QAAAL0EAsIFBAAB6QAAAL0EAsIFBAAB6wAAAL0EAsIFBAAB6wAAAL0EAsIFBAAB6wAAAL0EAsIFBAKAMQQBATkEA4HZBAKAMQQBATkEA4HZBAKAMQQBATkEA4HZBAKAMwQBATkEA4HZBAKAMwQBATkEA4HZBAKAMwQBATkEA4HZBAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMQQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMwQBANUEAwI9BAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMQQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAKAMwQDAD0EAQJxBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvQQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAAAvwQBAA0EAIJlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIQQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAABIwQBAHEEAgIlBAMBaQQAAL0EAQGdBAMBaQQAAL0EAQGdBAMBaQQAAL0EAQGdBAMBawQAAL0EAQGdBAMBawQAAL0EAQGdBAMBawQAAL0EAQGdBAAB6QQDAwUAAAGFBAAB6QQDAwUAAAGFBAAB6wQDAwUAAAGFBAAB6wQDAwUAAAGFBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhQQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAABhwQDAwUAAYIZBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7QQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAIA7wQDAwUAAkJdBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+QQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBAKA+wQDAwUAAsJpBACAyQQBgBkEAQJxBACAyQQBgBkEAQJxBACAyQQBgBkEAQJxBACAyQQBgBkEAQJxBACAyQQBgBkEAQJxBACAywQBgBkEAQJxBACAywQBgBkEAQJxBACAywQBgBkEAQJxBACAywQBgBkEAQJxBACAywQBgBkEAQJxBAKAMQQAAFkEA8KBBAKAMQQAAFkEA8KBBAKAMQQAAFkEA8KBBAKAMQQAAFkEA8KBBAKAMQQAAFkEA8KBBAKAMwQAAFkEA8KBBAKAMwQAAFkEA8KBBAKAMwQAAFkEA8KBBAKAMwQAAFkEA8KBBAKAMwQAAFkEA8KBBAIDUQABgBkEAEKRBAIDUQABgBkEAEKRBAIDUQABgBkEAEKRBAIDUQABgBkEAEKRBAIDUQABgBkEAEKRBAIDUwABgBkEAEKRBAIDUwABgBkEAEKRBAIDUwABgBkEAEKRBAIDUwABgBkEAEKRBAIDUwABgBkEAEKRBAEC1QADAwUAAEKRBAEC1QADAwUAAEKRBAEC1QADAwUAAEKRBAEC1QADAwUAAEKRBAEC1wADAwUAAEKRBAEC1wADAwUAAEKRBAEC1wADAwUAAEKRBAEC1wADAwUAAEKRBAIDUQAAAekAAEKRBAIDUQAAAekAAEKRBAIDUQAAAekAAEKRBAIDUQAAAekAAEKRBAIDUQAAAekAAEKRBAIDUwAAAekAAEKRBAIDUwAAAekAAEKRBAIDUwAAAekAAEKRBAIDUwAAAekAAEKRBAIDUwAAAekAAEKRBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMQQDAwUAAoKVBAKAMwQDAwUAAoKVBAKAMLine truncated
|
||||
}
|
||||
]
|
||||
}
|
||||
@@ -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
|
||||
|
@@ -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
|
||||
@@ -0,0 +1 @@
|
||||
This is a simple zone.
|
||||
@@ -0,0 +1,4 @@
|
||||
>game,T6
|
||||
|
||||
rawfile,SimpleZone.txt
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
Loaded 100 of 101 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user