mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-11-17 18:52:06 +00:00
chore: add possibility to provide loading progress callback when loading zones
This commit is contained in:
@@ -2,10 +2,11 @@
|
||||
|
||||
#include "GameLanguage.h"
|
||||
|
||||
#include <cstdint>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
enum class GameId
|
||||
enum class GameId : std::uint8_t
|
||||
{
|
||||
IW3,
|
||||
IW4,
|
||||
@@ -18,7 +19,7 @@ enum class GameId
|
||||
|
||||
// The full uppercase names are macros in the standard lib
|
||||
// So unfortunately not usable as values in the enum
|
||||
enum class GameEndianness
|
||||
enum class GameEndianness : std::uint8_t
|
||||
{
|
||||
/* Little endian */
|
||||
LE,
|
||||
@@ -26,12 +27,19 @@ enum class GameEndianness
|
||||
BE
|
||||
};
|
||||
|
||||
enum class GameWordSize
|
||||
enum class GameWordSize : std::uint8_t
|
||||
{
|
||||
ARCH_32,
|
||||
ARCH_64
|
||||
};
|
||||
|
||||
enum class GamePlatform : std::uint8_t
|
||||
{
|
||||
PC,
|
||||
XBOX,
|
||||
PS3
|
||||
};
|
||||
|
||||
static constexpr const char* GameId_Names[]{
|
||||
"IW3",
|
||||
"IW4",
|
||||
|
||||
9
src/Common/Utils/ProgressCallback.h
Normal file
9
src/Common/Utils/ProgressCallback.h
Normal file
@@ -0,0 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstdlib>
|
||||
|
||||
class ProgressCallback
|
||||
{
|
||||
public:
|
||||
virtual void OnProgress(size_t current, size_t total) = 0;
|
||||
};
|
||||
@@ -364,7 +364,7 @@ class LinkerImpl final : public Linker
|
||||
zoneDirectory = fs::current_path();
|
||||
auto absoluteZoneDirectory = absolute(zoneDirectory).string();
|
||||
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath);
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
|
||||
@@ -12,7 +12,7 @@ void FastFileContext::Destroy()
|
||||
|
||||
result::Expected<Zone*, std::string> FastFileContext::LoadFastFile(const std::string& path)
|
||||
{
|
||||
auto zone = ZoneLoading::LoadZone(path);
|
||||
auto zone = ZoneLoading::LoadZone(path, std::nullopt);
|
||||
if (!zone)
|
||||
return result::Unexpected(std::move(zone.error()));
|
||||
|
||||
|
||||
@@ -227,7 +227,7 @@ private:
|
||||
auto absoluteZoneDirectory = absolute(std::filesystem::path(zonePath).remove_filename()).string();
|
||||
|
||||
auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath);
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
@@ -289,7 +289,7 @@ private:
|
||||
|
||||
auto searchPathsForZone = paths.GetSearchPathsForZone(absoluteZoneDirectory);
|
||||
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath);
|
||||
auto maybeZone = ZoneLoading::LoadZone(zonePath, std::nullopt);
|
||||
if (!maybeZone)
|
||||
{
|
||||
con::error("Failed to load zone \"{}\": {}", zonePath, maybeZone.error());
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
std::optional<ZoneLoaderInspectionResultIW4> InspectZoneHeaderIw4(const ZoneHeader& header)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
if (header.m_version != ZoneConstants::ZONE_VERSION)
|
||||
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,13 +318,12 @@ 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
|
||||
{
|
||||
fs::path dumpFileNamePath = fs::path(fileName).filename();
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,9 +1,30 @@
|
||||
#pragma once
|
||||
|
||||
#include "Game/IGame.h"
|
||||
#include "Utils/ProgressCallback.h"
|
||||
#include "Zone/ZoneTypes.h"
|
||||
#include "ZoneLoader.h"
|
||||
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
|
||||
struct ZoneLoaderInspectionResult
|
||||
{
|
||||
// The game this zone is created for.
|
||||
GameId m_game_id;
|
||||
// Whether the zone is meant for a little-endian or big-endian loader.
|
||||
GameEndianness m_endianness;
|
||||
// Whether the zone is meant for a 32bit or 64bit loader.
|
||||
GameWordSize m_word_size;
|
||||
// The platform this zone is for.
|
||||
GamePlatform m_platform;
|
||||
// Whether this zone is confirmed official. False if not official or unknown.
|
||||
bool m_is_official;
|
||||
// Whether this zone contains a signature confirming the identity of the creator.
|
||||
bool m_is_signed;
|
||||
// Whether this zone is encrypted.
|
||||
bool m_is_encrypted;
|
||||
};
|
||||
|
||||
class IZoneLoaderFactory
|
||||
{
|
||||
@@ -15,7 +36,10 @@ public:
|
||||
IZoneLoaderFactory& operator=(const IZoneLoaderFactory& other) = default;
|
||||
IZoneLoaderFactory& operator=(IZoneLoaderFactory&& other) noexcept = default;
|
||||
|
||||
virtual std::unique_ptr<ZoneLoader> CreateLoaderForHeader(ZoneHeader& header, std::string& fileName) const = 0;
|
||||
[[nodiscard]] virtual std::optional<ZoneLoaderInspectionResult> InspectZoneHeader(const ZoneHeader& header) const = 0;
|
||||
[[nodiscard]] virtual std::unique_ptr<ZoneLoader> CreateLoaderForHeader(const ZoneHeader& header,
|
||||
const std::string& fileName,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback) const = 0;
|
||||
|
||||
static const IZoneLoaderFactory* GetZoneLoaderFactoryForGame(GameId game);
|
||||
};
|
||||
|
||||
@@ -11,19 +11,21 @@ namespace
|
||||
const unsigned pointerBitCount,
|
||||
const unsigned offsetBlockBitCount,
|
||||
const block_t insertBlock,
|
||||
MemoryManager& memory)
|
||||
MemoryManager& memory,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback)
|
||||
: m_entry_point_factory(std::move(entryPointFactory)),
|
||||
m_pointer_bit_count(pointerBitCount),
|
||||
m_offset_block_bit_count(offsetBlockBitCount),
|
||||
m_insert_block(insertBlock),
|
||||
m_memory(memory)
|
||||
m_memory(memory),
|
||||
m_progress_callback(std::move(progressCallback))
|
||||
{
|
||||
}
|
||||
|
||||
void PerformStep(ZoneLoader& zoneLoader, ILoadingStream& stream) override
|
||||
{
|
||||
const auto inputStream =
|
||||
ZoneInputStream::Create(m_pointer_bit_count, m_offset_block_bit_count, zoneLoader.m_blocks, m_insert_block, stream, m_memory);
|
||||
const auto inputStream = ZoneInputStream::Create(
|
||||
m_pointer_bit_count, m_offset_block_bit_count, zoneLoader.m_blocks, m_insert_block, stream, m_memory, std::move(m_progress_callback));
|
||||
|
||||
const auto entryPoint = m_entry_point_factory(*inputStream);
|
||||
assert(entryPoint);
|
||||
@@ -37,6 +39,7 @@ namespace
|
||||
unsigned m_offset_block_bit_count;
|
||||
block_t m_insert_block;
|
||||
MemoryManager& m_memory;
|
||||
std::optional<std::unique_ptr<ProgressCallback>> m_progress_callback;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -46,8 +49,10 @@ namespace step
|
||||
const unsigned pointerBitCount,
|
||||
const unsigned offsetBlockBitCount,
|
||||
const block_t insertBlock,
|
||||
MemoryManager& memory)
|
||||
MemoryManager& memory,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback)
|
||||
{
|
||||
return std::make_unique<StepLoadZoneContent>(std::move(entryPointFactory), pointerBitCount, offsetBlockBitCount, insertBlock, memory);
|
||||
return std::make_unique<StepLoadZoneContent>(
|
||||
std::move(entryPointFactory), pointerBitCount, offsetBlockBitCount, insertBlock, memory, std::move(progressCallback));
|
||||
}
|
||||
} // namespace step
|
||||
|
||||
@@ -13,5 +13,6 @@ namespace step
|
||||
unsigned pointerBitCount,
|
||||
unsigned offsetBlockBitCount,
|
||||
block_t insertBlock,
|
||||
MemoryManager& memory);
|
||||
MemoryManager& memory,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback);
|
||||
}
|
||||
|
||||
@@ -50,7 +50,8 @@ namespace
|
||||
std::vector<XBlock*>& blocks,
|
||||
const block_t insertBlock,
|
||||
ILoadingStream& stream,
|
||||
MemoryManager& memory)
|
||||
MemoryManager& memory,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback)
|
||||
: m_blocks(blocks),
|
||||
m_stream(stream),
|
||||
m_memory(memory),
|
||||
@@ -58,7 +59,11 @@ namespace
|
||||
m_block_mask((std::numeric_limits<uintptr_t>::max() >> (sizeof(uintptr_t) * 8 - blockBitCount)) << (pointerBitCount - blockBitCount)),
|
||||
m_block_shift(pointerBitCount - blockBitCount),
|
||||
m_offset_mask(std::numeric_limits<uintptr_t>::max() >> (sizeof(uintptr_t) * 8 - (pointerBitCount - blockBitCount))),
|
||||
m_last_fill_size(0)
|
||||
m_last_fill_size(0),
|
||||
m_has_progress_callback(progressCallback.has_value()),
|
||||
m_progress_callback(std::move(progressCallback).value_or(nullptr)),
|
||||
m_progress_current_size(0uz),
|
||||
m_progress_total_size(0uz)
|
||||
{
|
||||
assert(pointerBitCount % 8u == 0u);
|
||||
assert(insertBlock < static_cast<block_t>(blocks.size()));
|
||||
@@ -68,6 +73,8 @@ namespace
|
||||
std::memset(m_block_offsets.get(), 0, sizeof(size_t) * blockCount);
|
||||
|
||||
m_insert_block = blocks[insertBlock];
|
||||
|
||||
m_progress_total_size = CalculateTotalSize();
|
||||
}
|
||||
|
||||
[[nodiscard]] unsigned GetPointerBitCount() const override
|
||||
@@ -444,6 +451,12 @@ namespace
|
||||
void IncBlockPos(const XBlock& block, const size_t size)
|
||||
{
|
||||
m_block_offsets[block.m_index] += size;
|
||||
|
||||
if (m_has_progress_callback)
|
||||
{
|
||||
m_progress_current_size += size;
|
||||
m_progress_callback->OnProgress(m_progress_current_size, m_progress_total_size);
|
||||
}
|
||||
}
|
||||
|
||||
void Align(const XBlock& block, const unsigned align)
|
||||
@@ -455,6 +468,16 @@ namespace
|
||||
}
|
||||
}
|
||||
|
||||
[[nodiscard]] size_t CalculateTotalSize() const
|
||||
{
|
||||
size_t result = 0uz;
|
||||
|
||||
for (const auto& block : m_blocks)
|
||||
result += block->m_buffer_size;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<XBlock*>& m_blocks;
|
||||
std::unique_ptr<size_t[]> m_block_offsets;
|
||||
|
||||
@@ -475,6 +498,11 @@ namespace
|
||||
// These lookups map a block offset to a pointer in case of a platform mismatch
|
||||
std::unordered_map<uintptr_t, void*> m_pointer_redirect_lookup;
|
||||
std::unordered_map<uintptr_t, void*> m_alias_redirect_lookup;
|
||||
|
||||
bool m_has_progress_callback;
|
||||
std::unique_ptr<ProgressCallback> m_progress_callback;
|
||||
size_t m_progress_current_size;
|
||||
size_t m_progress_total_size;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
@@ -483,7 +511,8 @@ std::unique_ptr<ZoneInputStream> ZoneInputStream::Create(const unsigned pointerB
|
||||
std::vector<XBlock*>& blocks,
|
||||
const block_t insertBlock,
|
||||
ILoadingStream& stream,
|
||||
MemoryManager& memory)
|
||||
MemoryManager& memory,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback)
|
||||
{
|
||||
return std::make_unique<XBlockInputStream>(pointerBitCount, blockBitCount, blocks, insertBlock, stream, memory);
|
||||
return std::make_unique<XBlockInputStream>(pointerBitCount, blockBitCount, blocks, insertBlock, stream, memory, std::move(progressCallback));
|
||||
}
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "Loading/Exception/InvalidLookupPositionException.h"
|
||||
#include "Loading/ILoadingStream.h"
|
||||
#include "Utils/MemoryManager.h"
|
||||
#include "Utils/ProgressCallback.h"
|
||||
#include "Zone/Stream/IZoneStream.h"
|
||||
#include "Zone/XBlock.h"
|
||||
|
||||
@@ -10,6 +11,7 @@
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <optional>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
@@ -238,6 +240,11 @@ public:
|
||||
virtual void DebugOffsets(size_t assetIndex) const = 0;
|
||||
#endif
|
||||
|
||||
static std::unique_ptr<ZoneInputStream> Create(
|
||||
unsigned pointerBitCount, unsigned blockBitCount, std::vector<XBlock*>& blocks, block_t insertBlock, ILoadingStream& stream, MemoryManager& memory);
|
||||
static std::unique_ptr<ZoneInputStream> Create(unsigned pointerBitCount,
|
||||
unsigned blockBitCount,
|
||||
std::vector<XBlock*>& blocks,
|
||||
block_t insertBlock,
|
||||
ILoadingStream& stream,
|
||||
MemoryManager& memory,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback);
|
||||
};
|
||||
|
||||
@@ -7,11 +7,11 @@
|
||||
#include <filesystem>
|
||||
#include <format>
|
||||
#include <fstream>
|
||||
#include <iostream>
|
||||
|
||||
namespace fs = std::filesystem;
|
||||
|
||||
result::Expected<std::unique_ptr<Zone>, std::string> ZoneLoading::LoadZone(const std::string& path)
|
||||
result::Expected<std::unique_ptr<Zone>, std::string> ZoneLoading::LoadZone(const std::string& path,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback)
|
||||
{
|
||||
auto zoneName = fs::path(path).filename().replace_extension().string();
|
||||
std::ifstream file(path, std::fstream::in | std::fstream::binary);
|
||||
@@ -28,7 +28,7 @@ result::Expected<std::unique_ptr<Zone>, std::string> ZoneLoading::LoadZone(const
|
||||
for (auto game = 0u; game < static_cast<unsigned>(GameId::COUNT); game++)
|
||||
{
|
||||
const auto* factory = IZoneLoaderFactory::GetZoneLoaderFactoryForGame(static_cast<GameId>(game));
|
||||
zoneLoader = factory->CreateLoaderForHeader(header, zoneName);
|
||||
zoneLoader = factory->CreateLoaderForHeader(header, zoneName, std::move(progressCallback));
|
||||
|
||||
if (zoneLoader)
|
||||
break;
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "Utils/ProgressCallback.h"
|
||||
#include "Utils/Result.h"
|
||||
#include "Zone/Zone.h"
|
||||
|
||||
@@ -8,5 +9,6 @@
|
||||
class ZoneLoading
|
||||
{
|
||||
public:
|
||||
static result::Expected<std::unique_ptr<Zone>, std::string> LoadZone(const std::string& path);
|
||||
static result::Expected<std::unique_ptr<Zone>, std::string> LoadZone(const std::string& path,
|
||||
std::optional<std::unique_ptr<ProgressCallback>> progressCallback);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user