mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-19 15:52:53 +00:00
Merge pull request #24 from Laupetin/feature/improved-ipak-building
Improve IPak building
This commit is contained in:
commit
4fa66ac2ff
@ -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
|
||||
|
7
docs/example/IPakZone/build.sh
Normal file
7
docs/example/IPakZone/build.sh
Normal 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"
|
0
docs/example/IPakZone/images/sample_image.iwi
Normal file
0
docs/example/IPakZone/images/sample_image.iwi
Normal file
8
docs/example/IPakZone/zone_source/IPakZone.zone
Normal file
8
docs/example/IPakZone/zone_source/IPakZone.zone
Normal 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
|
11
docs/example/IPakZone/zone_source/IPakZone_ff.zone
Normal file
11
docs/example/IPakZone/zone_source/IPakZone_ff.zone
Normal 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
|
11
docs/example/IPakZone/zone_source/IPakZone_ipak.zone
Normal file
11
docs/example/IPakZone/zone_source/IPakZone_ipak.zone
Normal 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
|
@ -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;
|
||||
|
@ -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();
|
||||
|
@ -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;
|
||||
|
@ -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());
|
||||
}
|
@ -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();
|
||||
};
|
@ -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()
|
||||
});
|
||||
|
||||
|
@ -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);
|
||||
|
Loading…
x
Reference in New Issue
Block a user