mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-04-19 07:42:54 +00:00
Merge pull request #330 from Laupetin/test/obj-compiling-tests
Add tests for ObjCompiling module
This commit is contained in:
commit
98288f5d47
3
.github/workflows/ci.yaml
vendored
3
.github/workflows/ci.yaml
vendored
@ -49,6 +49,7 @@ jobs:
|
||||
working-directory: ${{ github.workspace }}/build/lib/Release_x86/tests
|
||||
run: |
|
||||
./ObjCommonTests
|
||||
./ObjCompilingTests
|
||||
./ObjLoadingTests
|
||||
./ParserTests
|
||||
./ZoneCodeGeneratorLibTests
|
||||
@ -86,6 +87,8 @@ jobs:
|
||||
$combinedExitCode = 0
|
||||
./ObjCommonTests
|
||||
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
|
||||
./ObjCompilingTests
|
||||
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
|
||||
./ObjLoadingTests
|
||||
$combinedExitCode = [System.Math]::max($combinedExitCode, $LASTEXITCODE)
|
||||
./ParserTests
|
||||
|
@ -170,8 +170,10 @@ group ""
|
||||
-- ========================
|
||||
-- Tests
|
||||
-- ========================
|
||||
include "test/Catch2Common.lua"
|
||||
include "test/ObjCommonTestUtils.lua"
|
||||
include "test/ObjCommonTests.lua"
|
||||
include "test/ObjCompilingTests.lua"
|
||||
include "test/ObjLoadingTests.lua"
|
||||
include "test/ParserTestUtils.lua"
|
||||
include "test/ParserTests.lua"
|
||||
@ -180,8 +182,10 @@ include "test/ZoneCommonTests.lua"
|
||||
|
||||
-- Tests group: Unit test and other tests projects
|
||||
group "Tests"
|
||||
Catch2Common:project()
|
||||
ObjCommonTestUtils:project()
|
||||
ObjCommonTests:project()
|
||||
ObjCompilingTests:project()
|
||||
ObjLoadingTests:project()
|
||||
ParserTestUtils:project()
|
||||
ParserTests:project()
|
||||
|
@ -3,6 +3,7 @@
|
||||
#include "Gdt/GdtLookup.h"
|
||||
#include "IObjCompiler.h"
|
||||
#include "IObjLoader.h"
|
||||
#include "SearchPath/OutputPathFilesystem.h"
|
||||
|
||||
#include <cassert>
|
||||
|
||||
@ -67,8 +68,10 @@ namespace zone_creator
|
||||
ZoneDefinitionContext zoneDefinitionContext(*context.m_definition);
|
||||
AssetCreationContext creationContext(*zone, &creatorCollection, &ignoredAssetLookup);
|
||||
|
||||
OutputPathFilesystem outDir(context.m_out_dir);
|
||||
OutputPathFilesystem cacheDir(context.m_cache_dir);
|
||||
objCompiler->ConfigureCreatorCollection(
|
||||
creatorCollection, *zone, zoneDefinitionContext, *context.m_asset_search_path, lookup, creationContext, context.m_out_dir, context.m_cache_dir);
|
||||
creatorCollection, *zone, zoneDefinitionContext, *context.m_asset_search_path, lookup, creationContext, outDir, cacheDir);
|
||||
objLoader->ConfigureCreatorCollection(creatorCollection, *zone, *context.m_asset_search_path, lookup);
|
||||
|
||||
for (const auto& assetEntry : context.m_definition->m_assets)
|
||||
|
18
src/ObjCommon/SearchPath/IOutputPath.h
Normal file
18
src/ObjCommon/SearchPath/IOutputPath.h
Normal file
@ -0,0 +1,18 @@
|
||||
#pragma once
|
||||
|
||||
#include <memory>
|
||||
#include <ostream>
|
||||
#include <string>
|
||||
|
||||
class IOutputPath
|
||||
{
|
||||
public:
|
||||
IOutputPath() = default;
|
||||
virtual ~IOutputPath() = default;
|
||||
IOutputPath(const IOutputPath& other) = default;
|
||||
IOutputPath(IOutputPath&& other) noexcept = default;
|
||||
IOutputPath& operator=(const IOutputPath& other) = default;
|
||||
IOutputPath& operator=(IOutputPath&& other) noexcept = default;
|
||||
|
||||
virtual std::unique_ptr<std::ostream> Open(const std::string& fileName) = 0;
|
||||
};
|
27
src/ObjCommon/SearchPath/OutputPathFilesystem.cpp
Normal file
27
src/ObjCommon/SearchPath/OutputPathFilesystem.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "OutputPathFilesystem.h"
|
||||
|
||||
#include <fstream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
OutputPathFilesystem::OutputPathFilesystem(const fs::path& path)
|
||||
: m_path(fs::weakly_canonical(path))
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<std::ostream> OutputPathFilesystem::Open(const std::string& fileName)
|
||||
{
|
||||
const auto fullNewPath = fs::weakly_canonical(m_path / fileName);
|
||||
|
||||
if (!fullNewPath.string().starts_with(m_path.string()))
|
||||
return nullptr;
|
||||
|
||||
const auto containingDirectory = fullNewPath.parent_path();
|
||||
fs::create_directories(containingDirectory);
|
||||
|
||||
std::ofstream stream(fullNewPath, std::ios::binary | std::ios::out);
|
||||
if (!stream.is_open())
|
||||
return nullptr;
|
||||
|
||||
return std::make_unique<std::ofstream>(std::move(stream));
|
||||
}
|
16
src/ObjCommon/SearchPath/OutputPathFilesystem.h
Normal file
16
src/ObjCommon/SearchPath/OutputPathFilesystem.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "IOutputPath.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
class OutputPathFilesystem final : public IOutputPath
|
||||
{
|
||||
public:
|
||||
explicit OutputPathFilesystem(const std::filesystem::path& path);
|
||||
|
||||
std::unique_ptr<std::ostream> Open(const std::string& fileName) override;
|
||||
|
||||
private:
|
||||
std::filesystem::path m_path;
|
||||
};
|
@ -6,7 +6,6 @@
|
||||
#include <memory>
|
||||
|
||||
using namespace IW3;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -22,7 +21,7 @@ namespace
|
||||
const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir)
|
||||
IOutputPath& outDir)
|
||||
{
|
||||
auto& memory = *zone.GetMemory();
|
||||
|
||||
@ -37,8 +36,8 @@ void ObjCompiler::ConfigureCreatorCollection(AssetCreatorCollection& collection,
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir,
|
||||
const fs::path& cacheDir) const
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const
|
||||
{
|
||||
ConfigurePostProcessors(collection, zone, zoneDefinition, searchPath, zoneStates, outDir);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace IW3
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir,
|
||||
const std::filesystem::path& cacheDir) const override;
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const override;
|
||||
};
|
||||
} // namespace IW3
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <memory>
|
||||
|
||||
using namespace IW4;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -22,7 +21,7 @@ namespace
|
||||
const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir)
|
||||
IOutputPath& outDir)
|
||||
{
|
||||
auto& memory = *zone.GetMemory();
|
||||
|
||||
@ -37,8 +36,8 @@ void ObjCompiler::ConfigureCreatorCollection(AssetCreatorCollection& collection,
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir,
|
||||
const fs::path& cacheDir) const
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const
|
||||
{
|
||||
ConfigurePostProcessors(collection, zone, zoneDefinition, searchPath, zoneStates, outDir);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace IW4
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir,
|
||||
const std::filesystem::path& cacheDir) const override;
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const override;
|
||||
};
|
||||
} // namespace IW4
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <memory>
|
||||
|
||||
using namespace IW5;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -22,7 +21,7 @@ namespace
|
||||
const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir)
|
||||
IOutputPath& outDir)
|
||||
{
|
||||
auto& memory = *zone.GetMemory();
|
||||
|
||||
@ -37,8 +36,8 @@ void ObjCompiler::ConfigureCreatorCollection(AssetCreatorCollection& collection,
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir,
|
||||
const fs::path& cacheDir) const
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const
|
||||
{
|
||||
ConfigurePostProcessors(collection, zone, zoneDefinition, searchPath, zoneStates, outDir);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace IW5
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir,
|
||||
const std::filesystem::path& cacheDir) const override;
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const override;
|
||||
};
|
||||
} // namespace IW5
|
||||
|
@ -6,7 +6,6 @@
|
||||
#include <memory>
|
||||
|
||||
using namespace T5;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -22,7 +21,7 @@ namespace
|
||||
const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir)
|
||||
IOutputPath& outDir)
|
||||
{
|
||||
auto& memory = *zone.GetMemory();
|
||||
|
||||
@ -37,8 +36,8 @@ void ObjCompiler::ConfigureCreatorCollection(AssetCreatorCollection& collection,
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir,
|
||||
const fs::path& cacheDir) const
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const
|
||||
{
|
||||
ConfigurePostProcessors(collection, zone, zoneDefinition, searchPath, zoneStates, outDir);
|
||||
}
|
||||
|
@ -13,7 +13,7 @@ namespace T5
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir,
|
||||
const std::filesystem::path& cacheDir) const override;
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const override;
|
||||
};
|
||||
} // namespace T5
|
||||
|
@ -2,6 +2,7 @@
|
||||
|
||||
#include "Game/T6/CommonT6.h"
|
||||
#include "Game/T6/T6.h"
|
||||
#include "KeyValuePairs/KeyValuePairsCreator.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <format>
|
||||
@ -9,54 +10,73 @@
|
||||
|
||||
using namespace T6;
|
||||
|
||||
KeyValuePairsCompiler::KeyValuePairsCompiler(MemoryManager& memory,
|
||||
const Zone& zone,
|
||||
const ZoneDefinition& zoneDefinition,
|
||||
ZoneAssetCreationStateContainer& zoneStates)
|
||||
: m_memory(memory),
|
||||
m_zone(zone),
|
||||
m_zone_definition(zoneDefinition),
|
||||
m_kvp_creator(zoneStates.GetZoneAssetCreationState<KeyValuePairsCreator>())
|
||||
namespace
|
||||
{
|
||||
}
|
||||
|
||||
std::optional<asset_type_t> KeyValuePairsCompiler::GetHandlingAssetType() const
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
AssetCreationResult KeyValuePairsCompiler::CreateAsset(const std::string& assetName, AssetCreationContext& context)
|
||||
{
|
||||
return AssetCreationResult::NoAction();
|
||||
}
|
||||
|
||||
void KeyValuePairsCompiler::FinalizeZone(AssetCreationContext& context)
|
||||
{
|
||||
m_kvp_creator.Finalize(m_zone_definition);
|
||||
const auto commonKvps = m_kvp_creator.GetFinalKeyValuePairs();
|
||||
if (commonKvps.empty())
|
||||
return;
|
||||
|
||||
auto* gameKvps = m_memory.Alloc<KeyValuePairs>();
|
||||
gameKvps->name = m_memory.Dup(m_zone.m_name.c_str());
|
||||
gameKvps->numVariables = commonKvps.size();
|
||||
gameKvps->keyValuePairs = m_memory.Alloc<KeyValuePair>(commonKvps.size());
|
||||
|
||||
const auto namespaceHash = Common::Com_HashKey(m_zone.m_name.c_str(), 64);
|
||||
for (auto kvpIndex = 0u; kvpIndex < gameKvps->numVariables; kvpIndex++)
|
||||
class KeyValuePairsCompiler final : public IAssetCreator
|
||||
{
|
||||
const auto& commonKvp = commonKvps[kvpIndex];
|
||||
auto& gameKvp = gameKvps->keyValuePairs[kvpIndex];
|
||||
public:
|
||||
KeyValuePairsCompiler(MemoryManager& memory, const Zone& zone, const ZoneDefinition& zoneDefinition, ZoneAssetCreationStateContainer& zoneStates)
|
||||
: m_memory(memory),
|
||||
m_zone(zone),
|
||||
m_zone_definition(zoneDefinition),
|
||||
m_kvp_creator(zoneStates.GetZoneAssetCreationState<KeyValuePairsCreator>())
|
||||
{
|
||||
}
|
||||
|
||||
assert(commonKvp.m_key_str || commonKvp.m_key_hash);
|
||||
if (commonKvp.m_key_str)
|
||||
gameKvp.keyHash = Common::Com_HashKey(commonKvp.m_key_str->c_str(), 64);
|
||||
else
|
||||
gameKvp.keyHash = *commonKvp.m_key_hash;
|
||||
[[nodiscard]] std::optional<asset_type_t> GetHandlingAssetType() const override
|
||||
{
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
gameKvp.namespaceHash = namespaceHash;
|
||||
gameKvp.value = m_memory.Dup(commonKvp.m_value.c_str());
|
||||
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override
|
||||
{
|
||||
return AssetCreationResult::NoAction();
|
||||
}
|
||||
|
||||
void FinalizeZone(AssetCreationContext& context) override
|
||||
{
|
||||
m_kvp_creator.Finalize(m_zone_definition);
|
||||
const auto commonKvps = m_kvp_creator.GetFinalKeyValuePairs();
|
||||
if (commonKvps.empty())
|
||||
return;
|
||||
|
||||
auto* gameKvps = m_memory.Alloc<KeyValuePairs>();
|
||||
gameKvps->name = m_memory.Dup(m_zone.m_name.c_str());
|
||||
gameKvps->numVariables = commonKvps.size();
|
||||
gameKvps->keyValuePairs = m_memory.Alloc<KeyValuePair>(commonKvps.size());
|
||||
|
||||
const auto namespaceHash = Common::Com_HashKey(m_zone.m_name.c_str(), 64);
|
||||
for (auto kvpIndex = 0u; kvpIndex < gameKvps->numVariables; kvpIndex++)
|
||||
{
|
||||
const auto& commonKvp = commonKvps[kvpIndex];
|
||||
auto& gameKvp = gameKvps->keyValuePairs[kvpIndex];
|
||||
|
||||
assert(commonKvp.m_key_str || commonKvp.m_key_hash);
|
||||
if (commonKvp.m_key_str)
|
||||
gameKvp.keyHash = Common::Com_HashKey(commonKvp.m_key_str->c_str(), 64);
|
||||
else
|
||||
gameKvp.keyHash = *commonKvp.m_key_hash;
|
||||
|
||||
gameKvp.namespaceHash = namespaceHash;
|
||||
gameKvp.value = m_memory.Dup(commonKvp.m_value.c_str());
|
||||
}
|
||||
|
||||
context.AddAsset(AssetRegistration<AssetKeyValuePairs>(m_zone.m_name, gameKvps));
|
||||
}
|
||||
|
||||
private:
|
||||
MemoryManager& m_memory;
|
||||
const Zone& m_zone;
|
||||
const ZoneDefinition& m_zone_definition;
|
||||
KeyValuePairsCreator& m_kvp_creator;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace T6
|
||||
{
|
||||
std::unique_ptr<IAssetCreator>
|
||||
CreateKeyValuePairsCompiler(MemoryManager& memory, const Zone& zone, const ZoneDefinition& zoneDefinition, ZoneAssetCreationStateContainer& zoneStates)
|
||||
{
|
||||
return std::make_unique<KeyValuePairsCompiler>(memory, zone, zoneDefinition, zoneStates);
|
||||
}
|
||||
|
||||
context.AddAsset(AssetRegistration<AssetKeyValuePairs>(m_zone.m_name, gameKvps));
|
||||
}
|
||||
} // namespace T6
|
||||
|
@ -1,27 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "Asset/IAssetCreator.h"
|
||||
#include "Game/T6/T6.h"
|
||||
#include "KeyValuePairs/KeyValuePairsCreator.h"
|
||||
#include "Asset/IZoneAssetCreationState.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
#include "Zone/Definition/ZoneDefinition.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace T6
|
||||
{
|
||||
class KeyValuePairsCompiler final : public IAssetCreator
|
||||
{
|
||||
public:
|
||||
KeyValuePairsCompiler(MemoryManager& memory, const Zone& zone, const ZoneDefinition& zoneDefinition, ZoneAssetCreationStateContainer& zoneStates);
|
||||
|
||||
[[nodiscard]] std::optional<asset_type_t> GetHandlingAssetType() const override;
|
||||
AssetCreationResult CreateAsset(const std::string& assetName, AssetCreationContext& context) override;
|
||||
void FinalizeZone(AssetCreationContext& context) override;
|
||||
|
||||
private:
|
||||
MemoryManager& m_memory;
|
||||
const Zone& m_zone;
|
||||
const ZoneDefinition& m_zone_definition;
|
||||
KeyValuePairsCreator& m_kvp_creator;
|
||||
};
|
||||
std::unique_ptr<IAssetCreator>
|
||||
CreateKeyValuePairsCompiler(MemoryManager& memory, const Zone& zone, const ZoneDefinition& zoneDefinition, ZoneAssetCreationStateContainer& zoneStates);
|
||||
} // namespace T6
|
||||
|
@ -8,7 +8,6 @@
|
||||
#include <memory>
|
||||
|
||||
using namespace T6;
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
@ -20,7 +19,7 @@ namespace
|
||||
{
|
||||
auto& memory = *zone.GetMemory();
|
||||
|
||||
collection.AddAssetCreator(std::make_unique<KeyValuePairsCompiler>(memory, zone, zoneDefinition.m_zone_definition, zoneStates));
|
||||
collection.AddAssetCreator(CreateKeyValuePairsCompiler(memory, zone, zoneDefinition.m_zone_definition, zoneStates));
|
||||
}
|
||||
|
||||
void ConfigurePostProcessors(AssetCreatorCollection& collection,
|
||||
@ -28,7 +27,7 @@ namespace
|
||||
const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir)
|
||||
IOutputPath& outDir)
|
||||
{
|
||||
auto& memory = *zone.GetMemory();
|
||||
|
||||
@ -46,8 +45,8 @@ void ObjCompiler::ConfigureCreatorCollection(AssetCreatorCollection& collection,
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const fs::path& outDir,
|
||||
const fs::path& cacheDir) const
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const
|
||||
{
|
||||
ConfigureCompilers(collection, zone, zoneDefinition, searchPath, zoneStates);
|
||||
ConfigurePostProcessors(collection, zone, zoneDefinition, searchPath, zoneStates, outDir);
|
||||
|
@ -13,7 +13,7 @@ namespace T6
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir,
|
||||
const std::filesystem::path& cacheDir) const override;
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const override;
|
||||
};
|
||||
} // namespace T6
|
||||
|
@ -4,14 +4,10 @@
|
||||
#include "Asset/IZoneAssetCreationState.h"
|
||||
#include "Asset/ZoneDefinitionContext.h"
|
||||
#include "Gdt/IGdtQueryable.h"
|
||||
#include "SearchPath/IOutputPath.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
#include "Zone/Definition/ZoneDefinition.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <filesystem>
|
||||
#include <string>
|
||||
|
||||
class IObjCompiler
|
||||
{
|
||||
public:
|
||||
@ -28,8 +24,8 @@ public:
|
||||
ISearchPath& searchPath,
|
||||
IGdtQueryable& gdt,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir,
|
||||
const std::filesystem::path& cacheDir) const = 0;
|
||||
IOutputPath& outDir,
|
||||
IOutputPath& cacheDir) const = 0;
|
||||
|
||||
static const IObjCompiler* GetObjCompilerForGame(GameId game);
|
||||
};
|
||||
|
@ -379,22 +379,26 @@ void IPakToCreate::AddImage(std::string imageName)
|
||||
m_image_names.emplace_back(std::move(imageName));
|
||||
}
|
||||
|
||||
void IPakToCreate::Build(ISearchPath& searchPath, const std::filesystem::path& outPath)
|
||||
void IPakToCreate::Build(ISearchPath& searchPath, IOutputPath& outPath)
|
||||
{
|
||||
auto filePath = outPath / std::format("{}.ipak", m_name);
|
||||
std::ofstream file(filePath, std::ios::out | std::ios::binary);
|
||||
if (!file.is_open())
|
||||
const auto file = outPath.Open(std::format("{}.ipak", m_name));
|
||||
if (!file)
|
||||
{
|
||||
std::cerr << std::format("Failed to open file for ipak {}\n", m_name);
|
||||
return;
|
||||
}
|
||||
|
||||
IPakWriter writer(file, searchPath, m_image_names);
|
||||
IPakWriter writer(*file, searchPath, m_image_names);
|
||||
writer.Write();
|
||||
|
||||
std::cout << std::format("Created ipak {} with {} entries\n", m_name, m_image_names.size());
|
||||
}
|
||||
|
||||
const std::vector<std::string>& IPakToCreate::GetImageNames() const
|
||||
{
|
||||
return m_image_names;
|
||||
}
|
||||
|
||||
IPakCreator::IPakCreator()
|
||||
: m_kvp_creator(nullptr)
|
||||
{
|
||||
@ -413,6 +417,7 @@ IPakToCreate* IPakCreator::GetOrAddIPak(const std::string& ipakName)
|
||||
|
||||
auto newIPak = std::make_unique<IPakToCreate>(ipakName);
|
||||
auto* result = newIPak.get();
|
||||
m_ipak_lookup.emplace(ipakName, result);
|
||||
m_ipaks.emplace_back(std::move(newIPak));
|
||||
|
||||
assert(m_kvp_creator);
|
||||
@ -421,7 +426,7 @@ IPakToCreate* IPakCreator::GetOrAddIPak(const std::string& ipakName)
|
||||
return result;
|
||||
}
|
||||
|
||||
void IPakCreator::Finalize(ISearchPath& searchPath, const std::filesystem::path& outPath)
|
||||
void IPakCreator::Finalize(ISearchPath& searchPath, IOutputPath& outPath)
|
||||
{
|
||||
for (const auto& ipakToCreate : m_ipaks)
|
||||
ipakToCreate->Build(searchPath, outPath);
|
||||
|
@ -2,9 +2,9 @@
|
||||
|
||||
#include "Asset/IZoneAssetCreationState.h"
|
||||
#include "KeyValuePairs/KeyValuePairsCreator.h"
|
||||
#include "SearchPath/IOutputPath.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@ -15,14 +15,15 @@ public:
|
||||
explicit IPakToCreate(std::string name);
|
||||
|
||||
void AddImage(std::string imageName);
|
||||
void Build(ISearchPath& searchPath, const std::filesystem::path& outPath);
|
||||
void Build(ISearchPath& searchPath, IOutputPath& outPath);
|
||||
[[nodiscard]] const std::vector<std::string>& GetImageNames() const;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<std::string> m_image_names;
|
||||
};
|
||||
|
||||
class IPakCreator : public IZoneAssetCreationState
|
||||
class IPakCreator final : public IZoneAssetCreationState
|
||||
{
|
||||
public:
|
||||
IPakCreator();
|
||||
@ -30,7 +31,7 @@ public:
|
||||
void Inject(ZoneAssetCreationInjection& inject) override;
|
||||
|
||||
IPakToCreate* GetOrAddIPak(const std::string& ipakName);
|
||||
void Finalize(ISearchPath& searchPath, const std::filesystem::path& outPath);
|
||||
void Finalize(ISearchPath& searchPath, IOutputPath& outPath);
|
||||
|
||||
private:
|
||||
KeyValuePairsCreator* m_kvp_creator;
|
||||
|
@ -2,36 +2,34 @@
|
||||
|
||||
#include "IPak/IPakCreator.h"
|
||||
|
||||
#include <format>
|
||||
#include <algorithm>
|
||||
|
||||
AbstractImageIPakPostProcessor::AbstractImageIPakPostProcessor(const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir)
|
||||
IOutputPath& outDir)
|
||||
: m_zone_definition(zoneDefinition),
|
||||
m_search_path(searchPath),
|
||||
m_ipak_creator(zoneStates.GetZoneAssetCreationState<IPakCreator>()),
|
||||
m_out_dir(outDir),
|
||||
m_initialized(false),
|
||||
m_obj_container_index(0u),
|
||||
m_current_ipak(nullptr),
|
||||
m_current_ipak_start_index(0u),
|
||||
m_current_ipak_end_index(0u)
|
||||
{
|
||||
FindNextObjContainer();
|
||||
}
|
||||
|
||||
bool AbstractImageIPakPostProcessor::AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition)
|
||||
{
|
||||
for (const auto& objContainer : zoneDefinition.m_zone_definition.m_obj_containers)
|
||||
{
|
||||
if (objContainer.m_type == ZoneDefinitionObjContainerType::IPAK)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::ranges::any_of(zoneDefinition.m_zone_definition.m_obj_containers,
|
||||
[](const ZoneDefinitionObjContainer& objContainer)
|
||||
{
|
||||
return objContainer.m_type == ZoneDefinitionObjContainerType::IPAK;
|
||||
});
|
||||
}
|
||||
|
||||
void AbstractImageIPakPostProcessor::FindNextObjContainer(AssetCreationContext& context)
|
||||
void AbstractImageIPakPostProcessor::FindNextObjContainer()
|
||||
{
|
||||
const auto objContainerCount = m_zone_definition.m_zone_definition.m_obj_containers.size();
|
||||
while (m_obj_container_index < objContainerCount)
|
||||
@ -55,17 +53,10 @@ void AbstractImageIPakPostProcessor::PostProcessAsset(XAssetInfoGeneric& assetIn
|
||||
if (assetInfo.m_name.empty() || assetInfo.m_name[0] == ',')
|
||||
return;
|
||||
|
||||
// Initialize on first image occurance
|
||||
if (!m_initialized)
|
||||
{
|
||||
FindNextObjContainer(context);
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
while (m_current_ipak && m_zone_definition.m_asset_index_in_definition >= m_current_ipak_end_index)
|
||||
FindNextObjContainer(context);
|
||||
FindNextObjContainer();
|
||||
|
||||
if (m_current_ipak && m_zone_definition.m_asset_index_in_definition <= m_current_ipak_start_index)
|
||||
if (m_current_ipak && m_zone_definition.m_asset_index_in_definition >= m_current_ipak_start_index)
|
||||
m_current_ipak->AddImage(assetInfo.m_name);
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,7 @@
|
||||
#include "Asset/IAssetPostProcessor.h"
|
||||
#include "Asset/ZoneDefinitionContext.h"
|
||||
#include "Image/IPak/IPakCreator.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include "SearchPath/IOutputPath.h"
|
||||
|
||||
class AbstractImageIPakPostProcessor : public IAssetPostProcessor
|
||||
{
|
||||
@ -12,7 +11,7 @@ public:
|
||||
AbstractImageIPakPostProcessor(const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir);
|
||||
IOutputPath& outDir);
|
||||
|
||||
static bool AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition);
|
||||
|
||||
@ -20,14 +19,13 @@ public:
|
||||
void FinalizeZone(AssetCreationContext& context) override;
|
||||
|
||||
private:
|
||||
void FindNextObjContainer(AssetCreationContext& context);
|
||||
void FindNextObjContainer();
|
||||
|
||||
const ZoneDefinitionContext& m_zone_definition;
|
||||
ISearchPath& m_search_path;
|
||||
IPakCreator& m_ipak_creator;
|
||||
const std::filesystem::path& m_out_dir;
|
||||
IOutputPath& m_out_dir;
|
||||
|
||||
bool m_initialized;
|
||||
unsigned m_obj_container_index;
|
||||
IPakToCreate* m_current_ipak;
|
||||
unsigned m_current_ipak_start_index;
|
||||
@ -42,7 +40,7 @@ public:
|
||||
ImageIPakPostProcessor(const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir)
|
||||
IOutputPath& outDir)
|
||||
: AbstractImageIPakPostProcessor(zoneDefinition, searchPath, zoneStates, outDir)
|
||||
{
|
||||
}
|
||||
@ -50,5 +48,5 @@ public:
|
||||
[[nodiscard]] asset_type_t GetHandlingAssetType() const override
|
||||
{
|
||||
return AssetType::EnumEntry;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -2,37 +2,35 @@
|
||||
|
||||
#include "Iwd/IwdCreator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
|
||||
AbstractImageIwdPostProcessor::AbstractImageIwdPostProcessor(const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir)
|
||||
IOutputPath& outDir)
|
||||
: m_zone_definition(zoneDefinition),
|
||||
m_search_path(searchPath),
|
||||
m_iwd_creator(zoneStates.GetZoneAssetCreationState<IwdCreator>()),
|
||||
m_out_dir(outDir),
|
||||
m_initialized(false),
|
||||
m_obj_container_index(0u),
|
||||
m_current_iwd(nullptr),
|
||||
m_current_iwd_start_index(0u),
|
||||
m_current_iwd_end_index(0u)
|
||||
{
|
||||
FindNextObjContainer();
|
||||
}
|
||||
|
||||
bool AbstractImageIwdPostProcessor::AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition)
|
||||
{
|
||||
for (const auto& objContainer : zoneDefinition.m_zone_definition.m_obj_containers)
|
||||
{
|
||||
if (objContainer.m_type == ZoneDefinitionObjContainerType::IWD)
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::ranges::any_of(zoneDefinition.m_zone_definition.m_obj_containers,
|
||||
[](const ZoneDefinitionObjContainer& objContainer)
|
||||
{
|
||||
return objContainer.m_type == ZoneDefinitionObjContainerType::IWD;
|
||||
});
|
||||
}
|
||||
|
||||
void AbstractImageIwdPostProcessor::FindNextObjContainer(AssetCreationContext& context)
|
||||
void AbstractImageIwdPostProcessor::FindNextObjContainer()
|
||||
{
|
||||
const auto objContainerCount = m_zone_definition.m_zone_definition.m_obj_containers.size();
|
||||
while (m_obj_container_index < objContainerCount)
|
||||
@ -56,17 +54,10 @@ void AbstractImageIwdPostProcessor::PostProcessAsset(XAssetInfoGeneric& assetInf
|
||||
if (assetInfo.m_name.empty() || assetInfo.m_name[0] == ',')
|
||||
return;
|
||||
|
||||
// Initialize on first image occurance
|
||||
if (!m_initialized)
|
||||
{
|
||||
FindNextObjContainer(context);
|
||||
m_initialized = true;
|
||||
}
|
||||
|
||||
while (m_current_iwd && m_zone_definition.m_asset_index_in_definition >= m_current_iwd_end_index)
|
||||
FindNextObjContainer(context);
|
||||
FindNextObjContainer();
|
||||
|
||||
if (m_current_iwd && m_zone_definition.m_asset_index_in_definition <= m_current_iwd_start_index)
|
||||
if (m_current_iwd && m_zone_definition.m_asset_index_in_definition >= m_current_iwd_start_index)
|
||||
m_current_iwd->AddFile(std::format("images/{}.iwi", assetInfo.m_name));
|
||||
}
|
||||
|
||||
|
@ -3,8 +3,7 @@
|
||||
#include "Asset/IAssetPostProcessor.h"
|
||||
#include "Asset/ZoneDefinitionContext.h"
|
||||
#include "Iwd/IwdCreator.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include "SearchPath/IOutputPath.h"
|
||||
|
||||
class AbstractImageIwdPostProcessor : public IAssetPostProcessor
|
||||
{
|
||||
@ -12,7 +11,7 @@ public:
|
||||
AbstractImageIwdPostProcessor(const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir);
|
||||
IOutputPath& outDir);
|
||||
|
||||
static bool AppliesToZoneDefinition(const ZoneDefinitionContext& zoneDefinition);
|
||||
|
||||
@ -20,14 +19,13 @@ public:
|
||||
void FinalizeZone(AssetCreationContext& context) override;
|
||||
|
||||
private:
|
||||
void FindNextObjContainer(AssetCreationContext& context);
|
||||
void FindNextObjContainer();
|
||||
|
||||
const ZoneDefinitionContext& m_zone_definition;
|
||||
ISearchPath& m_search_path;
|
||||
IwdCreator& m_iwd_creator;
|
||||
const std::filesystem::path& m_out_dir;
|
||||
IOutputPath& m_out_dir;
|
||||
|
||||
bool m_initialized;
|
||||
unsigned m_obj_container_index;
|
||||
IwdToCreate* m_current_iwd;
|
||||
unsigned m_current_iwd_start_index;
|
||||
@ -42,7 +40,7 @@ public:
|
||||
ImageIwdPostProcessor(const ZoneDefinitionContext& zoneDefinition,
|
||||
ISearchPath& searchPath,
|
||||
ZoneAssetCreationStateContainer& zoneStates,
|
||||
const std::filesystem::path& outDir)
|
||||
IOutputPath& outDir)
|
||||
: AbstractImageIwdPostProcessor(zoneDefinition, searchPath, zoneStates, outDir)
|
||||
{
|
||||
}
|
||||
@ -50,5 +48,5 @@ public:
|
||||
[[nodiscard]] asset_type_t GetHandlingAssetType() const override
|
||||
{
|
||||
return AssetType::EnumEntry;
|
||||
};
|
||||
}
|
||||
};
|
||||
|
@ -18,19 +18,19 @@ void IwdToCreate::AddFile(std::string filePath)
|
||||
m_file_paths.emplace_back(std::move(filePath));
|
||||
}
|
||||
|
||||
void IwdToCreate::Build(ISearchPath& searchPath, const std::filesystem::path& outPath)
|
||||
void IwdToCreate::Build(ISearchPath& searchPath, IOutputPath& outPath)
|
||||
{
|
||||
auto filePath = outPath / std::format("{}.iwd", m_name);
|
||||
std::ofstream file(filePath, std::ios::out | std::ios::binary);
|
||||
if (!file.is_open())
|
||||
const auto fileName = std::format("{}.iwd", m_name);
|
||||
const auto file = outPath.Open(fileName);
|
||||
if (!file)
|
||||
{
|
||||
std::cerr << std::format("Failed to open file for iwd {}\n", m_name);
|
||||
return;
|
||||
}
|
||||
|
||||
auto functions = FileToZlibWrapper::CreateFunctions32ForFile(&file);
|
||||
auto functions = FileToZlibWrapper::CreateFunctions32ForFile(file.get());
|
||||
|
||||
auto zipFile = zipOpen2(filePath.string().c_str(), APPEND_STATUS_CREATE, nullptr, &functions);
|
||||
const auto zipFile = zipOpen2(fileName.c_str(), APPEND_STATUS_CREATE, nullptr, &functions);
|
||||
if (!zipFile)
|
||||
{
|
||||
std::cerr << std::format("Failed to open file as zip for iwd {}\n", m_name);
|
||||
@ -79,6 +79,11 @@ void IwdToCreate::Build(ISearchPath& searchPath, const std::filesystem::path& ou
|
||||
std::cout << std::format("Created iwd {} with {} entries\n", m_name, m_file_paths.size());
|
||||
}
|
||||
|
||||
const std::vector<std::string>& IwdToCreate::GetFilePaths() const
|
||||
{
|
||||
return m_file_paths;
|
||||
}
|
||||
|
||||
IwdToCreate* IwdCreator::GetOrAddIwd(const std::string& iwdName)
|
||||
{
|
||||
const auto existingIwd = m_iwd_lookup.find(iwdName);
|
||||
@ -87,12 +92,13 @@ IwdToCreate* IwdCreator::GetOrAddIwd(const std::string& iwdName)
|
||||
|
||||
auto newIwd = std::make_unique<IwdToCreate>(iwdName);
|
||||
auto* result = newIwd.get();
|
||||
m_iwd_lookup.emplace(iwdName, result);
|
||||
m_iwds.emplace_back(std::move(newIwd));
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
void IwdCreator::Finalize(ISearchPath& searchPath, const std::filesystem::path& outPath)
|
||||
void IwdCreator::Finalize(ISearchPath& searchPath, IOutputPath& outPath)
|
||||
{
|
||||
std::cout << std::format("Writing {} iwd files to disk\n", m_iwds.size());
|
||||
for (const auto& iwdToCreate : m_iwds)
|
||||
|
@ -1,9 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include "Asset/IZoneAssetCreationState.h"
|
||||
#include "SearchPath/IOutputPath.h"
|
||||
#include "SearchPath/ISearchPath.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <unordered_map>
|
||||
@ -14,18 +14,19 @@ public:
|
||||
explicit IwdToCreate(std::string name);
|
||||
|
||||
void AddFile(std::string filePath);
|
||||
void Build(ISearchPath& searchPath, const std::filesystem::path& outPath);
|
||||
void Build(ISearchPath& searchPath, IOutputPath& outPath);
|
||||
[[nodiscard]] const std::vector<std::string>& GetFilePaths() const;
|
||||
|
||||
private:
|
||||
std::string m_name;
|
||||
std::vector<std::string> m_file_paths;
|
||||
};
|
||||
|
||||
class IwdCreator : public IZoneAssetCreationState
|
||||
class IwdCreator final : public IZoneAssetCreationState
|
||||
{
|
||||
public:
|
||||
IwdToCreate* GetOrAddIwd(const std::string& iwdName);
|
||||
void Finalize(ISearchPath& searchPath, const std::filesystem::path& outPath);
|
||||
void Finalize(ISearchPath& searchPath, IOutputPath& outPath);
|
||||
|
||||
private:
|
||||
std::unordered_map<std::string, IwdToCreate*> m_iwd_lookup;
|
||||
|
@ -1,7 +1,9 @@
|
||||
#include "KeyValuePairsCreator.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <ranges>
|
||||
|
||||
CommonKeyValuePair::CommonKeyValuePair(std::string keyStr, std::string value)
|
||||
: m_key_str(std::move(keyStr)),
|
||||
@ -49,6 +51,23 @@ void KeyValuePairsCreator::Finalize(const ZoneDefinition& zoneDefinition)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
std::ranges::sort(m_key_value_pairs,
|
||||
[](const CommonKeyValuePair& v0, const CommonKeyValuePair& v1)
|
||||
{
|
||||
if (v0.m_key_str.has_value())
|
||||
{
|
||||
if (!v1.m_key_str.has_value())
|
||||
return true;
|
||||
|
||||
return *v0.m_key_str < *v1.m_key_str;
|
||||
}
|
||||
|
||||
if (!v1.m_key_hash.has_value())
|
||||
return false;
|
||||
|
||||
return *v0.m_key_hash < *v1.m_key_hash;
|
||||
});
|
||||
}
|
||||
|
||||
std::vector<CommonKeyValuePair> KeyValuePairsCreator::GetFinalKeyValuePairs()
|
||||
|
@ -18,7 +18,7 @@ public:
|
||||
std::string m_value;
|
||||
};
|
||||
|
||||
class KeyValuePairsCreator : public IZoneAssetCreationState
|
||||
class KeyValuePairsCreator final : public IZoneAssetCreationState
|
||||
{
|
||||
public:
|
||||
void AddKeyValuePair(CommonKeyValuePair keyValuePair);
|
||||
|
@ -165,13 +165,13 @@ namespace T6
|
||||
if (ObjLoading::Configuration.Verbose)
|
||||
std::cout << std::format("Trying to load ipak '{}' for zone '{}'\n", ipakName, zone.m_name);
|
||||
|
||||
auto* existingIPak = IPak::Repository.GetContainerByName(ipakName);
|
||||
auto* existingIPak = IIPak::Repository.GetContainerByName(ipakName);
|
||||
if (existingIPak != nullptr)
|
||||
{
|
||||
if (ObjLoading::Configuration.Verbose)
|
||||
std::cout << std::format("Referencing loaded ipak '{}'.\n", ipakName);
|
||||
|
||||
IPak::Repository.AddContainerReference(existingIPak, &zone);
|
||||
IIPak::Repository.AddContainerReference(existingIPak, &zone);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -180,11 +180,11 @@ namespace T6
|
||||
auto file = searchPath.Open(ipakFilename);
|
||||
if (file.IsOpen())
|
||||
{
|
||||
auto ipak = std::make_unique<IPak>(ipakFilename, std::move(file.m_stream));
|
||||
auto ipak = IIPak::Create(ipakFilename, std::move(file.m_stream));
|
||||
|
||||
if (ipak->Initialize())
|
||||
{
|
||||
IPak::Repository.AddContainer(std::move(ipak), &zone);
|
||||
IIPak::Repository.AddContainer(std::move(ipak), &zone);
|
||||
|
||||
if (ObjLoading::Configuration.Verbose)
|
||||
std::cout << std::format("Found and loaded ipak '{}'.\n", ipakFilename);
|
||||
@ -277,7 +277,7 @@ namespace T6
|
||||
|
||||
void ObjLoader::UnloadContainersOfZone(Zone& zone) const
|
||||
{
|
||||
IPak::Repository.RemoveContainerReferences(&zone);
|
||||
IIPak::Repository.RemoveContainerReferences(&zone);
|
||||
}
|
||||
|
||||
namespace
|
||||
|
@ -1,35 +1,22 @@
|
||||
#include "IPak.h"
|
||||
|
||||
#include "Exception/IPakLoadException.h"
|
||||
#include "IPakStreamManager.h"
|
||||
#include "ObjContainer/IPak/IPakTypes.h"
|
||||
#include "Utils/FileUtils.h"
|
||||
#include "zlib.h"
|
||||
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
#include <sstream>
|
||||
#include <vector>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
ObjContainerRepository<IPak, Zone> IPak::Repository;
|
||||
ObjContainerRepository<IIPak, Zone> IIPak::Repository;
|
||||
|
||||
class IPak::Impl : public ObjContainerReferenceable
|
||||
namespace
|
||||
{
|
||||
std::string m_path;
|
||||
std::unique_ptr<std::istream> m_stream;
|
||||
|
||||
bool m_initialized;
|
||||
|
||||
std::unique_ptr<IPakSection> m_index_section;
|
||||
std::unique_ptr<IPakSection> m_data_section;
|
||||
|
||||
std::vector<IPakIndexEntry> m_index_entries;
|
||||
|
||||
IPakStreamManager m_stream_manager;
|
||||
|
||||
static uint32_t R_HashString(const char* str, uint32_t hash)
|
||||
std::uint32_t R_HashString(const char* str, std::uint32_t hash)
|
||||
{
|
||||
for (const auto* pos = str; *pos; pos++)
|
||||
{
|
||||
@ -38,203 +25,189 @@ class IPak::Impl : public ObjContainerReferenceable
|
||||
|
||||
return hash;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool ReadIndexSection()
|
||||
namespace
|
||||
{
|
||||
class IPak final : public IIPak
|
||||
{
|
||||
m_stream->seekg(m_index_section->offset);
|
||||
IPakIndexEntry indexEntry{};
|
||||
|
||||
for (unsigned itemIndex = 0; itemIndex < m_index_section->itemCount; itemIndex++)
|
||||
public:
|
||||
IPak(std::string path, std::unique_ptr<std::istream> stream)
|
||||
: m_path(std::move(path)),
|
||||
m_stream(std::move(stream)),
|
||||
m_initialized(false),
|
||||
m_index_section(nullptr),
|
||||
m_data_section(nullptr),
|
||||
m_stream_manager(*m_stream)
|
||||
{
|
||||
m_stream->read(reinterpret_cast<char*>(&indexEntry), sizeof(indexEntry));
|
||||
if (m_stream->gcount() != sizeof(indexEntry))
|
||||
{
|
||||
printf("Unexpected eof when trying to load index entry %u.\n", itemIndex);
|
||||
}
|
||||
|
||||
bool Initialize() override
|
||||
{
|
||||
if (m_initialized)
|
||||
return true;
|
||||
|
||||
if (!ReadHeader())
|
||||
return false;
|
||||
}
|
||||
|
||||
m_index_entries.push_back(indexEntry);
|
||||
}
|
||||
|
||||
std::ranges::sort(m_index_entries,
|
||||
[](const IPakIndexEntry& entry1, const IPakIndexEntry& entry2)
|
||||
{
|
||||
return entry1.key.combinedKey < entry2.key.combinedKey;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadSection()
|
||||
{
|
||||
IPakSection section{};
|
||||
|
||||
m_stream->read(reinterpret_cast<char*>(§ion), sizeof(section));
|
||||
if (m_stream->gcount() != sizeof(section))
|
||||
{
|
||||
printf("Unexpected eof when trying to load section.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (section.type)
|
||||
{
|
||||
case ipak_consts::IPAK_INDEX_SECTION:
|
||||
m_index_section = std::make_unique<IPakSection>(section);
|
||||
break;
|
||||
|
||||
case ipak_consts::IPAK_DATA_SECTION:
|
||||
m_data_section = std::make_unique<IPakSection>(section);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadHeader()
|
||||
{
|
||||
IPakHeader header{};
|
||||
|
||||
m_stream->read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (m_stream->gcount() != sizeof(header))
|
||||
{
|
||||
printf("Unexpected eof when trying to load header.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.magic != ipak_consts::IPAK_MAGIC)
|
||||
{
|
||||
printf("Invalid ipak magic '0x%x'.\n", header.magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.version != ipak_consts::IPAK_VERSION)
|
||||
{
|
||||
printf("Unsupported ipak version '%u'.\n", header.version);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned section = 0; section < header.sectionCount; section++)
|
||||
{
|
||||
if (!ReadSection())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_index_section == nullptr)
|
||||
{
|
||||
printf("IPak does not contain an index section.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_data_section == nullptr)
|
||||
{
|
||||
printf("IPak does not contain a data section.\n");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadIndexSection())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public:
|
||||
Impl(std::string path, std::unique_ptr<std::istream> stream)
|
||||
: m_path(std::move(path)),
|
||||
m_stream(std::move(stream)),
|
||||
m_initialized(false),
|
||||
m_index_section(nullptr),
|
||||
m_data_section(nullptr),
|
||||
m_stream_manager(*m_stream)
|
||||
{
|
||||
}
|
||||
|
||||
~Impl() override = default;
|
||||
|
||||
std::string GetName() override
|
||||
{
|
||||
return fs::path(m_path).filename().replace_extension("").string();
|
||||
}
|
||||
|
||||
bool Initialize()
|
||||
{
|
||||
if (m_initialized)
|
||||
m_initialized = true;
|
||||
return true;
|
||||
|
||||
if (!ReadHeader())
|
||||
return false;
|
||||
|
||||
m_initialized = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::unique_ptr<iobjstream> GetEntryData(const Hash nameHash, const Hash dataHash)
|
||||
{
|
||||
IPakIndexEntryKey wantedKey{};
|
||||
wantedKey.nameHash = nameHash;
|
||||
wantedKey.dataHash = dataHash;
|
||||
|
||||
for (auto& entry : m_index_entries)
|
||||
{
|
||||
if (entry.key.combinedKey == wantedKey.combinedKey)
|
||||
{
|
||||
return m_stream_manager.OpenStream(static_cast<int64_t>(m_data_section->offset) + entry.offset, entry.size);
|
||||
}
|
||||
else if (entry.key.combinedKey > wantedKey.combinedKey)
|
||||
{
|
||||
// The index entries are sorted so if the current entry is higher than the wanted entry we can cancel here
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
[[nodiscard]] std::unique_ptr<iobjstream> GetEntryStream(const Hash nameHash, const Hash dataHash) const override
|
||||
{
|
||||
IPakIndexEntryKey wantedKey{};
|
||||
wantedKey.nameHash = nameHash;
|
||||
wantedKey.dataHash = dataHash;
|
||||
|
||||
static Hash HashString(const std::string& str)
|
||||
{
|
||||
return R_HashString(str.c_str(), 0);
|
||||
}
|
||||
for (auto& entry : m_index_entries)
|
||||
{
|
||||
if (entry.key.combinedKey == wantedKey.combinedKey)
|
||||
{
|
||||
return m_stream_manager.OpenStream(static_cast<int64_t>(m_data_section->offset) + entry.offset, entry.size);
|
||||
}
|
||||
else if (entry.key.combinedKey > wantedKey.combinedKey)
|
||||
{
|
||||
// The index entries are sorted so if the current entry is higher than the wanted entry we can cancel here
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
static Hash HashData(const void* data, const size_t dataSize)
|
||||
{
|
||||
return crc32(0, static_cast<const Bytef*>(data), dataSize);
|
||||
}
|
||||
};
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
IPak::IPak(std::string path, std::unique_ptr<std::istream> stream)
|
||||
std::string GetName() override
|
||||
{
|
||||
return fs::path(m_path).filename().replace_extension("").string();
|
||||
}
|
||||
|
||||
private:
|
||||
bool ReadIndexSection()
|
||||
{
|
||||
m_stream->seekg(m_index_section->offset);
|
||||
IPakIndexEntry indexEntry{};
|
||||
|
||||
for (unsigned itemIndex = 0; itemIndex < m_index_section->itemCount; itemIndex++)
|
||||
{
|
||||
m_stream->read(reinterpret_cast<char*>(&indexEntry), sizeof(indexEntry));
|
||||
if (m_stream->gcount() != sizeof(indexEntry))
|
||||
{
|
||||
std::cerr << std::format("Unexpected eof when trying to load index entry {}.\n", itemIndex);
|
||||
return false;
|
||||
}
|
||||
|
||||
m_index_entries.push_back(indexEntry);
|
||||
}
|
||||
|
||||
std::ranges::sort(m_index_entries,
|
||||
[](const IPakIndexEntry& entry1, const IPakIndexEntry& entry2)
|
||||
{
|
||||
return entry1.key.combinedKey < entry2.key.combinedKey;
|
||||
});
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadSection()
|
||||
{
|
||||
IPakSection section{};
|
||||
|
||||
m_stream->read(reinterpret_cast<char*>(§ion), sizeof(section));
|
||||
if (m_stream->gcount() != sizeof(section))
|
||||
{
|
||||
std::cerr << "Unexpected eof when trying to load section.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (section.type)
|
||||
{
|
||||
case ipak_consts::IPAK_INDEX_SECTION:
|
||||
m_index_section = std::make_unique<IPakSection>(section);
|
||||
break;
|
||||
|
||||
case ipak_consts::IPAK_DATA_SECTION:
|
||||
m_data_section = std::make_unique<IPakSection>(section);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool ReadHeader()
|
||||
{
|
||||
IPakHeader header{};
|
||||
|
||||
m_stream->read(reinterpret_cast<char*>(&header), sizeof(header));
|
||||
if (m_stream->gcount() != sizeof(header))
|
||||
{
|
||||
std::cerr << "Unexpected eof when trying to load header.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.magic != ipak_consts::IPAK_MAGIC)
|
||||
{
|
||||
std::cerr << std::format("Invalid ipak magic '{:#x}'.\n", header.magic);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (header.version != ipak_consts::IPAK_VERSION)
|
||||
{
|
||||
std::cerr << std::format("Unsupported ipak version '{}'.\n", header.version);
|
||||
return false;
|
||||
}
|
||||
|
||||
for (unsigned section = 0; section < header.sectionCount; section++)
|
||||
{
|
||||
if (!ReadSection())
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_index_section == nullptr)
|
||||
{
|
||||
std::cerr << "IPak does not contain an index section.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (m_data_section == nullptr)
|
||||
{
|
||||
std::cerr << "IPak does not contain a data section.\n";
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!ReadIndexSection())
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
std::string m_path;
|
||||
std::unique_ptr<std::istream> m_stream;
|
||||
|
||||
bool m_initialized;
|
||||
|
||||
std::unique_ptr<IPakSection> m_index_section;
|
||||
std::unique_ptr<IPakSection> m_data_section;
|
||||
|
||||
std::vector<IPakIndexEntry> m_index_entries;
|
||||
|
||||
IPakStreamManager m_stream_manager;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<IIPak> IIPak::Create(std::string path, std::unique_ptr<std::istream> stream)
|
||||
{
|
||||
m_impl = new Impl(std::move(path), std::move(stream));
|
||||
return std::make_unique<IPak>(std::move(path), std::move(stream));
|
||||
}
|
||||
|
||||
IPak::~IPak()
|
||||
IIPak::Hash IIPak::HashString(const std::string& str)
|
||||
{
|
||||
delete m_impl;
|
||||
m_impl = nullptr;
|
||||
return R_HashString(str.c_str(), 0);
|
||||
}
|
||||
|
||||
std::string IPak::GetName()
|
||||
IIPak::Hash IIPak::HashData(const void* data, const size_t dataSize)
|
||||
{
|
||||
return m_impl->GetName();
|
||||
}
|
||||
|
||||
bool IPak::Initialize()
|
||||
{
|
||||
return m_impl->Initialize();
|
||||
}
|
||||
|
||||
std::unique_ptr<iobjstream> IPak::GetEntryStream(const Hash nameHash, const Hash dataHash) const
|
||||
{
|
||||
return m_impl->GetEntryData(nameHash, dataHash);
|
||||
}
|
||||
|
||||
IPak::Hash IPak::HashString(const std::string& str)
|
||||
{
|
||||
return Impl::HashString(str);
|
||||
}
|
||||
|
||||
IPak::Hash IPak::HashData(const void* data, const size_t dataSize)
|
||||
{
|
||||
return Impl::HashData(data, dataSize);
|
||||
return crc32(0, static_cast<const Bytef*>(data), dataSize);
|
||||
}
|
||||
|
@ -2,30 +2,31 @@
|
||||
|
||||
#include "ObjContainer/ObjContainerReferenceable.h"
|
||||
#include "ObjContainer/ObjContainerRepository.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
#include "Utils/ObjStream.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <istream>
|
||||
#include <memory>
|
||||
#include <string>
|
||||
|
||||
class IPak final : public ObjContainerReferenceable
|
||||
class IIPak : public ObjContainerReferenceable
|
||||
{
|
||||
class Impl;
|
||||
Impl* m_impl;
|
||||
|
||||
public:
|
||||
typedef uint32_t Hash;
|
||||
static ObjContainerRepository<IIPak, Zone> Repository;
|
||||
typedef std::uint32_t Hash;
|
||||
|
||||
static ObjContainerRepository<IPak, Zone> Repository;
|
||||
IIPak() = default;
|
||||
virtual ~IIPak() = default;
|
||||
IIPak(const IIPak& other) = default;
|
||||
IIPak(IIPak&& other) noexcept = default;
|
||||
IIPak& operator=(const IIPak& other) = default;
|
||||
IIPak& operator=(IIPak&& other) noexcept = default;
|
||||
|
||||
IPak(std::string path, std::unique_ptr<std::istream> stream);
|
||||
~IPak() override;
|
||||
|
||||
std::string GetName() override;
|
||||
|
||||
bool Initialize();
|
||||
_NODISCARD std::unique_ptr<iobjstream> GetEntryStream(Hash nameHash, Hash dataHash) const;
|
||||
virtual bool Initialize() = 0;
|
||||
[[nodiscard]] virtual std::unique_ptr<iobjstream> GetEntryStream(Hash nameHash, Hash dataHash) const = 0;
|
||||
|
||||
static std::unique_ptr<IIPak> Create(std::string path, std::unique_ptr<std::istream> stream);
|
||||
static Hash HashString(const std::string& str);
|
||||
static Hash HashData(const void* data, size_t dataSize);
|
||||
};
|
||||
|
@ -38,7 +38,7 @@ namespace
|
||||
{
|
||||
if (image->streamedPartCount > 0)
|
||||
{
|
||||
for (auto* ipak : IPak::Repository)
|
||||
for (auto* ipak : IIPak::Repository)
|
||||
{
|
||||
auto ipakStream = ipak->GetEntryStream(image->hash, image->streamedParts[0].hash);
|
||||
|
||||
|
@ -6,6 +6,33 @@
|
||||
|
||||
class MemoryManager
|
||||
{
|
||||
public:
|
||||
MemoryManager();
|
||||
virtual ~MemoryManager();
|
||||
MemoryManager(const MemoryManager& other) = delete;
|
||||
MemoryManager(MemoryManager&& other) noexcept = default;
|
||||
MemoryManager& operator=(const MemoryManager& other) = delete;
|
||||
MemoryManager& operator=(MemoryManager&& other) noexcept = default;
|
||||
|
||||
void* AllocRaw(size_t size);
|
||||
char* Dup(const char* str);
|
||||
|
||||
template<typename T> std::add_pointer_t<T> Alloc(const size_t count = 1u)
|
||||
{
|
||||
return static_cast<std::add_pointer_t<T>>(AllocRaw(sizeof(T) * count));
|
||||
}
|
||||
|
||||
template<class T, class... ValType> std::add_pointer_t<T> Create(ValType&&... val)
|
||||
{
|
||||
Allocation<T>* allocation = new Allocation<T>(std::forward<ValType>(val)...);
|
||||
m_destructible.emplace_back(allocation, &allocation->m_entry);
|
||||
return &allocation->m_entry;
|
||||
}
|
||||
|
||||
void Free(const void* data);
|
||||
void Delete(const void* data);
|
||||
|
||||
protected:
|
||||
class IDestructible
|
||||
{
|
||||
public:
|
||||
@ -47,30 +74,4 @@ class MemoryManager
|
||||
|
||||
std::vector<void*> m_allocations;
|
||||
std::vector<AllocationInfo> m_destructible;
|
||||
|
||||
public:
|
||||
MemoryManager();
|
||||
virtual ~MemoryManager();
|
||||
MemoryManager(const MemoryManager& other) = delete;
|
||||
MemoryManager(MemoryManager&& other) noexcept = default;
|
||||
MemoryManager& operator=(const MemoryManager& other) = delete;
|
||||
MemoryManager& operator=(MemoryManager&& other) noexcept = default;
|
||||
|
||||
void* AllocRaw(size_t size);
|
||||
char* Dup(const char* str);
|
||||
|
||||
template<typename T> std::add_pointer_t<T> Alloc(const size_t count = 1u)
|
||||
{
|
||||
return static_cast<std::add_pointer_t<T>>(AllocRaw(sizeof(T) * count));
|
||||
}
|
||||
|
||||
template<class T, class... ValType> std::add_pointer_t<T> Create(ValType&&... val)
|
||||
{
|
||||
Allocation<T>* allocation = new Allocation<T>(std::forward<ValType>(val)...);
|
||||
m_destructible.emplace_back(allocation, &allocation->m_entry);
|
||||
return &allocation->m_entry;
|
||||
}
|
||||
|
||||
void Free(const void* data);
|
||||
void Delete(const void* data);
|
||||
};
|
||||
|
@ -1,10 +1,15 @@
|
||||
#include "ZoneDefinition.h"
|
||||
|
||||
ZoneDefinitionObjContainer::ZoneDefinitionObjContainer(std::string name, const ZoneDefinitionObjContainerType type, const unsigned start)
|
||||
: ZoneDefinitionObjContainer(std::move(name), type, start, 0u)
|
||||
{
|
||||
}
|
||||
|
||||
ZoneDefinitionObjContainer::ZoneDefinitionObjContainer(std::string name, const ZoneDefinitionObjContainerType type, const unsigned start, const unsigned end)
|
||||
: m_name(std::move(name)),
|
||||
m_type(type),
|
||||
m_asset_start(start),
|
||||
m_asset_end(0u)
|
||||
m_asset_end(end)
|
||||
{
|
||||
}
|
||||
|
||||
|
@ -32,6 +32,7 @@ public:
|
||||
unsigned m_asset_end;
|
||||
|
||||
ZoneDefinitionObjContainer(std::string name, ZoneDefinitionObjContainerType type, unsigned start);
|
||||
ZoneDefinitionObjContainer(std::string name, ZoneDefinitionObjContainerType type, unsigned start, unsigned end);
|
||||
};
|
||||
|
||||
class ZoneDefinitionAsset
|
||||
|
51
test/Catch2Common.lua
Normal file
51
test/Catch2Common.lua
Normal file
@ -0,0 +1,51 @@
|
||||
Catch2Common = {}
|
||||
|
||||
function Catch2Common:include(includes)
|
||||
if includes:handle(self:name()) then
|
||||
includedirs {
|
||||
path.join(TestFolder(), "Catch2Common")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function Catch2Common:link(links)
|
||||
links:add(self:name())
|
||||
links:linkto(catch2)
|
||||
end
|
||||
|
||||
function Catch2Common:use()
|
||||
|
||||
end
|
||||
|
||||
function Catch2Common:name()
|
||||
return "Catch2Common"
|
||||
end
|
||||
|
||||
function Catch2Common:project()
|
||||
local folder = TestFolder()
|
||||
local includes = Includes:create()
|
||||
local links = Links:create()
|
||||
|
||||
project(self:name())
|
||||
targetdir(TargetDirectoryTest)
|
||||
location "%{wks.location}/test/%{prj.name}"
|
||||
kind "StaticLib"
|
||||
language "C++"
|
||||
|
||||
files {
|
||||
path.join(folder, "Catch2Common/**.h"),
|
||||
path.join(folder, "Catch2Common/**.cpp")
|
||||
}
|
||||
|
||||
vpaths {
|
||||
["*"] = {
|
||||
path.join(folder, "Catch2Common")
|
||||
}
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
catch2:include(includes)
|
||||
|
||||
links:linkto(catch2)
|
||||
links:linkall()
|
||||
end
|
27
test/Catch2Common/OatTestPaths.cpp
Normal file
27
test/Catch2Common/OatTestPaths.cpp
Normal file
@ -0,0 +1,27 @@
|
||||
#include "OatTestPaths.h"
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
namespace oat::paths
|
||||
{
|
||||
std::filesystem::path GetSourceDirectory()
|
||||
{
|
||||
return fs::current_path() / "src";
|
||||
}
|
||||
|
||||
std::filesystem::path GetTestDirectory()
|
||||
{
|
||||
return fs::current_path() / "test";
|
||||
}
|
||||
|
||||
std::filesystem::path GetTempDirectory()
|
||||
{
|
||||
auto result = fs::current_path() / "build" / ".tmp";
|
||||
if (!fs::is_directory(result))
|
||||
fs::create_directories(result);
|
||||
|
||||
return result;
|
||||
}
|
||||
} // namespace oat::paths
|
10
test/Catch2Common/OatTestPaths.h
Normal file
10
test/Catch2Common/OatTestPaths.h
Normal file
@ -0,0 +1,10 @@
|
||||
#pragma once
|
||||
|
||||
#include <filesystem>
|
||||
|
||||
namespace oat::paths
|
||||
{
|
||||
std::filesystem::path GetSourceDirectory();
|
||||
std::filesystem::path GetTestDirectory();
|
||||
std::filesystem::path GetTempDirectory();
|
||||
} // namespace oat::paths
|
48
test/Catch2Common/main.cpp
Normal file
48
test/Catch2Common/main.cpp
Normal file
@ -0,0 +1,48 @@
|
||||
#include <catch2/catch_session.hpp>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
int main(const int argc, char* argv[])
|
||||
{
|
||||
const fs::path absoluteBinDir(fs::canonical(argv[0]).parent_path());
|
||||
|
||||
const auto expectedLibDir = absoluteBinDir.parent_path().parent_path();
|
||||
const auto expectedBuildDir = expectedLibDir.parent_path();
|
||||
const auto expectedRootDir = expectedBuildDir.parent_path();
|
||||
|
||||
if (absoluteBinDir.filename() != "tests" || expectedLibDir.filename() != "lib" || expectedBuildDir.filename() != "build")
|
||||
{
|
||||
std::cerr << std::format("Expected test binary to be in the folder it was compiled into (build/lib/?/tests) but was {}\n", absoluteBinDir.string());
|
||||
std::cerr << "Please do not move test executable out of compilation folder\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto expectedSrcDir = expectedRootDir / "src";
|
||||
if (!fs::is_directory(expectedSrcDir))
|
||||
{
|
||||
std::cerr << std::format("Expected source directory to exist in {}, but it did not\n", expectedSrcDir.string());
|
||||
std::cerr << "Please do not move test executable out of compilation folder\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
const auto expectedTestDir = expectedRootDir / "test";
|
||||
if (!fs::is_directory(expectedTestDir))
|
||||
{
|
||||
std::cerr << std::format("Expected test directory to exist in {}, but it did not\n", expectedTestDir.string());
|
||||
std::cerr << "Please do not move test executable out of compilation folder\n";
|
||||
return 1;
|
||||
}
|
||||
|
||||
fs::current_path(expectedRootDir);
|
||||
|
||||
const auto result = Catch::Session().run(argc, argv);
|
||||
|
||||
const auto tempDir = expectedBuildDir / ".tmp";
|
||||
if (fs::is_directory(tempDir))
|
||||
fs::remove_all(tempDir);
|
||||
|
||||
return result;
|
||||
}
|
135
test/ObjCommonTestUtils/SearchPath/MockOutputPath.cpp
Normal file
135
test/ObjCommonTestUtils/SearchPath/MockOutputPath.cpp
Normal file
@ -0,0 +1,135 @@
|
||||
#include "MockOutputPath.h"
|
||||
|
||||
#include "Utils/ObjStream.h"
|
||||
|
||||
#include <cstring>
|
||||
|
||||
namespace
|
||||
{
|
||||
class MockFileBuffer final : public std::streambuf
|
||||
{
|
||||
public:
|
||||
MockFileBuffer()
|
||||
: m_pos(0u)
|
||||
{
|
||||
}
|
||||
|
||||
std::vector<std::uint8_t> data()
|
||||
{
|
||||
return std::move(m_data);
|
||||
}
|
||||
|
||||
protected:
|
||||
int_type overflow(const int_type b) override
|
||||
{
|
||||
if (!std::char_traits<char>::eq_int_type(b, std::char_traits<char>::eof()))
|
||||
{
|
||||
m_data.insert(m_data.begin() + static_cast<decltype(m_data)::difference_type>(m_pos), static_cast<std::uint8_t>(b));
|
||||
++m_pos;
|
||||
}
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
std::streamsize xsputn(const char* ptr, const std::streamsize count) override
|
||||
{
|
||||
const auto curSize = m_data.size();
|
||||
const auto overrideCount = m_pos < curSize ? std::min(curSize - m_pos, static_cast<size_t>(count)) : 0u;
|
||||
const auto insertCount = count - overrideCount;
|
||||
|
||||
if (overrideCount > 0)
|
||||
{
|
||||
std::memcpy(&m_data[m_pos], ptr, overrideCount);
|
||||
m_pos += overrideCount;
|
||||
ptr += overrideCount;
|
||||
}
|
||||
|
||||
if (insertCount > 0)
|
||||
{
|
||||
m_data.insert(m_data.begin() + static_cast<decltype(m_data)::difference_type>(m_pos), ptr, ptr + insertCount);
|
||||
m_pos += static_cast<size_t>(insertCount);
|
||||
}
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
pos_type seekoff(const off_type off, const std::ios_base::seekdir dir, const std::ios_base::openmode mode) override
|
||||
{
|
||||
if (dir == std::ios::beg)
|
||||
return seekpos(off, mode);
|
||||
if (dir == std::ios::cur)
|
||||
return seekpos(static_cast<pos_type>(m_pos) + off, mode);
|
||||
if (off < static_cast<off_type>(m_data.size()))
|
||||
return seekpos(static_cast<off_type>(m_data.size()) - off, mode);
|
||||
return std::char_traits<char>::eof();
|
||||
}
|
||||
|
||||
pos_type seekpos(const pos_type pos, std::ios_base::openmode) override
|
||||
{
|
||||
if (pos > m_data.size())
|
||||
m_data.resize(static_cast<decltype(m_data)::size_type>(pos));
|
||||
m_pos = static_cast<size_t>(pos);
|
||||
|
||||
return pos;
|
||||
}
|
||||
|
||||
private:
|
||||
std::vector<std::uint8_t> m_data;
|
||||
std::size_t m_pos;
|
||||
};
|
||||
|
||||
class MockFileWrapper final : public std::ostream
|
||||
{
|
||||
public:
|
||||
MockFileWrapper(std::string name, std::vector<MockOutputFile>& files)
|
||||
: std::ostream(&m_buf),
|
||||
m_name(std::move(name)),
|
||||
m_files(files)
|
||||
{
|
||||
}
|
||||
|
||||
~MockFileWrapper() override
|
||||
{
|
||||
m_files.emplace_back(std::move(m_name), m_buf.data());
|
||||
}
|
||||
|
||||
private:
|
||||
MockFileBuffer m_buf;
|
||||
std::string m_name;
|
||||
std::vector<MockOutputFile>& m_files;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
MockOutputFile::MockOutputFile() = default;
|
||||
|
||||
MockOutputFile::MockOutputFile(std::string name, std::vector<std::uint8_t> data)
|
||||
: m_name(std::move(name)),
|
||||
m_data(std::move(data))
|
||||
{
|
||||
}
|
||||
|
||||
std::string MockOutputFile::AsString() const
|
||||
{
|
||||
return std::string(reinterpret_cast<const char*>(m_data.data()), m_data.size());
|
||||
}
|
||||
|
||||
std::unique_ptr<std::ostream> MockOutputPath::Open(const std::string& fileName)
|
||||
{
|
||||
return std::make_unique<MockFileWrapper>(fileName, m_files);
|
||||
}
|
||||
|
||||
const MockOutputFile* MockOutputPath::GetMockedFile(const std::string& name) const
|
||||
{
|
||||
for (const auto& file : m_files)
|
||||
{
|
||||
if (file.m_name == name)
|
||||
return &file;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
const std::vector<MockOutputFile>& MockOutputPath::GetMockedFileList() const
|
||||
{
|
||||
return m_files;
|
||||
}
|
32
test/ObjCommonTestUtils/SearchPath/MockOutputPath.h
Normal file
32
test/ObjCommonTestUtils/SearchPath/MockOutputPath.h
Normal file
@ -0,0 +1,32 @@
|
||||
#pragma once
|
||||
|
||||
#include "SearchPath/IOutputPath.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <sstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
class MockOutputFile
|
||||
{
|
||||
public:
|
||||
std::string m_name;
|
||||
std::vector<std::uint8_t> m_data;
|
||||
|
||||
MockOutputFile();
|
||||
MockOutputFile(std::string name, std::vector<std::uint8_t> data);
|
||||
|
||||
[[nodiscard]] std::string AsString() const;
|
||||
};
|
||||
|
||||
class MockOutputPath final : public IOutputPath
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<std::ostream> Open(const std::string& fileName) override;
|
||||
|
||||
[[nodiscard]] const MockOutputFile* GetMockedFile(const std::string& name) const;
|
||||
[[nodiscard]] const std::vector<MockOutputFile>& GetMockedFileList() const;
|
||||
|
||||
private:
|
||||
std::vector<MockOutputFile> m_files;
|
||||
};
|
14
test/ObjCommonTestUtils/Utils/TestMemoryManager.h
Normal file
14
test/ObjCommonTestUtils/Utils/TestMemoryManager.h
Normal file
@ -0,0 +1,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils/MemoryManager.h"
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
class TestMemoryManager : public MemoryManager
|
||||
{
|
||||
public:
|
||||
[[nodiscard]] size_t GetAllocationCount() const
|
||||
{
|
||||
return m_allocations.size() + m_destructible.size();
|
||||
}
|
||||
};
|
@ -44,6 +44,7 @@ function ObjCommonTests:project()
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Catch2Common:include(includes)
|
||||
ObjCommon:include(includes)
|
||||
ObjImage:include(includes)
|
||||
catch2:include(includes)
|
||||
@ -51,5 +52,6 @@ function ObjCommonTests:project()
|
||||
links:linkto(ObjCommon)
|
||||
links:linkto(ObjImage)
|
||||
links:linkto(catch2)
|
||||
links:linkto(Catch2Common)
|
||||
links:linkall()
|
||||
end
|
||||
|
60
test/ObjCompilingTests.lua
Normal file
60
test/ObjCompilingTests.lua
Normal file
@ -0,0 +1,60 @@
|
||||
ObjCompilingTests = {}
|
||||
|
||||
function ObjCompilingTests:include(includes)
|
||||
if includes:handle(self:name()) then
|
||||
includedirs {
|
||||
path.join(TestFolder(), "ObjCompilingTests")
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
function ObjCompilingTests:link(links)
|
||||
|
||||
end
|
||||
|
||||
function ObjCompilingTests:use()
|
||||
|
||||
end
|
||||
|
||||
function ObjCompilingTests:name()
|
||||
return "ObjCompilingTests"
|
||||
end
|
||||
|
||||
function ObjCompilingTests:project()
|
||||
local folder = TestFolder()
|
||||
local includes = Includes:create()
|
||||
local links = Links:create()
|
||||
|
||||
project(self:name())
|
||||
targetdir(TargetDirectoryTest)
|
||||
location "%{wks.location}/test/%{prj.name}"
|
||||
kind "ConsoleApp"
|
||||
language "C++"
|
||||
|
||||
files {
|
||||
path.join(folder, "ObjCompilingTests/**.h"),
|
||||
path.join(folder, "ObjCompilingTests/**.cpp")
|
||||
}
|
||||
|
||||
vpaths {
|
||||
["*"] = {
|
||||
path.join(folder, "ObjCompilingTests")
|
||||
}
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Catch2Common:include(includes)
|
||||
ObjCommonTestUtils:include(includes)
|
||||
ParserTestUtils:include(includes)
|
||||
ObjLoading:include(includes)
|
||||
ObjCompiling:include(includes)
|
||||
catch2:include(includes)
|
||||
|
||||
links:linkto(ObjCommonTestUtils)
|
||||
links:linkto(ParserTestUtils)
|
||||
links:linkto(ObjLoading)
|
||||
links:linkto(ObjCompiling)
|
||||
links:linkto(catch2)
|
||||
links:linkto(Catch2Common)
|
||||
links:linkall()
|
||||
end
|
@ -0,0 +1,130 @@
|
||||
#include "Game/T6/KeyValuePairs/KeyValuePairsCompilerT6.h"
|
||||
|
||||
#include "Game/T6/CommonT6.h"
|
||||
#include "Game/T6/GameAssetPoolT6.h"
|
||||
#include "KeyValuePairs/KeyValuePairsCreator.h"
|
||||
#include "Utils/TestMemoryManager.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <memory>
|
||||
|
||||
using namespace T6;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
class TestContext
|
||||
{
|
||||
public:
|
||||
TestContext()
|
||||
: m_memory(),
|
||||
m_zone("test", 0, IGame::GetGameById(GameId::T6)),
|
||||
m_zone_definition(),
|
||||
m_zone_states(m_zone),
|
||||
m_creators(m_zone),
|
||||
m_ignored_assets(),
|
||||
m_context(m_zone, &m_creators, &m_ignored_assets),
|
||||
m_kvp_creator(m_zone_states.GetZoneAssetCreationState<KeyValuePairsCreator>())
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<IAssetCreator> CreateSut()
|
||||
{
|
||||
return CreateKeyValuePairsCompiler(m_memory, m_zone, m_zone_definition, m_zone_states);
|
||||
}
|
||||
|
||||
TestMemoryManager m_memory;
|
||||
Zone m_zone;
|
||||
ZoneDefinition m_zone_definition;
|
||||
ZoneAssetCreationStateContainer m_zone_states;
|
||||
AssetCreatorCollection m_creators;
|
||||
IgnoredAssetLookup m_ignored_assets;
|
||||
AssetCreationContext m_context;
|
||||
|
||||
KeyValuePairsCreator& m_kvp_creator;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace test::game::t6::keyvaluepairs
|
||||
{
|
||||
TEST_CASE("KeyValuePairsCompilerT6: Does not handle any asset type", "[keyvaluepairs][t6]")
|
||||
{
|
||||
TestContext testContext;
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
REQUIRE(!sut->GetHandlingAssetType().has_value());
|
||||
}
|
||||
|
||||
TEST_CASE("KeyValuePairsCompilerT6: Does not take any action", "[keyvaluepairs][t6]")
|
||||
{
|
||||
TestContext testContext;
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
REQUIRE(sut->CreateAsset("anyAsset", testContext.m_context).HasTakenAction() == false);
|
||||
}
|
||||
|
||||
TEST_CASE("KeyValuePairsCompilerT6: Does nothing without any KeyValuePairs", "[keyvaluepairs][t6]")
|
||||
{
|
||||
TestContext testContext;
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
sut->FinalizeZone(testContext.m_context);
|
||||
|
||||
REQUIRE(testContext.m_memory.GetAllocationCount() == 0u);
|
||||
REQUIRE(testContext.m_zone.m_pools->GetTotalAssetCount() == 0u);
|
||||
}
|
||||
|
||||
TEST_CASE("KeyValuePairsCompilerT6: Creates KeyValuePairs asset with identical name to the zone", "[keyvaluepairs][t6]")
|
||||
{
|
||||
TestContext testContext;
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
testContext.m_kvp_creator.AddKeyValuePair(CommonKeyValuePair("ipak_read", "test_ipak"));
|
||||
|
||||
sut->FinalizeZone(testContext.m_context);
|
||||
|
||||
REQUIRE(testContext.m_memory.GetAllocationCount() > 0u);
|
||||
REQUIRE(testContext.m_zone.m_pools->GetTotalAssetCount() == 1u);
|
||||
|
||||
XAssetInfo<KeyValuePairs>* assetInfo = *dynamic_cast<GameAssetPoolT6*>(testContext.m_zone.m_pools.get())->m_key_value_pairs->begin();
|
||||
REQUIRE(assetInfo);
|
||||
REQUIRE(assetInfo->m_name == "test");
|
||||
|
||||
auto* asset = assetInfo->Asset();
|
||||
REQUIRE(asset->name == "test"s);
|
||||
REQUIRE(asset->numVariables == 1u);
|
||||
REQUIRE(asset->keyValuePairs != nullptr);
|
||||
|
||||
REQUIRE(asset->keyValuePairs[0].keyHash == 0x0001bdc1);
|
||||
REQUIRE(asset->keyValuePairs[0].namespaceHash == 0x0000d2d3);
|
||||
REQUIRE(asset->keyValuePairs[0].value == "test_ipak"s);
|
||||
}
|
||||
|
||||
TEST_CASE("KeyValuePairsCompilerT6: Creates KeyValuePairs asset with predefined hash", "[keyvaluepairs][t6]")
|
||||
{
|
||||
TestContext testContext;
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
testContext.m_kvp_creator.AddKeyValuePair(CommonKeyValuePair(0xDDEEFFAA, "hello_there"));
|
||||
|
||||
sut->FinalizeZone(testContext.m_context);
|
||||
|
||||
REQUIRE(testContext.m_memory.GetAllocationCount() > 0u);
|
||||
REQUIRE(testContext.m_zone.m_pools->GetTotalAssetCount() == 1u);
|
||||
|
||||
XAssetInfo<KeyValuePairs>* assetInfo = *dynamic_cast<GameAssetPoolT6*>(testContext.m_zone.m_pools.get())->m_key_value_pairs->begin();
|
||||
REQUIRE(assetInfo);
|
||||
REQUIRE(assetInfo->m_name == "test");
|
||||
|
||||
auto* asset = assetInfo->Asset();
|
||||
REQUIRE(asset->name == "test"s);
|
||||
REQUIRE(asset->numVariables == 1u);
|
||||
REQUIRE(asset->keyValuePairs != nullptr);
|
||||
|
||||
REQUIRE(asset->keyValuePairs[0].keyHash == 0xDDEEFFAA);
|
||||
REQUIRE(asset->keyValuePairs[0].namespaceHash == 0x0000d2d3);
|
||||
REQUIRE(asset->keyValuePairs[0].value == "hello_there"s);
|
||||
}
|
||||
} // namespace test::game::t6::keyvaluepairs
|
86
test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp
Normal file
86
test/ObjCompilingTests/Image/IPak/IPakCreatorTest.cpp
Normal file
@ -0,0 +1,86 @@
|
||||
#include "Image/IPak/IPakCreator.h"
|
||||
|
||||
#include "Asset/AssetCreatorCollection.h"
|
||||
#include "ObjContainer/IPak/IPak.h"
|
||||
#include "SearchPath/MockOutputPath.h"
|
||||
#include "SearchPath/MockSearchPath.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
class TestContext
|
||||
{
|
||||
public:
|
||||
TestContext()
|
||||
: m_zone("test", 0, IGame::GetGameById(GameId::T6)),
|
||||
m_zone_states(m_zone),
|
||||
m_out_dir()
|
||||
{
|
||||
}
|
||||
|
||||
IPakCreator& CreateSut()
|
||||
{
|
||||
return m_zone_states.GetZoneAssetCreationState<IPakCreator>();
|
||||
}
|
||||
|
||||
Zone m_zone;
|
||||
MockSearchPath m_search_path;
|
||||
ZoneAssetCreationStateContainer m_zone_states;
|
||||
MockOutputPath m_out_dir;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace test::image::ipak
|
||||
{
|
||||
TEST_CASE("IPakCreator: Does nothing if no ipak was defined", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
auto& sut = testContext.CreateSut();
|
||||
|
||||
sut.Finalize(testContext.m_search_path, testContext.m_out_dir);
|
||||
REQUIRE(testContext.m_out_dir.GetMockedFileList().empty());
|
||||
}
|
||||
|
||||
TEST_CASE("IPakCreator: Writes IPak file", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
auto& sut = testContext.CreateSut();
|
||||
|
||||
auto* ipak = sut.GetOrAddIPak("amazing");
|
||||
ipak->AddImage("random");
|
||||
|
||||
constexpr auto iwiData = "hello world";
|
||||
testContext.m_search_path.AddFileData("images/random.iwi", iwiData);
|
||||
|
||||
sut.Finalize(testContext.m_search_path, testContext.m_out_dir);
|
||||
|
||||
const auto* file = testContext.m_out_dir.GetMockedFile("amazing.ipak");
|
||||
REQUIRE(file);
|
||||
|
||||
const auto* data = file->m_data.data();
|
||||
REQUIRE(data[0] == 'K');
|
||||
REQUIRE(data[1] == 'A');
|
||||
REQUIRE(data[2] == 'P');
|
||||
REQUIRE(data[3] == 'I');
|
||||
|
||||
auto readIpak = IIPak::Create("amazing.ipak", std::make_unique<std::istringstream>(file->AsString()));
|
||||
REQUIRE(readIpak->Initialize());
|
||||
|
||||
auto entry = readIpak->GetEntryStream(IIPak::HashString("random"), IIPak::HashData(iwiData, std::char_traits<char>::length(iwiData)));
|
||||
REQUIRE(entry);
|
||||
|
||||
char readBuffer[std::char_traits<char>::length(iwiData) + 10];
|
||||
entry->read(readBuffer, sizeof(readBuffer));
|
||||
|
||||
REQUIRE(entry->gcount() == std::char_traits<char>::length(iwiData));
|
||||
REQUIRE(std::strncmp(iwiData, readBuffer, std::char_traits<char>::length(iwiData)) == 0);
|
||||
}
|
||||
} // namespace test::image::ipak
|
278
test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp
Normal file
278
test/ObjCompilingTests/Image/ImageIPakPostProcessorTest.cpp
Normal file
@ -0,0 +1,278 @@
|
||||
#include "Image/ImageIPakPostProcessor.h"
|
||||
|
||||
#include "Game/T6/T6.h"
|
||||
#include "KeyValuePairs/KeyValuePairsCreator.h"
|
||||
#include "SearchPath/MockOutputPath.h"
|
||||
#include "SearchPath/MockSearchPath.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
using namespace T6;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
class TestContext
|
||||
{
|
||||
public:
|
||||
TestContext()
|
||||
: m_zone("test", 0, IGame::GetGameById(GameId::T6)),
|
||||
m_zone_definition(),
|
||||
m_zone_definition_context(m_zone_definition),
|
||||
m_zone_states(m_zone),
|
||||
m_creators(m_zone),
|
||||
m_ignored_assets(),
|
||||
m_out_dir(),
|
||||
m_context(m_zone, &m_creators, &m_ignored_assets),
|
||||
m_ipak_creator(m_zone_states.GetZoneAssetCreationState<IPakCreator>())
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<IAssetPostProcessor> CreateSut()
|
||||
{
|
||||
return std::make_unique<ImageIPakPostProcessor<AssetImage>>(m_zone_definition_context, m_search_path, m_zone_states, m_out_dir);
|
||||
}
|
||||
|
||||
Zone m_zone;
|
||||
ZoneDefinition m_zone_definition;
|
||||
ZoneDefinitionContext m_zone_definition_context;
|
||||
MockSearchPath m_search_path;
|
||||
ZoneAssetCreationStateContainer m_zone_states;
|
||||
AssetCreatorCollection m_creators;
|
||||
IgnoredAssetLookup m_ignored_assets;
|
||||
MockOutputPath m_out_dir;
|
||||
AssetCreationContext m_context;
|
||||
|
||||
IPakCreator& m_ipak_creator;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace test::image
|
||||
{
|
||||
TEST_CASE("ImageIPakPostProcessor: Handles asset type of specified asset", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
REQUIRE(sut->GetHandlingAssetType() == ASSET_TYPE_IMAGE);
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIPakPostProcessor: Adds images to ipak when obj container is specified", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIpak", ZoneDefinitionObjContainerType::IPAK, 0, 2);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
auto result = testContext.m_ipak_creator.GetOrAddIPak("testIpak");
|
||||
REQUIRE(result);
|
||||
|
||||
const auto& imageNames = result->GetImageNames();
|
||||
REQUIRE(imageNames.size() == 2u);
|
||||
REQUIRE(imageNames[0] == "testImage0");
|
||||
REQUIRE(imageNames[1] == "testImage1");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIPakPostProcessor: Respects lower obj container boundary when adding images to ipak", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIpak", ZoneDefinitionObjContainerType::IPAK, 1, 3);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
auto result = testContext.m_ipak_creator.GetOrAddIPak("testIpak");
|
||||
REQUIRE(result);
|
||||
|
||||
const auto& imageNames = result->GetImageNames();
|
||||
REQUIRE(imageNames.size() == 2u);
|
||||
REQUIRE(imageNames[0] == "testImage1");
|
||||
REQUIRE(imageNames[1] == "testImage2");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIPakPostProcessor: Respects upper obj container boundary when adding images to ipak", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIpak", ZoneDefinitionObjContainerType::IPAK, 0, 2);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
auto result = testContext.m_ipak_creator.GetOrAddIPak("testIpak");
|
||||
REQUIRE(result);
|
||||
|
||||
const auto& imageNames = result->GetImageNames();
|
||||
REQUIRE(imageNames.size() == 2u);
|
||||
REQUIRE(imageNames[0] == "testImage0");
|
||||
REQUIRE(imageNames[1] == "testImage1");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIPakPostProcessor: Respects upper and lower obj container boundary when adding images to ipak", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIpak", ZoneDefinitionObjContainerType::IPAK, 1, 3);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset3(ASSET_TYPE_IMAGE, "testImage3", nullptr);
|
||||
sut->PostProcessAsset(imageAsset3, testContext.m_context);
|
||||
|
||||
auto result = testContext.m_ipak_creator.GetOrAddIPak("testIpak");
|
||||
REQUIRE(result);
|
||||
|
||||
const auto& imageNames = result->GetImageNames();
|
||||
REQUIRE(imageNames.size() == 2u);
|
||||
REQUIRE(imageNames[0] == "testImage1");
|
||||
REQUIRE(imageNames[1] == "testImage2");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIPakPostProcessor: Can add images to multiple ipak", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIpak0", ZoneDefinitionObjContainerType::IPAK, 0, 2);
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIpak1", ZoneDefinitionObjContainerType::IPAK, 2, 4);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset3(ASSET_TYPE_IMAGE, "testImage3", nullptr);
|
||||
sut->PostProcessAsset(imageAsset3, testContext.m_context);
|
||||
|
||||
auto result0 = testContext.m_ipak_creator.GetOrAddIPak("testIpak0");
|
||||
REQUIRE(result0);
|
||||
|
||||
const auto& imageNames0 = result0->GetImageNames();
|
||||
REQUIRE(imageNames0.size() == 2u);
|
||||
REQUIRE(imageNames0[0] == "testImage0");
|
||||
REQUIRE(imageNames0[1] == "testImage1");
|
||||
|
||||
auto result1 = testContext.m_ipak_creator.GetOrAddIPak("testIpak1");
|
||||
REQUIRE(result1);
|
||||
|
||||
const auto& imageNames1 = result1->GetImageNames();
|
||||
REQUIRE(imageNames1.size() == 2u);
|
||||
REQUIRE(imageNames1[0] == "testImage2");
|
||||
REQUIRE(imageNames1[1] == "testImage3");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIPakPostProcessor: Can add images to multiple ipak with gap between", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIpak0", ZoneDefinitionObjContainerType::IPAK, 0, 2);
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIpak1", ZoneDefinitionObjContainerType::IPAK, 3, 5);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset3(ASSET_TYPE_IMAGE, "testImage3", nullptr);
|
||||
sut->PostProcessAsset(imageAsset3, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset4(ASSET_TYPE_IMAGE, "testImage4", nullptr);
|
||||
sut->PostProcessAsset(imageAsset4, testContext.m_context);
|
||||
|
||||
auto result0 = testContext.m_ipak_creator.GetOrAddIPak("testIpak0");
|
||||
REQUIRE(result0);
|
||||
|
||||
const auto& imageNames0 = result0->GetImageNames();
|
||||
REQUIRE(imageNames0.size() == 2u);
|
||||
REQUIRE(imageNames0[0] == "testImage0");
|
||||
REQUIRE(imageNames0[1] == "testImage1");
|
||||
|
||||
auto result1 = testContext.m_ipak_creator.GetOrAddIPak("testIpak1");
|
||||
REQUIRE(result1);
|
||||
|
||||
const auto& imageNames1 = result1->GetImageNames();
|
||||
REQUIRE(imageNames1.size() == 2u);
|
||||
REQUIRE(imageNames1[0] == "testImage3");
|
||||
REQUIRE(imageNames1[1] == "testImage4");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIPakPostProcessor: Writes IPak when finalizing", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIpak", ZoneDefinitionObjContainerType::IPAK, 0, 2);
|
||||
|
||||
testContext.m_search_path.AddFileData("images/testImage0.iwi", "asdf0");
|
||||
testContext.m_search_path.AddFileData("images/testImage1.iwi", "asdf1");
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
sut->FinalizeZone(testContext.m_context);
|
||||
|
||||
const auto* mockFile = testContext.m_out_dir.GetMockedFile("testIpak.ipak");
|
||||
REQUIRE(mockFile);
|
||||
}
|
||||
} // namespace test::image
|
277
test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp
Normal file
277
test/ObjCompilingTests/Image/ImageIwdPostProcessorTest.cpp
Normal file
@ -0,0 +1,277 @@
|
||||
#include "Image/ImageIwdPostProcessor.h"
|
||||
|
||||
#include "Game/T6/T6.h"
|
||||
#include "SearchPath/MockOutputPath.h"
|
||||
#include "SearchPath/MockSearchPath.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
|
||||
using namespace T6;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
class TestContext
|
||||
{
|
||||
public:
|
||||
TestContext()
|
||||
: m_zone("test", 0, IGame::GetGameById(GameId::T6)),
|
||||
m_zone_definition(),
|
||||
m_zone_definition_context(m_zone_definition),
|
||||
m_zone_states(m_zone),
|
||||
m_creators(m_zone),
|
||||
m_ignored_assets(),
|
||||
m_out_dir(),
|
||||
m_context(m_zone, &m_creators, &m_ignored_assets),
|
||||
m_iwd_creator(m_zone_states.GetZoneAssetCreationState<IwdCreator>())
|
||||
{
|
||||
}
|
||||
|
||||
std::unique_ptr<IAssetPostProcessor> CreateSut()
|
||||
{
|
||||
return std::make_unique<ImageIwdPostProcessor<AssetImage>>(m_zone_definition_context, m_search_path, m_zone_states, m_out_dir);
|
||||
}
|
||||
|
||||
Zone m_zone;
|
||||
ZoneDefinition m_zone_definition;
|
||||
ZoneDefinitionContext m_zone_definition_context;
|
||||
MockSearchPath m_search_path;
|
||||
ZoneAssetCreationStateContainer m_zone_states;
|
||||
AssetCreatorCollection m_creators;
|
||||
IgnoredAssetLookup m_ignored_assets;
|
||||
MockOutputPath m_out_dir;
|
||||
AssetCreationContext m_context;
|
||||
|
||||
IwdCreator& m_iwd_creator;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace test::image
|
||||
{
|
||||
TEST_CASE("ImageIwdPostProcessor: Handles asset type of specified asset", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
REQUIRE(sut->GetHandlingAssetType() == ASSET_TYPE_IMAGE);
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIwdPostProcessor: Adds images to iwd when obj container is specified", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIwd", ZoneDefinitionObjContainerType::IWD, 0, 2);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
auto result = testContext.m_iwd_creator.GetOrAddIwd("testIwd");
|
||||
REQUIRE(result);
|
||||
|
||||
const auto& filePaths = result->GetFilePaths();
|
||||
REQUIRE(filePaths.size() == 2u);
|
||||
REQUIRE(filePaths[0] == "images/testImage0.iwi");
|
||||
REQUIRE(filePaths[1] == "images/testImage1.iwi");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIwdPostProcessor: Respects lower obj container boundary when adding images to iwd", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIwd", ZoneDefinitionObjContainerType::IWD, 1, 3);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
auto result = testContext.m_iwd_creator.GetOrAddIwd("testIwd");
|
||||
REQUIRE(result);
|
||||
|
||||
const auto& filePaths = result->GetFilePaths();
|
||||
REQUIRE(filePaths.size() == 2u);
|
||||
REQUIRE(filePaths[0] == "images/testImage1.iwi");
|
||||
REQUIRE(filePaths[1] == "images/testImage2.iwi");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIwdPostProcessor: Respects upper obj container boundary when adding images to iwd", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIwd", ZoneDefinitionObjContainerType::IWD, 0, 2);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
auto result = testContext.m_iwd_creator.GetOrAddIwd("testIwd");
|
||||
REQUIRE(result);
|
||||
|
||||
const auto& filePaths = result->GetFilePaths();
|
||||
REQUIRE(filePaths.size() == 2u);
|
||||
REQUIRE(filePaths[0] == "images/testImage0.iwi");
|
||||
REQUIRE(filePaths[1] == "images/testImage1.iwi");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIwdPostProcessor: Respects upper and lower obj container boundary when adding images to iwd", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIwd", ZoneDefinitionObjContainerType::IWD, 1, 3);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset3(ASSET_TYPE_IMAGE, "testImage3", nullptr);
|
||||
sut->PostProcessAsset(imageAsset3, testContext.m_context);
|
||||
|
||||
auto result = testContext.m_iwd_creator.GetOrAddIwd("testIwd");
|
||||
REQUIRE(result);
|
||||
|
||||
const auto& filePaths = result->GetFilePaths();
|
||||
REQUIRE(filePaths.size() == 2u);
|
||||
REQUIRE(filePaths[0] == "images/testImage1.iwi");
|
||||
REQUIRE(filePaths[1] == "images/testImage2.iwi");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIwdPostProcessor: Can add images to multiple iwd", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIwd0", ZoneDefinitionObjContainerType::IWD, 0, 2);
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIwd1", ZoneDefinitionObjContainerType::IWD, 2, 4);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset3(ASSET_TYPE_IMAGE, "testImage3", nullptr);
|
||||
sut->PostProcessAsset(imageAsset3, testContext.m_context);
|
||||
|
||||
auto result0 = testContext.m_iwd_creator.GetOrAddIwd("testIwd0");
|
||||
REQUIRE(result0);
|
||||
|
||||
const auto& filePaths0 = result0->GetFilePaths();
|
||||
REQUIRE(filePaths0.size() == 2u);
|
||||
REQUIRE(filePaths0[0] == "images/testImage0.iwi");
|
||||
REQUIRE(filePaths0[1] == "images/testImage1.iwi");
|
||||
|
||||
auto result1 = testContext.m_iwd_creator.GetOrAddIwd("testIwd1");
|
||||
REQUIRE(result1);
|
||||
|
||||
const auto& filePaths1 = result1->GetFilePaths();
|
||||
REQUIRE(filePaths1.size() == 2u);
|
||||
REQUIRE(filePaths1[0] == "images/testImage2.iwi");
|
||||
REQUIRE(filePaths1[1] == "images/testImage3.iwi");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIwdPostProcessor: Can add images to multiple iwd with gap between", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIwd0", ZoneDefinitionObjContainerType::IWD, 0, 2);
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIwd1", ZoneDefinitionObjContainerType::IWD, 3, 5);
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset2(ASSET_TYPE_IMAGE, "testImage2", nullptr);
|
||||
sut->PostProcessAsset(imageAsset2, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset3(ASSET_TYPE_IMAGE, "testImage3", nullptr);
|
||||
sut->PostProcessAsset(imageAsset3, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset4(ASSET_TYPE_IMAGE, "testImage4", nullptr);
|
||||
sut->PostProcessAsset(imageAsset4, testContext.m_context);
|
||||
|
||||
auto result0 = testContext.m_iwd_creator.GetOrAddIwd("testIwd0");
|
||||
REQUIRE(result0);
|
||||
|
||||
const auto& filePaths0 = result0->GetFilePaths();
|
||||
REQUIRE(filePaths0.size() == 2u);
|
||||
REQUIRE(filePaths0[0] == "images/testImage0.iwi");
|
||||
REQUIRE(filePaths0[1] == "images/testImage1.iwi");
|
||||
|
||||
auto result1 = testContext.m_iwd_creator.GetOrAddIwd("testIwd1");
|
||||
REQUIRE(result1);
|
||||
|
||||
const auto& filePaths1 = result1->GetFilePaths();
|
||||
REQUIRE(filePaths1.size() == 2u);
|
||||
REQUIRE(filePaths1[0] == "images/testImage3.iwi");
|
||||
REQUIRE(filePaths1[1] == "images/testImage4.iwi");
|
||||
}
|
||||
|
||||
TEST_CASE("ImageIwdPostProcessor: Writes Iwd when finalizing", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
testContext.m_zone_definition.m_obj_containers.emplace_back("testIwd", ZoneDefinitionObjContainerType::IWD, 0, 2);
|
||||
|
||||
testContext.m_search_path.AddFileData("images/testImage0.iwi", "asdf0");
|
||||
testContext.m_search_path.AddFileData("images/testImage1.iwi", "asdf1");
|
||||
|
||||
const auto sut = testContext.CreateSut();
|
||||
|
||||
XAssetInfo<GfxImage> imageAsset0(ASSET_TYPE_IMAGE, "testImage0", nullptr);
|
||||
sut->PostProcessAsset(imageAsset0, testContext.m_context);
|
||||
|
||||
++testContext.m_zone_definition_context.m_asset_index_in_definition;
|
||||
XAssetInfo<GfxImage> imageAsset1(ASSET_TYPE_IMAGE, "testImage1", nullptr);
|
||||
sut->PostProcessAsset(imageAsset1, testContext.m_context);
|
||||
|
||||
sut->FinalizeZone(testContext.m_context);
|
||||
|
||||
const auto* mockFile = testContext.m_out_dir.GetMockedFile("testIwd.iwd");
|
||||
REQUIRE(mockFile);
|
||||
}
|
||||
} // namespace test::image
|
94
test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp
Normal file
94
test/ObjCompilingTests/Iwd/IwdCreatorTest.cpp
Normal file
@ -0,0 +1,94 @@
|
||||
#include "Iwd/IwdCreator.h"
|
||||
|
||||
#include "Asset/AssetCreatorCollection.h"
|
||||
#include "Game/T6/T6.h"
|
||||
#include "SearchPath/MockOutputPath.h"
|
||||
#include "SearchPath/MockSearchPath.h"
|
||||
#include "Utils/FileToZlibWrapper.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <memory>
|
||||
#include <unzip.h>
|
||||
|
||||
using namespace T6;
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace
|
||||
{
|
||||
class TestContext
|
||||
{
|
||||
public:
|
||||
TestContext()
|
||||
: m_zone("test", 0, IGame::GetGameById(GameId::T6)),
|
||||
m_zone_states(m_zone),
|
||||
m_out_dir()
|
||||
{
|
||||
}
|
||||
|
||||
IwdCreator& CreateSut()
|
||||
{
|
||||
return m_zone_states.GetZoneAssetCreationState<IwdCreator>();
|
||||
}
|
||||
|
||||
Zone m_zone;
|
||||
MockSearchPath m_search_path;
|
||||
ZoneAssetCreationStateContainer m_zone_states;
|
||||
MockOutputPath m_out_dir;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
namespace test::iwd
|
||||
{
|
||||
TEST_CASE("IwdCreator: Does nothing if no iwd was defined", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
auto& sut = testContext.CreateSut();
|
||||
|
||||
sut.Finalize(testContext.m_search_path, testContext.m_out_dir);
|
||||
REQUIRE(testContext.m_out_dir.GetMockedFileList().empty());
|
||||
}
|
||||
|
||||
TEST_CASE("IwdCreator: Writes Iwd file", "[image]")
|
||||
{
|
||||
TestContext testContext;
|
||||
auto& sut = testContext.CreateSut();
|
||||
|
||||
auto* ipak = sut.GetOrAddIwd("amazing");
|
||||
ipak->AddFile("images/random.iwi");
|
||||
|
||||
constexpr auto iwiData = "hello world";
|
||||
testContext.m_search_path.AddFileData("images/random.iwi", iwiData);
|
||||
|
||||
sut.Finalize(testContext.m_search_path, testContext.m_out_dir);
|
||||
|
||||
const auto* file = testContext.m_out_dir.GetMockedFile("amazing.iwd");
|
||||
REQUIRE(file);
|
||||
|
||||
const auto* data = file->m_data.data();
|
||||
REQUIRE(data[0] == 'P');
|
||||
REQUIRE(data[1] == 'K');
|
||||
|
||||
std::istringstream ss(file->AsString());
|
||||
auto zlibFunctions = FileToZlibWrapper::CreateFunctions32ForFile(&ss);
|
||||
auto zip = unzOpen2("amazing.iwd", &zlibFunctions);
|
||||
REQUIRE(zip);
|
||||
|
||||
REQUIRE(unzGoToFirstFile(zip) == UNZ_OK);
|
||||
|
||||
unz_file_info fileInfo;
|
||||
char fileNameBuffer[64];
|
||||
char readBuffer[std::char_traits<char>::length(iwiData) + 10];
|
||||
REQUIRE(unzGetCurrentFileInfo(zip, &fileInfo, fileNameBuffer, sizeof(fileNameBuffer), nullptr, 0, nullptr, 0) == UNZ_OK);
|
||||
|
||||
REQUIRE("images/random.iwi"s == fileNameBuffer);
|
||||
REQUIRE(fileInfo.uncompressed_size == std::char_traits<char>::length(iwiData));
|
||||
|
||||
REQUIRE(unzOpenCurrentFile(zip) == UNZ_OK);
|
||||
REQUIRE(unzReadCurrentFile(zip, readBuffer, sizeof(readBuffer)) == std::char_traits<char>::length(iwiData));
|
||||
REQUIRE(std::strncmp(iwiData, readBuffer, std::char_traits<char>::length(iwiData)) == 0);
|
||||
}
|
||||
} // namespace test::iwd
|
@ -0,0 +1,101 @@
|
||||
#include "KeyValuePairs/KeyValuePairsCreator.h"
|
||||
|
||||
#include "Utils/TestMemoryManager.h"
|
||||
|
||||
#include <catch2/catch_test_macros.hpp>
|
||||
#include <catch2/generators/catch_generators.hpp>
|
||||
#include <catch2/matchers/catch_matchers_floating_point.hpp>
|
||||
#include <memory>
|
||||
|
||||
using namespace std::string_literals;
|
||||
|
||||
namespace test::keyvaluepairs
|
||||
{
|
||||
TEST_CASE("KeyValuePairsCreator: ZoneDefinition with no properties produces no KeyValuePairs", "[keyvaluepairs]")
|
||||
{
|
||||
KeyValuePairsCreator sut;
|
||||
|
||||
ZoneDefinition zoneDefinition;
|
||||
sut.Finalize(zoneDefinition);
|
||||
|
||||
const auto kvps = sut.GetFinalKeyValuePairs();
|
||||
|
||||
REQUIRE(kvps.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("KeyValuePairsCreator: ZoneDefinition with unrelated properties produce no KeyValuePairs", "[keyvaluepairs]")
|
||||
{
|
||||
KeyValuePairsCreator sut;
|
||||
|
||||
ZoneDefinition zoneDefinition;
|
||||
zoneDefinition.m_properties.AddProperty("linker.test", "yes");
|
||||
sut.Finalize(zoneDefinition);
|
||||
|
||||
const auto kvps = sut.GetFinalKeyValuePairs();
|
||||
|
||||
REQUIRE(kvps.empty());
|
||||
}
|
||||
|
||||
TEST_CASE("KeyValuePairsCreator: ZoneDefinition with level properties produce KeyValuePairs", "[keyvaluepairs]")
|
||||
{
|
||||
KeyValuePairsCreator sut;
|
||||
|
||||
ZoneDefinition zoneDefinition;
|
||||
zoneDefinition.m_properties.AddProperty("linker.test", "yes");
|
||||
zoneDefinition.m_properties.AddProperty("level.random", "lemao");
|
||||
sut.Finalize(zoneDefinition);
|
||||
|
||||
const auto kvps = sut.GetFinalKeyValuePairs();
|
||||
|
||||
REQUIRE(kvps.size() == 1);
|
||||
REQUIRE(kvps[0].m_key_str);
|
||||
REQUIRE(!kvps[0].m_key_hash);
|
||||
REQUIRE(*kvps[0].m_key_str == "random");
|
||||
REQUIRE(kvps[0].m_value == "lemao");
|
||||
}
|
||||
|
||||
TEST_CASE("KeyValuePairsCreator: ZoneDefinition can have level properties with hash", "[keyvaluepairs]")
|
||||
{
|
||||
KeyValuePairsCreator sut;
|
||||
|
||||
ZoneDefinition zoneDefinition;
|
||||
zoneDefinition.m_properties.AddProperty("level.@D34DB33F", "yes");
|
||||
zoneDefinition.m_properties.AddProperty("level.@112233", "no");
|
||||
zoneDefinition.m_properties.AddProperty("level.@0", "maybe");
|
||||
sut.Finalize(zoneDefinition);
|
||||
|
||||
const auto kvps = sut.GetFinalKeyValuePairs();
|
||||
|
||||
REQUIRE(kvps.size() == 3);
|
||||
REQUIRE(*kvps[0].m_key_hash == 0);
|
||||
REQUIRE(kvps[0].m_value == "maybe");
|
||||
REQUIRE(*kvps[1].m_key_hash == 0x112233);
|
||||
REQUIRE(kvps[1].m_value == "no");
|
||||
REQUIRE(*kvps[2].m_key_hash == 0xD34DB33F);
|
||||
REQUIRE(kvps[2].m_value == "yes");
|
||||
}
|
||||
|
||||
TEST_CASE("KeyValuePairsCreator: ZoneDefinition can have level properties with name and/or hash", "[keyvaluepairs]")
|
||||
{
|
||||
KeyValuePairsCreator sut;
|
||||
|
||||
ZoneDefinition zoneDefinition;
|
||||
zoneDefinition.m_properties.AddProperty("level.ipak_read", "asdf");
|
||||
zoneDefinition.m_properties.AddProperty("level.hello_world", "foo");
|
||||
zoneDefinition.m_properties.AddProperty("level.@D34DB33F", "yes");
|
||||
zoneDefinition.m_properties.AddProperty("level.@112233", "no");
|
||||
sut.Finalize(zoneDefinition);
|
||||
|
||||
const auto kvps = sut.GetFinalKeyValuePairs();
|
||||
|
||||
REQUIRE(kvps.size() == 4);
|
||||
REQUIRE(*kvps[0].m_key_str == "hello_world");
|
||||
REQUIRE(kvps[0].m_value == "foo");
|
||||
REQUIRE(*kvps[1].m_key_str == "ipak_read");
|
||||
REQUIRE(kvps[1].m_value == "asdf");
|
||||
REQUIRE(*kvps[2].m_key_hash == 0x112233);
|
||||
REQUIRE(kvps[2].m_value == "no");
|
||||
REQUIRE(*kvps[3].m_key_hash == 0xD34DB33F);
|
||||
REQUIRE(kvps[3].m_value == "yes");
|
||||
}
|
||||
} // namespace test::keyvaluepairs
|
@ -43,6 +43,7 @@ function ObjLoadingTests:project()
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Catch2Common:include(includes)
|
||||
ObjCommonTestUtils:include(includes)
|
||||
ParserTestUtils:include(includes)
|
||||
ObjLoading:include(includes)
|
||||
@ -52,5 +53,6 @@ function ObjLoadingTests:project()
|
||||
links:linkto(ParserTestUtils)
|
||||
links:linkto(ObjLoading)
|
||||
links:linkto(catch2)
|
||||
links:linkto(Catch2Common)
|
||||
links:linkall()
|
||||
end
|
||||
|
@ -43,6 +43,7 @@ function ParserTests:project()
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Catch2Common:include(includes)
|
||||
ParserTestUtils:include(includes)
|
||||
Parser:include(includes)
|
||||
catch2:include(includes)
|
||||
@ -50,5 +51,6 @@ function ParserTests:project()
|
||||
links:linkto(ParserTestUtils)
|
||||
links:linkto(Parser)
|
||||
links:linkto(catch2)
|
||||
links:linkto(Catch2Common)
|
||||
links:linkall()
|
||||
end
|
||||
|
@ -43,6 +43,7 @@ function ZoneCodeGeneratorLibTests:project()
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Catch2Common:include(includes)
|
||||
ZoneCodeGeneratorLib:include(includes)
|
||||
ParserTestUtils:include(includes)
|
||||
catch2:include(includes)
|
||||
@ -50,5 +51,6 @@ function ZoneCodeGeneratorLibTests:project()
|
||||
links:linkto(ZoneCodeGeneratorLib)
|
||||
links:linkto(ParserTestUtils)
|
||||
links:linkto(catch2)
|
||||
links:linkto(Catch2Common)
|
||||
links:linkall()
|
||||
end
|
||||
|
@ -45,6 +45,7 @@ function ZoneCommonTests:project()
|
||||
}
|
||||
|
||||
self:include(includes)
|
||||
Catch2Common:include(includes)
|
||||
ObjCommonTestUtils:include(includes)
|
||||
ZoneCommon:include(includes)
|
||||
catch2:include(includes)
|
||||
@ -52,6 +53,7 @@ function ZoneCommonTests:project()
|
||||
links:linkto(ObjCommonTestUtils)
|
||||
links:linkto(ZoneCommon)
|
||||
links:linkto(catch2)
|
||||
links:linkto(Catch2Common)
|
||||
links:linkall()
|
||||
|
||||
ZoneCode:use()
|
||||
|
1
thirdparty/catch2.lua
vendored
1
thirdparty/catch2.lua
vendored
@ -49,6 +49,7 @@ function catch2:project()
|
||||
|
||||
defines {
|
||||
"DO_NOT_USE_WMAIN",
|
||||
"CATCH_AMALGAMATED_CUSTOM_MAIN",
|
||||
"_CRT_SECURE_NO_WARNINGS"
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user