Merge pull request #24 from Laupetin/feature/improved-ipak-building

Improve IPak building
This commit is contained in:
Jan 2023-10-21 16:08:17 +02:00 committed by GitHub
commit 4fa66ac2ff
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
13 changed files with 201 additions and 54 deletions

View File

@ -4,6 +4,9 @@
// Overwrite the name of the zone to be "ui" // Overwrite the name of the zone to be "ui"
>name,ui >name,ui
// Set type to fastfile
>type,fastfile
// Add custom assets // Add custom assets
material,,clanlvl_box material,,clanlvl_box
material,,xp material,,xp

View File

@ -0,0 +1,7 @@
#!/bin/bash
# You can now either build the fastfile by building the project itself
Linker IPakZone
# ...or by building both targets separately
Linker "IPakZone/IPakZone_ff" "IPakZone/IPakZone_ipak"

View File

@ -0,0 +1,8 @@
// Set the game to "Call Of Duty: Black Ops 2"
>game,T6
// Set type to none, only makes the other two projects build
>type,none
build,IPakZone_ff
build,IPakZone_ipak

View File

@ -0,0 +1,11 @@
// Set the game to "Call Of Duty: Black Ops 2"
>game,T6
// Overwrite the name of the fastfile to be "IPakZone"
>name,IPakZone
// Set type to fastfile
>type,fastfile
// Add custom assets
image,sample_image

View File

@ -0,0 +1,11 @@
// Set the game to "Call Of Duty: Black Ops 2"
>game,T6
// Overwrite the name of the ipak to be "IPakZone"
>name,IPakZone
// Set type to ipak
>type,ipak
// Add custom assets
image,sample_image

View File

