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"
>name,ui
// Set type to fastfile
>type,fastfile
// Add custom assets
material,,clanlvl_box
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
{
NONE,
FASTFILE,
IPAK,
@ -52,6 +53,7 @@ enum class ProjectType
constexpr const char* PROJECT_TYPE_NAMES[static_cast<unsigned>(ProjectType::MAX)]
{
"none",
"fastfile",
"ipak"
};
@ -168,7 +170,7 @@ class LinkerImpl final : public Linker
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;
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)
{
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;
}
}
}
if (firstNameEntry)
name = projectName;
name = targetName;
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;
{
const auto definitionFileName = projectName + ".zone";
const auto definitionFileName = targetName + ".zone";
const auto definitionStream = sourceSearchPath->Open(definitionFileName);
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;
}
@ -213,14 +215,14 @@ class LinkerImpl final : public Linker
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;
}
if (!GetNameFromZoneDefinition(zoneDefinition->m_name, projectName, *zoneDefinition))
if (!GetNameFromZoneDefinition(zoneDefinition->m_name, targetName, *zoneDefinition))
return nullptr;
if (!IncludeAdditionalZoneDefinitions(projectName, *zoneDefinition, sourceSearchPath))
if (!IncludeAdditionalZoneDefinitions(targetName, *zoneDefinition, sourceSearchPath))
return nullptr;
if (!IncludeAssetLists(*zoneDefinition, sourceSearchPath))
@ -229,7 +231,7 @@ class LinkerImpl final : public Linker
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())
return true;
@ -242,7 +244,7 @@ class LinkerImpl final : public Linker
for (const auto& ignore : context.m_definition->m_ignores)
{
if (ignore == projectName)
if (ignore == targetName)
continue;
std::vector<AssetListEntry> assetList;
@ -269,9 +271,9 @@ class LinkerImpl final : public Linker
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);
for (auto i = rangeBegin; i != rangeEnd; ++i)
{
@ -282,29 +284,34 @@ class LinkerImpl final : public Linker
return false;
}
if (firstGameEntry)
if (firstTypeEntry)
{
projectType = parsedProjectType;
firstGameEntry = false;
firstTypeEntry = false;
}
else
{
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;
return false;
}
}
}
if (firstGameEntry)
if (firstTypeEntry)
{
if (zoneDefinition.m_assets.empty())
projectType = ProjectType::NONE;
else
projectType = ProjectType::FASTFILE;
}
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;
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)
{
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;
}
}
@ -327,7 +334,7 @@ class LinkerImpl final : public Linker
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;
}
@ -360,13 +367,13 @@ class LinkerImpl final : public Linker
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
{
const auto context = std::make_unique<ZoneCreationContext>(assetSearchPath, &zoneDefinition);
if (!ProcessZoneDefinitionIgnores(projectName, *context, sourceSearchPath))
if (!ProcessZoneDefinitionIgnores(targetName, *context, sourceSearchPath))
return nullptr;
if (!GetGameNameFromZoneDefinition(context->m_game_name, projectName, zoneDefinition))
if (!GetGameNameFromZoneDefinition(context->m_game_name, targetName, zoneDefinition))
return nullptr;
if (!LoadGdtFilesFromZoneDefinition(context->m_gdt_files, zoneDefinition, gdtSearchPath))
return nullptr;
@ -405,9 +412,10 @@ class LinkerImpl final : public Linker
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;
if (zone)
result = WriteZoneToFile(projectName, zone.get());
@ -415,7 +423,7 @@ class LinkerImpl final : public Linker
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));
auto ipakFilePath(ipakFolderPath);
@ -450,44 +458,65 @@ class LinkerImpl final : public Linker
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);
const auto zoneDefinition = ReadZoneDefinition(projectName, &sourceSearchPaths);
const auto zoneDefinition = ReadZoneDefinition(targetName, &sourceSearchPaths);
if (!zoneDefinition)
return false;
ProjectType projectType;
if (!GetProjectTypeFromZoneDefinition(projectType, projectName, *zoneDefinition))
if (!GetProjectTypeFromZoneDefinition(projectType, targetName, *zoneDefinition))
return false;
auto result = true;
if (projectType != ProjectType::NONE)
{
std::string gameName;
if (!GetGameNameFromZoneDefinition(gameName, projectName, *zoneDefinition))
if (!GetGameNameFromZoneDefinition(gameName, targetName, *zoneDefinition))
return false;
utils::MakeStringLowerCase(gameName);
auto assetSearchPaths = m_search_paths.GetAssetSearchPathsForProject(gameName, projectName);
auto gdtSearchPaths = m_search_paths.GetGdtSearchPathsForProject(gameName, projectName);
auto result = false;
switch (projectType)
{
case ProjectType::FASTFILE:
result = BuildFastFile(projectName, *zoneDefinition, assetSearchPaths, gdtSearchPaths, sourceSearchPaths);
result = BuildFastFile(projectName, targetName, *zoneDefinition, assetSearchPaths, gdtSearchPaths, sourceSearchPaths);
break;
case ProjectType::IPAK:
result = BuildIPak(projectName, *zoneDefinition, assetSearchPaths, sourceSearchPaths);
result = BuildIPak(projectName, *zoneDefinition, assetSearchPaths);
break;
default:
assert(false);
result = false;
break;
}
}
m_search_paths.UnloadProjectSpecificSearchPaths();
result = result && BuildReferencedTargets(projectName, targetName, *zoneDefinition);
return result;
}
@ -536,6 +565,40 @@ class LinkerImpl final : public Linker
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:
LinkerImpl()
: m_search_paths(m_args)
@ -554,9 +617,17 @@ public:
return false;
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;
break;

View File

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

View File

@ -41,7 +41,7 @@ private:
public:
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_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 "Sequence/SequenceZoneDefinitionAssetList.h"
#include "Sequence/SequenceZoneDefinitionBuild.h"
#include "Sequence/SequenceZoneDefinitionEntry.h"
#include "Sequence/SequenceZoneDefinitionIgnore.h"
#include "Sequence/SequenceZoneDefinitionInclude.h"
@ -18,6 +19,7 @@ const std::vector<AbstractParser<ZoneDefinitionParserValue, ZoneDefinition>::seq
new SequenceZoneDefinitionInclude(),
new SequenceZoneDefinitionIgnore(),
new SequenceZoneDefinitionAssetList(),
new SequenceZoneDefinitionBuild(),
new SequenceZoneDefinitionEntry()
});

View File

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