mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-19 15:52:53 +00:00
refactor: rework search paths
This commit is contained in:
parent
fd421c4784
commit
be6c30c503
@ -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)
|
||||
while (!targetsToBuild.empty())
|
||||
{
|
||||
auto sourceSearchPaths = m_search_paths.GetSourceSearchPathsForProject(projectName);
|
||||
const auto currentTarget = std::move(targetsToBuild.front());
|
||||
targetsToBuild.pop_front();
|
||||
alreadyBuiltTargets.emplace(currentTarget);
|
||||
|
||||
const auto zoneDefinition = ReadZoneDefinition(targetName, &sourceSearchPaths);
|
||||
PathProjectContext projectContext(paths, projectName);
|
||||
|
||||
const auto zoneDefinition = ReadZoneDefinition(paths, targetName);
|
||||
if (!zoneDefinition)
|
||||
return false;
|
||||
|
||||
auto result = true;
|
||||
PathGameContext gameContext(paths, projectName, zoneDefinition->m_game);
|
||||
|
||||
if (!zoneDefinition->m_assets.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);
|
||||
if (!BuildFastFile(paths, projectName, targetName, *zoneDefinition))
|
||||
return false;
|
||||
|
||||
result = result && BuildFastFile(projectName, targetName, *zoneDefinition, assetSearchPaths, gdtSearchPaths, sourceSearchPaths);
|
||||
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()
|
||||
|
@ -9,7 +9,6 @@
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <regex>
|
||||
#include <type_traits>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
@ -129,13 +128,7 @@ const CommandLineOption* const COMMAND_LINE_OPTIONS[]{
|
||||
|
||||
LinkerArgs::LinkerArgs()
|
||||
: m_verbose(false),
|
||||
m_base_folder_depends_on_project(false),
|
||||
m_out_folder_depends_on_project(false),
|
||||
m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v<decltype(COMMAND_LINE_OPTIONS)>),
|
||||
m_bin_pattern(R"(\?bin\?)"),
|
||||
m_base_pattern(R"(\?base\?)"),
|
||||
m_game_pattern(R"(\?game\?)"),
|
||||
m_project_pattern(R"(\?project\?)")
|
||||
m_argument_parser(COMMAND_LINE_OPTIONS, std::extent_v<decltype(COMMAND_LINE_OPTIONS)>)
|
||||
{
|
||||
}
|
||||
|
||||
@ -172,76 +165,6 @@ void LinkerArgs::SetVerbose(const bool isVerbose)
|
||||
ObjWriting::Configuration.Verbose = isVerbose;
|
||||
}
|
||||
|
||||
std::string LinkerArgs::GetBasePathForProject(const std::string& projectName) const
|
||||
{
|
||||
return std::regex_replace(m_base_folder, m_project_pattern, projectName);
|
||||
}
|
||||
|
||||
void LinkerArgs::SetDefaultBasePath()
|
||||
{
|
||||
const auto currentDir = fs::absolute(fs::current_path());
|
||||
|
||||
if (currentDir.filename() == "bin")
|
||||
{
|
||||
m_base_folder = currentDir.parent_path().string();
|
||||
}
|
||||
else
|
||||
{
|
||||
m_base_folder = currentDir.string();
|
||||
}
|
||||
}
|
||||
|
||||
std::set<std::string> LinkerArgs::GetProjectIndependentSearchPaths(const std::set<std::string>& set) const
|
||||
{
|
||||
std::set<std::string> out;
|
||||
|
||||
for (const auto& path : set)
|
||||
{
|
||||
if (path.find(PATTERN_GAME) != std::string::npos)
|
||||
continue;
|
||||
if (path.find(PATTERN_PROJECT) != std::string::npos)
|
||||
continue;
|
||||
|
||||
if (path.find(PATTERN_BASE) != std::string::npos)
|
||||
{
|
||||
if (m_base_folder_depends_on_project)
|
||||
continue;
|
||||
|
||||
out.emplace(std::regex_replace(path, m_base_pattern, m_base_folder));
|
||||
}
|
||||
else
|
||||
{
|
||||
out.emplace(path);
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
std::set<std::string> LinkerArgs::GetSearchPathsForProject(const std::set<std::string>& set, const std::string& gameName, const std::string& projectName) const
|
||||
{
|
||||
std::set<std::string> out;
|
||||
const auto basePath = GetBasePathForProject(projectName);
|
||||
|
||||
for (const auto& path : set)
|
||||
{
|
||||
if (path.find(PATTERN_GAME) == std::string::npos && path.find(PATTERN_PROJECT) == std::string::npos
|
||||
&& (!m_base_folder_depends_on_project || path.find(PATTERN_BASE) == std::string::npos))
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
fs::path p(std::regex_replace(std::regex_replace(std::regex_replace(std::regex_replace(path, m_project_pattern, projectName), m_game_pattern, gameName),
|
||||
m_base_pattern,
|
||||
basePath),
|
||||
m_bin_pattern,
|
||||
m_bin_folder));
|
||||
out.emplace(p.make_preferred().string());
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
bool LinkerArgs::ParseArgs(const int argc, const char** argv, bool& shouldContinue)
|
||||
{
|
||||
shouldContinue = true;
|
||||
@ -284,15 +207,13 @@ bool LinkerArgs::ParseArgs(const int argc, const char** argv, bool& shouldContin
|
||||
if (m_argument_parser.IsOptionSpecified(OPTION_BASE_FOLDER))
|
||||
m_base_folder = m_argument_parser.GetValueForOption(OPTION_BASE_FOLDER);
|
||||
else
|
||||
SetDefaultBasePath();
|
||||
m_base_folder_depends_on_project = m_base_folder.find(PATTERN_GAME) != std::string::npos || m_base_folder.find(PATTERN_PROJECT) != std::string::npos;
|
||||
m_base_folder = DEFAULT_BASE_FOLDER;
|
||||
|
||||
// --output-folder
|
||||
if (m_argument_parser.IsOptionSpecified(OPTION_OUTPUT_FOLDER))
|
||||
m_out_folder = m_argument_parser.GetValueForOption(OPTION_OUTPUT_FOLDER);
|
||||
else
|
||||
m_out_folder = DEFAULT_OUTPUT_FOLDER;
|
||||
m_out_folder_depends_on_project = m_out_folder.find(PATTERN_PROJECT) != std::string::npos;
|
||||
|
||||
// --asset-search-path
|
||||
if (m_argument_parser.IsOptionSpecified(OPTION_ASSET_SEARCH_PATH))
|
||||
@ -358,38 +279,3 @@ bool LinkerArgs::ParseArgs(const int argc, const char** argv, bool& shouldContin
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string LinkerArgs::GetOutputFolderPathForProject(const std::string& projectName) const
|
||||
{
|
||||
return std::regex_replace(std::regex_replace(m_out_folder, m_project_pattern, projectName), m_base_pattern, GetBasePathForProject(projectName));
|
||||
}
|
||||
|
||||
std::set<std::string> LinkerArgs::GetProjectIndependentAssetSearchPaths() const
|
||||
{
|
||||
return GetProjectIndependentSearchPaths(m_asset_search_paths);
|
||||
}
|
||||
|
||||
std::set<std::string> LinkerArgs::GetProjectIndependentGdtSearchPaths() const
|
||||
{
|
||||
return GetProjectIndependentSearchPaths(m_gdt_search_paths);
|
||||
}
|
||||
|
||||
std::set<std::string> LinkerArgs::GetProjectIndependentSourceSearchPaths() const
|
||||
{
|
||||
return GetProjectIndependentSearchPaths(m_source_search_paths);
|
||||
}
|
||||
|
||||
std::set<std::string> LinkerArgs::GetAssetSearchPathsForProject(const std::string& gameName, const std::string& projectName) const
|
||||
{
|
||||
return GetSearchPathsForProject(m_asset_search_paths, gameName, projectName);
|
||||
}
|
||||
|
||||
std::set<std::string> LinkerArgs::GetGdtSearchPathsForProject(const std::string& gameName, const std::string& projectName) const
|
||||
{
|
||||
return GetSearchPathsForProject(m_gdt_search_paths, gameName, projectName);
|
||||
}
|
||||
|
||||
std::set<std::string> LinkerArgs::GetSourceSearchPathsForProject(const std::string& projectName) const
|
||||
{
|
||||
return GetSearchPathsForProject(m_source_search_paths, "", projectName);
|
||||
}
|
||||
|
@ -1,22 +1,14 @@
|
||||
#pragma once
|
||||
#include "Utils/Arguments/ArgumentParser.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <vector>
|
||||
|
||||
class LinkerArgs
|
||||
{
|
||||
public:
|
||||
static constexpr auto PATTERN_BIN = "?bin?";
|
||||
static constexpr auto PATTERN_BASE = "?base?";
|
||||
static constexpr auto PATTERN_GAME = "?game?";
|
||||
static constexpr auto PATTERN_PROJECT = "?project?";
|
||||
|
||||
static constexpr auto DEFAULT_BASE_FOLDER = ".";
|
||||
static constexpr auto DEFAULT_BASE_FOLDER_MOD_TOOLS = "..";
|
||||
static constexpr auto DEFAULT_CACHE_FOLDER = "?base?/.oat/cache/?project?";
|
||||
static constexpr auto DEFAULT_OUTPUT_FOLDER = "?base?/zone_out/?project?";
|
||||
static constexpr auto DEFAULT_ASSET_SEARCH_PATH = "?bin?/raw/?game?;?base?/raw;?base?/raw/?game?;?base?/zone_raw/?project?";
|
||||
static constexpr auto DEFAULT_GDT_SEARCH_PATH = "?base?/source_data;?base?/zone_raw/?project?/source_data";
|
||||
@ -25,21 +17,6 @@ public:
|
||||
LinkerArgs();
|
||||
bool ParseArgs(int argc, const char** argv, bool& shouldContinue);
|
||||
|
||||
/**
|
||||
* \brief Converts the output path specified by command line arguments to a path applies for the specified project.
|
||||
* \param projectName The name of the project to resolve the path input for.
|
||||
* \return An output path for the project based on the user input.
|
||||
*/
|
||||
_NODISCARD std::string GetOutputFolderPathForProject(const std::string& projectName) const;
|
||||
|
||||
_NODISCARD std::set<std::string> GetProjectIndependentAssetSearchPaths() const;
|
||||
_NODISCARD std::set<std::string> GetProjectIndependentGdtSearchPaths() const;
|
||||
_NODISCARD std::set<std::string> GetProjectIndependentSourceSearchPaths() const;
|
||||
|
||||
_NODISCARD std::set<std::string> GetAssetSearchPathsForProject(const std::string& gameName, const std::string& projectName) const;
|
||||
_NODISCARD std::set<std::string> GetGdtSearchPathsForProject(const std::string& gameName, const std::string& projectName) const;
|
||||
_NODISCARD std::set<std::string> GetSourceSearchPathsForProject(const std::string& projectName) const;
|
||||
|
||||
bool m_verbose;
|
||||
|
||||
std::vector<std::string> m_zones_to_load;
|
||||
@ -48,8 +25,6 @@ public:
|
||||
std::string m_bin_folder;
|
||||
std::string m_base_folder;
|
||||
std::string m_out_folder;
|
||||
bool m_base_folder_depends_on_project;
|
||||
bool m_out_folder_depends_on_project;
|
||||
|
||||
std::set<std::string> m_asset_search_paths;
|
||||
std::set<std::string> m_gdt_search_paths;
|
||||
@ -63,18 +38,7 @@ private:
|
||||
static void PrintVersion();
|
||||
|
||||
void SetBinFolder(const char* argv0);
|
||||
|
||||
void SetVerbose(bool isVerbose);
|
||||
|
||||
_NODISCARD std::string GetBasePathForProject(const std::string& projectName) const;
|
||||
void SetDefaultBasePath();
|
||||
_NODISCARD std::set<std::string> GetProjectIndependentSearchPaths(const std::set<std::string>& set) const;
|
||||
_NODISCARD std::set<std::string>
|
||||
GetSearchPathsForProject(const std::set<std::string>& set, const std::string& gameName, const std::string& projectName) const;
|
||||
|
||||
ArgumentParser m_argument_parser;
|
||||
std::regex m_bin_pattern;
|
||||
std::regex m_base_pattern;
|
||||
std::regex m_game_pattern;
|
||||
std::regex m_project_pattern;
|
||||
};
|
||||
|
325
src/Linker/LinkerPaths.cpp
Normal file
325
src/Linker/LinkerPaths.cpp
Normal file
@ -0,0 +1,325 @@
|
||||
#include "LinkerPaths.h"
|
||||
|
||||
#include "SearchPath/IWD.h"
|
||||
#include "SearchPath/SearchPathFilesystem.h"
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <sstream>
|
||||
#include <type_traits>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum class PathTemplateParameterType : std::uint8_t
|
||||
{
|
||||
BIN = 1 << 0,
|
||||
BASE = 1 << 1,
|
||||
PROJECT = 1 << 2,
|
||||
GAME = 1 << 3,
|
||||
};
|
||||
|
||||
class LinkerPathTemplate
|
||||
{
|
||||
static constexpr auto PATTERN_BIN = "?bin?";
|
||||
static constexpr auto PATTERN_BASE = "?base?";
|
||||
static constexpr auto PATTERN_GAME = "?game?";
|
||||
static constexpr auto PATTERN_PROJECT = "?project?";
|
||||
|
||||
struct Pattern
|
||||
{
|
||||
const char* m_str;
|
||||
PathTemplateParameterType m_type;
|
||||
};
|
||||
|
||||
static constexpr Pattern PATTERNS[]{
|
||||
{PATTERN_BIN, PathTemplateParameterType::BIN },
|
||||
{PATTERN_BASE, PathTemplateParameterType::BASE },
|
||||
{PATTERN_GAME, PathTemplateParameterType::GAME },
|
||||
{PATTERN_PROJECT, PathTemplateParameterType::PROJECT},
|
||||
};
|
||||
|
||||
public:
|
||||
LinkerPathTemplate()
|
||||
: m_parameter_type_flags(0u)
|
||||
{
|
||||
}
|
||||
|
||||
void CreateFromString(const std::string& templateString)
|
||||
{
|
||||
const auto templateStringLength = templateString.size();
|
||||
auto partStart = 0u;
|
||||
for (auto i = 0u; i < templateStringLength; i++)
|
||||
{
|
||||
if (templateString[i] != '?')
|
||||
continue;
|
||||
|
||||
for (const auto& pattern : PATTERNS)
|
||||
{
|
||||
const auto patternLength = std::strlen(pattern.m_str);
|
||||
if (templateString.compare(i, patternLength, pattern.m_str) == 0)
|
||||
{
|
||||
m_parts.emplace_back(templateString.substr(partStart, i - partStart));
|
||||
m_parameters.emplace_back(pattern.m_type);
|
||||
m_parameter_type_flags |= static_cast<std::underlying_type_t<PathTemplateParameterType>>(pattern.m_type);
|
||||
i += patternLength;
|
||||
|
||||
partStart = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (partStart < templateStringLength)
|
||||
m_parts.emplace_back(templateString.substr(partStart, templateStringLength - partStart));
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string
|
||||
Render(const std::string& binDir, const std::string& baseDir, const std::string& projectName, const std::string& gameName) const
|
||||
{
|
||||
if (m_parts.empty())
|
||||
return "";
|
||||
|
||||
if (m_parameters.empty())
|
||||
return m_parts[0];
|
||||
|
||||
std::ostringstream ss;
|
||||
ss << m_parts[0];
|
||||
|
||||
const auto partsCount = m_parts.size();
|
||||
const auto parameterCount = m_parameters.size();
|
||||
for (auto parameterIndex = 0u; parameterIndex < parameterCount; parameterIndex++)
|
||||
{
|
||||
switch (m_parameters[parameterIndex])
|
||||
{
|
||||
case PathTemplateParameterType::BIN:
|
||||
ss << binDir;
|
||||
break;
|
||||
case PathTemplateParameterType::BASE:
|
||||
ss << baseDir;
|
||||
break;
|
||||
case PathTemplateParameterType::PROJECT:
|
||||
ss << projectName;
|
||||
break;
|
||||
case PathTemplateParameterType::GAME:
|
||||
ss << gameName;
|
||||
break;
|
||||
default:
|
||||
assert(false);
|
||||
break;
|
||||
}
|
||||
|
||||
if (parameterIndex + 1 < partsCount)
|
||||
ss << m_parts[parameterIndex + 1];
|
||||
}
|
||||
|
||||
return ss.str();
|
||||
}
|
||||
|
||||
[[nodiscard]] bool CanRender(const std::underlying_type_t<PathTemplateParameterType> availableParameters) const
|
||||
{
|
||||
return (m_parameter_type_flags & ~availableParameters) == 0;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::string> m_parts;
|
||||
std::vector<PathTemplateParameterType> m_parameters;
|
||||
std::underlying_type_t<PathTemplateParameterType> m_parameter_type_flags;
|
||||
};
|
||||
|
||||
class LinkerSearchPathBuilder final : public ILinkerSearchPathBuilder
|
||||
{
|
||||
static constexpr auto INDEPENDENT_MASK = static_cast<unsigned>(PathTemplateParameterType::BIN) | static_cast<unsigned>(PathTemplateParameterType::BASE);
|
||||
static constexpr auto PROJECT_MASK = static_cast<unsigned>(PathTemplateParameterType::BIN) | static_cast<unsigned>(PathTemplateParameterType::BASE)
|
||||
| static_cast<unsigned>(PathTemplateParameterType::PROJECT);
|
||||
static constexpr auto GAME_MASK = static_cast<unsigned>(PathTemplateParameterType::BIN) | static_cast<unsigned>(PathTemplateParameterType::BASE)
|
||||
| static_cast<unsigned>(PathTemplateParameterType::PROJECT) | static_cast<unsigned>(PathTemplateParameterType::GAME);
|
||||
|
||||
public:
|
||||
LinkerSearchPathBuilder(const char* typeName, const std::string& binDir, const std::string& baseDir)
|
||||
: m_type_name(typeName),
|
||||
m_bin_dir(binDir),
|
||||
m_base_dir(baseDir)
|
||||
{
|
||||
}
|
||||
|
||||
void BuildFromArgs(const std::set<std::string>& templates)
|
||||
{
|
||||
m_templates.reserve(templates.size());
|
||||
for (const auto& templateString : templates)
|
||||
{
|
||||
LinkerPathTemplate templateStruct;
|
||||
templateStruct.CreateFromString(templateString);
|
||||
m_templates.emplace_back(std::move(templateStruct));
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ISearchPath> BuildIndependentSearchPaths() const override
|
||||
{
|
||||
SearchPaths searchPaths;
|
||||
auto hasSearchPath = false;
|
||||
|
||||
for (const auto& curTemplate : m_templates)
|
||||
{
|
||||
if (curTemplate.CanRender(INDEPENDENT_MASK))
|
||||
{
|
||||
auto renderedTemplate = curTemplate.Render(m_bin_dir, m_base_dir, std::string(), std::string());
|
||||
if (AddSearchPath(searchPaths, renderedTemplate))
|
||||
hasSearchPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSearchPath)
|
||||
return std::make_unique<SearchPaths>(std::move(searchPaths));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ISearchPath> BuildSearchPathsSpecificToProject(const std::string& projectName) const override
|
||||
{
|
||||
SearchPaths searchPaths;
|
||||
auto hasSearchPath = false;
|
||||
|
||||
for (const auto& curTemplate : m_templates)
|
||||
{
|
||||
if (!curTemplate.CanRender(INDEPENDENT_MASK) && curTemplate.CanRender(PROJECT_MASK))
|
||||
{
|
||||
auto renderedTemplate = curTemplate.Render(m_bin_dir, m_base_dir, projectName, std::string());
|
||||
if (AddSearchPath(searchPaths, renderedTemplate))
|
||||
hasSearchPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSearchPath)
|
||||
return std::make_unique<SearchPaths>(std::move(searchPaths));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
[[nodiscard]] std::unique_ptr<ISearchPath> BuildSearchPathsSpecificToProjectAndGame(const std::string& projectName, GameId game) const override
|
||||
{
|
||||
SearchPaths searchPaths;
|
||||
auto hasSearchPath = false;
|
||||
|
||||
for (const auto& curTemplate : m_templates)
|
||||
{
|
||||
if (!curTemplate.CanRender(PROJECT_MASK) && curTemplate.CanRender(GAME_MASK))
|
||||
{
|
||||
auto renderedTemplate = curTemplate.Render(m_bin_dir, m_base_dir, projectName, GameId_Names[static_cast<unsigned>(game)]);
|
||||
if (AddSearchPath(searchPaths, renderedTemplate))
|
||||
hasSearchPath = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (hasSearchPath)
|
||||
return std::make_unique<SearchPaths>(std::move(searchPaths));
|
||||
|
||||
return {};
|
||||
}
|
||||
|
||||
private:
|
||||
bool AddSearchPath(SearchPaths& searchPaths, const std::string& path) const
|
||||
{
|
||||
if (!fs::is_directory(path))
|
||||
{
|
||||
std::cout << std::format("Adding {} search path (Not found): {}\n", m_type_name, path);
|
||||
return false;
|
||||
}
|
||||
|
||||
std::cout << std::format("Adding {} search path: {}\n", m_type_name, path);
|
||||
searchPaths.CommitSearchPath(std::make_unique<SearchPathFilesystem>(path));
|
||||
return true;
|
||||
}
|
||||
|
||||
const char* m_type_name;
|
||||
std::vector<LinkerPathTemplate> m_templates;
|
||||
const std::string& m_bin_dir;
|
||||
const std::string& m_base_dir;
|
||||
};
|
||||
|
||||
class LinkerPaths final : public ILinkerPaths
|
||||
{
|
||||
public:
|
||||
LinkerPaths(std::string binDir,
|
||||
std::string baseDir,
|
||||
LinkerSearchPathBuilder assetSearchPaths,
|
||||
LinkerSearchPathBuilder gdtSearchPaths,
|
||||
LinkerSearchPathBuilder sourceSearchPaths,
|
||||
LinkerPathTemplate cacheTemplate,
|
||||
LinkerPathTemplate outTemplate)
|
||||
: m_bin_dir(std::move(binDir)),
|
||||
m_base_dir(std::move(baseDir)),
|
||||
m_asset_search_paths(std::move(assetSearchPaths)),
|
||||
m_gdt_search_paths(std::move(gdtSearchPaths)),
|
||||
m_source_search_paths(std::move(sourceSearchPaths)),
|
||||
m_cache_template(std::move(cacheTemplate)),
|
||||
m_out_template(std::move(outTemplate))
|
||||
{
|
||||
}
|
||||
|
||||
[[nodiscard]] const ILinkerSearchPathBuilder& AssetSearchPaths() const override
|
||||
{
|
||||
return m_asset_search_paths;
|
||||
}
|
||||
|
||||
[[nodiscard]] const ILinkerSearchPathBuilder& GdtSearchPaths() const override
|
||||
{
|
||||
return m_gdt_search_paths;
|
||||
}
|
||||
|
||||
[[nodiscard]] const ILinkerSearchPathBuilder& SourceSearchPaths() const override
|
||||
{
|
||||
return m_source_search_paths;
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string BuildCacheFolderPath(const std::string& projectName, GameId game) const override
|
||||
{
|
||||
return m_cache_template.Render(m_bin_dir, m_base_dir, projectName, GameId_Names[static_cast<unsigned>(game)]);
|
||||
}
|
||||
|
||||
[[nodiscard]] std::string BuildOutputFolderPath(const std::string& projectName, GameId game) const override
|
||||
{
|
||||
return m_out_template.Render(m_bin_dir, m_base_dir, projectName, GameId_Names[static_cast<unsigned>(game)]);
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_bin_dir;
|
||||
std::string m_base_dir;
|
||||
LinkerSearchPathBuilder m_asset_search_paths;
|
||||
LinkerSearchPathBuilder m_gdt_search_paths;
|
||||
LinkerSearchPathBuilder m_source_search_paths;
|
||||
LinkerPathTemplate m_cache_template;
|
||||
LinkerPathTemplate m_out_template;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ILinkerPaths> ILinkerPaths::FromArgs(const LinkerArgs& args)
|
||||
{
|
||||
LinkerSearchPathBuilder assetSearchPaths("asset", args.m_bin_folder, args.m_base_folder);
|
||||
assetSearchPaths.BuildFromArgs(args.m_asset_search_paths);
|
||||
|
||||
LinkerSearchPathBuilder gdtSearchPaths("gdt", args.m_bin_folder, args.m_base_folder);
|
||||
gdtSearchPaths.BuildFromArgs(args.m_gdt_search_paths);
|
||||
|
||||
LinkerSearchPathBuilder sourceSearchPaths("source", args.m_bin_folder, args.m_base_folder);
|
||||
sourceSearchPaths.BuildFromArgs(args.m_source_search_paths);
|
||||
|
||||
LinkerPathTemplate cacheTemplate;
|
||||
cacheTemplate.CreateFromString(args.DEFAULT_CACHE_FOLDER);
|
||||
|
||||
LinkerPathTemplate outTemplate;
|
||||
outTemplate.CreateFromString(args.m_out_folder);
|
||||
|
||||
return std::make_unique<LinkerPaths>(args.m_bin_folder,
|
||||
args.m_base_folder,
|
||||
std::move(assetSearchPaths),
|
||||
std::move(gdtSearchPaths),
|
||||
std::move(sourceSearchPaths),
|
||||
std::move(cacheTemplate),
|
||||
std::move(outTemplate));
|
||||
}
|
74
src/Linker/LinkerPaths.h
Normal file
74
src/Linker/LinkerPaths.h
Normal file
@ -0,0 +1,74 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/IGame.h"
|
||||
#include "LinkerArgs.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class ILinkerSearchPathBuilder
|
||||
{
|
||||
public:
|
||||
ILinkerSearchPathBuilder() = default;
|
||||
virtual ~ILinkerSearchPathBuilder() = default;
|
||||
ILinkerSearchPathBuilder(const ILinkerSearchPathBuilder& other) = default;
|
||||
ILinkerSearchPathBuilder(ILinkerSearchPathBuilder&& other) noexcept = default;
|
||||
ILinkerSearchPathBuilder& operator=(const ILinkerSearchPathBuilder& other) = default;
|
||||
ILinkerSearchPathBuilder& operator=(ILinkerSearchPathBuilder&& other) noexcept = default;
|
||||
|
||||
[[nodiscard]] virtual std::unique_ptr<ISearchPath> BuildIndependentSearchPaths() const = 0;
|
||||
[[nodiscard]] virtual std::unique_ptr<ISearchPath> BuildSearchPathsSpecificToProject(const std::string& projectName) const = 0;
|
||||
[[nodiscard]] virtual std::unique_ptr<ISearchPath> BuildSearchPathsSpecificToProjectAndGame(const std::string& projectName, GameId game) const = 0;
|
||||
};
|
||||
|
||||
class ILinkerPaths
|
||||
{
|
||||
public:
|
||||
ILinkerPaths() = default;
|
||||
virtual ~ILinkerPaths() = default;
|
||||
ILinkerPaths(const ILinkerPaths& other) = default;
|
||||
ILinkerPaths(ILinkerPaths&& other) noexcept = default;
|
||||
ILinkerPaths& operator=(const ILinkerPaths& other) = default;
|
||||
ILinkerPaths& operator=(ILinkerPaths&& other) noexcept = default;
|
||||
|
||||
/**
|
||||
* \brief Creates linker search paths based on templates from user specified args.
|
||||
* \param args The user specified args.
|
||||
* \return Linker search paths based on user specified templates.
|
||||
*/
|
||||
static std::unique_ptr<ILinkerPaths> FromArgs(const LinkerArgs& args);
|
||||
|
||||
/**
|
||||
* \brief Grants access to the builder for asset search paths.
|
||||
* \return A builder instance for building asset search paths.
|
||||
*/
|
||||
[[nodiscard]] virtual const ILinkerSearchPathBuilder& AssetSearchPaths() const = 0;
|
||||
|
||||
/**
|
||||
* \brief Grants access to the builder for gdt search paths.
|
||||
* \return A builder instance for building gdt search paths.
|
||||
*/
|
||||
[[nodiscard]] virtual const ILinkerSearchPathBuilder& GdtSearchPaths() const = 0;
|
||||
|
||||
/**
|
||||
* \brief Grants access to the builder for source search paths.
|
||||
* \return A builder instance for building source search paths.
|
||||
*/
|
||||
[[nodiscard]] virtual const ILinkerSearchPathBuilder& SourceSearchPaths() const = 0;
|
||||
|
||||
/**
|
||||
* \brief Builds the cache path based on the specified information.
|
||||
* \param projectName The name of the project to resolve the path input for.
|
||||
* \param game The game to resolve the path input for.
|
||||
* \return A cache path based on the input and preconfigured template.
|
||||
*/
|
||||
[[nodiscard]] virtual std::string BuildCacheFolderPath(const std::string& projectName, GameId game) const = 0;
|
||||
|
||||
/**
|
||||
* \brief Builds the output path based on the specified information.
|
||||
* \param projectName The name of the project to resolve the path input for.
|
||||
* \param game The game to resolve the path input for.
|
||||
* \return An output path based on the input and preconfigured template.
|
||||
*/
|
||||
[[nodiscard]] virtual std::string BuildOutputFolderPath(const std::string& projectName, GameId game) const = 0;
|
||||
};
|
@ -1,190 +0,0 @@
|
||||
#include "LinkerSearchPaths.h"
|
||||
|
||||
#include "ObjContainer/IWD/IWD.h"
|
||||
#include "ObjLoading.h"
|
||||
#include "SearchPath/SearchPathFilesystem.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
LinkerSearchPaths::LinkerSearchPaths(const LinkerArgs& args)
|
||||
: m_args(args)
|
||||
{
|
||||
}
|
||||
|
||||
void LinkerSearchPaths::LoadSearchPath(ISearchPath& searchPath) const
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
{
|
||||
std::cout << std::format("Loading search path: \"{}\"\n", searchPath.GetPath());
|
||||
}
|
||||
|
||||
ObjLoading::LoadIWDsInSearchPath(searchPath);
|
||||
}
|
||||
|
||||
void LinkerSearchPaths::UnloadSearchPath(ISearchPath& searchPath) const
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
{
|
||||
std::cout << std::format("Unloading search path: \"{}\"\n", searchPath.GetPath());
|
||||
}
|
||||
|
||||
ObjLoading::UnloadIWDsInSearchPath(searchPath);
|
||||
}
|
||||
|
||||
SearchPaths LinkerSearchPaths::GetAssetSearchPathsForProject(const std::string& gameName, const std::string& projectName)
|
||||
{
|
||||
SearchPaths searchPathsForProject;
|
||||
|
||||
for (const auto& searchPathStr : m_args.GetAssetSearchPathsForProject(gameName, projectName))
|
||||
{
|
||||
auto absolutePath = fs::absolute(searchPathStr);
|
||||
|
||||
if (!fs::is_directory(absolutePath))
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding asset search path (Not found): {}\n", absolutePath.string());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding asset search path: {}\n", absolutePath.string());
|
||||
|
||||
auto searchPath = std::make_unique<SearchPathFilesystem>(searchPathStr);
|
||||
LoadSearchPath(*searchPath);
|
||||
searchPathsForProject.IncludeSearchPath(searchPath.get());
|
||||
m_loaded_project_search_paths.emplace_back(std::move(searchPath));
|
||||
}
|
||||
|
||||
searchPathsForProject.IncludeSearchPath(&m_asset_search_paths);
|
||||
|
||||
for (auto* iwd : IWD::Repository)
|
||||
{
|
||||
searchPathsForProject.IncludeSearchPath(iwd);
|
||||
}
|
||||
|
||||
return searchPathsForProject;
|
||||
}
|
||||
|
||||
SearchPaths LinkerSearchPaths::GetGdtSearchPathsForProject(const std::string& gameName, const std::string& projectName)
|
||||
{
|
||||
SearchPaths searchPathsForProject;
|
||||
|
||||
for (const auto& searchPathStr : m_args.GetGdtSearchPathsForProject(gameName, projectName))
|
||||
{
|
||||
auto absolutePath = fs::absolute(searchPathStr);
|
||||
|
||||
if (!fs::is_directory(absolutePath))
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding gdt search path (Not found): {}\n", absolutePath.string());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding gdt search path: {}\n", absolutePath.string());
|
||||
|
||||
searchPathsForProject.CommitSearchPath(std::make_unique<SearchPathFilesystem>(searchPathStr));
|
||||
}
|
||||
|
||||
searchPathsForProject.IncludeSearchPath(&m_gdt_search_paths);
|
||||
|
||||
return searchPathsForProject;
|
||||
}
|
||||
|
||||
SearchPaths LinkerSearchPaths::GetSourceSearchPathsForProject(const std::string& projectName)
|
||||
{
|
||||
SearchPaths searchPathsForProject;
|
||||
|
||||
for (const auto& searchPathStr : m_args.GetSourceSearchPathsForProject(projectName))
|
||||
{
|
||||
auto absolutePath = fs::absolute(searchPathStr);
|
||||
|
||||
if (!fs::is_directory(absolutePath))
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding source search path (Not found): {}\n", absolutePath.string());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding source search path: {}\n", absolutePath.string());
|
||||
|
||||
searchPathsForProject.CommitSearchPath(std::make_unique<SearchPathFilesystem>(searchPathStr));
|
||||
}
|
||||
|
||||
searchPathsForProject.IncludeSearchPath(&m_source_search_paths);
|
||||
|
||||
return searchPathsForProject;
|
||||
}
|
||||
|
||||
bool LinkerSearchPaths::BuildProjectIndependentSearchPaths()
|
||||
{
|
||||
for (const auto& path : m_args.GetProjectIndependentAssetSearchPaths())
|
||||
{
|
||||
auto absolutePath = fs::absolute(path);
|
||||
|
||||
if (!fs::is_directory(absolutePath))
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding asset search path (Not found): {}\n", absolutePath.string());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding asset search path: {}\n", absolutePath.string());
|
||||
|
||||
auto searchPath = std::make_unique<SearchPathFilesystem>(absolutePath.string());
|
||||
LoadSearchPath(*searchPath);
|
||||
m_asset_search_paths.CommitSearchPath(std::move(searchPath));
|
||||
}
|
||||
|
||||
for (const auto& path : m_args.GetProjectIndependentGdtSearchPaths())
|
||||
{
|
||||
auto absolutePath = fs::absolute(path);
|
||||
|
||||
if (!fs::is_directory(absolutePath))
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Loading gdt search path (Not found): {}\n", absolutePath.string());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding gdt search path: {}\n", absolutePath.string());
|
||||
|
||||
m_gdt_search_paths.CommitSearchPath(std::make_unique<SearchPathFilesystem>(absolutePath.string()));
|
||||
}
|
||||
|
||||
for (const auto& path : m_args.GetProjectIndependentSourceSearchPaths())
|
||||
{
|
||||
auto absolutePath = fs::absolute(path);
|
||||
|
||||
if (!fs::is_directory(absolutePath))
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Loading source search path (Not found): {}\n", absolutePath.string());
|
||||
continue;
|
||||
}
|
||||
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Adding source search path: {}\n", absolutePath.string());
|
||||
|
||||
m_source_search_paths.CommitSearchPath(std::make_unique<SearchPathFilesystem>(absolutePath.string()));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void LinkerSearchPaths::UnloadProjectSpecificSearchPaths()
|
||||
{
|
||||
for (const auto& loadedSearchPath : m_loaded_project_search_paths)
|
||||
{
|
||||
UnloadSearchPath(*loadedSearchPath);
|
||||
}
|
||||
|
||||
m_loaded_project_search_paths.clear();
|
||||
}
|
@ -1,45 +0,0 @@
|
||||
#pragma once
|
||||
#include "LinkerArgs.h"
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
class LinkerSearchPaths
|
||||
{
|
||||
public:
|
||||
explicit LinkerSearchPaths(const LinkerArgs& args);
|
||||
|
||||
/**
|
||||
* \brief Loads a search path.
|
||||
* \param searchPath The search path to load.
|
||||
*/
|
||||
void LoadSearchPath(ISearchPath& searchPath) const;
|
||||
|
||||
/**
|
||||
* \brief Unloads a search path.
|
||||
* \param searchPath The search path to unload.
|
||||
*/
|
||||
void UnloadSearchPath(ISearchPath& searchPath) const;
|
||||
|
||||
SearchPaths GetAssetSearchPathsForProject(const std::string& gameName, const std::string& projectName);
|
||||
|
||||
SearchPaths GetGdtSearchPathsForProject(const std::string& gameName, const std::string& projectName);
|
||||
|
||||
SearchPaths GetSourceSearchPathsForProject(const std::string& projectName);
|
||||
|
||||
/**
|
||||
* \brief Initializes the Linker object's search paths based on the user's input.
|
||||
* \return \c true if building the search paths was successful, otherwise \c false.
|
||||
*/
|
||||
bool BuildProjectIndependentSearchPaths();
|
||||
|
||||
void UnloadProjectSpecificSearchPaths();
|
||||
|
||||
private:
|
||||
const LinkerArgs& m_args;
|
||||
std::vector<std::unique_ptr<ISearchPath>> m_loaded_project_search_paths;
|
||||
SearchPaths m_asset_search_paths;
|
||||
SearchPaths m_gdt_search_paths;
|
||||
SearchPaths m_source_search_paths;
|
||||
};
|
@ -1,363 +0,0 @@
|
||||
#include "IWD.h"
|
||||
|
||||
#include "ObjLoading.h"
|
||||
#include "Utils/FileToZlibWrapper.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unzip.h>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
ObjContainerRepository<IWD, ISearchPath> IWD::Repository;
|
||||
|
||||
class IWDFile final : public objbuf
|
||||
{
|
||||
public:
|
||||
class IParent
|
||||
{
|
||||
public:
|
||||
virtual ~IParent() = default;
|
||||
|
||||
virtual void OnIWDFileClose() = 0;
|
||||
};
|
||||
|
||||
private:
|
||||
IParent* m_parent;
|
||||
bool m_open;
|
||||
int64_t m_size;
|
||||
unzFile m_container;
|
||||
bool m_peeked;
|
||||
int_type m_peek_symbol;
|
||||
|
||||
public:
|
||||
IWDFile(IParent* parent, const unzFile container, const int64_t size)
|
||||
: m_parent(parent),
|
||||
m_open(true),
|
||||
m_size(size),
|
||||
m_container(container),
|
||||
m_peeked(false),
|
||||
m_peek_symbol(0)
|
||||
{
|
||||
}
|
||||
|
||||
~IWDFile() override
|
||||
{
|
||||
if (m_open)
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
protected:
|
||||
int_type underflow() override
|
||||
{
|
||||
if (m_peeked)
|
||||
return m_peek_symbol;
|
||||
|
||||
const auto result = unzReadCurrentFile(m_container, &m_peek_symbol, 1u);
|
||||
|
||||
if (result >= 0)
|
||||
{
|
||||
m_peeked = true;
|
||||
return static_cast<int_type>(m_peek_symbol);
|
||||
}
|
||||
|
||||
return EOF;
|
||||
}
|
||||
|
||||
int_type uflow() override
|
||||
{
|
||||
if (m_peeked)
|
||||
{
|
||||
m_peeked = false;
|
||||
return m_peek_symbol;
|
||||
}
|
||||
|
||||
const auto result = unzReadCurrentFile(m_container, &m_peek_symbol, 1u);
|
||||
return result >= 0 ? static_cast<int_type>(m_peek_symbol) : EOF;
|
||||
}
|
||||
|
||||
std::streamsize xsgetn(char* ptr, std::streamsize count) override
|
||||
{
|
||||
if (m_peeked && count >= 1)
|
||||
{
|
||||
*ptr = static_cast<char>(m_peek_symbol);
|
||||
ptr++;
|
||||
count--;
|
||||
}
|
||||
|
||||
const auto result = unzReadCurrentFile(m_container, ptr, static_cast<unsigned>(count));
|
||||
|
||||
return result >= 0 ? static_cast<std::streamsize>(result) : 0;
|
||||
}
|
||||
|
||||
pos_type seekoff(const off_type off, const std::ios_base::seekdir dir, const std::ios_base::openmode mode) override
|
||||
{
|
||||
const auto currentPos = unztell64(m_container);
|
||||
|
||||
pos_type targetPos;
|
||||
if (dir == std::ios_base::beg)
|
||||
{
|
||||
targetPos = off;
|
||||
}
|
||||
else if (dir == std::ios_base::cur)
|
||||
{
|
||||
targetPos = currentPos + off;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetPos = m_size - off;
|
||||
}
|
||||
|
||||
return seekpos(targetPos, mode);
|
||||
}
|
||||
|
||||
pos_type seekpos(const pos_type pos, const std::ios_base::openmode mode) override
|
||||
{
|
||||
const auto currentPos = unztell64(m_container);
|
||||
|
||||
if (static_cast<pos_type>(currentPos) < pos)
|
||||
{
|
||||
auto skipAmount = pos - static_cast<pos_type>(currentPos);
|
||||
while (skipAmount > 0)
|
||||
{
|
||||
char temp[1024];
|
||||
const auto toRead = skipAmount > sizeof(temp) ? sizeof(temp) : static_cast<size_t>(skipAmount);
|
||||
unzReadCurrentFile(m_container, temp, toRead);
|
||||
skipAmount -= toRead;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
if (currentPos == pos)
|
||||
{
|
||||
// This is fine
|
||||
return currentPos;
|
||||
}
|
||||
|
||||
return std::streampos(-1);
|
||||
}
|
||||
|
||||
public:
|
||||
_NODISCARD bool is_open() const override
|
||||
{
|
||||
return m_open;
|
||||
}
|
||||
|
||||
bool close() override
|
||||
{
|
||||
unzCloseCurrentFile(m_container);
|
||||
m_open = false;
|
||||
|
||||
m_parent->OnIWDFileClose();
|
||||
|
||||
return true;
|
||||
}
|
||||
};
|
||||
|
||||
class IWD::Impl : public ISearchPath, public IObjContainer, public IWDFile::IParent
|
||||
{
|
||||
class IWDEntry
|
||||
{
|
||||
public:
|
||||
int64_t m_size{};
|
||||
unz_file_pos m_file_pos{};
|
||||
};
|
||||
|
||||
std::string m_path;
|
||||
std::unique_ptr<std::istream> m_stream;
|
||||
unzFile m_unz_file;
|
||||
|
||||
IWDFile* m_last_file;
|
||||
|
||||
std::map<std::string, IWDEntry> m_entry_map;
|
||||
|
||||
public:
|
||||
Impl(std::string path, std::unique_ptr<std::istream> stream)
|
||||
: m_path(std::move(path)),
|
||||
m_stream(std::move(stream)),
|
||||
m_unz_file(nullptr),
|
||||
m_last_file(nullptr)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl() override
|
||||
{
|
||||
if (m_unz_file != nullptr)
|
||||
{
|
||||
unzClose(m_unz_file);
|
||||
m_unz_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Impl(const Impl& other) = delete;
|
||||
Impl(Impl&& other) noexcept = default;
|
||||
Impl& operator=(const Impl& other) = delete;
|
||||
Impl& operator=(Impl&& other) noexcept = default;
|
||||
|
||||
bool Initialize()
|
||||
{
|
||||
auto ioFunctions = FileToZlibWrapper::CreateFunctions32ForFile(m_stream.get());
|
||||
m_unz_file = unzOpen2("", &ioFunctions);
|
||||
|
||||
if (m_unz_file == nullptr)
|
||||
{
|
||||
printf("Could not open IWD \"%s\"\n", m_path.c_str());
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ret = unzGoToFirstFile(m_unz_file);
|
||||
while (ret == Z_OK)
|
||||
{
|
||||
unz_file_info64 info;
|
||||
char fileNameBuffer[256];
|
||||
unzGetCurrentFileInfo64(m_unz_file, &info, fileNameBuffer, sizeof(fileNameBuffer), nullptr, 0, nullptr, 0);
|
||||
|
||||
std::string fileName(fileNameBuffer);
|
||||
std::filesystem::path path(fileName);
|
||||
|
||||
if (path.has_filename())
|
||||
{
|
||||
IWDEntry entry;
|
||||
entry.m_size = info.uncompressed_size;
|
||||
unzGetFilePos(m_unz_file, &entry.m_file_pos);
|
||||
m_entry_map.emplace(std::move(fileName), entry);
|
||||
}
|
||||
|
||||
ret = unzGoToNextFile(m_unz_file);
|
||||
}
|
||||
|
||||
if (ObjLoading::Configuration.Verbose)
|
||||
{
|
||||
printf("Loaded IWD \"%s\" with %u entries\n", m_path.c_str(), m_entry_map.size());
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SearchPathOpenFile Open(const std::string& fileName) override
|
||||
{
|
||||
if (m_unz_file == nullptr)
|
||||
{
|
||||
return SearchPathOpenFile();
|
||||
}
|
||||
|
||||
auto iwdFilename = fileName;
|
||||
std::ranges::replace(iwdFilename, '\\', '/');
|
||||
|
||||
const auto iwdEntry = m_entry_map.find(iwdFilename);
|
||||
|
||||
if (iwdEntry != m_entry_map.end())
|
||||
{
|
||||
if (m_last_file != nullptr)
|
||||
{
|
||||
throw std::runtime_error("Trying to open new IWD file while last one was not yet closed.");
|
||||
}
|
||||
|
||||
auto pos = iwdEntry->second.m_file_pos;
|
||||
unzGoToFilePos(m_unz_file, &pos);
|
||||
|
||||
if (unzOpenCurrentFile(m_unz_file) == UNZ_OK)
|
||||
{
|
||||
auto result = std::make_unique<IWDFile>(this, m_unz_file, iwdEntry->second.m_size);
|
||||
m_last_file = result.get();
|
||||
return SearchPathOpenFile(std::make_unique<iobjstream>(std::move(result)), iwdEntry->second.m_size);
|
||||
}
|
||||
|
||||
return SearchPathOpenFile();
|
||||
}
|
||||
|
||||
return SearchPathOpenFile();
|
||||
}
|
||||
|
||||
std::string GetPath() override
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
std::string GetName() override
|
||||
{
|
||||
return fs::path(m_path).filename().string();
|
||||
}
|
||||
|
||||
void Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback) override
|
||||
{
|
||||
if (options.m_disk_files_only)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (auto& [entryName, entry] : m_entry_map)
|
||||
{
|
||||
std::filesystem::path entryPath(entryName);
|
||||
|
||||
if (!options.m_should_include_subdirectories && entryPath.has_parent_path())
|
||||
continue;
|
||||
|
||||
if (options.m_filter_extensions && options.m_extension != entryPath.extension().string())
|
||||
continue;
|
||||
|
||||
callback(entryName);
|
||||
}
|
||||
}
|
||||
|
||||
void OnIWDFileClose() override
|
||||
{
|
||||
m_last_file = nullptr;
|
||||
}
|
||||
};
|
||||
|
||||
IWD::IWD(std::string path, std::unique_ptr<std::istream> stream)
|
||||
{
|
||||
m_impl = new Impl(std::move(path), std::move(stream));
|
||||
}
|
||||
|
||||
IWD::~IWD()
|
||||
{
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
}
|
||||
|
||||
IWD::IWD(IWD&& other) noexcept
|
||||
{
|
||||
m_impl = other.m_impl;
|
||||
other.m_impl = nullptr;
|
||||
}
|
||||
|
||||
IWD& IWD::operator=(IWD&& other) noexcept
|
||||
{
|
||||
m_impl = other.m_impl;
|
||||
other.m_impl = nullptr;
|
||||
|
||||
return *this;
|
||||
}
|
||||
|
||||
bool IWD::Initialize()
|
||||
{
|
||||
return m_impl->Initialize();
|
||||
}
|
||||
|
||||
SearchPathOpenFile IWD::Open(const std::string& fileName)
|
||||
{
|
||||
return m_impl->Open(fileName);
|
||||
}
|
||||
|
||||
std::string IWD::GetPath()
|
||||
{
|
||||
return m_impl->GetPath();
|
||||
}
|
||||
|
||||
std::string IWD::GetName()
|
||||
{
|
||||
return m_impl->GetName();
|
||||
}
|
||||
|
||||
void IWD::Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback)
|
||||
{
|
||||
return m_impl->Find(options, callback);
|
||||
}
|
@ -1,36 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include "ObjContainer/ObjContainerRepository.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/ObjStream.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
class IWD final : public ISearchPath, IObjContainer
|
||||
{
|
||||
class Impl;
|
||||
Impl* m_impl;
|
||||
|
||||
public:
|
||||
static ObjContainerRepository<IWD, ISearchPath> Repository;
|
||||
|
||||
IWD(std::string path, std::unique_ptr<std::istream> stream);
|
||||
~IWD() override;
|
||||
|
||||
IWD(const IWD& other) = delete;
|
||||
IWD(IWD&& other) noexcept;
|
||||
IWD& operator=(const IWD& other) = delete;
|
||||
IWD& operator=(IWD&& other) noexcept;
|
||||
|
||||
/**
|
||||
* \brief Initializes the IWD container.
|
||||
* \return \c true when initialization was successful.
|
||||
*/
|
||||
bool Initialize();
|
||||
|
||||
SearchPathOpenFile Open(const std::string& fileName) override;
|
||||
std::string GetPath() override;
|
||||
std::string GetName() override;
|
||||
void Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback) override;
|
||||
};
|
@ -1,46 +1,3 @@
|
||||
#include "ObjLoading.h"
|
||||
|
||||
#include "IObjLoader.h"
|
||||
#include "ObjContainer/IWD/IWD.h"
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
#include "Utils/ObjFileStream.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
ObjLoading::Configuration_t ObjLoading::Configuration;
|
||||
|
||||
void ObjLoading::LoadIWDsInSearchPath(ISearchPath& searchPath)
|
||||
{
|
||||
searchPath.Find(SearchPathSearchOptions().IncludeSubdirectories(false).FilterExtensions("iwd"),
|
||||
[&searchPath](const std::string& path)
|
||||
{
|
||||
auto file = std::make_unique<std::ifstream>(path, std::fstream::in | std::fstream::binary);
|
||||
|
||||
if (file->is_open())
|
||||
{
|
||||
auto iwd = std::make_unique<IWD>(path, std::move(file));
|
||||
|
||||
if (iwd->Initialize())
|
||||
{
|
||||
IWD::Repository.AddContainer(std::move(iwd), &searchPath);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
void ObjLoading::UnloadIWDsInSearchPath(ISearchPath& searchPath)
|
||||
{
|
||||
IWD::Repository.RemoveContainerReferences(&searchPath);
|
||||
}
|
||||
|
||||
SearchPaths ObjLoading::GetIWDSearchPaths()
|
||||
{
|
||||
SearchPaths iwdPaths;
|
||||
|
||||
for (auto* iwd : IWD::Repository)
|
||||
{
|
||||
iwdPaths.IncludeSearchPath(iwd);
|
||||
}
|
||||
|
||||
return iwdPaths;
|
||||
}
|
||||
|
@ -1,8 +1,5 @@
|
||||
#pragma once
|
||||
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
|
||||
class ObjLoading
|
||||
{
|
||||
public:
|
||||
@ -13,22 +10,4 @@ public:
|
||||
bool MenuPermissiveParsing = false;
|
||||
bool MenuNoOptimization = false;
|
||||
} Configuration;
|
||||
|
||||
/**
|
||||
* \brief Loads all IWDs that can be found in a specified search path.
|
||||
* \param searchPath The search path that contains IWDs to be loaded.
|
||||
*/
|
||||
static void LoadIWDsInSearchPath(ISearchPath& searchPath);
|
||||
|
||||
/**
|
||||
* \brief Unloads all IWDs that were loaded from the specified search path.
|
||||
* \param searchPath The search path that was used to load the IWDs to be unloaded.
|
||||
*/
|
||||
static void UnloadIWDsInSearchPath(ISearchPath& searchPath);
|
||||
|
||||
/**
|
||||
* \brief Creates a \c SearchPaths object containing all IWDs that are currently loaded.
|
||||
* \return A \c SearchPaths object containing all IWDs that are currently loaded.
|
||||
*/
|
||||
static SearchPaths GetIWDSearchPaths();
|
||||
};
|
||||
|
@ -7,6 +7,7 @@
|
||||
#include <functional>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class SearchPathOpenFile
|
||||
{
|
||||
@ -42,7 +43,7 @@ public:
|
||||
* \brief Returns the path to the search path.
|
||||
* \return The path to the search path.
|
||||
*/
|
||||
virtual std::string GetPath() = 0;
|
||||
virtual const std::string& GetPath() = 0;
|
||||
|
||||
/**
|
||||
* \brief Iterates through all files of the search path.
|
||||
|
337
src/ObjLoading/SearchPath/IWD.cpp
Normal file
337
src/ObjLoading/SearchPath/IWD.cpp
Normal file
@ -0,0 +1,337 @@
|
||||
#include "IWD.h"
|
||||
|
||||
#include "ObjLoading.h"
|
||||
#include "Utils/FileToZlibWrapper.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <filesystem>
|
||||
#include <fstream>
|
||||
#include <map>
|
||||
#include <memory>
|
||||
#include <unzip.h>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
class IwdFile final : public objbuf
|
||||
{
|
||||
public:
|
||||
class IParent
|
||||
{
|
||||
public:
|
||||
IParent() = default;
|
||||
virtual ~IParent() = default;
|
||||
IParent(const IParent& other) = default;
|
||||
IParent(IParent&& other) noexcept = default;
|
||||
IParent& operator=(const IParent& other) = default;
|
||||
IParent& operator=(IParent&& other) noexcept = default;
|
||||
|
||||
virtual void OnIwdFileClose() = 0;
|
||||
};
|
||||
|
||||
IwdFile(IParent* parent, const unzFile container, const int64_t size)
|
||||
: m_parent(parent),
|
||||
m_open(true),
|
||||
m_size(size),
|
||||
m_container(container),
|
||||
m_peeked(false),
|
||||
m_peek_symbol(0)
|
||||
{
|
||||
}
|
||||
|
||||
~IwdFile() override
|
||||
{
|
||||
if (m_open)
|
||||
{
|
||||
close();
|
||||
}
|
||||
}
|
||||
|
||||
IwdFile(const IwdFile& other) = default;
|
||||
IwdFile(IwdFile&& other) noexcept = default;
|
||||
IwdFile& operator=(const IwdFile& other) = default;
|
||||
IwdFile& operator=(IwdFile&& other) noexcept = default;
|
||||
|
||||
_NODISCARD bool is_open() const override
|
||||
{
|
||||
return m_open;
|
||||
}
|
||||
|
||||
bool close() override
|
||||
{
|
||||
unzCloseCurrentFile(m_container);
|
||||
m_open = false;
|
||||
|
||||
m_parent->OnIwdFileClose();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
protected:
|
||||
int_type underflow() override
|
||||
{
|
||||
if (m_peeked)
|
||||
return m_peek_symbol;
|
||||
|
||||
const auto result = unzReadCurrentFile(m_container, &m_peek_symbol, 1u);
|
||||
|
||||
if (result >= 0)
|
||||
{
|
||||
m_peeked = true;
|
||||
return m_peek_symbol;
|
||||
}
|
||||
|
||||
return EOF;
|
||||
}
|
||||
|
||||
int_type uflow() override
|
||||
{
|
||||
if (m_peeked)
|
||||
{
|
||||
m_peeked = false;
|
||||
return m_peek_symbol;
|
||||
}
|
||||
|
||||
const auto result = unzReadCurrentFile(m_container, &m_peek_symbol, 1u);
|
||||
return result >= 0 ? m_peek_symbol : EOF;
|
||||
}
|
||||
|
||||
std::streamsize xsgetn(char* ptr, std::streamsize count) override
|
||||
{
|
||||
if (m_peeked && count >= 1)
|
||||
{
|
||||
*ptr = static_cast<char>(m_peek_symbol);
|
||||
ptr++;
|
||||
count--;
|
||||
}
|
||||
|
||||
const auto result = unzReadCurrentFile(m_container, ptr, static_cast<unsigned>(count));
|
||||
|
||||
return result >= 0 ? static_cast<std::streamsize>(result) : 0;
|
||||
}
|
||||
|
||||
pos_type seekoff(const off_type off, const std::ios_base::seekdir dir, const std::ios_base::openmode mode) override
|
||||
{
|
||||
const auto currentPos = unztell64(m_container);
|
||||
|
||||
pos_type targetPos;
|
||||
if (dir == std::ios_base::beg)
|
||||
{
|
||||
targetPos = off;
|
||||
}
|
||||
else if (dir == std::ios_base::cur)
|
||||
{
|
||||
targetPos = static_cast<pos_type>(currentPos) + off;
|
||||
}
|
||||
else
|
||||
{
|
||||
targetPos = m_size - off;
|
||||
}
|
||||
|
||||
return seekpos(targetPos, mode);
|
||||
}
|
||||
|
||||
pos_type seekpos(const pos_type pos, const std::ios_base::openmode mode) override
|
||||
{
|
||||
const auto currentPos = unztell64(m_container);
|
||||
|
||||
if (static_cast<pos_type>(currentPos) < pos)
|
||||
{
|
||||
auto skipAmount = pos - static_cast<pos_type>(currentPos);
|
||||
while (skipAmount > 0)
|
||||
{
|
||||
char temp[1024];
|
||||
const auto toRead = skipAmount > sizeof(temp) ? sizeof(temp) : static_cast<size_t>(skipAmount);
|
||||
unzReadCurrentFile(m_container, temp, toRead);
|
||||
skipAmount -= toRead;
|
||||
}
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
if (currentPos == pos)
|
||||
return currentPos;
|
||||
|
||||
return std::streampos(-1);
|
||||
}
|
||||
|
||||
private:
|
||||
IParent* m_parent;
|
||||
bool m_open;
|
||||
int64_t m_size;
|
||||
unzFile m_container;
|
||||
bool m_peeked;
|
||||
int_type m_peek_symbol;
|
||||
};
|
||||
|
||||
struct IwdEntry
|
||||
{
|
||||
int64_t m_size;
|
||||
unz_file_pos m_file_pos;
|
||||
|
||||
explicit IwdEntry(const int64_t size)
|
||||
: m_size(size),
|
||||
m_file_pos{}
|
||||
{
|
||||
}
|
||||
};
|
||||
|
||||
class Iwd final : public ISearchPath, public IwdFile::IParent
|
||||
{
|
||||
public:
|
||||
Iwd(std::string path, std::ifstream stream)
|
||||
: m_path(std::move(path)),
|
||||
m_stream(std::move(stream)),
|
||||
m_unz_file{},
|
||||
m_has_open_file(false)
|
||||
{
|
||||
}
|
||||
|
||||
~Iwd() override
|
||||
{
|
||||
if (m_unz_file != nullptr)
|
||||
{
|
||||
unzClose(m_unz_file);
|
||||
m_unz_file = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
Iwd(const Iwd& other) = delete;
|
||||
Iwd(Iwd&& other) noexcept = delete;
|
||||
Iwd& operator=(const Iwd& other) = delete;
|
||||
Iwd& operator=(Iwd&& other) noexcept = delete;
|
||||
|
||||
/**
|
||||
* \brief Initializes the IWD container.
|
||||
* \return \c true when initialization was successful.
|
||||
*/
|
||||
bool Initialize()
|
||||
{
|
||||
auto ioFunctions = FileToZlibWrapper::CreateFunctions32ForFile(&m_stream);
|
||||
m_unz_file = unzOpen2("", &ioFunctions);
|
||||
|
||||
if (m_unz_file == nullptr)
|
||||
{
|
||||
std::cerr << std::format("Could not open IWD \"{}\"\n", m_path);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto ret = unzGoToFirstFile(m_unz_file);
|
||||
while (ret == Z_OK)
|
||||
{
|
||||
unz_file_info64 info;
|
||||
char fileNameBuffer[256];
|
||||
unzGetCurrentFileInfo64(m_unz_file, &info, fileNameBuffer, sizeof(fileNameBuffer), nullptr, 0, nullptr, 0);
|
||||
|
||||
std::string fileName(fileNameBuffer);
|
||||
fs::path path(fileName);
|
||||
|
||||
if (path.has_filename())
|
||||
{
|
||||
IwdEntry entry(static_cast<std::int64_t>(info.uncompressed_size));
|
||||
unzGetFilePos(m_unz_file, &entry.m_file_pos);
|
||||
m_entry_map.emplace(std::move(fileName), entry);
|
||||
}
|
||||
|
||||
ret = unzGoToNextFile(m_unz_file);
|
||||
}
|
||||
|
||||
std::cout << std::format("Loaded IWD \"{}\" with {} entries\n", m_path, m_entry_map.size());
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
SearchPathOpenFile Open(const std::string& fileName) override
|
||||
{
|
||||
if (m_unz_file == nullptr)
|
||||
{
|
||||
return SearchPathOpenFile();
|
||||
}
|
||||
|
||||
auto iwdFilename = fileName;
|
||||
std::ranges::replace(iwdFilename, '\\', '/');
|
||||
|
||||
const auto iwdEntry = m_entry_map.find(iwdFilename);
|
||||
|
||||
if (iwdEntry != m_entry_map.end())
|
||||
{
|
||||
assert(!m_has_open_file);
|
||||
if (m_has_open_file)
|
||||
{
|
||||
std::cerr << "Trying to open new IWD file while last one was not yet closed.\n";
|
||||
return SearchPathOpenFile();
|
||||
}
|
||||
|
||||
auto pos = iwdEntry->second.m_file_pos;
|
||||
unzGoToFilePos(m_unz_file, &pos);
|
||||
|
||||
if (unzOpenCurrentFile(m_unz_file) == UNZ_OK)
|
||||
{
|
||||
auto result = std::make_unique<IwdFile>(this, m_unz_file, iwdEntry->second.m_size);
|
||||
m_has_open_file = true;
|
||||
return SearchPathOpenFile(std::make_unique<iobjstream>(std::move(result)), iwdEntry->second.m_size);
|
||||
}
|
||||
|
||||
return SearchPathOpenFile();
|
||||
}
|
||||
|
||||
return SearchPathOpenFile();
|
||||
}
|
||||
|
||||
const std::string& GetPath() override
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
|
||||
void OnIwdFileClose() override
|
||||
{
|
||||
m_has_open_file = false;
|
||||
}
|
||||
|
||||
void Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback) override
|
||||
{
|
||||
if (options.m_disk_files_only)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
for (const auto& [entryName, entry] : m_entry_map)
|
||||
{
|
||||
std::filesystem::path entryPath(entryName);
|
||||
|
||||
if (!options.m_should_include_subdirectories && entryPath.has_parent_path())
|
||||
continue;
|
||||
|
||||
if (options.m_filter_extensions && options.m_extension != entryPath.extension().string())
|
||||
continue;
|
||||
|
||||
callback(entryName);
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
std::string m_path;
|
||||
std::ifstream m_stream;
|
||||
unzFile m_unz_file;
|
||||
bool m_has_open_file;
|
||||
|
||||
std::map<std::string, IwdEntry> m_entry_map;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace iwd
|
||||
{
|
||||
std::unique_ptr<ISearchPath> LoadFromFile(const std::string& path)
|
||||
{
|
||||
std::ifstream inputStream(path, std::ios::in | std::ios::binary);
|
||||
if (!inputStream.is_open())
|
||||
return {};
|
||||
|
||||
auto iwd = std::make_unique<Iwd>(path, std::move(inputStream));
|
||||
if (!iwd->Initialize())
|
||||
return {};
|
||||
|
||||
return iwd;
|
||||
}
|
||||
} // namespace iwd
|
11
src/ObjLoading/SearchPath/IWD.h
Normal file
11
src/ObjLoading/SearchPath/IWD.h
Normal file
@ -0,0 +1,11 @@
|
||||
#pragma once
|
||||
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
namespace iwd
|
||||
{
|
||||
std::unique_ptr<ISearchPath> LoadFromFile(const std::string& path);
|
||||
}
|
@ -3,16 +3,18 @@
|
||||
#include "Utils/ObjFileStream.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
SearchPathFilesystem::SearchPathFilesystem(std::string path)
|
||||
: m_path(std::move(path))
|
||||
{
|
||||
m_path = std::move(path);
|
||||
}
|
||||
|
||||
std::string SearchPathFilesystem::GetPath()
|
||||
const std::string& SearchPathFilesystem::GetPath()
|
||||
{
|
||||
return m_path;
|
||||
}
|
||||
@ -23,9 +25,7 @@ SearchPathOpenFile SearchPathFilesystem::Open(const std::string& fileName)
|
||||
std::ifstream file(filePath.string(), std::fstream::in | std::fstream::binary);
|
||||
|
||||
if (file.is_open())
|
||||
{
|
||||
return SearchPathOpenFile(std::make_unique<std::ifstream>(std::move(file)), static_cast<int64_t>(file_size(filePath)));
|
||||
}
|
||||
|
||||
return SearchPathOpenFile();
|
||||
}
|
||||
@ -59,6 +59,6 @@ void SearchPathFilesystem::Find(const SearchPathSearchOptions& options, const st
|
||||
}
|
||||
catch (std::filesystem::filesystem_error& e)
|
||||
{
|
||||
printf("Directory Iterator threw error when trying to find files: \"%s\"\n", e.what());
|
||||
std::cerr << std::format("Directory Iterator threw error when trying to find files: \"{}\"\n", e.what());
|
||||
}
|
||||
}
|
||||
|
@ -12,6 +12,6 @@ public:
|
||||
explicit SearchPathFilesystem(std::string path);
|
||||
|
||||
SearchPathOpenFile Open(const std::string& fileName) override;
|
||||
std::string GetPath() override;
|
||||
const std::string& GetPath() override;
|
||||
void Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback) override;
|
||||
};
|
||||
|
@ -18,9 +18,10 @@ SearchPathOpenFile SearchPaths::Open(const std::string& fileName)
|
||||
return SearchPathOpenFile();
|
||||
}
|
||||
|
||||
std::string SearchPaths::GetPath()
|
||||
const std::string& SearchPaths::GetPath()
|
||||
{
|
||||
return "SearchPaths: " + std::to_string(m_search_paths.size()) + " entries";
|
||||
static const std::string STATIC_NAME = "SearchPaths";
|
||||
return STATIC_NAME;
|
||||
}
|
||||
|
||||
void SearchPaths::Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback)
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "ISearchPath.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class SearchPaths final : public ISearchPath
|
||||
@ -16,7 +17,7 @@ public:
|
||||
~SearchPaths() override = default;
|
||||
|
||||
SearchPathOpenFile Open(const std::string& fileName) override;
|
||||
std::string GetPath() override;
|
||||
const std::string& GetPath() override;
|
||||
void Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback) override;
|
||||
|
||||
SearchPaths(const SearchPaths& other) = delete;
|
||||
|
@ -4,16 +4,17 @@
|
||||
#include "ContentLister/ZoneDefWriter.h"
|
||||
#include "IObjLoader.h"
|
||||
#include "IObjWriter.h"
|
||||
#include "ObjContainer/IWD/IWD.h"
|
||||
#include "ObjLoading.h"
|
||||
#include "ObjWriting.h"
|
||||
#include "SearchPath/IWD.h"
|
||||
#include "SearchPath/SearchPathFilesystem.h"
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
#include "UnlinkerArgs.h"
|
||||
#include "UnlinkerPaths.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/ObjFileStream.h"
|
||||
#include "ZoneLoading.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
@ -37,13 +38,14 @@ public:
|
||||
if (!shouldContinue)
|
||||
return true;
|
||||
|
||||
if (!BuildSearchPaths())
|
||||
UnlinkerPaths paths;
|
||||
if (!paths.LoadUserPaths(m_args))
|
||||
return false;
|
||||
|
||||
if (!LoadZones())
|
||||
if (!LoadZones(paths))
|
||||
return false;
|
||||
|
||||
const auto result = UnlinkZones();
|
||||
const auto result = UnlinkZones(paths);
|
||||
|
||||
UnloadZones();
|
||||
return result;
|
||||
@ -55,106 +57,6 @@ private:
|
||||
return m_args.m_task != UnlinkerArgs::ProcessingTask::LIST && !m_args.m_skip_obj;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Loads a search path.
|
||||
* \param searchPath The search path to load.
|
||||
*/
|
||||
void LoadSearchPath(ISearchPath& searchPath) const
|
||||
{
|
||||
if (ShouldLoadObj())
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Loading search path: \"{}\"\n", searchPath.GetPath());
|
||||
|
||||
ObjLoading::LoadIWDsInSearchPath(searchPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Unloads a search path.
|
||||
* \param searchPath The search path to unload.
|
||||
*/
|
||||
void UnloadSearchPath(ISearchPath& searchPath) const
|
||||
{
|
||||
if (ShouldLoadObj())
|
||||
{
|
||||
if (m_args.m_verbose)
|
||||
std::cout << std::format("Unloading search path: \"{}\"\n", searchPath.GetPath());
|
||||
|
||||
ObjLoading::UnloadIWDsInSearchPath(searchPath);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Loads all search paths that are valid for the specified zone and returns them.
|
||||
* \param zonePath The path to the zone file that should be prepared for.
|
||||
* \return A \c SearchPaths object that contains all search paths that should be considered when loading the specified zone.
|
||||
*/
|
||||
SearchPaths GetSearchPathsForZone(const std::string& zonePath)
|
||||
{
|
||||
SearchPaths searchPathsForZone;
|
||||
const auto absoluteZoneDirectory = fs::absolute(std::filesystem::path(zonePath).remove_filename()).string();
|
||||
|
||||
if (m_last_zone_search_path != nullptr && m_last_zone_search_path->GetPath() == absoluteZoneDirectory)
|
||||
{
|
||||
searchPathsForZone.IncludeSearchPath(m_last_zone_search_path.get());
|
||||
}
|
||||
else if (m_absolute_search_paths.find(absoluteZoneDirectory) == m_absolute_search_paths.end())
|
||||
{
|
||||
if (m_last_zone_search_path)
|
||||
{
|
||||
UnloadSearchPath(*m_last_zone_search_path);
|
||||
}
|
||||
|
||||
m_last_zone_search_path = std::make_unique<SearchPathFilesystem>(absoluteZoneDirectory);
|
||||
searchPathsForZone.IncludeSearchPath(m_last_zone_search_path.get());
|
||||
LoadSearchPath(*m_last_zone_search_path);
|
||||
}
|
||||
|
||||
for (auto* iwd : IWD::Repository)
|
||||
{
|
||||
searchPathsForZone.IncludeSearchPath(iwd);
|
||||
}
|
||||
|
||||
return searchPathsForZone;
|
||||
}
|
||||
|
||||
/**
|
||||
* \brief Initializes the Unlinker object's search paths based on the user's input.
|
||||
* \return \c true if building the search paths was successful, otherwise \c false.
|
||||
*/
|
||||
bool BuildSearchPaths()
|
||||
{
|
||||
for (const auto& path : m_args.m_user_search_paths)
|
||||
{
|
||||
auto absolutePath = fs::absolute(path);
|
||||
|
||||
if (!fs::is_directory(absolutePath))
|
||||
{
|
||||
std::cerr << std::format("Could not find directory of search path: \"{}\"\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto searchPath = std::make_unique<SearchPathFilesystem>(absolutePath.string());
|
||||
LoadSearchPath(*searchPath);
|
||||
m_search_paths.CommitSearchPath(std::move(searchPath));
|
||||
|
||||
m_absolute_search_paths.insert(absolutePath.string());
|
||||
}
|
||||
|
||||
if (m_args.m_verbose)
|
||||
{
|
||||
std::cout << std::format("{} SearchPaths{}\n", m_absolute_search_paths.size(), !m_absolute_search_paths.empty() ? ":" : "");
|
||||
for (const auto& absoluteSearchPath : m_absolute_search_paths)
|
||||
std::cout << std::format(" \"{}\"\n", absoluteSearchPath);
|
||||
|
||||
if (!m_absolute_search_paths.empty())
|
||||
std::cerr << "\n";
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool WriteZoneDefinitionFile(const Zone& zone, const fs::path& zoneDefinitionFileFolder) const
|
||||
{
|
||||
auto zoneDefinitionFilePath(zoneDefinitionFileFolder);
|
||||
@ -251,7 +153,7 @@ private:
|
||||
* \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.
|
||||
* \return \c true if handling the zone was successful, otherwise \c false
|
||||
*/
|
||||
bool HandleZone(ISearchPath& searchPath, Zone& zone) const
|
||||
{
|
||||
@ -310,7 +212,7 @@ private:
|
||||
return true;
|
||||
}
|
||||
|
||||
bool LoadZones()
|
||||
bool LoadZones(UnlinkerPaths& paths)
|
||||
{
|
||||
for (const auto& zonePath : m_args.m_zones_to_load)
|
||||
{
|
||||
@ -322,9 +224,7 @@ private:
|
||||
|
||||
auto absoluteZoneDirectory = absolute(std::filesystem::path(zonePath).remove_filename()).string();
|
||||
|
||||
auto searchPathsForZone = GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
searchPathsForZone.IncludeSearchPath(&m_search_paths);
|
||||
|
||||
auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
auto zone = ZoneLoading::LoadZone(zonePath);
|
||||
if (zone == nullptr)
|
||||
{
|
||||
@ -338,7 +238,7 @@ private:
|
||||
if (ShouldLoadObj())
|
||||
{
|
||||
const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game->GetId());
|
||||
objLoader->LoadReferencedContainersForZone(searchPathsForZone, *zone);
|
||||
objLoader->LoadReferencedContainersForZone(*searchPathsForZone, *zone);
|
||||
}
|
||||
|
||||
m_loaded_zones.emplace_back(std::move(zone));
|
||||
@ -370,7 +270,7 @@ private:
|
||||
m_loaded_zones.clear();
|
||||
}
|
||||
|
||||
bool UnlinkZones()
|
||||
bool UnlinkZones(UnlinkerPaths& paths) const
|
||||
{
|
||||
for (const auto& zonePath : m_args.m_zones_to_unlink)
|
||||
{
|
||||
@ -385,8 +285,7 @@ private:
|
||||
zoneDirectory = fs::current_path();
|
||||
auto absoluteZoneDirectory = absolute(zoneDirectory).string();
|
||||
|
||||
auto searchPathsForZone = GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
searchPathsForZone.IncludeSearchPath(&m_search_paths);
|
||||
auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
|
||||
std::string zoneName;
|
||||
auto zone = ZoneLoading::LoadZone(zonePath);
|
||||
@ -402,9 +301,9 @@ private:
|
||||
|
||||
const auto* objLoader = IObjLoader::GetObjLoaderForGame(zone->m_game->GetId());
|
||||
if (ShouldLoadObj())
|
||||
objLoader->LoadReferencedContainersForZone(searchPathsForZone, *zone);
|
||||
objLoader->LoadReferencedContainersForZone(*searchPathsForZone, *zone);
|
||||
|
||||
if (!HandleZone(searchPathsForZone, *zone))
|
||||
if (!HandleZone(*searchPathsForZone, *zone))
|
||||
return false;
|
||||
|
||||
if (ShouldLoadObj())
|
||||
@ -419,10 +318,6 @@ private:
|
||||
}
|
||||
|
||||
UnlinkerArgs m_args;
|
||||
SearchPaths m_search_paths;
|
||||
std::unique_ptr<SearchPathFilesystem> m_last_zone_search_path;
|
||||
std::set<std::string> m_absolute_search_paths;
|
||||
|
||||
std::vector<std::unique_ptr<Zone>> m_loaded_zones;
|
||||
};
|
||||
|
||||
|
@ -1,7 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils/Arguments/ArgumentParser.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <regex>
|
||||
#include <set>
|
||||
#include <string>
|
||||
@ -37,7 +39,7 @@ public:
|
||||
LIST
|
||||
};
|
||||
|
||||
enum class AssetTypeHandling
|
||||
enum class AssetTypeHandling : std::uint8_t
|
||||
{
|
||||
EXCLUDE,
|
||||
INCLUDE
|
||||
|
72
src/Unlinker/UnlinkerPaths.cpp
Normal file
72
src/Unlinker/UnlinkerPaths.cpp
Normal file
@ -0,0 +1,72 @@
|
||||
#include "UnlinkerPaths.h"
|
||||
|
||||
#include "SearchPath/IWD.h"
|
||||
#include "SearchPath/SearchPathFilesystem.h"
|
||||
#include "Utils/StringUtils.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
bool UnlinkerPaths::LoadUserPaths(const UnlinkerArgs& args)
|
||||
{
|
||||
for (const auto& path : args.m_user_search_paths)
|
||||
{
|
||||
auto absolutePath = fs::absolute(path);
|
||||
|
||||
if (!fs::is_directory(absolutePath))
|
||||
{
|
||||
std::cerr << std::format("Could not find directory of search path: \"{}\"\n", path);
|
||||
return false;
|
||||
}
|
||||
|
||||
auto searchPathName = absolutePath.string();
|
||||
m_user_paths.CommitSearchPath(std::make_unique<SearchPathFilesystem>(searchPathName));
|
||||
m_specified_user_paths.emplace(std::move(searchPathName));
|
||||
}
|
||||
|
||||
std::cout << std::format("{} SearchPaths{}\n", m_specified_user_paths.size(), !m_specified_user_paths.empty() ? ":" : "");
|
||||
for (const auto& absoluteSearchPath : m_specified_user_paths)
|
||||
std::cout << std::format(" \"{}\"\n", absoluteSearchPath);
|
||||
|
||||
if (!m_specified_user_paths.empty())
|
||||
std::cerr << "\n";
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<ISearchPath> UnlinkerPaths::GetSearchPathsForZone(const std::string& zonePath)
|
||||
{
|
||||
const auto absoluteZoneDirectory = fs::absolute(std::filesystem::path(zonePath).remove_filename());
|
||||
const auto absoluteZoneDirectoryString = absoluteZoneDirectory.string();
|
||||
if (m_last_zone_path != absoluteZoneDirectoryString)
|
||||
{
|
||||
m_last_zone_path = absoluteZoneDirectoryString;
|
||||
m_last_zone_search_paths = SearchPaths();
|
||||
m_last_zone_search_paths.CommitSearchPath(std::make_unique<SearchPathFilesystem>(absoluteZoneDirectoryString));
|
||||
|
||||
std::filesystem::directory_iterator iterator(absoluteZoneDirectory);
|
||||
const auto end = fs::end(iterator);
|
||||
for (auto i = fs::begin(iterator); i != end; ++i)
|
||||
{
|
||||
if (!i->is_regular_file())
|
||||
continue;
|
||||
|
||||
auto extension = i->path().extension().string();
|
||||
utils::MakeStringLowerCase(extension);
|
||||
if (extension == ".iwd")
|
||||
{
|
||||
auto iwd = iwd::LoadFromFile(i->path().string());
|
||||
if (iwd)
|
||||
m_last_zone_search_paths.CommitSearchPath(std::move(iwd));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto result = std::make_unique<SearchPaths>();
|
||||
result->IncludeSearchPath(&m_last_zone_search_paths);
|
||||
result->IncludeSearchPath(&m_user_paths);
|
||||
|
||||
return result;
|
||||
}
|
21
src/Unlinker/UnlinkerPaths.h
Normal file
21
src/Unlinker/UnlinkerPaths.h
Normal file
@ -0,0 +1,21 @@
|
||||
#pragma once
|
||||
|
||||
#include "SearchPath/SearchPaths.h"
|
||||
#include "UnlinkerArgs.h"
|
||||
|
||||
#include <string>
|
||||
#include <unordered_set>
|
||||
|
||||
class UnlinkerPaths
|
||||
{
|
||||
public:
|
||||
bool LoadUserPaths(const UnlinkerArgs& args);
|
||||
std::unique_ptr<ISearchPath> GetSearchPathsForZone(const std::string& zonePath);
|
||||
|
||||
private:
|
||||
std::string m_last_zone_path;
|
||||
SearchPaths m_last_zone_search_paths;
|
||||
|
||||
std::unordered_set<std::string> m_specified_user_paths;
|
||||
SearchPaths m_user_paths;
|
||||
};
|
@ -17,9 +17,10 @@ SearchPathOpenFile MockSearchPath::Open(const std::string& fileName)
|
||||
return {std::make_unique<std::istringstream>(foundFileData->second), foundFileData->second.size()};
|
||||
}
|
||||
|
||||
std::string MockSearchPath::GetPath()
|
||||
const std::string& MockSearchPath::GetPath()
|
||||
{
|
||||
return "MockFiles";
|
||||
const static std::string NAME = "MockFiles";
|
||||
return NAME;
|
||||
}
|
||||
|
||||
void MockSearchPath::Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback) {}
|
||||
|
@ -11,6 +11,6 @@ public:
|
||||
void AddFileData(std::string fileName, std::string fileData);
|
||||
|
||||
SearchPathOpenFile Open(const std::string& fileName) override;
|
||||
std::string GetPath() override;
|
||||
const std::string& GetPath() override;
|
||||
void Find(const SearchPathSearchOptions& options, const std::function<void(const std::string&)>& callback) override;
|
||||
};
|
||||
|
Loading…
x
Reference in New Issue
Block a user