@ -44,6 +44,7 @@ const IZoneCreator* const ZONE_CREATORS[]
enum class ProjectType enum class ProjectType
{ {
NONE,
FASTFILE, FASTFILE,
IPAK, IPAK,
@ -52,6 +53,7 @@ enum class ProjectType
constexpr const char* PROJECT_TYPE_NAMES[static_cast<unsigned>(ProjectType::MAX)] constexpr const char* PROJECT_TYPE_NAMES[static_cast<unsigned>(ProjectType::MAX)]
{ {
"none",
"fastfile", "fastfile",
"ipak" "ipak"
}; };
@ -168,7 +170,7 @@ class LinkerImpl final : public Linker
return true; return true;
} }
static bool GetNameFromZoneDefinition(std::string& name, const std::string& projectName, const ZoneDefinition& zoneDefinition) static bool GetNameFromZoneDefinition(std::string& name, const std::string& targetName, const ZoneDefinition& zoneDefinition)
{ {
auto firstNameEntry = true; auto firstNameEntry = true;
const auto [rangeBegin, rangeEnd] = zoneDefinition.m_metadata_lookup.equal_range(METADATA_NAME); const auto [rangeBegin, rangeEnd] = zoneDefinition.m_metadata_lookup.equal_range(METADATA_NAME);
@ -183,27 +185,27 @@ class LinkerImpl final : public Linker
{ {
if (name != i->second->m_value) if (name != i->second->m_value)
{ {
std::cout << "Conflicting names in project \"" << projectName << "\": " << name << " != " << i->second << std::endl; std::cout << "Conflicting names in target \"" << targetName << "\": " << name << " != " << i->second << std::endl;
return false; return false;
} }
} }
} }
if (firstNameEntry) if (firstNameEntry)
name = projectName; name = targetName;
return true; return true;
} }
std::unique_ptr<ZoneDefinition> ReadZoneDefinition(const std::string& projectName, ISearchPath* sourceSearchPath) const std::unique_ptr<ZoneDefinition> ReadZoneDefinition(const std::string& targetName, ISearchPath* sourceSearchPath) const
{ {
std::unique_ptr<ZoneDefinition> zoneDefinition; std::unique_ptr<ZoneDefinition> zoneDefinition;
{ {
const auto definitionFileName = projectName + ".zone"; const auto definitionFileName = targetName + ".zone";
const auto definitionStream = sourceSearchPath->Open(definitionFileName); const auto definitionStream = sourceSearchPath->Open(definitionFileName);
if (!definitionStream.IsOpen()) if (!definitionStream.IsOpen())
{ {
std::cout << "Could not find zone definition file for project \"" << projectName << "\"." << std::endl; std::cout << "Could not find zone definition file for target \"" << targetName << "\"." << std::endl;
return nullptr; return nullptr;
} }
@ -213,14 +215,14 @@ class LinkerImpl final : public Linker
if (!zoneDefinition) if (!zoneDefinition)
{ {
std::cout << "Failed to read zone definition file for project \"" << projectName << "\"." << std::endl; std::cout << "Failed to read zone definition file for target \"" << targetName << "\"." << std::endl;
return nullptr; return nullptr;
} }
if (!GetNameFromZoneDefinition(zoneDefinition->m_name, projectName, *zoneDefinition)) if (!GetNameFromZoneDefinition(zoneDefinition->m_name, targetName, *zoneDefinition))
return nullptr; return nullptr;
if (!IncludeAdditionalZoneDefinitions(projectName, *zoneDefinition, sourceSearchPath)) if (!IncludeAdditionalZoneDefinitions(targetName, *zoneDefinition, sourceSearchPath))
return nullptr; return nullptr;
if (!IncludeAssetLists(*zoneDefinition, sourceSearchPath)) if (!IncludeAssetLists(*zoneDefinition, sourceSearchPath))
@ -229,7 +231,7 @@ class LinkerImpl final : public Linker
return zoneDefinition; return zoneDefinition;
} }
bool ProcessZoneDefinitionIgnores(const std::string& projectName, ZoneCreationContext& context, ISearchPath* sourceSearchPath) const bool ProcessZoneDefinitionIgnores(const std::string& targetName, ZoneCreationContext& context, ISearchPath* sourceSearchPath) const
{ {
if (context.m_definition->m_ignores.empty()) if (context.m_definition->m_ignores.empty())
return true; return true;
@ -242,7 +244,7 @@ class LinkerImpl final : public Linker
for (const auto& ignore : context.m_definition->m_ignores) for (const auto& ignore : context.m_definition->m_ignores)
{ {
if (ignore == projectName) if (ignore == targetName)
continue; continue;
std::vector<AssetListEntry> assetList; std::vector<AssetListEntry> assetList;
@ -269,9 +271,9 @@ class LinkerImpl final : public Linker
return false; return false;
} }
static bool GetProjectTypeFromZoneDefinition(ProjectType& projectType, const std::string& projectName, const ZoneDefinition& zoneDefinition) static bool GetProjectTypeFromZoneDefinition(ProjectType& projectType, const std::string& targetName, const ZoneDefinition& zoneDefinition)
{ {
auto firstGameEntry = true; auto firstTypeEntry = true;
const auto [rangeBegin, rangeEnd] = zoneDefinition.m_metadata_lookup.equal_range(METADATA_TYPE); const auto [rangeBegin, rangeEnd] = zoneDefinition.m_metadata_lookup.equal_range(METADATA_TYPE);
for (auto i = rangeBegin; i != rangeEnd; ++i) for (auto i = rangeBegin; i != rangeEnd; ++i)
{ {
@ -282,29 +284,34 @@ class LinkerImpl final : public Linker
return false; return false;
} }
if (firstGameEntry) if (firstTypeEntry)
{ {
projectType = parsedProjectType; projectType = parsedProjectType;
firstGameEntry = false; firstTypeEntry = false;
} }
else else
{ {
if (projectType != parsedProjectType) if (projectType != parsedProjectType)
{ {
std::cerr << "Conflicting types in project \"" << projectName << "\": " << PROJECT_TYPE_NAMES[static_cast<unsigned>(projectType)] std::cerr << "Conflicting types in target \"" << targetName << "\": " << PROJECT_TYPE_NAMES[static_cast<unsigned>(projectType)]
<< " != " << PROJECT_TYPE_NAMES[static_cast<unsigned>(parsedProjectType)] << std::endl; << " != " << PROJECT_TYPE_NAMES[static_cast<unsigned>(parsedProjectType)] << std::endl;
return false; return false;
} }
} }
} }
if (firstGameEntry) if (firstTypeEntry)
{
if (zoneDefinition.m_assets.empty())
projectType = ProjectType::NONE;
else
projectType = ProjectType::FASTFILE; projectType = ProjectType::FASTFILE;
}
return true; return true;
} }
static bool GetGameNameFromZoneDefinition(std::string& gameName, const std::string& projectName, const ZoneDefinition& zoneDefinition) static bool GetGameNameFromZoneDefinition(std::string& gameName, const std::string& targetName, const ZoneDefinition& zoneDefinition)
{ {
auto firstGameEntry = true; auto firstGameEntry = true;
const auto [rangeBegin, rangeEnd] = zoneDefinition.m_metadata_lookup.equal_range(METADATA_GAME); const auto [rangeBegin, rangeEnd] = zoneDefinition.m_metadata_lookup.equal_range(METADATA_GAME);
@ -319,7 +326,7 @@ class LinkerImpl final : public Linker
{ {
if (gameName != i->second->m_value) if (gameName != i->second->m_value)
{ {
std::cout << "Conflicting game names in project \"" << projectName << "\": " << gameName << " != " << i->second << std::endl; std::cout << "Conflicting game names in target \"" << targetName << "\": " << gameName << " != " << i->second << std::endl;
return false; return false;
} }
} }
@ -327,7 +334,7 @@ class LinkerImpl final : public Linker
if (firstGameEntry) if (firstGameEntry)
{ {
std::cout << "No game name was specified for project \"" << projectName << "\"" << std::endl; std::cout << "No game name was specified for target \"" << targetName << "\"" << std::endl;
return false; return false;
} }
@ -360,13 +367,13 @@ class LinkerImpl final : public Linker
return true; return true;
} }
std::unique_ptr<Zone> CreateZoneForDefinition(const std::string& projectName, ZoneDefinition& zoneDefinition, ISearchPath* assetSearchPath, ISearchPath* gdtSearchPath, std::unique_ptr<Zone> CreateZoneForDefinition(const std::string& targetName, ZoneDefinition& zoneDefinition, ISearchPath* assetSearchPath, ISearchPath* gdtSearchPath,
ISearchPath* sourceSearchPath) const ISearchPath* sourceSearchPath) const
{ {
const auto context = std::make_unique<ZoneCreationContext>(assetSearchPath, &zoneDefinition); const auto context = std::make_unique<ZoneCreationContext>(assetSearchPath, &zoneDefinition);
if (!ProcessZoneDefinitionIgnores(projectName, *context, sourceSearchPath)) if (!ProcessZoneDefinitionIgnores(targetName, *context, sourceSearchPath))
return nullptr; return nullptr;
if (!GetGameNameFromZoneDefinition(context->m_game_name, projectName, zoneDefinition)) if (!GetGameNameFromZoneDefinition(context->m_game_name, targetName, zoneDefinition))
return nullptr; return nullptr;
if (!LoadGdtFilesFromZoneDefinition(context->m_gdt_files, zoneDefinition, gdtSearchPath)) if (!LoadGdtFilesFromZoneDefinition(context->m_gdt_files, zoneDefinition, gdtSearchPath))
return nullptr; return nullptr;
@ -405,9 +412,10 @@ class LinkerImpl final : public Linker
return true; return true;
} }
bool BuildFastFile(const std::string& projectName, ZoneDefinition& zoneDefinition, SearchPaths& assetSearchPaths, SearchPaths& gdtSearchPaths, SearchPaths& sourceSearchPaths) const bool BuildFastFile(const std::string& projectName, const std::string& targetName, ZoneDefinition& zoneDefinition, SearchPaths& assetSearchPaths, SearchPaths& gdtSearchPaths,
SearchPaths& sourceSearchPaths) const
{ {
const auto zone = CreateZoneForDefinition(projectName, zoneDefinition, &assetSearchPaths, &gdtSearchPaths, &sourceSearchPaths); const auto zone = CreateZoneForDefinition(targetName, zoneDefinition, &assetSearchPaths, &gdtSearchPaths, &sourceSearchPaths);
auto result = zone != nullptr; auto result = zone != nullptr;
if (zone) if (zone)
result = WriteZoneToFile(projectName, zone.get()); result = WriteZoneToFile(projectName, zone.get());
@ -415,7 +423,7 @@ class LinkerImpl final : public Linker
return result; return result;
} }
bool BuildIPak(const std::string& projectName, const ZoneDefinition& zoneDefinition, SearchPaths& assetSearchPaths, SearchPaths& sourceSearchPaths) const bool BuildIPak(const std::string& projectName, const ZoneDefinition& zoneDefinition, SearchPaths& assetSearchPaths) const
{ {
const fs::path ipakFolderPath(m_args.GetOutputFolderPathForProject(projectName)); const fs::path ipakFolderPath(m_args.GetOutputFolderPathForProject(projectName));
auto ipakFilePath(ipakFolderPath); auto ipakFilePath(ipakFolderPath);
@ -450,44 +458,65 @@ class LinkerImpl final : public Linker
return true; return true;
} }
bool BuildProject(const std::string& projectName) bool BuildReferencedTargets(const std::string& projectName, const std::string& targetName, const ZoneDefinition& zoneDefinition)
{
return std::all_of(zoneDefinition.m_targets_to_build.begin(), zoneDefinition.m_targets_to_build.end(), [this, &projectName, &targetName](const std::string& buildTargetName)
{
if (buildTargetName == targetName)
{
std::cerr << "Cannot build target with same name: \"" << targetName << "\"\n";
return false;
}
std::cout << "Building referenced target \"" << buildTargetName << "\"\n";
return BuildProject(projectName, buildTargetName);
});
}
bool BuildProject(const std::string& projectName, const std::string& targetName)
{ {
auto sourceSearchPaths = m_search_paths.GetSourceSearchPathsForProject(projectName); auto sourceSearchPaths = m_search_paths.GetSourceSearchPathsForProject(projectName);
const auto zoneDefinition = ReadZoneDefinition(projectName, &sourceSearchPaths); const auto zoneDefinition = ReadZoneDefinition(targetName, &sourceSearchPaths);
if (!zoneDefinition) if (!zoneDefinition)
return false; return false;
ProjectType projectType; ProjectType projectType;
if (!GetProjectTypeFromZoneDefinition(projectType, projectName, *zoneDefinition)) if (!GetProjectTypeFromZoneDefinition(projectType, targetName, *zoneDefinition))
return false; return false;
auto result = true;
if (projectType != ProjectType::NONE)
{
std::string gameName; std::string gameName;
if (!GetGameNameFromZoneDefinition(gameName, projectName, *zoneDefinition)) if (!GetGameNameFromZoneDefinition(gameName, targetName, *zoneDefinition))
return false; return false;
utils::MakeStringLowerCase(gameName); utils::MakeStringLowerCase(gameName);
auto assetSearchPaths = m_search_paths.GetAssetSearchPathsForProject(gameName, projectName); auto assetSearchPaths = m_search_paths.GetAssetSearchPathsForProject(gameName, projectName);
auto gdtSearchPaths = m_search_paths.GetGdtSearchPathsForProject(gameName, projectName); auto gdtSearchPaths = m_search_paths.GetGdtSearchPathsForProject(gameName, projectName);
auto result = false;
switch (projectType) switch (projectType)
{ {
case ProjectType::FASTFILE: case ProjectType::FASTFILE:
result = BuildFastFile(projectName, *zoneDefinition, assetSearchPaths, gdtSearchPaths, sourceSearchPaths); result = BuildFastFile(projectName, targetName, *zoneDefinition, assetSearchPaths, gdtSearchPaths, sourceSearchPaths);
break; break;
case ProjectType::IPAK: case ProjectType::IPAK:
result = BuildIPak(projectName, *zoneDefinition, assetSearchPaths, sourceSearchPaths); result = BuildIPak(projectName, *zoneDefinition, assetSearchPaths);
break; break;
default: default:
assert(false); assert(false);
result = false;
break; break;
} }
}
m_search_paths.UnloadProjectSpecificSearchPaths(); m_search_paths.UnloadProjectSpecificSearchPaths();
result = result && BuildReferencedTargets(projectName, targetName, *zoneDefinition);
return result; return result;
} }
@ -536,6 +565,40 @@ class LinkerImpl final : public Linker
m_loaded_zones.clear(); m_loaded_zones.clear();
} }
static bool GetProjectAndTargetFromProjectSpecifier(const std::string& projectSpecifier, std::string& projectName, std::string& targetName)
{
const auto targetNameSeparatorIndex = projectSpecifier.find_first_of('/');
if (targetNameSeparatorIndex == std::string::npos)
{
projectName = projectSpecifier;
targetName = projectSpecifier;
}
else if (projectSpecifier.find_first_of('/', targetNameSeparatorIndex + 1) != std::string::npos)
{
std::cerr << "Project specifier cannot have more than one target name: \"" << projectSpecifier << "\"\n";
return false;
}
else
{
projectName = projectSpecifier.substr(0, targetNameSeparatorIndex);
targetName = projectSpecifier.substr(targetNameSeparatorIndex + 1);
}
if (projectName.empty())
{
std::cerr << "Project name cannot be empty: \"" << projectSpecifier << "\"\n";
return false;
}
if (targetName.empty())
{
std::cerr << "Target name cannot be empty: \"" << projectSpecifier << "\"\n";
return false;
}
return true;
}
public: public:
LinkerImpl() LinkerImpl()
: m_search_paths(m_args) : m_search_paths(m_args)
@ -554,9 +617,17 @@ public:
return false; return false;
auto result = true; auto result = true;
for (const auto& projectName : m_args.m_projects_to_build) for (const auto& projectSpecifier : m_args.m_project_specifiers_to_build)
{ {
if (!BuildProject(projectName)) std::string projectName;
std::string targetName;
if (!GetProjectAndTargetFromProjectSpecifier(projectSpecifier, projectName, targetName))
{
result = false;
break;
}
if (!BuildProject(projectName, targetName))
{ {
result = false; result = false;
break; break;

View File

@ -210,8 +210,8 @@ bool LinkerArgs::ParseArgs(const int argc, const char** argv)
return false; return false;
} }
m_projects_to_build = m_argument_parser.GetArguments(); m_project_specifiers_to_build = m_argument_parser.GetArguments();
if (m_projects_to_build.empty()) if (m_project_specifiers_to_build.empty())
{ {
// No projects to build specified... // No projects to build specified...
PrintUsage(); PrintUsage();

View File

@ -41,7 +41,7 @@ private:
public: public:
std::vector<std::string> m_zones_to_load; std::vector<std::string> m_zones_to_load;
std::vector<std::string> m_projects_to_build; std::vector<std::string> m_project_specifiers_to_build;
std::string m_base_folder; std::string m_base_folder;
std::string m_out_folder; std::string m_out_folder;

View File

@ -0,0 +1,19 @@
#include "SequenceZoneDefinitionBuild.h"
#include "Parsing/ZoneDefinition/Matcher/ZoneDefinitionMatcherFactory.h"
SequenceZoneDefinitionBuild::SequenceZoneDefinitionBuild()
{
const ZoneDefinitionMatcherFactory create(this);
AddMatchers({
create.Keyword("build"),
create.Char(','),
create.Field().Capture(CAPTURE_BUILD_TARGET_NAME)
});
}
void SequenceZoneDefinitionBuild::ProcessMatch(ZoneDefinition* state, SequenceResult<ZoneDefinitionParserValue>& result) const
{
state->m_targets_to_build.emplace_back(result.NextCapture(CAPTURE_BUILD_TARGET_NAME).FieldValue());
}

View File

@ -0,0 +1,14 @@
#pragma once
#include "Parsing/ZoneDefinition/ZoneDefinitionParser.h"
class SequenceZoneDefinitionBuild final : public ZoneDefinitionParser::sequence_t
{
static constexpr auto CAPTURE_BUILD_TARGET_NAME = 1;
protected:
void ProcessMatch(ZoneDefinition* state, SequenceResult<ZoneDefinitionParserValue>& result) const override;
public:
SequenceZoneDefinitionBuild();
};

View File

@ -1,6 +1,7 @@
#include "ZoneDefinitionParser.h" #include "ZoneDefinitionParser.h"
#include "Sequence/SequenceZoneDefinitionAssetList.h" #include "Sequence/SequenceZoneDefinitionAssetList.h"
#include "Sequence/SequenceZoneDefinitionBuild.h"
#include "Sequence/SequenceZoneDefinitionEntry.h" #include "Sequence/SequenceZoneDefinitionEntry.h"
#include "Sequence/SequenceZoneDefinitionIgnore.h" #include "Sequence/SequenceZoneDefinitionIgnore.h"
#include "Sequence/SequenceZoneDefinitionInclude.h" #include "Sequence/SequenceZoneDefinitionInclude.h"
@ -18,6 +19,7 @@ const std::vector<AbstractParser<ZoneDefinitionParserValue, ZoneDefinition>::seq
new SequenceZoneDefinitionInclude(), new SequenceZoneDefinitionInclude(),
new SequenceZoneDefinitionIgnore(), new SequenceZoneDefinitionIgnore(),
new SequenceZoneDefinitionAssetList(), new SequenceZoneDefinitionAssetList(),
new SequenceZoneDefinitionBuild(),
new SequenceZoneDefinitionEntry() new SequenceZoneDefinitionEntry()
}); });

View File

@ -37,6 +37,7 @@ public:
std::vector<std::string> m_includes; std::vector<std::string> m_includes;
std::vector<std::string> m_asset_lists; std::vector<std::string> m_asset_lists;
std::vector<std::string> m_ignores; std::vector<std::string> m_ignores;
std::vector<std::string> m_targets_to_build;
std::vector<ZoneDefinitionEntry> m_assets; std::vector<ZoneDefinitionEntry> m_assets;
void AddMetaData(std::string key, std::string value); void AddMetaData(std::string key, std::string value);