2
0
mirror of https://github.com/Laupetin/OpenAssetTools.git synced 2025-12-27 20:41:49 +00:00

feat: dump iw4 xbox fastfile data

This commit is contained in:
Michael Oliver
2025-12-17 13:35:58 +00:00
parent bcf2b0a3ae
commit 38953d1daa
2 changed files with 155 additions and 59 deletions

View File

@@ -18,6 +18,7 @@ namespace IW4
static constexpr const char* MAGIC_IW4X = "IW4x"; static constexpr const char* MAGIC_IW4X = "IW4x";
static constexpr int ZONE_VERSION = 276; static constexpr int ZONE_VERSION = 276;
static constexpr int IW4X_ZONE_VERSION = 3; static constexpr int IW4X_ZONE_VERSION = 3;
static constexpr int ZONE_VERSION_XENON = 269;
static_assert(std::char_traits<char>::length(MAGIC_SIGNED_INFINITY_WARD) == sizeof(ZoneHeader::m_magic)); static_assert(std::char_traits<char>::length(MAGIC_SIGNED_INFINITY_WARD) == sizeof(ZoneHeader::m_magic));
static_assert(std::char_traits<char>::length(MAGIC_SIGNED_OAT) == sizeof(ZoneHeader::m_magic)); static_assert(std::char_traits<char>::length(MAGIC_SIGNED_OAT) == sizeof(ZoneHeader::m_magic));
@@ -40,6 +41,20 @@ namespace IW4
0x77, 0xCD, 0x62, 0x7D, 0x9D, 0x40, 0x26, 0x44, 0x4B, 0x3B, 0x0A, 0x89, 0x02, 0x03, 0x01, 0x00, 0x01, 0x77, 0xCD, 0x62, 0x7D, 0x9D, 0x40, 0x26, 0x44, 0x4B, 0x3B, 0x0A, 0x89, 0x02, 0x03, 0x01, 0x00, 0x01,
}; };
inline static const uint8_t RSA_PUBLIC_KEY_INFINITY_WARD_XENON[270]{
48u, 130u, 1u, 10u, 2u, 130u, 1u, 1u, 0u, 205u, 150u, 80u, 200u, 178u, 78u, 16u, 228u, 121u, 5u, 65u, 110u, 155u, 239u,
206u, 81u, 196u, 45u, 158u, 201u, 209u, 132u, 35u, 240u, 172u, 34u, 36u, 24u, 233u, 157u, 40u, 203u, 175u, 180u, 127u, 96u, 179u,
103u, 50u, 67u, 252u, 15u, 169u, 112u, 120u, 66u, 204u, 169u, 134u, 64u, 202u, 50u, 202u, 130u, 155u, 12u, 99u, 176u, 81u, 137u,
20u, 41u, 234u, 21u, 51u, 63u, 123u, 235u, 102u, 237u, 247u, 22u, 247u, 69u, 6u, 198u, 65u, 98u, 222u, 0u, 117u, 250u, 140u,
143u, 229u, 191u, 115u, 203u, 117u, 44u, 68u, 9u, 80u, 216u, 30u, 81u, 226u, 88u, 178u, 86u, 111u, 229u, 245u, 192u, 32u, 79u,
95u, 85u, 143u, 141u, 209u, 55u, 204u, 232u, 59u, 9u, 6u, 44u, 93u, 185u, 123u, 142u, 227u, 199u, 131u, 203u, 143u, 64u, 99u,
137u, 174u, 107u, 15u, 222u, 56u, 94u, 215u, 244u, 101u, 140u, 48u, 90u, 139u, 158u, 162u, 66u, 3u, 247u, 128u, 171u, 223u, 160u,
143u, 149u, 160u, 187u, 182u, 29u, 163u, 189u, 59u, 111u, 66u, 104u, 221u, 66u, 251u, 230u, 50u, 2u, 59u, 8u, 238u, 52u, 60u,
143u, 30u, 232u, 89u, 17u, 8u, 9u, 41u, 190u, 105u, 177u, 212u, 85u, 16u, 111u, 243u, 146u, 23u, 9u, 62u, 112u, 160u, 26u,
230u, 125u, 109u, 49u, 208u, 248u, 51u, 223u, 240u, 41u, 85u, 98u, 29u, 95u, 62u, 141u, 58u, 29u, 90u, 19u, 176u, 249u, 137u,
137u, 145u, 143u, 6u, 186u, 58u, 55u, 22u, 197u, 125u, 137u, 239u, 239u, 99u, 78u, 243u, 137u, 25u, 6u, 137u, 241u, 123u, 220u,
245u, 110u, 54u, 10u, 139u, 200u, 196u, 93u, 194u, 125u, 19u, 157u, 2u, 3u, 1u, 0u, 1u};
static constexpr size_t AUTHED_CHUNK_SIZE = 0x2000; static constexpr size_t AUTHED_CHUNK_SIZE = 0x2000;
static constexpr unsigned AUTHED_CHUNK_COUNT_PER_GROUP = 256; static constexpr unsigned AUTHED_CHUNK_COUNT_PER_GROUP = 256;

