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:
@@ -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()
|
||||
|
||||
Reference in New Issue
Block a user