2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2025-12-08 20:27:48 +00:00

refactor: rework search paths

This commit is contained in:
Jan
2024-10-27 19:21:47 +01:00
parent fd421c4784
commit be6c30c503
24 changed files with 1072 additions and 1066 deletions

View File

@@ -1,9 +1,8 @@
#include "Linker.h"
#include "LinkerArgs.h"
#include "LinkerSearchPaths.h"
#include "LinkerPaths.h"
#include "ObjContainer/IPak/IPakWriter.h"
#include "ObjContainer/IWD/IWD.h"
#include "ObjContainer/SoundBank/SoundBankWriter.h"
#include "ObjWriting.h"
#include "SearchPath/SearchPaths.h"
@@ -20,17 +19,143 @@
#include <filesystem>
#include <format>
#include <fstream>
#include <set>
#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
{
LinkerArgs m_args;
LinkerSearchPaths m_search_paths;
std::vector<std::unique_ptr<Zone>> m_loaded_zones;
bool IncludeAdditionalZoneDefinitions(const std::string& initialFileName, ZoneDefinition& zoneDefinition, ISearchPath* sourceSearchPath) const
bool IncludeAdditionalZoneDefinitions(const std::string& initialFileName, ZoneDefinition& zoneDefinition, ISearchPath& sourceSearchPath) const
{
std::set<std::string> sourceNames;
sourceNames.emplace(initialFileName);
@@ -50,7 +175,7 @@ class LinkerImpl final : public Linker
std::unique_ptr<ZoneDefinition> includeDefinition;
{
const auto definitionFileName = std::format("{}.zone", source);
const auto definitionStream = sourceSearchPath->Open(definitionFileName);
const auto definitionStream = sourceSearchPath.Open(definitionFileName);
if (!definitionStream.IsOpen())
{
std::cerr << std::format("Could not find zone definition file for project \"{}\".\n", source);
@@ -80,11 +205,11 @@ class LinkerImpl final : public Linker
return true;
}
bool ReadAssetList(const std::string& zoneName, const GameId game, AssetList& assetList, ISearchPath* sourceSearchPath) const
bool ReadAssetList(LinkerPathManager& paths, const std::string& zoneName, const GameId game, AssetList& assetList) const
{
{
const auto assetListFileName = std::format("assetlist/{}.csv", zoneName);
const auto assetListStream = sourceSearchPath->Open(assetListFileName);
const auto assetListStream = paths.m_source_paths.GetSearchPaths().Open(assetListFileName);
if (assetListStream.IsOpen())
{
@@ -102,7 +227,7 @@ class LinkerImpl final : public Linker
}
{
const auto zoneDefinition = ReadZoneDefinition(zoneName, sourceSearchPath);
const auto zoneDefinition = ReadZoneDefinition(paths, zoneName);
if (zoneDefinition)
{
@@ -117,12 +242,12 @@ class LinkerImpl final : public Linker
return false;
}
bool IncludeAssetLists(ZoneDefinition& zoneDefinition, ISearchPath* sourceSearchPath) const
bool IncludeAssetLists(LinkerPathManager& paths, ZoneDefinition& zoneDefinition) const
{
for (const auto& assetListName : zoneDefinition.m_asset_lists)
{
AssetList assetList;
if (!ReadAssetList(assetListName, zoneDefinition.m_game, assetList, sourceSearchPath))
if (!ReadAssetList(paths, assetListName, zoneDefinition.m_game, assetList))
{
std::cerr << std::format("Failed to read asset list \"{}\"\n", assetListName);
return false;
@@ -134,12 +259,13 @@ class LinkerImpl final : public Linker
return true;
}
std::unique_ptr<ZoneDefinition> ReadZoneDefinition(const std::string& targetName, ISearchPath* sourceSearchPath) const
std::unique_ptr<ZoneDefinition> ReadZoneDefinition(LinkerPathManager& paths, const std::string& targetName) 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);
const auto definitionStream = sourceSearchPath.Open(definitionFileName);
if (!definitionStream.IsOpen())
{
std::cerr << std::format("Could not find zone definition file for target \"{}\".\n", targetName);
@@ -159,13 +285,13 @@ class LinkerImpl final : public Linker
if (!IncludeAdditionalZoneDefinitions(targetName, *zoneDefinition, sourceSearchPath))
return nullptr;
if (!IncludeAssetLists(*zoneDefinition, sourceSearchPath))
if (!IncludeAssetLists(paths, *zoneDefinition))
return nullptr;
return zoneDefinition;
}
bool ProcessZoneDefinitionIgnores(const std::string& targetName, ZoneCreationContext& context, ISearchPath* sourceSearchPath) const
bool ProcessZoneDefinitionIgnores(LinkerPathManager& paths, const std::string& targetName, ZoneCreationContext& context) const
{
if (context.m_definition->m_ignores.empty())
return true;
@@ -176,7 +302,7 @@ class LinkerImpl final : public Linker
continue;
std::vector<AssetListEntry> assetList;
if (!ReadAssetList(ignore, context.m_definition->m_game, context.m_ignored_assets, sourceSearchPath))
if (!ReadAssetList(paths, ignore, context.m_definition->m_game, context.m_ignored_assets))
{
std::cerr << std::format("Failed to read asset listing for ignoring assets of project \"{}\".\n", ignore);
return false;
@@ -210,25 +336,21 @@ class LinkerImpl final : public Linker
return true;
}
std::unique_ptr<Zone> CreateZoneForDefinition(const std::string& targetName,
ZoneDefinition& zoneDefinition,
ISearchPath* assetSearchPath,
ISearchPath* gdtSearchPath,
ISearchPath* sourceSearchPath) const
std::unique_ptr<Zone> CreateZoneForDefinition(LinkerPathManager& paths, const std::string& targetName, ZoneDefinition& zoneDefinition) const
{
ZoneCreationContext context(&zoneDefinition, assetSearchPath);
if (!ProcessZoneDefinitionIgnores(targetName, context, sourceSearchPath))
ZoneCreationContext context(&zoneDefinition, &paths.m_asset_paths.GetSearchPaths());
if (!ProcessZoneDefinitionIgnores(paths, targetName, context))
return nullptr;
if (!LoadGdtFilesFromZoneDefinition(context.m_gdt_files, zoneDefinition, gdtSearchPath))
if (!LoadGdtFilesFromZoneDefinition(context.m_gdt_files, zoneDefinition, &paths.m_gdt_paths.GetSearchPaths()))
return nullptr;
const auto* creator = IZoneCreator::GetCreatorForGame(zoneDefinition.m_game);
return creator->CreateZoneForDefinition(context);
}
bool WriteZoneToFile(const std::string& projectName, Zone* zone) const
bool WriteZoneToFile(const LinkerPathManager& paths, const std::string& projectName, Zone* zone) const
{
const fs::path zoneFolderPath(m_args.GetOutputFolderPathForProject(projectName));
const fs::path zoneFolderPath(paths.m_linker_paths->BuildOutputFolderPath(projectName, zone->m_game->GetId()));
auto zoneFilePath(zoneFolderPath);
zoneFilePath.append(zone->m_name + ".ff");
@@ -256,26 +378,21 @@ class LinkerImpl final : public Linker
return true;
}
bool BuildFastFile(const std::string& projectName,
const std::string& targetName,
ZoneDefinition& zoneDefinition,
SearchPaths& assetSearchPaths,
SearchPaths& gdtSearchPaths,
SearchPaths& sourceSearchPaths) const
bool BuildFastFile(LinkerPathManager& paths, const std::string& projectName, const std::string& targetName, ZoneDefinition& zoneDefinition) const
{
SoundBankWriter::OutputPath = fs::path(m_args.GetOutputFolderPathForProject(projectName));
SoundBankWriter::OutputPath = fs::path(paths.m_linker_paths->BuildOutputFolderPath(projectName, zoneDefinition.m_game));
const auto zone = CreateZoneForDefinition(targetName, zoneDefinition, &assetSearchPaths, &gdtSearchPaths, &sourceSearchPaths);
const auto zone = CreateZoneForDefinition(paths, targetName, zoneDefinition);
auto result = zone != nullptr;
if (zone)
result = WriteZoneToFile(projectName, zone.get());
result = WriteZoneToFile(paths, projectName, zone.get());
return result;
}
bool BuildIPak(const std::string& projectName, const ZoneDefinition& zoneDefinition, SearchPaths& assetSearchPaths) const
bool BuildIPak(const LinkerPathManager& paths, const std::string& projectName, const ZoneDefinition& zoneDefinition, SearchPaths& assetSearchPaths) const
{
const fs::path ipakFolderPath(m_args.GetOutputFolderPathForProject(projectName));
const fs::path ipakFolderPath(paths.m_linker_paths->BuildOutputFolderPath(projectName, zoneDefinition.m_game));
auto ipakFilePath(ipakFolderPath);
ipakFilePath.append(std::format("{}.ipak", zoneDefinition.m_name));
@@ -309,46 +426,44 @@ class LinkerImpl final : public Linker
return true;
}
bool BuildReferencedTargets(const std::string& projectName, const std::string& targetName, const ZoneDefinition& zoneDefinition)
bool BuildProject(LinkerPathManager& paths, const std::string& projectName, const std::string& targetName) const
{
return std::ranges::all_of(zoneDefinition.m_targets_to_build,
[this, &projectName, &targetName](const std::string& buildTargetName)
{
if (buildTargetName == targetName)
{
std::cerr << std::format("Cannot build target with same name: \"{}\"\n", targetName);
return false;
}
std::deque<std::string> targetsToBuild;
std::unordered_set<std::string> alreadyBuiltTargets;
std::cout << std::format("Building referenced target \"{}\"\n", buildTargetName);
return BuildProject(projectName, buildTargetName);
});
}
targetsToBuild.emplace_back(targetName);
bool BuildProject(const std::string& projectName, const std::string& targetName)
{
auto sourceSearchPaths = m_search_paths.GetSourceSearchPathsForProject(projectName);
const auto zoneDefinition = ReadZoneDefinition(targetName, &sourceSearchPaths);
if (!zoneDefinition)
return false;
auto result = true;
if (!zoneDefinition->m_assets.empty())
while (!targetsToBuild.empty())
{
const auto& gameName = GameId_Names[static_cast<unsigned>(zoneDefinition->m_game)];
auto assetSearchPaths = m_search_paths.GetAssetSearchPathsForProject(gameName, projectName);
auto gdtSearchPaths = m_search_paths.GetGdtSearchPathsForProject(gameName, projectName);
const auto currentTarget = std::move(targetsToBuild.front());
targetsToBuild.pop_front();
alreadyBuiltTargets.emplace(currentTarget);
result = result && BuildFastFile(projectName, targetName, *zoneDefinition, assetSearchPaths, gdtSearchPaths, sourceSearchPaths);
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);
std::cout << std::format("Building referenced target \"{}\"\n", referencedTarget);
}
}
}
}
m_search_paths.UnloadProjectSpecificSearchPaths();
result = result && BuildReferencedTargets(projectName, targetName, *zoneDefinition);
return result;
return true;
}
bool LoadZones()
@@ -434,11 +549,6 @@ class LinkerImpl final : public Linker
}
public:
LinkerImpl()
: m_search_paths(m_args)
{
}
bool Start(const int argc, const char** argv) override
{
auto shouldContinue = true;
@@ -448,8 +558,7 @@ public:
if (!shouldContinue)
return true;
if (!m_search_paths.BuildProjectIndependentSearchPaths())
return false;
LinkerPathManager paths(m_args);
if (!LoadZones())
return false;
@@ -465,7 +574,7 @@ public:
break;
}
if (!BuildProject(projectName, targetName))
if (!BuildProject(paths, projectName, targetName))
{
result = false;
break;
@@ -476,6 +585,10 @@ public:
return result;
}
private:
LinkerArgs m_args;
std::vector<std::unique_ptr<Zone>> m_loaded_zones;
};
std::unique_ptr<Linker> Linker::Create()