mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-20 00:02:55 +00:00
Unlinker: Make parsing specified command line arguments its own class
This commit is contained in:
parent
f3779bac03
commit
23f77bb335
@ -26,7 +26,7 @@ int ObjLoaderT6::Com_HashKey(const char* str, const int maxLen)
|
|||||||
return hash ^ ((hash ^ (hash >> 10)) >> 10);
|
return hash ^ ((hash ^ (hash >> 10)) >> 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjLoaderT6::SupportsZone(Zone* zone)
|
bool ObjLoaderT6::SupportsZone(Zone* zone) const
|
||||||
{
|
{
|
||||||
return zone->m_game == &g_GameT6;
|
return zone->m_game == &g_GameT6;
|
||||||
}
|
}
|
||||||
@ -119,7 +119,7 @@ void ObjLoaderT6::LoadCommonIPaks(ISearchPath* searchPath, Zone* zone)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjLoaderT6::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone)
|
void ObjLoaderT6::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const
|
||||||
{
|
{
|
||||||
auto* assetPoolT6 = dynamic_cast<GameAssetPoolT6*>(zone->GetPools());
|
auto* assetPoolT6 = dynamic_cast<GameAssetPoolT6*>(zone->GetPools());
|
||||||
const int zoneNameHash = Com_HashKey(zone->m_name.c_str(), 64);
|
const int zoneNameHash = Com_HashKey(zone->m_name.c_str(), 64);
|
||||||
@ -144,7 +144,7 @@ void ObjLoaderT6::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone*
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjLoaderT6::UnloadContainersOfZone(Zone* zone)
|
void ObjLoaderT6::UnloadContainersOfZone(Zone* zone) const
|
||||||
{
|
{
|
||||||
IPak::Repository.RemoveContainerReferences(zone);
|
IPak::Repository.RemoveContainerReferences(zone);
|
||||||
}
|
}
|
||||||
@ -242,7 +242,7 @@ void ObjLoaderT6::LoadImageData(ISearchPath* searchPath, Zone* zone)
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void ObjLoaderT6::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone)
|
void ObjLoaderT6::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) const
|
||||||
{
|
{
|
||||||
LoadImageData(searchPath, zone);
|
LoadImageData(searchPath, zone);
|
||||||
}
|
}
|
||||||
|
@ -21,10 +21,10 @@ class ObjLoaderT6 final : public IObjLoader
|
|||||||
static void LoadCommonIPaks(ISearchPath* searchPath, Zone* zone);
|
static void LoadCommonIPaks(ISearchPath* searchPath, Zone* zone);
|
||||||
|
|
||||||
public:
|
public:
|
||||||
bool SupportsZone(Zone* zone) override;
|
bool SupportsZone(Zone* zone) const override;
|
||||||
|
|
||||||
void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) override;
|
void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const override;
|
||||||
void UnloadContainersOfZone(Zone* zone) override;
|
void UnloadContainersOfZone(Zone* zone) const override;
|
||||||
|
|
||||||
void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) override;
|
void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) const override;
|
||||||
};
|
};
|
||||||
|
@ -13,25 +13,25 @@ public:
|
|||||||
* \param zone The zone to check.
|
* \param zone The zone to check.
|
||||||
* \return \c true if the specified zone is supported.
|
* \return \c true if the specified zone is supported.
|
||||||
*/
|
*/
|
||||||
virtual bool SupportsZone(Zone* zone) = 0;
|
virtual bool SupportsZone(Zone* zone) const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Loads all containers that are referenced by a specified zone.
|
* \brief Loads all containers that are referenced by a specified zone.
|
||||||
* \param searchPath The search path object to use to find the referenced containers.
|
* \param searchPath The search path object to use to find the referenced containers.
|
||||||
* \param zone The zone to check for referenced containers.
|
* \param zone The zone to check for referenced containers.
|
||||||
*/
|
*/
|
||||||
virtual void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) = 0;
|
virtual void LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone) const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Unloads all containers of a specified zone. If a container is also loaded by another zone it will only be unloaded when all referencing zones are unloaded.
|
* \brief Unloads all containers of a specified zone. If a container is also loaded by another zone it will only be unloaded when all referencing zones are unloaded.
|
||||||
* \param zone The zone to unload all containers for.
|
* \param zone The zone to unload all containers for.
|
||||||
*/
|
*/
|
||||||
virtual void UnloadContainersOfZone(Zone* zone) = 0;
|
virtual void UnloadContainersOfZone(Zone* zone) const = 0;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Loads the obj data for all assets of a specified zone.
|
* \brief Loads the obj data for all assets of a specified zone.
|
||||||
* \param searchPath The search path object to use to find obj files.
|
* \param searchPath The search path object to use to find obj files.
|
||||||
* \param zone The zone of the assets to load the obj data for.
|
* \param zone The zone of the assets to load the obj data for.
|
||||||
*/
|
*/
|
||||||
virtual void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) = 0;
|
virtual void LoadObjDataForZone(ISearchPath* searchPath, Zone* zone) const = 0;
|
||||||
};
|
};
|
@ -6,14 +6,14 @@
|
|||||||
|
|
||||||
ObjLoading::Configuration_t ObjLoading::Configuration;
|
ObjLoading::Configuration_t ObjLoading::Configuration;
|
||||||
|
|
||||||
IObjLoader* objLoaders[]
|
const IObjLoader* const OBJ_LOADERS[]
|
||||||
{
|
{
|
||||||
new ObjLoaderT6()
|
new ObjLoaderT6()
|
||||||
};
|
};
|
||||||
|
|
||||||
void ObjLoading::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone)
|
void ObjLoading::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone* zone)
|
||||||
{
|
{
|
||||||
for (auto* loader : objLoaders)
|
for (auto* loader : OBJ_LOADERS)
|
||||||
{
|
{
|
||||||
if (loader->SupportsZone(zone))
|
if (loader->SupportsZone(zone))
|
||||||
{
|
{
|
||||||
@ -25,7 +25,7 @@ void ObjLoading::LoadReferencedContainersForZone(ISearchPath* searchPath, Zone*
|
|||||||
|
|
||||||
void ObjLoading::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone)
|
void ObjLoading::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone)
|
||||||
{
|
{
|
||||||
for (auto* loader : objLoaders)
|
for (auto* loader : OBJ_LOADERS)
|
||||||
{
|
{
|
||||||
if (loader->SupportsZone(zone))
|
if (loader->SupportsZone(zone))
|
||||||
{
|
{
|
||||||
@ -37,7 +37,7 @@ void ObjLoading::LoadObjDataForZone(ISearchPath* searchPath, Zone* zone)
|
|||||||
|
|
||||||
void ObjLoading::UnloadContainersOfZone(Zone* zone)
|
void ObjLoading::UnloadContainersOfZone(Zone* zone)
|
||||||
{
|
{
|
||||||
for (auto* loader : objLoaders)
|
for (auto* loader : OBJ_LOADERS)
|
||||||
{
|
{
|
||||||
if (loader->SupportsZone(zone))
|
if (loader->SupportsZone(zone))
|
||||||
{
|
{
|
||||||
|
@ -8,7 +8,7 @@ class IZoneDumper
|
|||||||
public:
|
public:
|
||||||
virtual ~IZoneDumper() = default;
|
virtual ~IZoneDumper() = default;
|
||||||
|
|
||||||
virtual bool CanHandleZone(Zone* zone) = 0;
|
virtual bool CanHandleZone(Zone* zone) const = 0;
|
||||||
virtual bool DumpZone(Zone* zone, const std::string& basePath) = 0;
|
virtual bool DumpZone(Zone* zone, const std::string& basePath) const = 0;
|
||||||
virtual bool WriteZoneDefinition(Zone* zone, FileAPI::File* file, bool minimalistic) = 0;
|
virtual bool WriteZoneDefinition(Zone* zone, FileAPI::File* file) const = 0;
|
||||||
};
|
};
|
@ -10,12 +10,12 @@
|
|||||||
#include "AssetDumpers/AssetDumperLocalizeEntry.h"
|
#include "AssetDumpers/AssetDumperLocalizeEntry.h"
|
||||||
#include "AssetDumpers/AssetDumperGfxImage.h"
|
#include "AssetDumpers/AssetDumperGfxImage.h"
|
||||||
|
|
||||||
bool ZoneDumperT6::CanHandleZone(Zone* zone)
|
bool ZoneDumperT6::CanHandleZone(Zone* zone) const
|
||||||
{
|
{
|
||||||
return zone->m_game == &g_GameT6;
|
return zone->m_game == &g_GameT6;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ZoneDumperT6::DumpZone(Zone* zone, const std::string& basePath)
|
bool ZoneDumperT6::DumpZone(Zone* zone, const std::string& basePath) const
|
||||||
{
|
{
|
||||||
#define DUMP_ASSET_POOL(dumperType, poolName) \
|
#define DUMP_ASSET_POOL(dumperType, poolName) \
|
||||||
if(assetPools->poolName) \
|
if(assetPools->poolName) \
|
||||||
@ -80,7 +80,7 @@ bool ZoneDumperT6::DumpZone(Zone* zone, const std::string& basePath)
|
|||||||
#undef DUMP_ASSET_POOL
|
#undef DUMP_ASSET_POOL
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ZoneDumperT6::WriteZoneDefinition(Zone* zone, FileAPI::File* file, bool minimalistic)
|
bool ZoneDumperT6::WriteZoneDefinition(Zone* zone, FileAPI::File* file) const
|
||||||
{
|
{
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
@ -4,7 +4,7 @@
|
|||||||
class ZoneDumperT6 final : public IZoneDumper
|
class ZoneDumperT6 final : public IZoneDumper
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
bool CanHandleZone(Zone* zone) override;
|
bool CanHandleZone(Zone* zone) const override;
|
||||||
bool DumpZone(Zone* zone, const std::string& basePath) override;
|
bool DumpZone(Zone* zone, const std::string& basePath) const override;
|
||||||
bool WriteZoneDefinition(Zone* zone, FileAPI::File* file, bool minimalistic) override;
|
bool WriteZoneDefinition(Zone* zone, FileAPI::File* file) const override;
|
||||||
};
|
};
|
||||||
|
@ -2,14 +2,16 @@
|
|||||||
#include "Dumping/IZoneDumper.h"
|
#include "Dumping/IZoneDumper.h"
|
||||||
#include "Game/T6/ZoneDumperT6.h"
|
#include "Game/T6/ZoneDumperT6.h"
|
||||||
|
|
||||||
IZoneDumper* zoneDumper[]
|
ObjWriting::Configuration_t ObjWriting::Configuration;
|
||||||
|
|
||||||
|
const IZoneDumper* const ZONE_DUMPER[]
|
||||||
{
|
{
|
||||||
new ZoneDumperT6()
|
new ZoneDumperT6()
|
||||||
};
|
};
|
||||||
|
|
||||||
bool ObjWriting::DumpZone(Zone* zone, const std::string& basePath)
|
bool ObjWriting::DumpZone(Zone* zone, const std::string& basePath)
|
||||||
{
|
{
|
||||||
for (auto dumper : zoneDumper)
|
for (auto dumper : ZONE_DUMPER)
|
||||||
{
|
{
|
||||||
if (dumper->CanHandleZone(zone))
|
if (dumper->CanHandleZone(zone))
|
||||||
{
|
{
|
||||||
@ -26,7 +28,7 @@ bool ObjWriting::DumpZone(Zone* zone, const std::string& basePath)
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool ObjWriting::WriteZoneDefinition(Zone* zone, FileAPI::File* file, bool minimalistic)
|
bool ObjWriting::WriteZoneDefinition(Zone* zone, FileAPI::File* file)
|
||||||
{
|
{
|
||||||
return file->Printf("// %s", "Insert zone definition here") > 0;
|
return file->Printf("// %s", "Insert zone definition here") > 0;
|
||||||
}
|
}
|
@ -7,6 +7,21 @@
|
|||||||
class ObjWriting
|
class ObjWriting
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
|
static class Configuration_t
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
enum class ImageOutputFormat_e
|
||||||
|
{
|
||||||
|
DDS,
|
||||||
|
IWI
|
||||||
|
};
|
||||||
|
|
||||||
|
bool Verbose = false;
|
||||||
|
ImageOutputFormat_e ImageOutputFormat = ImageOutputFormat_e::DDS;
|
||||||
|
bool MinimalZoneFileOutput = false;
|
||||||
|
|
||||||
|
} Configuration;
|
||||||
|
|
||||||
static bool DumpZone(Zone* zone, const std::string& basePath);
|
static bool DumpZone(Zone* zone, const std::string& basePath);
|
||||||
static bool WriteZoneDefinition(Zone* zone, FileAPI::File* file, bool minimalistic);
|
static bool WriteZoneDefinition(Zone* zone, FileAPI::File* file);
|
||||||
};
|
};
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
#include "Unlinker.h"
|
#include "Unlinker.h"
|
||||||
|
|
||||||
#include "Utils/Arguments/ArgumentParser.h"
|
#include "Utils/Arguments/ArgumentParser.h"
|
||||||
#include "Utils/Arguments/UsageInformation.h"
|
|
||||||
#include "ZoneLoading.h"
|
#include "ZoneLoading.h"
|
||||||
#include "ObjWriting.h"
|
#include "ObjWriting.h"
|
||||||
#include "ContentPrinter.h"
|
#include "ContentPrinter.h"
|
||||||
@ -15,67 +14,19 @@
|
|||||||
#include <regex>
|
#include <regex>
|
||||||
#include <filesystem>
|
#include <filesystem>
|
||||||
#include "ObjContainer/IWD/IWD.h"
|
#include "ObjContainer/IWD/IWD.h"
|
||||||
|
#include "UnlinkerArgs.h"
|
||||||
const CommandLineOption* optionHelp = CommandLineOption::Builder::Create()
|
|
||||||
.WithShortName("?")
|
|
||||||
.WithLongName("help")
|
|
||||||
.WithDescription("Displays usage information.")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
const CommandLineOption* optionVerbose = CommandLineOption::Builder::Create()
|
|
||||||
.WithShortName("v")
|
|
||||||
.WithLongName("verbose")
|
|
||||||
.WithDescription("Outputs a lot more and more detailed messages.")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
const CommandLineOption* optionMinimalZoneFile = CommandLineOption::Builder::Create()
|
|
||||||
.WithShortName("min")
|
|
||||||
.WithLongName("minimal-zone")
|
|
||||||
.WithDescription(
|
|
||||||
"Minimizes the size of the zone file output by only including assets that are not a dependency of another asset.")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
const CommandLineOption* optionList = CommandLineOption::Builder::Create()
|
|
||||||
.WithShortName("l")
|
|
||||||
.WithLongName("list")
|
|
||||||
.WithDescription(
|
|
||||||
"Lists the contents of a zone instead of writing them to the disk.")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
const CommandLineOption* optionOutputFolder = CommandLineOption::Builder::Create()
|
|
||||||
.WithShortName("o")
|
|
||||||
.WithLongName("output-folder")
|
|
||||||
.WithDescription(
|
|
||||||
"Specifies the output folder containing the contents of the unlinked zones. Defaults to ./%zoneName%")
|
|
||||||
.WithParameter("outputFolderPath")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
const CommandLineOption* optionSearchPath = CommandLineOption::Builder::Create()
|
|
||||||
.WithLongName("search-path")
|
|
||||||
.WithDescription(
|
|
||||||
"Specifies a semi-colon separated list of paths to search for additional game files.")
|
|
||||||
.WithParameter("searchPathString")
|
|
||||||
.Build();
|
|
||||||
|
|
||||||
const CommandLineOption* commandLineOptions[]
|
|
||||||
{
|
|
||||||
optionHelp,
|
|
||||||
optionVerbose,
|
|
||||||
optionMinimalZoneFile,
|
|
||||||
optionList,
|
|
||||||
optionOutputFolder,
|
|
||||||
optionSearchPath
|
|
||||||
};
|
|
||||||
|
|
||||||
class Unlinker::Impl
|
class Unlinker::Impl
|
||||||
{
|
{
|
||||||
|
UnlinkerArgs m_args;
|
||||||
SearchPaths m_search_paths;
|
SearchPaths m_search_paths;
|
||||||
SearchPathFilesystem* m_last_zone_search_path;
|
SearchPathFilesystem* m_last_zone_search_path;
|
||||||
std::set<std::string> m_absolute_search_paths;
|
std::set<std::string> m_absolute_search_paths;
|
||||||
|
|
||||||
ArgumentParser m_argument_parser;
|
bool ShouldLoadObj() const
|
||||||
bool m_verbose;
|
{
|
||||||
bool m_should_load_obj;
|
return m_args.m_task != UnlinkerArgs::ProcessingTask::LIST;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Loads a search path.
|
* \brief Loads a search path.
|
||||||
@ -83,9 +34,9 @@ class Unlinker::Impl
|
|||||||
*/
|
*/
|
||||||
void LoadSearchPath(ISearchPath* searchPath) const
|
void LoadSearchPath(ISearchPath* searchPath) const
|
||||||
{
|
{
|
||||||
if (m_should_load_obj)
|
if (ShouldLoadObj())
|
||||||
{
|
{
|
||||||
if (m_verbose)
|
if (m_args.m_verbose)
|
||||||
{
|
{
|
||||||
printf("Loading search path: \"%s\"\n", searchPath->GetPath().c_str());
|
printf("Loading search path: \"%s\"\n", searchPath->GetPath().c_str());
|
||||||
}
|
}
|
||||||
@ -100,9 +51,9 @@ class Unlinker::Impl
|
|||||||
*/
|
*/
|
||||||
void UnloadSearchPath(ISearchPath* searchPath) const
|
void UnloadSearchPath(ISearchPath* searchPath) const
|
||||||
{
|
{
|
||||||
if (m_should_load_obj)
|
if (ShouldLoadObj())
|
||||||
{
|
{
|
||||||
if (m_verbose)
|
if (m_args.m_verbose)
|
||||||
{
|
{
|
||||||
printf("Unloading search path: \"%s\"\n", searchPath->GetPath().c_str());
|
printf("Unloading search path: \"%s\"\n", searchPath->GetPath().c_str());
|
||||||
}
|
}
|
||||||
@ -141,87 +92,13 @@ class Unlinker::Impl
|
|||||||
return searchPathsForZone;
|
return searchPathsForZone;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Splits a path string as user input into a list of paths.
|
|
||||||
* \param pathsString The path string that was taken as user input.
|
|
||||||
* \param output A set for strings to save the output to.
|
|
||||||
* \return \c true if the user input was valid and could be processed successfully, otherwise \c false.
|
|
||||||
*/
|
|
||||||
static bool ParsePathsString(const std::string& pathsString, std::set<std::string>& output)
|
|
||||||
{
|
|
||||||
std::ostringstream currentPath;
|
|
||||||
bool pathStart = true;
|
|
||||||
int quotationPos = 0; // 0 = before quotations, 1 = in quotations, 2 = after quotations
|
|
||||||
|
|
||||||
for (auto character : pathsString)
|
|
||||||
{
|
|
||||||
switch (character)
|
|
||||||
{
|
|
||||||
case '"':
|
|
||||||
if (quotationPos == 0 && pathStart)
|
|
||||||
{
|
|
||||||
quotationPos = 1;
|
|
||||||
}
|
|
||||||
else if (quotationPos == 1)
|
|
||||||
{
|
|
||||||
quotationPos = 2;
|
|
||||||
pathStart = false;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
case ';':
|
|
||||||
if (quotationPos != 1)
|
|
||||||
{
|
|
||||||
std::string path = currentPath.str();
|
|
||||||
if (!path.empty())
|
|
||||||
{
|
|
||||||
output.insert(path);
|
|
||||||
currentPath = std::ostringstream();
|
|
||||||
}
|
|
||||||
|
|
||||||
pathStart = true;
|
|
||||||
quotationPos = 0;
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
currentPath << character;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
|
|
||||||
default:
|
|
||||||
currentPath << character;
|
|
||||||
pathStart = false;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (currentPath.tellp() > 0)
|
|
||||||
{
|
|
||||||
output.insert(currentPath.str());
|
|
||||||
}
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Initializes the Unlinker object's search paths based on the user's input.
|
* \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.
|
* \return \c true if building the search paths was successful, otherwise \c false.
|
||||||
*/
|
*/
|
||||||
bool BuildSearchPaths()
|
bool BuildSearchPaths()
|
||||||
{
|
{
|
||||||
if (m_argument_parser.IsOptionSpecified(optionSearchPath))
|
for (const auto& path : m_args.m_user_search_paths)
|
||||||
{
|
|
||||||
std::set<std::string> pathList;
|
|
||||||
if (!ParsePathsString(m_argument_parser.GetValueForOption(optionSearchPath), pathList))
|
|
||||||
{
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
for (const auto& path : pathList)
|
|
||||||
{
|
{
|
||||||
std::string absolutePath = std::filesystem::absolute(path).string();
|
std::string absolutePath = std::filesystem::absolute(path).string();
|
||||||
|
|
||||||
@ -237,9 +114,8 @@ class Unlinker::Impl
|
|||||||
|
|
||||||
m_absolute_search_paths.insert(absolutePath);
|
m_absolute_search_paths.insert(absolutePath);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if (m_verbose)
|
if (m_args.m_verbose)
|
||||||
{
|
{
|
||||||
printf("%u SearchPaths%s\n", m_absolute_search_paths.size(), !m_absolute_search_paths.empty() ? ":" : "");
|
printf("%u SearchPaths%s\n", m_absolute_search_paths.size(), !m_absolute_search_paths.empty() ? ":" : "");
|
||||||
for (const auto& absoluteSearchPath : m_absolute_search_paths)
|
for (const auto& absoluteSearchPath : m_absolute_search_paths)
|
||||||
@ -256,62 +132,21 @@ class Unlinker::Impl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Prints a command line usage help text for the Unlinker tool to stdout.
|
|
||||||
*/
|
|
||||||
static void PrintUsage()
|
|
||||||
{
|
|
||||||
UsageInformation usage("unlinker.exe");
|
|
||||||
|
|
||||||
for (auto commandLineOption : commandLineOptions)
|
|
||||||
{
|
|
||||||
usage.AddCommandLineOption(commandLineOption);
|
|
||||||
}
|
|
||||||
|
|
||||||
usage.AddArgument("pathToZone");
|
|
||||||
usage.SetVariableArguments(true);
|
|
||||||
|
|
||||||
usage.Print();
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* \brief Converts the output path specified by command line arguments to a path applies for the specified zone.
|
|
||||||
* \param path The path that was specified as user input.
|
|
||||||
* \param zone The zone to resolve the path input for.
|
|
||||||
* \return An output path for the zone based on the user input.
|
|
||||||
*/
|
|
||||||
std::string ResolveOutputFolderPath(const std::string& path, Zone* zone) const
|
|
||||||
{
|
|
||||||
return std::regex_replace(path, std::regex("%zoneName%"), zone->m_name);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Performs the tasks specified by the command line arguments on the specified zone.
|
* \brief Performs the tasks specified by the command line arguments on the specified zone.
|
||||||
* \param zone The zone to handle.
|
* \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(Zone* zone)
|
bool HandleZone(Zone* zone) const
|
||||||
{
|
{
|
||||||
if (m_argument_parser.IsOptionSpecified(optionList))
|
if (m_args.m_task == UnlinkerArgs::ProcessingTask::LIST)
|
||||||
{
|
{
|
||||||
const ContentPrinter printer(zone);
|
const ContentPrinter printer(zone);
|
||||||
printer.PrintContent();
|
printer.PrintContent();
|
||||||
}
|
}
|
||||||
else
|
else if (m_args.m_task == UnlinkerArgs::ProcessingTask::DUMP)
|
||||||
{
|
{
|
||||||
const bool minimalisticZoneDefinition = m_argument_parser.IsOptionSpecified(optionMinimalZoneFile);
|
const std::string outputFolderPath = m_args.GetOutputFolderPathForZone(zone);
|
||||||
|
|
||||||
std::string outputFolderPath;
|
|
||||||
if (m_argument_parser.IsOptionSpecified(optionOutputFolder))
|
|
||||||
{
|
|
||||||
outputFolderPath = ResolveOutputFolderPath(m_argument_parser.GetValueForOption(optionOutputFolder),
|
|
||||||
zone);
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
outputFolderPath = ResolveOutputFolderPath("./%zoneName%", zone);
|
|
||||||
}
|
|
||||||
|
|
||||||
FileAPI::DirectoryCreate(outputFolderPath);
|
FileAPI::DirectoryCreate(outputFolderPath);
|
||||||
|
|
||||||
const std::string zoneDefinitionFileFolder = utils::Path::Combine(outputFolderPath, "zone_source");
|
const std::string zoneDefinitionFileFolder = utils::Path::Combine(outputFolderPath, "zone_source");
|
||||||
@ -323,7 +158,7 @@ class Unlinker::Impl
|
|||||||
|
|
||||||
if (zoneDefinitionFile.IsOpen())
|
if (zoneDefinitionFile.IsOpen())
|
||||||
{
|
{
|
||||||
ObjWriting::WriteZoneDefinition(zone, &zoneDefinitionFile, minimalisticZoneDefinition);
|
ObjWriting::WriteZoneDefinition(zone, &zoneDefinitionFile);
|
||||||
ObjWriting::DumpZone(zone, outputFolderPath);
|
ObjWriting::DumpZone(zone, outputFolderPath);
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@ -338,19 +173,10 @@ class Unlinker::Impl
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void SetVerbose(const bool verbose)
|
|
||||||
{
|
|
||||||
m_verbose = verbose;
|
|
||||||
ObjLoading::Configuration.Verbose = verbose;
|
|
||||||
}
|
|
||||||
|
|
||||||
public:
|
public:
|
||||||
Impl()
|
Impl()
|
||||||
: m_argument_parser(commandLineOptions, _countof(commandLineOptions))
|
|
||||||
{
|
{
|
||||||
m_last_zone_search_path = nullptr;
|
m_last_zone_search_path = nullptr;
|
||||||
m_verbose = false;
|
|
||||||
m_should_load_obj = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -358,41 +184,15 @@ public:
|
|||||||
*/
|
*/
|
||||||
bool Start(const int argc, const char** argv)
|
bool Start(const int argc, const char** argv)
|
||||||
{
|
{
|
||||||
if (!m_argument_parser.ParseArguments(argc, argv))
|
if (!m_args.ParseArgs(argc, argv))
|
||||||
{
|
|
||||||
PrintUsage();
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
SetVerbose(m_argument_parser.IsOptionSpecified(optionVerbose));
|
|
||||||
m_should_load_obj = !m_argument_parser.IsOptionSpecified(optionList);
|
|
||||||
|
|
||||||
// Check if the user requested help
|
|
||||||
if (m_argument_parser.IsOptionSpecified(optionHelp))
|
|
||||||
{
|
|
||||||
PrintUsage();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::vector<std::string> arguments = m_argument_parser.GetArguments();
|
|
||||||
const size_t argCount = arguments.size();
|
|
||||||
if (argCount <= 1)
|
|
||||||
{
|
|
||||||
// No zones to load specified...
|
|
||||||
PrintUsage();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!BuildSearchPaths())
|
if (!BuildSearchPaths())
|
||||||
{
|
|
||||||
return false;
|
return false;
|
||||||
}
|
|
||||||
|
|
||||||
for (unsigned argIndex = 1; argIndex < argCount; argIndex++)
|
for (const auto& zonePath : m_args.m_zones_to_load)
|
||||||
{
|
{
|
||||||
const std::string& zonePath = arguments[argIndex];
|
if (!FileAPI::FileExists(zonePath))
|
||||||
|
|
||||||
if(!FileAPI::FileExists(zonePath))
|
|
||||||
{
|
{
|
||||||
printf("Could not find file \"%s\".\n", zonePath.c_str());
|
printf("Could not find file \"%s\".\n", zonePath.c_str());
|
||||||
continue;
|
continue;
|
||||||
@ -410,12 +210,12 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_verbose)
|
if (m_args.m_verbose)
|
||||||
{
|
{
|
||||||
printf("Loaded zone \"%s\"\n", zone->m_name.c_str());
|
printf("Loaded zone \"%s\"\n", zone->m_name.c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_should_load_obj)
|
if (ShouldLoadObj())
|
||||||
{
|
{
|
||||||
ObjLoading::LoadReferencedContainersForZone(&searchPathsForZone, zone);
|
ObjLoading::LoadReferencedContainersForZone(&searchPathsForZone, zone);
|
||||||
ObjLoading::LoadObjDataForZone(&searchPathsForZone, zone);
|
ObjLoading::LoadObjDataForZone(&searchPathsForZone, zone);
|
||||||
@ -426,7 +226,7 @@ public:
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (m_should_load_obj)
|
if (ShouldLoadObj())
|
||||||
{
|
{
|
||||||
ObjLoading::UnloadContainersOfZone(zone);
|
ObjLoading::UnloadContainersOfZone(zone);
|
||||||
}
|
}
|
||||||
|
242
src/Unlinker/UnlinkerArgs.cpp
Normal file
242
src/Unlinker/UnlinkerArgs.cpp
Normal file
@ -0,0 +1,242 @@
|
|||||||
|
#include "UnlinkerArgs.h"
|
||||||
|
#include "Utils/Arguments/UsageInformation.h"
|
||||||
|
#include "ObjLoading.h"
|
||||||
|
#include "ObjWriting.h"
|
||||||
|
#include <regex>
|
||||||
|
|
||||||
|
const CommandLineOption* const OPTION_HELP = CommandLineOption::Builder::Create()
|
||||||
|
.WithShortName("?")
|
||||||
|
.WithLongName("help")
|
||||||
|
.WithDescription("Displays usage information.")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
const CommandLineOption* const OPTION_VERBOSE = CommandLineOption::Builder::Create()
|
||||||
|
.WithShortName("v")
|
||||||
|
.WithLongName("verbose")
|
||||||
|
.WithDescription("Outputs a lot more and more detailed messages.")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
const CommandLineOption* const OPTION_MINIMAL_ZONE_FILE = CommandLineOption::Builder::Create()
|
||||||
|
.WithShortName("min")
|
||||||
|
.WithLongName("minimal-zone")
|
||||||
|
.WithDescription(
|
||||||
|
"Minimizes the size of the zone file output by only including assets that are not a dependency of another asset.")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
const CommandLineOption* const OPTION_LIST = CommandLineOption::Builder::Create()
|
||||||
|
.WithShortName("l")
|
||||||
|
.WithLongName("list")
|
||||||
|
.WithDescription(
|
||||||
|
"Lists the contents of a zone instead of writing them to the disk.")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
const CommandLineOption* const OPTION_OUTPUT_FOLDER = CommandLineOption::Builder::Create()
|
||||||
|
.WithShortName("o")
|
||||||
|
.WithLongName("output-folder")
|
||||||
|
.WithDescription(
|
||||||
|
"Specifies the output folder containing the contents of the unlinked zones. Defaults to ./%zoneName%")
|
||||||
|
.WithParameter("outputFolderPath")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
const CommandLineOption* const OPTION_SEARCH_PATH = CommandLineOption::Builder::Create()
|
||||||
|
.WithLongName("search-path")
|
||||||
|
.WithDescription(
|
||||||
|
"Specifies a semi-colon separated list of paths to search for additional game files.")
|
||||||
|
.WithParameter("searchPathString")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
const CommandLineOption* const OPTION_IMAGE_FORMAT = CommandLineOption::Builder::Create()
|
||||||
|
.WithLongName("image-format")
|
||||||
|
.WithDescription(
|
||||||
|
"Specifies the format of dumped image files. Valid values are: DDS, IWI")
|
||||||
|
.WithParameter("imageFormatValue")
|
||||||
|
.Build();
|
||||||
|
|
||||||
|
const CommandLineOption* const COMMAND_LINE_OPTIONS[]
|
||||||
|
{
|
||||||
|
OPTION_HELP,
|
||||||
|
OPTION_VERBOSE,
|
||||||
|
OPTION_MINIMAL_ZONE_FILE,
|
||||||
|
OPTION_LIST,
|
||||||
|
OPTION_OUTPUT_FOLDER,
|
||||||
|
OPTION_SEARCH_PATH,
|
||||||
|
OPTION_IMAGE_FORMAT
|
||||||
|
};
|
||||||
|
|
||||||
|
UnlinkerArgs::UnlinkerArgs()
|
||||||
|
: m_argument_parser(COMMAND_LINE_OPTIONS, _countof(COMMAND_LINE_OPTIONS))
|
||||||
|
{
|
||||||
|
m_task = ProcessingTask::DUMP;
|
||||||
|
m_output_folder = "./%zoneName%";
|
||||||
|
|
||||||
|
m_verbose = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnlinkerArgs::ParsePathsString(const std::string& pathsString, std::set<std::string>& output)
|
||||||
|
{
|
||||||
|
std::ostringstream currentPath;
|
||||||
|
bool pathStart = true;
|
||||||
|
int quotationPos = 0; // 0 = before quotations, 1 = in quotations, 2 = after quotations
|
||||||
|
|
||||||
|
for (auto character : pathsString)
|
||||||
|
{
|
||||||
|
switch (character)
|
||||||
|
{
|
||||||
|
case '"':
|
||||||
|
if (quotationPos == 0 && pathStart)
|
||||||
|
{
|
||||||
|
quotationPos = 1;
|
||||||
|
}
|
||||||
|
else if (quotationPos == 1)
|
||||||
|
{
|
||||||
|
quotationPos = 2;
|
||||||
|
pathStart = false;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
case ';':
|
||||||
|
if (quotationPos != 1)
|
||||||
|
{
|
||||||
|
std::string path = currentPath.str();
|
||||||
|
if (!path.empty())
|
||||||
|
{
|
||||||
|
output.insert(path);
|
||||||
|
currentPath = std::ostringstream();
|
||||||
|
}
|
||||||
|
|
||||||
|
pathStart = true;
|
||||||
|
quotationPos = 0;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
currentPath << character;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
currentPath << character;
|
||||||
|
pathStart = false;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (currentPath.tellp() > 0)
|
||||||
|
{
|
||||||
|
output.insert(currentPath.str());
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlinkerArgs::PrintUsage()
|
||||||
|
{
|
||||||
|
UsageInformation usage("unlinker.exe");
|
||||||
|
|
||||||
|
for (auto commandLineOption : COMMAND_LINE_OPTIONS)
|
||||||
|
{
|
||||||
|
usage.AddCommandLineOption(commandLineOption);
|
||||||
|
}
|
||||||
|
|
||||||
|
usage.AddArgument("pathToZone");
|
||||||
|
usage.SetVariableArguments(true);
|
||||||
|
|
||||||
|
usage.Print();
|
||||||
|
}
|
||||||
|
|
||||||
|
void UnlinkerArgs::SetVerbose(const bool isVerbose)
|
||||||
|
{
|
||||||
|
m_verbose = isVerbose;
|
||||||
|
ObjLoading::Configuration.Verbose = isVerbose;
|
||||||
|
ObjWriting::Configuration.Verbose = isVerbose;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnlinkerArgs::SetImageDumpingMode()
|
||||||
|
{
|
||||||
|
std::string specifiedValue = m_argument_parser.GetValueForOption(OPTION_IMAGE_FORMAT);
|
||||||
|
for (auto& c : specifiedValue)
|
||||||
|
c = tolower(c);
|
||||||
|
|
||||||
|
if(specifiedValue == "dds")
|
||||||
|
{
|
||||||
|
ObjWriting::Configuration.ImageOutputFormat = ObjWriting::Configuration_t::ImageOutputFormat_e::DDS;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(specifiedValue == "iwi")
|
||||||
|
{
|
||||||
|
ObjWriting::Configuration.ImageOutputFormat = ObjWriting::Configuration_t::ImageOutputFormat_e::IWI;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::string originalValue = m_argument_parser.GetValueForOption(OPTION_IMAGE_FORMAT);
|
||||||
|
printf("Illegal value: \"%s\" is not a valid image output format. Use -? to see usage information.\n", originalValue.c_str());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool UnlinkerArgs::ParseArgs(const int argc, const char** argv)
|
||||||
|
{
|
||||||
|
if (!m_argument_parser.ParseArguments(argc, argv))
|
||||||
|
{
|
||||||
|
PrintUsage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if the user requested help
|
||||||
|
if (m_argument_parser.IsOptionSpecified(OPTION_HELP))
|
||||||
|
{
|
||||||
|
PrintUsage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::vector<std::string> arguments = m_argument_parser.GetArguments();
|
||||||
|
const size_t argCount = arguments.size();
|
||||||
|
if (argCount <= 1)
|
||||||
|
{
|
||||||
|
// No zones to load specified...
|
||||||
|
PrintUsage();
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// -v; --verbose
|
||||||
|
SetVerbose(m_argument_parser.IsOptionSpecified(OPTION_VERBOSE));
|
||||||
|
|
||||||
|
// -min; --minimal-zone
|
||||||
|
ObjWriting::Configuration.MinimalZoneFileOutput = m_argument_parser.IsOptionSpecified(OPTION_MINIMAL_ZONE_FILE);
|
||||||
|
|
||||||
|
// -l; --list
|
||||||
|
if (m_argument_parser.IsOptionSpecified(OPTION_LIST))
|
||||||
|
m_task = ProcessingTask::LIST;
|
||||||
|
|
||||||
|
// -o; --output-folder
|
||||||
|
if (m_argument_parser.IsOptionSpecified(OPTION_OUTPUT_FOLDER))
|
||||||
|
m_output_folder = m_argument_parser.GetValueForOption(OPTION_OUTPUT_FOLDER);
|
||||||
|
|
||||||
|
// --search-path
|
||||||
|
if (m_argument_parser.IsOptionSpecified(OPTION_SEARCH_PATH))
|
||||||
|
{
|
||||||
|
if (!ParsePathsString(m_argument_parser.GetValueForOption(OPTION_SEARCH_PATH), m_user_search_paths))
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// --image-format
|
||||||
|
if(m_argument_parser.IsOptionSpecified(OPTION_IMAGE_FORMAT))
|
||||||
|
{
|
||||||
|
if (!SetImageDumpingMode())
|
||||||
|
{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string UnlinkerArgs::GetOutputFolderPathForZone(Zone* zone) const
|
||||||
|
{
|
||||||
|
return std::regex_replace(m_output_folder, std::regex("%zoneName%"), zone->m_name);
|
||||||
|
}
|
52
src/Unlinker/UnlinkerArgs.h
Normal file
52
src/Unlinker/UnlinkerArgs.h
Normal file
@ -0,0 +1,52 @@
|
|||||||
|
#pragma once
|
||||||
|
#include "Utils/Arguments/ArgumentParser.h"
|
||||||
|
#include "Zone/Zone.h"
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
#include <set>
|
||||||
|
|
||||||
|
class UnlinkerArgs
|
||||||
|
{
|
||||||
|
ArgumentParser m_argument_parser;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Prints a command line usage help text for the Unlinker tool to stdout.
|
||||||
|
*/
|
||||||
|
static void PrintUsage();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Splits a path string as user input into a list of paths.
|
||||||
|
* \param pathsString The path string that was taken as user input.
|
||||||
|
* \param output A set for strings to save the output to.
|
||||||
|
* \return \c true if the user input was valid and could be processed successfully, otherwise \c false.
|
||||||
|
*/
|
||||||
|
static bool ParsePathsString(const std::string& pathsString, std::set<std::string>& output);
|
||||||
|
|
||||||
|
void SetVerbose(bool isVerbose);
|
||||||
|
bool SetImageDumpingMode();
|
||||||
|
|
||||||
|
public:
|
||||||
|
enum class ProcessingTask
|
||||||
|
{
|
||||||
|
DUMP,
|
||||||
|
LIST
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<std::string> m_zones_to_load;
|
||||||
|
std::set<std::string> m_user_search_paths;
|
||||||
|
|
||||||
|
ProcessingTask m_task;
|
||||||
|
std::string m_output_folder;
|
||||||
|
|
||||||
|
bool m_verbose;
|
||||||
|
|
||||||
|
UnlinkerArgs();
|
||||||
|
bool ParseArgs(int argc, const char** argv);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Converts the output path specified by command line arguments to a path applies for the specified zone.
|
||||||
|
* \param zone The zone to resolve the path input for.
|
||||||
|
* \return An output path for the zone based on the user input.
|
||||||
|
*/
|
||||||
|
std::string GetOutputFolderPathForZone(Zone* zone) const;
|
||||||
|
};
|
@ -4,7 +4,7 @@
|
|||||||
const std::string PREFIX_LONG = "--";
|
const std::string PREFIX_LONG = "--";
|
||||||
const std::string PREFIX_SHORT = "-";
|
const std::string PREFIX_SHORT = "-";
|
||||||
|
|
||||||
ArgumentParser::ArgumentParser(const CommandLineOption** options, const size_t optionCount)
|
ArgumentParser::ArgumentParser(const CommandLineOption* const* options, const size_t optionCount)
|
||||||
{
|
{
|
||||||
for(unsigned optionIndex = 0; optionIndex < optionCount; optionIndex++)
|
for(unsigned optionIndex = 0; optionIndex < optionCount; optionIndex++)
|
||||||
{
|
{
|
||||||
|
@ -11,7 +11,7 @@ class ArgumentParser
|
|||||||
std::vector<std::string> m_matched_arguments;
|
std::vector<std::string> m_matched_arguments;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
ArgumentParser(const CommandLineOption** options, size_t optionCount);
|
ArgumentParser(const CommandLineOption* const* options, size_t optionCount);
|
||||||
|
|
||||||
bool ParseArguments(std::vector<std::string>& args);
|
bool ParseArguments(std::vector<std::string>& args);
|
||||||
bool ParseArguments(int argc, const char** argv);
|
bool ParseArguments(int argc, const char** argv);
|
||||||
|
Loading…
x
Reference in New Issue
Block a user