View File

@@ -12,6 +12,7 @@
#include "Loading/Processor/ProcessorInflate.h" #include "Loading/Processor/ProcessorInflate.h"
#include "Loading/Steps/StepAddProcessor.h" #include "Loading/Steps/StepAddProcessor.h"
#include "Loading/Steps/StepAllocXBlocks.h" #include "Loading/Steps/StepAllocXBlocks.h"
#include "Loading/Steps/StepDumpData.h"
#include "Loading/Steps/StepLoadHash.h" #include "Loading/Steps/StepLoadHash.h"
#include "Loading/Steps/StepLoadSignature.h" #include "Loading/Steps/StepLoadSignature.h"
#include "Loading/Steps/StepLoadZoneContent.h" #include "Loading/Steps/StepLoadZoneContent.h"
@@ -23,14 +24,26 @@
#include "Loading/Steps/StepVerifyMagic.h" #include "Loading/Steps/StepVerifyMagic.h"
#include "Loading/Steps/StepVerifySignature.h" #include "Loading/Steps/StepVerifySignature.h"
#include "Utils/ClassUtils.h" #include "Utils/ClassUtils.h"
#include "Utils/Endianness.h"
#include "Utils/Logging/Log.h" #include "Utils/Logging/Log.h"
#include <cassert> #include <cassert>
#include <cstring> #include <cstring>
#include <filesystem>
#include <iostream> #include <iostream>
#include <type_traits> #include <type_traits>
using namespace IW4; using namespace IW4;
namespace fs = std::filesystem;
class StepLogPosition final : public ILoadingStep
{
public:
void PerformStep(ZoneLoader& zoneLoader, ILoadingStream& stream) override
{
con::info("Stream position before dump: 0x{:X}", stream.Pos());
}
};
namespace namespace
{ {
@@ -42,9 +55,9 @@ namespace
std::optional<ZoneLoaderInspectionResultIW4> InspectZoneHeaderIw4(const ZoneHeader& header) std::optional<ZoneLoaderInspectionResultIW4> InspectZoneHeaderIw4(const ZoneHeader& header)
{ {
if (header.m_version != ZoneConstants::ZONE_VERSION)
return std::nullopt;
if (endianness::FromLittleEndian(header.m_version) == ZoneConstants::ZONE_VERSION)
{
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_IW4X, std::char_traits<char>::length(ZoneConstants::MAGIC_IW4X))) if (!memcmp(header.m_magic, ZoneConstants::MAGIC_IW4X, std::char_traits<char>::length(ZoneConstants::MAGIC_IW4X)))
{ {
if (*reinterpret_cast<const uint32_t*>(&header.m_magic[std::char_traits<char>::length(ZoneConstants::MAGIC_IW4X)]) if (*reinterpret_cast<const uint32_t*>(&header.m_magic[std::char_traits<char>::length(ZoneConstants::MAGIC_IW4X)])
@@ -101,6 +114,42 @@ namespace
.m_is_iw4x = false, .m_is_iw4x = false,
}; };
} }
}
else if (endianness::FromBigEndian(header.m_version) == ZoneConstants::ZONE_VERSION_XENON)
{
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_UNSIGNED, std::char_traits<char>::length(ZoneConstants::MAGIC_UNSIGNED)))
{
return ZoneLoaderInspectionResultIW4{
.m_generic_result =
ZoneLoaderInspectionResult{
.m_game_id = GameId::IW4,
.m_endianness = GameEndianness::BE,
.m_word_size = GameWordSize::ARCH_32,
.m_platform = GamePlatform::XBOX,
.m_is_official = false,
.m_is_signed = false,
.m_is_encrypted = false,
},
.m_is_iw4x = false,
};
}
if (!memcmp(header.m_magic, ZoneConstants::MAGIC_SIGNED_INFINITY_WARD, std::char_traits<char>::length(ZoneConstants::MAGIC_SIGNED_INFINITY_WARD)))
{
return ZoneLoaderInspectionResultIW4{
.m_generic_result =
ZoneLoaderInspectionResult{
.m_game_id = GameId::IW4,
.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 = false,
},
.m_is_iw4x = false,
};
}
}
return std::nullopt; return std::nullopt;
} }
@@ -121,17 +170,28 @@ namespace
#undef XBLOCK_DEF #undef XBLOCK_DEF
} }
std::unique_ptr<cryptography::IPublicKeyAlgorithm> SetupRsa(const bool isOfficial) std::unique_ptr<cryptography::IPublicKeyAlgorithm> SetupRsa(const bool isOfficial, const GamePlatform platform)
{ {
if (isOfficial) if (isOfficial)
{ {
auto rsa = cryptography::CreateRsa(cryptography::HashingAlgorithm::RSA_HASH_SHA256, cryptography::RsaPaddingMode::RSA_PADDING_PSS); auto rsa = cryptography::CreateRsa(cryptography::HashingAlgorithm::RSA_HASH_SHA256, cryptography::RsaPaddingMode::RSA_PADDING_PSS);
if (platform == GamePlatform::PC)
{
if (!rsa->SetKey(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD, sizeof(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD))) if (!rsa->SetKey(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD, sizeof(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD)))
{ {
con::error("Invalid public key for signature checking"); con::error("Invalid public key for signature checking");
return nullptr; return nullptr;
} }
}
else if (platform == GamePlatform::XBOX)
{
if (!rsa->SetKey(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD_XENON, sizeof(ZoneConstants::RSA_PUBLIC_KEY_INFINITY_WARD_XENON)))
{
con::error("Invalid public key for signature checking");
return nullptr;
}
}
return rsa; return rsa;
} }
@@ -151,7 +211,9 @@ namespace
return; return;
// If file is signed setup a RSA instance. // If file is signed setup a RSA instance.
auto rsa = SetupRsa(inspectResult.m_generic_result.m_is_official); auto rsa = SetupRsa(inspectResult.m_generic_result.m_is_official, inspectResult.m_generic_result.m_platform);
zoneLoader.AddLoadingStep(std::make_unique<StepLogPosition>());
zoneLoader.AddLoadingStep(step::CreateStepVerifyMagic(ZoneConstants::MAGIC_AUTH_HEADER)); zoneLoader.AddLoadingStep(step::CreateStepVerifyMagic(ZoneConstants::MAGIC_AUTH_HEADER));
zoneLoader.AddLoadingStep(step::CreateStepSkipBytes(4)); // Skip reserved zoneLoader.AddLoadingStep(step::CreateStepSkipBytes(4)); // Skip reserved
@@ -227,6 +289,11 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH
// Skip timestamp // Skip timestamp
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(8)); zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(8));
if (inspectResult->m_generic_result.m_platform == GamePlatform::XBOX)
{
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(16));
}
// Add steps for loading the auth header which also contain the signature of the zone if it is signed. // Add steps for loading the auth header which also contain the signature of the zone if it is signed.
AddAuthHeaderSteps(*inspectResult, *zoneLoader, fileName); AddAuthHeaderSteps(*inspectResult, *zoneLoader, fileName);
@@ -238,6 +305,8 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH
zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(1)); zoneLoader->AddLoadingStep(step::CreateStepSkipBytes(1));
} }
if (inspectResult->m_generic_result.m_endianness == GameEndianness::LE)
{
// Start of the XFile struct // Start of the XFile struct
zoneLoader->AddLoadingStep(step::CreateStepLoadZoneSizes()); zoneLoader->AddLoadingStep(step::CreateStepLoadZoneSizes());
zoneLoader->AddLoadingStep(step::CreateStepAllocXBlocks()); zoneLoader->AddLoadingStep(step::CreateStepAllocXBlocks());
@@ -253,6 +322,18 @@ std::unique_ptr<ZoneLoader> ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH
ZoneConstants::INSERT_BLOCK, ZoneConstants::INSERT_BLOCK,
zonePtr->Memory(), zonePtr->Memory(),
std::move(progressCallback))); std::move(progressCallback)));
}
else
{
fs::path dumpFileNamePath = fs::path(fileName).filename();
dumpFileNamePath.replace_extension(".dat");
std::string dumpFileName = dumpFileNamePath.string();
con::warn("Dumping xbox assets is not supported, making a full fastfile data dump to {}", dumpFileName);
zoneLoader->AddLoadingStep(std::make_unique<StepLogPosition>());
zoneLoader->AddLoadingStep(step::CreateStepDumpData(dumpFileName, 0xFFFFFFFF));
}
return zoneLoader; return zoneLoader;
} }