diff --git a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp index 062177fb..768f9cd9 100644 --- a/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp +++ b/src/ZoneLoading/Game/IW4/ZoneLoaderFactoryIW4.cpp @@ -19,6 +19,7 @@ #include "Loading/Steps/StepLoadZoneSizes.h" #include "Loading/Steps/StepRemoveProcessor.h" #include "Loading/Steps/StepSkipBytes.h" +#include "Loading/Steps/StepSkipZoneImageHeaders.h" #include "Loading/Steps/StepVerifyFileName.h" #include "Loading/Steps/StepVerifyHash.h" #include "Loading/Steps/StepVerifyMagic.h" @@ -280,63 +281,7 @@ std::unique_ptr ZoneLoaderFactory::CreateLoaderForHeader(const ZoneH if (inspectResult->m_generic_result.m_platform == GamePlatform::XBOX) { - - // Xbox fastfiles have additional header data before the auth header: - // - 4 bytes: language flags bitmask - // - 4 bytes: image count - // - sizeof(XAssetStreamFile) * image count (for each language in the bitmask) - // - 4 bytes: unknown - // - 4 bytes: unknown - // struct XAssetStreamFile // sizeof=0xC - // { - // unsigned int fileIndex; - // unsigned int offset; - // unsigned int offsetEnd; - // }; - - struct SkipXAssetStreamFiles : public ILoadingStep - { - void PerformStep(ZoneLoader& zoneLoader, ILoadingStream& stream) override - { - uint32_t languageFlags; - stream.Load(&languageFlags, sizeof(languageFlags)); - languageFlags = endianness::FromBigEndian(languageFlags); - - uint32_t imageCount; - stream.Load(&imageCount, sizeof(imageCount)); - imageCount = endianness::FromBigEndian(imageCount); - - // Count how many languages are set in the bitmask - uint32_t languageCount = 0; - for (int i = 0; i < 15; i++) - { - if (languageFlags & (1 << i)) - { - languageCount++; - } - } - - // Skip image stream file data (12 bytes per image per language) - const size_t imageDataSize = 12 * imageCount * languageCount; - if (imageDataSize > 0) - { - std::vector tempBuffer(std::min(imageDataSize, size_t(8192))); - size_t skipped = 0; - while (skipped < imageDataSize) - { - const size_t toSkip = std::min(imageDataSize - skipped, tempBuffer.size()); - stream.Load(tempBuffer.data(), toSkip); - skipped += toSkip; - } - } - - // Skip the final 8 bytes (2 unknown 4-byte values) - uint8_t finalBytes[8]; - stream.Load(finalBytes, sizeof(finalBytes)); - } - }; - - zoneLoader->AddLoadingStep(std::make_unique()); + zoneLoader->AddLoadingStep(step::CreateStepSkipZoneImageHeaders()); } // Add steps for loading the auth header which also contain the signature of the zone if it is signed. diff --git a/src/ZoneLoading/Loading/Steps/StepSkipZoneImageHeaders.cpp b/src/ZoneLoading/Loading/Steps/StepSkipZoneImageHeaders.cpp new file mode 100644 index 00000000..89d002d9 --- /dev/null +++ b/src/ZoneLoading/Loading/Steps/StepSkipZoneImageHeaders.cpp @@ -0,0 +1,75 @@ +#include "StepSkipZoneImageHeaders.h" + +#include "Utils/Endianness.h" + +#include +#include +#include + +namespace +{ + class StepSkipZoneImageHeaders final : public ILoadingStep + { + public: + void PerformStep(ZoneLoader& zoneLoader, ILoadingStream& stream) override + { + + // Xbox fastfiles have additional header data before the auth header: + // - 4 bytes: language flags bitmask + // - 4 bytes: image count + // - sizeof(XAssetStreamFile) * image count (for each language in the bitmask) + // - 4 bytes: unknown + // - 4 bytes: unknown + // struct XAssetStreamFile // sizeof=0xC + // { + // unsigned int fileIndex; + // unsigned int offset; + // unsigned int offsetEnd; + // }; + + uint32_t languageFlags; + stream.Load(&languageFlags, sizeof(languageFlags)); + languageFlags = endianness::FromBigEndian(languageFlags); + + uint32_t imageCount; + stream.Load(&imageCount, sizeof(imageCount)); + imageCount = endianness::FromBigEndian(imageCount); + + // Count how many languages are set in the bitmask + uint32_t languageCount = 0; + for (int i = 0; i < 15; i++) + { + if (languageFlags & (1 << i)) + { + languageCount++; + } + } + + // Skip image stream file data (12 bytes per image per language) + const size_t imageDataSize = 12 * imageCount * languageCount; + if (imageDataSize > 0) + { + std::vector tempBuffer(std::min(imageDataSize, size_t(8192))); + size_t skipped = 0; + while (skipped < imageDataSize) + { + const size_t toSkip = std::min(imageDataSize - skipped, tempBuffer.size()); + stream.Load(tempBuffer.data(), toSkip); + skipped += toSkip; + } + } + + // Skip the final 8 bytes (2 unknown 4-byte values) + uint8_t finalBytes[8]; + stream.Load(finalBytes, sizeof(finalBytes)); + } + }; +} // namespace + +namespace step +{ + std::unique_ptr CreateStepSkipZoneImageHeaders() + { + return std::make_unique(); + } +} // namespace step diff --git a/src/ZoneLoading/Loading/Steps/StepSkipZoneImageHeaders.h b/src/ZoneLoading/Loading/Steps/StepSkipZoneImageHeaders.h new file mode 100644 index 00000000..77782cad --- /dev/null +++ b/src/ZoneLoading/Loading/Steps/StepSkipZoneImageHeaders.h @@ -0,0 +1,10 @@ +#pragma once + +#include "Loading/ILoadingStep.h" + +#include + +namespace step +{ + std::unique_ptr CreateStepSkipZoneImageHeaders(); +}