mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2026-07-03 22:30:07 +00:00
chore: add possibility to provide loading progress callback when loading zones
This commit is contained in:
@@ -11,7 +11,6 @@
|
||||
#include "Loading/Steps/StepAllocXBlocks.h"
|
||||
#include "Loading/Steps/StepLoadZoneContent.h"
|
||||
#include "Loading/Steps/StepLoadZoneSizes.h"
|
||||
#include "Loading/Steps/StepSkipBytes.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
|
||||
#include <cassert>
|
||||
@@ -22,24 +21,6 @@ using namespace IW3;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool CanLoad(const ZoneHeader& header, bool* isSecure, bool* isOfficial)
|
||||
{
|
||||
assert(isSecure != nullptr);
|
||||
assert(isOfficial != nullptr);
|
||||
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
return false;
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
|
||||
{
|
||||
*isSecure = false;
|
||||
*isOfficial = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetupBlock(ZoneLoader& zoneLoader)
|
||||
{
|
||||
#define XBLOCK_DEF(name, type) std::make_unique<XBlock>(STR(name), name, type)
|
||||
@@ -58,13 +39,33 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
{
|
||||
bool isSecure;
|
||||
bool isOfficial;
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
return std::nullopt;
|
||||
|
||||
// Check if this file is a supported IW4 zone.
|
||||
if (!CanLoad(header, &isSecure, &isOfficial))
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
|
||||
{
|
||||
return ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::IW3,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = true,
|
||||
.m_is_signed = false,
|
||||
.m_is_encrypted = false,
|
||||
};
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeader(header);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
// Create new zone
|
||||
@@ -93,7 +94,8 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
|
||||
32u,
|
||||
ZoneConstants::OFFSET_BLOCK_BIT_COUNT,
|
||||
ZoneConstants::INSERT_BLOCK,
|
||||
zonePtr->Memory()));
|
||||
zonePtr->Memory(),
|
||||
std::move(progressCallback)));
|
||||
|
||||
return zoneLoader;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace IW3
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override;
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
} // namespace IW3
|
||||
|
||||
@@ -34,47 +34,75 @@ using namespace IW4;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool CanLoad(ZoneHeader& header, bool* isSecure, bool* isOfficial, bool* isIw4x)
|
||||
struct ZoneLoaderInspectionResultIW4
|
||||
{
|
||||
assert(isSecure != nullptr);
|
||||
assert(isOfficial != nullptr);
|
||||
assert(isIw4x != nullptr);
|
||||
ZoneLoaderInspectionResult m_generic_result;
|
||||
bool m_is_iw4x;
|
||||
};
|
||||
|
||||
std::optional<ZoneLoaderInspectionResultIW4> InspectZoneHeaderIw4(const ZoneHeader& header)
|
||||
{
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
return std::nullopt;
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_IW4X, std::char_traits<char>::length(ZoneConstants::MAGIC_IW4X)))
|
||||
{
|
||||
if (*reinterpret_cast<uint32_t*>(&header.m_magic[std::char_traits<char>::length(ZoneConstants::MAGIC_IW4X)]) == ZoneConstants::IW4X_ZONE_VERSION)
|
||||
if (*reinterpret_cast<const uint32_t*>(&header.m_magic[std::char_traits<char>::length(ZoneConstants::MAGIC_IW4X)])
|
||||
== ZoneConstants::IW4X_ZONE_VERSION)
|
||||
{
|
||||
*isSecure = false;
|
||||
*isOfficial = false;
|
||||
*isIw4x = true;
|
||||
return true;
|
||||
return ZoneLoaderInspectionResultIW4{
|
||||
.m_generic_result =
|
||||
ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::IW4,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = false,
|
||||
.m_is_signed = false,
|
||||
.m_is_encrypted = false,
|
||||
},
|
||||
.m_is_iw4x = true,
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, std::char_traits<char>::length(ZoneConstants::MAGIC_SIGNED_INFINITY_WARD)))
|
||||
{
|
||||
*isSecure = true;
|
||||
*isOfficial = true;
|
||||
*isIw4x = false;
|
||||
return true;
|
||||
return ZoneLoaderInspectionResultIW4{
|
||||
.m_generic_result =
|
||||
ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::IW4,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = true,
|
||||
.m_is_signed = true,
|
||||
.m_is_encrypted = false,
|
||||
},
|
||||
.m_is_iw4x = false,
|
||||
};
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
|
||||
{
|
||||
*isSecure = false;
|
||||
*isOfficial = true;
|
||||
*isIw4x = false;
|
||||
return true;
|
||||
return ZoneLoaderInspectionResultIW4{
|
||||
.m_generic_result =
|
||||
ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::IW4,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = false,
|
||||
.m_is_signed = false,
|
||||
.m_is_encrypted = false,
|
||||
},
|
||||
.m_is_iw4x = false,
|
||||
};
|
||||
}
|
||||
|
||||
return false;
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
void SetupBlock(ZoneLoader& zoneLoader)
|
||||
@@ -116,14 +144,14 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void AddAuthHeaderSteps(const bool isSecure, const bool isOfficial, ZoneLoader& zoneLoader, std::string& fileName)
|
||||
void AddAuthHeaderSteps(const ZoneLoaderInspectionResultIW4& inspectResult, ZoneLoader& zoneLoader, const std::string& fileName)
|
||||
{
|
||||
// Unsigned zones do not have an auth header
|
||||
if (!isSecure)
|
||||
if (!inspectResult.m_generic_result.m_is_signed)
|
||||
return;
|
||||
|
||||
// If file is signed setup a RSA instance.
|
||||
auto rsa = SetupRsa(isOfficial);
|
||||
auto rsa = SetupRsa(inspectResult.m_generic_result.m_is_official);
|
||||
|
||||
zoneLoader.AddLoadingStep(step::CreateStepVerifyMagic(ZoneConstants::MAGIC_AUTH_HEADER));
|
||||
zoneLoader.AddLoadingStep(step::CreateStepSkipBytes(4)); // Skip reserved
|
||||
@@ -165,14 +193,21 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
{
|
||||
bool isSecure;
|
||||
bool isOfficial;
|
||||
bool isIw4x;
|
||||
auto resultIw4 = InspectZoneHeaderIw4(header);
|
||||
if (!resultIw4)
|
||||
return std::nullopt;
|
||||
|
||||
// Check if this file is a supported IW4 zone.
|
||||
if (!CanLoad(header, &isSecure, &isOfficial, &isIw4x))
|
||||
return resultIw4->m_generic_result;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeaderIw4(header);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
// Create new zone
|
||||
@@ -193,11 +228,11 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(8));
|
||||
|
||||
// Add steps for loading the auth header which also contain the signature of the zone if it is signed.
|
||||
AddAuthHeaderSteps(isSecure, isOfficial, *zoneLoader, fileName);
|
||||
AddAuthHeaderSteps(*inspectResult, *zoneLoader, fileName);
|
||||
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
|
||||
|
||||
if (isIw4x) // IW4x has one extra byte of padding here for protection purposes
|
||||
if (inspectResult->m_is_iw4x) // IW4x has one extra byte of padding here for protection purposes
|
||||
{
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorIW4xDecryption()));
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(1));
|
||||
@@ -216,7 +251,8 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
|
||||
32u,
|
||||
ZoneConstants::OFFSET_BLOCK_BIT_COUNT,
|
||||
ZoneConstants::INSERT_BLOCK,
|
||||
zonePtr->Memory()));
|
||||
zonePtr->Memory(),
|
||||
std::move(progressCallback)));
|
||||
|
||||
return zoneLoader;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace IW4
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override;
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
} // namespace IW4
|
||||
|
||||
@@ -33,33 +33,6 @@ using namespace IW5;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool CanLoad(const ZoneHeader& header, bool* isSecure, bool* isOfficial)
|
||||
{
|
||||
assert(isSecure != nullptr);
|
||||
assert(isOfficial != nullptr);
|
||||
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, std::char_traits<char>::length(ZoneConstants::MAGIC_SIGNED_INFINITY_WARD)))
|
||||
{
|
||||
*isSecure = true;
|
||||
*isOfficial = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
|
||||
{
|
||||
*isSecure = false;
|
||||
*isOfficial = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetupBlock(ZoneLoader& zoneLoader)
|
||||
{
|
||||
#define XBLOCK_DEF(name, type) std::make_unique<XBlock>(STR(name), name, type)
|
||||
@@ -100,14 +73,14 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
void AddAuthHeaderSteps(const bool isSecure, const bool isOfficial, ZoneLoader& zoneLoader, std::string& fileName)
|
||||
void AddAuthHeaderSteps(const ZoneLoaderInspectionResult& inspectResult, ZoneLoader& zoneLoader, const std::string& fileName)
|
||||
{
|
||||
// Unsigned zones do not have an auth header
|
||||
if (!isSecure)
|
||||
if (!inspectResult.m_is_signed)
|
||||
return;
|
||||
|
||||
// If file is signed setup a RSA instance.
|
||||
auto rsa = SetupRsa(isOfficial);
|
||||
auto rsa = SetupRsa(inspectResult.m_is_official);
|
||||
|
||||
zoneLoader.AddLoadingStep(step::CreateStepVerifyMagic(ZoneConstants::MAGIC_AUTH_HEADER));
|
||||
zoneLoader.AddLoadingStep(step::CreateStepSkipBytes(4)); // Skip reserved
|
||||
@@ -149,13 +122,46 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
{
|
||||
bool isSecure;
|
||||
bool isOfficial;
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
return std::nullopt;
|
||||
|
||||
// Check if this file is a supported IW4 zone.
|
||||
if (!CanLoad(header, &isSecure, &isOfficial))
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, std::char_traits<char>::length(ZoneConstants::MAGIC_SIGNED_INFINITY_WARD)))
|
||||
{
|
||||
return ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::IW5,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = true,
|
||||
.m_is_signed = true,
|
||||
.m_is_encrypted = false,
|
||||
};
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
|
||||
{
|
||||
return ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::IW5,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = false,
|
||||
.m_is_signed = false,
|
||||
.m_is_encrypted = false,
|
||||
};
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeader(header);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
// Create new zone
|
||||
@@ -176,7 +182,7 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
|
||||
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(8));
|
||||
|
||||
// Add steps for loading the auth header which also contain the signature of the zone if it is signed.
|
||||
AddAuthHeaderSteps(isSecure, isOfficial, *zoneLoader, fileName);
|
||||
AddAuthHeaderSteps(*inspectResult, *zoneLoader, fileName);
|
||||
|
||||
zoneLoader->AddLoadingStep(step::CreateStepAddProcessor(processor::CreateProcessorInflate(ZoneConstants::AUTHED_CHUNK_SIZE)));
|
||||
|
||||
@@ -193,7 +199,8 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
|
||||
32u,
|
||||
ZoneConstants::OFFSET_BLOCK_BIT_COUNT,
|
||||
ZoneConstants::INSERT_BLOCK,
|
||||
zonePtr->Memory()));
|
||||
zonePtr->Memory(),
|
||||
std::move(progressCallback)));
|
||||
|
||||
return zoneLoader;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace IW5
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override;
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
} // namespace IW5
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
#include "Loading/Steps/StepAllocXBlocks.h"
|
||||
#include "Loading/Steps/StepLoadZoneContent.h"
|
||||
#include "Loading/Steps/StepLoadZoneSizes.h"
|
||||
#include "Loading/Steps/StepSkipBytes.h"
|
||||
#include "Utils/ClassUtils.h"
|
||||
|
||||
#include <cassert>
|
||||
@@ -22,26 +21,6 @@ using namespace T5;
|
||||
|
||||
namespace
|
||||
{
|
||||
bool CanLoad(const ZoneHeader& header, bool* isSecure, bool* isOfficial)
|
||||
{
|
||||
assert(isSecure != nullptr);
|
||||
assert(isOfficial != nullptr);
|
||||
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
|
||||
{
|
||||
*isSecure = false;
|
||||
*isOfficial = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetupBlock(ZoneLoader& zoneLoader)
|
||||
{
|
||||
#define XBLOCK_DEF(name, type) std::make_unique<XBlock>(STR(name), name, type)
|
||||
@@ -58,13 +37,34 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
{
|
||||
bool isSecure;
|
||||
bool isOfficial;
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
return std::nullopt;
|
||||
|
||||
// Check if this file is a supported IW4 zone.
|
||||
if (!CanLoad(header, &isSecure, &isOfficial))
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
|
||||
{
|
||||
return ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::T5,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
// There is no way to know whether unsigned zones are official.
|
||||
.m_is_official = false,
|
||||
.m_is_signed = false,
|
||||
.m_is_encrypted = false,
|
||||
};
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeader(header);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
// Create new zone
|
||||
@@ -93,7 +93,8 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
|
||||
32u,
|
||||
ZoneConstants::OFFSET_BLOCK_BIT_COUNT,
|
||||
ZoneConstants::INSERT_BLOCK,
|
||||
zonePtr->Memory()));
|
||||
zonePtr->Memory(),
|
||||
std::move(progressCallback)));
|
||||
|
||||
return zoneLoader;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace T5
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override;
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
} // namespace T5
|
||||
|
||||
@@ -24,11 +24,10 @@
|
||||
#include "Zone/XChunk/XChunkProcessorSalsa20Decryption.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <cstdio>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <memory>
|
||||
|
||||
using namespace T6;
|
||||
@@ -36,6 +35,130 @@ namespace fs = std::filesystem;
|
||||
|
||||
namespace
|
||||
{
|
||||
enum class ZoneCompressionTypeT6 : std::uint8_t
|
||||
{
|
||||
DEFLATE,
|
||||
LZX
|
||||
};
|
||||
|
||||
struct ZoneLoaderInspectionResultT6
|
||||
{
|
||||
ZoneLoaderInspectionResult m_generic_result;
|
||||
ZoneCompressionTypeT6 m_compression_type;
|
||||
};
|
||||
|
||||
std::optional<ZoneLoaderInspectionResultT6> InspectZoneHeaderT6(const ZoneHeader& header)
|
||||
{
|
||||
if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION_PC)
|
||||
{
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8))
|
||||
{
|
||||
return ZoneLoaderInspectionResultT6{
|
||||
.m_generic_result =
|
||||
ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::T6,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = true,
|
||||
.m_is_signed = true,
|
||||
.m_is_encrypted = true,
|
||||
},
|
||||
.m_compression_type = ZoneCompressionTypeT6::DEFLATE,
|
||||
};
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_OAT, 8))
|
||||
{
|
||||
return ZoneLoaderInspectionResultT6{
|
||||
.m_generic_result =
|
||||
ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::T6,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = false,
|
||||
.m_is_signed = true,
|
||||
.m_is_encrypted = true,
|
||||
},
|
||||
.m_compression_type = ZoneCompressionTypeT6::DEFLATE,
|
||||
};
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, 8))
|
||||
{
|
||||
return ZoneLoaderInspectionResultT6{
|
||||
.m_generic_result =
|
||||
ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::T6,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = false,
|
||||
.m_is_signed = false,
|
||||
.m_is_encrypted = true,
|
||||
},
|
||||
.m_compression_type = ZoneCompressionTypeT6::DEFLATE,
|
||||
};
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED_SERVER, 8))
|
||||
{
|
||||
return ZoneLoaderInspectionResultT6{
|
||||
.m_generic_result =
|
||||
ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::T6,
|
||||
.m_endianness = GameEndianness::LE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::PC,
|
||||
.m_is_official = true,
|
||||
.m_is_signed = false,
|
||||
.m_is_encrypted = false,
|
||||
},
|
||||
.m_compression_type = ZoneCompressionTypeT6::DEFLATE,
|
||||
};
|
||||
}
|
||||
}
|
||||
else if (endianness::FromBigEndian(header.m_version) == ZoneConstants::ZONE_VERSION_XENON)
|
||||
{
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8))
|
||||
{
|
||||
return ZoneLoaderInspectionResultT6{
|
||||
.m_generic_result =
|
||||
ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::T6,
|
||||
.m_endianness = GameEndianness::BE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::XBOX,
|
||||
.m_is_official = true,
|
||||
.m_is_signed = true,
|
||||
.m_is_encrypted = true,
|
||||
},
|
||||
.m_compression_type = ZoneCompressionTypeT6::DEFLATE,
|
||||
};
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_LZX_TREYARCH, 8))
|
||||
{
|
||||
return ZoneLoaderInspectionResultT6{
|
||||
.m_generic_result =
|
||||
ZoneLoaderInspectionResult{
|
||||
.m_game_id = GameId::T6,
|
||||
.m_endianness = GameEndianness::BE,
|
||||
.m_word_size = GameWordSize::ARCH_32,
|
||||
.m_platform = GamePlatform::XBOX,
|
||||
.m_is_official = true,
|
||||
.m_is_signed = true,
|
||||
.m_is_encrypted = true,
|
||||
},
|
||||
.m_compression_type = ZoneCompressionTypeT6::LZX,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return std::nullopt;
|
||||
}
|
||||
|
||||
GameLanguage GetZoneLanguage(const std::string& zoneName)
|
||||
{
|
||||
const auto& languagePrefixes = IGame::GetGameById(GameId::T6)->GetLanguagePrefixes();
|
||||
@@ -51,68 +174,6 @@ namespace
|
||||
return GameLanguage::LANGUAGE_NONE;
|
||||
}
|
||||
|
||||
bool CanLoad(const ZoneHeader& header, bool& isBigEndian, bool& isSecure, bool& isOfficial, bool& isEncrypted, bool& isLzxCompressed)
|
||||
{
|
||||
if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION_PC)
|
||||
{
|
||||
isBigEndian = false;
|
||||
isLzxCompressed = false;
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8))
|
||||
{
|
||||
isSecure = true;
|
||||
isOfficial = true;
|
||||
isEncrypted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_OAT, 8))
|
||||
{
|
||||
isSecure = true;
|
||||
isOfficial = false;
|
||||
isEncrypted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, 8))
|
||||
{
|
||||
isSecure = false;
|
||||
isOfficial = true;
|
||||
isEncrypted = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED_SERVER, 8))
|
||||
{
|
||||
isSecure = false;
|
||||
isOfficial = true;
|
||||
isEncrypted = false;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
else if (endianness::FromBigEndian(header.m_version) == ZoneConstants::ZONE_VERSION_XENON)
|
||||
{
|
||||
isBigEndian = true;
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_TREYARCH, 8))
|
||||
{
|
||||
isSecure = true;
|
||||
isOfficial = true;
|
||||
isEncrypted = true;
|
||||
isLzxCompressed = false;
|
||||
return true;
|
||||
}
|
||||
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_LZX_TREYARCH, 8))
|
||||
{
|
||||
isSecure = true;
|
||||
isOfficial = true;
|
||||
isEncrypted = true;
|
||||
isLzxCompressed = true;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void SetupBlock(ZoneLoader& zoneLoader)
|
||||
{
|
||||
#define XBLOCK_DEF(name, type) std::make_unique<XBlock>(STR(name), name, type)
|
||||
@@ -169,26 +230,16 @@ namespace
|
||||
return signatureLoadStepPtr;
|
||||
}
|
||||
|
||||
ICapturedDataProvider*
|
||||
AddXChunkProcessor(const bool isBigEndian, const bool isEncrypted, const bool isLzxCompressed, ZoneLoader& zoneLoader, std::string& fileName)
|
||||
ICapturedDataProvider* AddXChunkProcessor(const ZoneLoaderInspectionResultT6& inspectResult, ZoneLoader& zoneLoader, const std::string& fileName)
|
||||
{
|
||||
ICapturedDataProvider* result = nullptr;
|
||||
std::unique_ptr<processor::IProcessorXChunks> xChunkProcessor;
|
||||
auto xChunkProcessor = processor::CreateProcessorXChunks(
|
||||
ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, inspectResult.m_generic_result.m_endianness, ZoneConstants::VANILLA_BUFFER_SIZE);
|
||||
|
||||
if (isBigEndian)
|
||||
{
|
||||
xChunkProcessor = processor::CreateProcessorXChunks(
|
||||
ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, GameEndianness::BE, ZoneConstants::VANILLA_BUFFER_SIZE);
|
||||
}
|
||||
else
|
||||
{
|
||||
xChunkProcessor = processor::CreateProcessorXChunks(
|
||||
ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, GameEndianness::LE, ZoneConstants::VANILLA_BUFFER_SIZE);
|
||||
}
|
||||
const uint8_t (&salsa20Key)[32] = inspectResult.m_generic_result.m_platform == GamePlatform::XBOX ? ZoneConstants::SALSA20_KEY_TREYARCH_XENON
|
||||
: ZoneConstants::SALSA20_KEY_TREYARCH_PC;
|
||||
|
||||
const uint8_t (&salsa20Key)[32] = isBigEndian ? ZoneConstants::SALSA20_KEY_TREYARCH_XENON : ZoneConstants::SALSA20_KEY_TREYARCH_PC;
|
||||
|
||||
if (isEncrypted)
|
||||
if (inspectResult.m_generic_result.m_is_encrypted)
|
||||
{
|
||||
// If zone is encrypted, the decryption is applied before the decompression. T6 Zones always use Salsa20.
|
||||
auto chunkProcessorSalsa20 =
|
||||
@@ -197,7 +248,7 @@ namespace
|
||||
xChunkProcessor->AddChunkProcessor(std::move(chunkProcessorSalsa20));
|
||||
}
|
||||
|
||||
if (isLzxCompressed)
|
||||
if (inspectResult.m_compression_type == ZoneCompressionTypeT6::LZX)
|
||||
{
|
||||
// Decompress the chunks using lzx
|
||||
xChunkProcessor->AddChunkProcessor(std::make_unique<XChunkProcessorLzxDecompress>(ZoneConstants::STREAM_COUNT));
|
||||
@@ -215,12 +266,21 @@ namespace
|
||||
}
|
||||
} // namespace
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const
|
||||
std::optional<ZoneLoaderInspectionResult> ZoneLoaderFactory::InspectZoneHeader(const ZoneHeader& header) const
|
||||
{
|
||||
bool isBigEndian, isSecure, isOfficial, isEncrypted, isLzxCompressed;
|
||||
auto resultT6 = InspectZoneHeaderT6(header);
|
||||
if (!resultT6)
|
||||
return std::nullopt;
|
||||
|
||||
// Check if this file is a supported T6 zone.
|
||||
if (!CanLoad(header, isBigEndian, isSecure, isOfficial, isEncrypted, isLzxCompressed))
|
||||
return resultT6->m_generic_result;
|
||||
}
|
||||
|
||||
std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const
|
||||
{
|
||||
const auto inspectResult = InspectZoneHeaderT6(header);
|
||||
if (!inspectResult)
|
||||
return nullptr;
|
||||
|
||||
// Create new zone
|
||||
@@ -235,15 +295,15 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
|
||||
SetupBlock(*zoneLoader);
|
||||
|
||||
// If file is signed setup a RSA instance.
|
||||
auto rsa = isSecure ? SetupRsa(isOfficial) : nullptr;
|
||||
auto rsa = inspectResult->m_generic_result.m_is_signed ? SetupRsa(inspectResult->m_generic_result.m_is_official) : nullptr;
|
||||
|
||||
// Add steps for loading the auth header which also contain the signature of the zone if it is signed.
|
||||
ISignatureProvider* signatureProvider = AddAuthHeaderSteps(isSecure, *zoneLoader, fileName);
|
||||
ISignatureProvider* signatureProvider = AddAuthHeaderSteps(inspectResult->m_generic_result.m_is_signed, *zoneLoader, fileName);
|
||||
|
||||
// Setup loading XChunks from the zone from this point on.
|
||||
ICapturedDataProvider* signatureDataProvider = AddXChunkProcessor(isBigEndian, isEncrypted, isLzxCompressed, *zoneLoader, fileName);
|
||||
ICapturedDataProvider* signatureDataProvider = AddXChunkProcessor(*inspectResult, *zoneLoader, fileName);
|
||||
|
||||
if (!isBigEndian)
|
||||
if (inspectResult->m_generic_result.m_endianness == GameEndianness::LE)
|
||||
{
|
||||
// Start of the XFile struct
|
||||
zoneLoader->AddLoadingStep(step::CreateStepLoadZoneSizes());
|
||||
@@ -258,12 +318,11 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(ZoneHeader&
|
||||
32u,
|
||||
ZoneConstants::OFFSET_BLOCK_BIT_COUNT,
|
||||
ZoneConstants::INSERT_BLOCK,
|
||||
zonePtr->Memory()));
|
||||
zonePtr->Memory(),
|
||||
std::move(progressCallback)));
|
||||
|
||||
if (isSecure)
|
||||
{
|
||||
if (inspectResult->m_generic_result.m_is_signed)
|
||||
zoneLoader->AddLoadingStep(step::CreateStepVerifySignature(std::move(rsa), signatureProvider, signatureDataProvider));
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
|
||||
@@ -9,6 +9,9 @@ namespace T6
|
||||
class ZoneLoaderFactory final : public IZoneLoaderFactory
|
||||
{
|
||||
public:
|
||||
std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const override;
|
||||
[[nodiscard]] std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const override;
|
||||
[[nodiscard]] std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const override;
|
||||
};
|
||||
} // namespace T6
|
||||
|
||||
Reference in New Issue
Block a user