From 1058ee7881feafc37e139a8d898d156472f25ed3 Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 22 Mar 2021 09:49:42 +0100 Subject: [PATCH] Write null memory at end of t6 fastfiles like the original linker does because the game's reader needs it for some reason --- src/ZoneCommon/Game/T6/ZoneConstantsT6.h | 3 ++ .../Game/T6/ZoneWriterFactoryT6.cpp | 28 +++++++++++++++---- .../Writing/Steps/StepAddOutputProcessor.cpp | 7 +++-- .../Writing/Steps/StepAddOutputProcessor.h | 3 +- src/ZoneWriting/Writing/Steps/StepAlign.cpp | 17 +++++++++++ src/ZoneWriting/Writing/Steps/StepAlign.h | 17 +++++++++++ .../Steps/StepRemoveOutputProcessor.cpp | 12 ++++++++ .../Writing/Steps/StepRemoveOutputProcessor.h | 14 ++++++++++ 8 files changed, 91 insertions(+), 10 deletions(-) create mode 100644 src/ZoneWriting/Writing/Steps/StepAlign.cpp create mode 100644 src/ZoneWriting/Writing/Steps/StepAlign.h create mode 100644 src/ZoneWriting/Writing/Steps/StepRemoveOutputProcessor.cpp create mode 100644 src/ZoneWriting/Writing/Steps/StepRemoveOutputProcessor.h diff --git a/src/ZoneCommon/Game/T6/ZoneConstantsT6.h b/src/ZoneCommon/Game/T6/ZoneConstantsT6.h index 9e057b4f..5f66fdbd 100644 --- a/src/ZoneCommon/Game/T6/ZoneConstantsT6.h +++ b/src/ZoneCommon/Game/T6/ZoneConstantsT6.h @@ -30,6 +30,9 @@ namespace T6 static constexpr int OFFSET_BLOCK_BIT_COUNT = 3; static constexpr block_t INSERT_BLOCK = XFILE_BLOCK_VIRTUAL; + static constexpr size_t FILE_SUFFIX_ZERO_MIN_SIZE = 0x40; + static constexpr size_t FILE_SUFFIX_ZERO_ALIGN = 0x40; + static constexpr const char* MAGIC_AUTH_HEADER = "PHEEBs71"; inline static const uint8_t SALSA20_KEY_TREYARCH[] { diff --git a/src/ZoneWriting/Game/T6/ZoneWriterFactoryT6.cpp b/src/ZoneWriting/Game/T6/ZoneWriterFactoryT6.cpp index e62ecbe4..3bf154f5 100644 --- a/src/ZoneWriting/Game/T6/ZoneWriterFactoryT6.cpp +++ b/src/ZoneWriting/Game/T6/ZoneWriterFactoryT6.cpp @@ -12,7 +12,10 @@ #include "Zone/XChunk/XChunkProcessorDeflate.h" #include "Zone/XChunk/XChunkProcessorSalsa20Encryption.h" #include "Writing/Steps/StepAddOutputProcessor.h" +#include "Writing/Steps/StepAlign.h" +#include "Writing/Steps/StepRemoveOutputProcessor.h" #include "Writing/Steps/StepWriteXBlockSizes.h" +#include "Writing/Steps/StepWriteZero.h" #include "Writing/Steps/StepWriteZoneContentToFile.h" #include "Writing/Steps/StepWriteZoneContentToMemory.h" #include "Writing/Steps/StepWriteZoneHeader.h" @@ -71,10 +74,11 @@ public: return header; } - ICapturedDataProvider* AddXChunkProcessor(const bool isEncrypted) + void AddXChunkProcessor(const bool isEncrypted, ICapturedDataProvider** dataToSignProviderPtr, OutputProcessorXChunks** xChunkProcessorPtr) const { - ICapturedDataProvider* result = nullptr; auto xChunkProcessor = std::make_unique(ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, ZoneConstants::XCHUNK_MAX_WRITE_SIZE, ZoneConstants::VANILLA_BUFFER_SIZE); + if (xChunkProcessorPtr) + *xChunkProcessorPtr = xChunkProcessor.get(); // Decompress the chunks using zlib xChunkProcessor->AddChunkProcessor(std::make_unique()); @@ -84,14 +88,16 @@ public: // If zone is encrypted, the decryption is applied before the decompression. T6 Zones always use Salsa20. auto chunkProcessorSalsa20 = std::make_unique(ZoneConstants::STREAM_COUNT, m_zone->m_name, ZoneConstants::SALSA20_KEY_TREYARCH, sizeof(ZoneConstants::SALSA20_KEY_TREYARCH)); - result = chunkProcessorSalsa20.get(); + + // If there is encryption, the signed data of the zone is the final hash blocks provided by the Salsa20 IV adaption algorithm + if (dataToSignProviderPtr) + *dataToSignProviderPtr = chunkProcessorSalsa20.get(); + xChunkProcessor->AddChunkProcessor(std::move(chunkProcessorSalsa20)); } m_writer->AddWritingStep(std::make_unique(std::move(xChunkProcessor))); - // If there is encryption, the signed data of the zone is the final hash blocks provided by the Salsa20 IV adaption algorithm - return result; } std::unique_ptr CreateWriter() @@ -110,7 +116,9 @@ public: m_writer->AddWritingStep(std::make_unique(CreateHeaderForParams(isSecure, false, isEncrypted))); // Setup loading XChunks from the zone from this point on. - auto* signatureDataProvider = AddXChunkProcessor(isEncrypted); + ICapturedDataProvider* dataToSignProvider; + OutputProcessorXChunks* xChunksProcessor; + AddXChunkProcessor(isEncrypted, &dataToSignProvider, &xChunksProcessor); // Start of the XFile struct //m_writer->AddWritingStep(std::make_unique(8)); // Skip size and externalSize fields since they are not interesting for us @@ -120,6 +128,14 @@ public: // Start of the zone content m_writer->AddWritingStep(std::make_unique(contentInMemoryPtr)); + // Stop writing in XChunks + m_writer->AddWritingStep(std::make_unique(xChunksProcessor)); + + // Pad ending with zeros like the original linker does it. The game's reader needs it for some reason. + // From my observations this is most likely the logic behind the amount of bytes: At least 0x40 bytes and aligned to the next 0x40 + m_writer->AddWritingStep(std::make_unique(ZoneConstants::FILE_SUFFIX_ZERO_MIN_SIZE)); + m_writer->AddWritingStep(std::make_unique(ZoneConstants::FILE_SUFFIX_ZERO_ALIGN, '\0')); + // Return the fully setup zoneloader return std::move(m_writer); } diff --git a/src/ZoneWriting/Writing/Steps/StepAddOutputProcessor.cpp b/src/ZoneWriting/Writing/Steps/StepAddOutputProcessor.cpp index fbcb4598..a6183119 100644 --- a/src/ZoneWriting/Writing/Steps/StepAddOutputProcessor.cpp +++ b/src/ZoneWriting/Writing/Steps/StepAddOutputProcessor.cpp @@ -6,11 +6,12 @@ StepAddOutputProcessor::StepAddOutputProcessor(std::unique_ptrAddStreamProcessor(std::move(m_stream_processor)); + stream->Flush(); + zoneWriter->AddStreamProcessor(std::move(m_stream_processor)); m_stream_processor = nullptr; } \ No newline at end of file diff --git a/src/ZoneWriting/Writing/Steps/StepAddOutputProcessor.h b/src/ZoneWriting/Writing/Steps/StepAddOutputProcessor.h index 87453c1b..638a842a 100644 --- a/src/ZoneWriting/Writing/Steps/StepAddOutputProcessor.h +++ b/src/ZoneWriting/Writing/Steps/StepAddOutputProcessor.h @@ -2,6 +2,7 @@ #include +#include "Writing/OutputStreamProcessor.h" #include "Writing/IWritingStep.h" class StepAddOutputProcessor final : public IWritingStep @@ -11,5 +12,5 @@ class StepAddOutputProcessor final : public IWritingStep public: explicit StepAddOutputProcessor(std::unique_ptr streamProcessor); - void PerformStep(ZoneWriter* zoneLoader, IWritingStream* stream) override; + void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override; }; diff --git a/src/ZoneWriting/Writing/Steps/StepAlign.cpp b/src/ZoneWriting/Writing/Steps/StepAlign.cpp new file mode 100644 index 00000000..9f8cdde7 --- /dev/null +++ b/src/ZoneWriting/Writing/Steps/StepAlign.cpp @@ -0,0 +1,17 @@ +#include "StepAlign.h" + +StepAlign::StepAlign(const size_t alignTo, const uint8_t alignValue) + : m_align_to(alignTo), + m_align_value(alignValue) +{ +} + +void StepAlign::PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) +{ + const auto pos = stream->Pos(); + const auto targetPos = (pos + m_align_to - 1) / m_align_to * m_align_to; + const auto valueCount = static_cast(targetPos - pos); + + for(auto i = 0u; i < valueCount; i++) + stream->Write(&m_align_value, sizeof(m_align_value)); +} diff --git a/src/ZoneWriting/Writing/Steps/StepAlign.h b/src/ZoneWriting/Writing/Steps/StepAlign.h new file mode 100644 index 00000000..aa730a2f --- /dev/null +++ b/src/ZoneWriting/Writing/Steps/StepAlign.h @@ -0,0 +1,17 @@ +#pragma once + +#include +#include + +#include "Writing/IWritingStep.h" + +class StepAlign final : public IWritingStep +{ + size_t m_align_to; + uint8_t m_align_value; + +public: + StepAlign(size_t alignTo, uint8_t alignValue); + + void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override; +}; diff --git a/src/ZoneWriting/Writing/Steps/StepRemoveOutputProcessor.cpp b/src/ZoneWriting/Writing/Steps/StepRemoveOutputProcessor.cpp new file mode 100644 index 00000000..a057d619 --- /dev/null +++ b/src/ZoneWriting/Writing/Steps/StepRemoveOutputProcessor.cpp @@ -0,0 +1,12 @@ +#include "StepRemoveOutputProcessor.h" + +StepRemoveOutputProcessor::StepRemoveOutputProcessor(OutputStreamProcessor* streamProcessor) + : m_stream_processor(streamProcessor) +{ +} + +void StepRemoveOutputProcessor::PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) +{ + stream->Flush(); + zoneWriter->RemoveStreamProcessor(m_stream_processor); +} diff --git a/src/ZoneWriting/Writing/Steps/StepRemoveOutputProcessor.h b/src/ZoneWriting/Writing/Steps/StepRemoveOutputProcessor.h new file mode 100644 index 00000000..7b8fc1f8 --- /dev/null +++ b/src/ZoneWriting/Writing/Steps/StepRemoveOutputProcessor.h @@ -0,0 +1,14 @@ +#pragma once + +#include "Writing/OutputStreamProcessor.h" +#include "Writing/IWritingStep.h" + +class StepRemoveOutputProcessor final : public IWritingStep +{ + OutputStreamProcessor* m_stream_processor; + +public: + explicit StepRemoveOutputProcessor(OutputStreamProcessor* streamProcessor); + + void PerformStep(ZoneWriter* zoneWriter, IWritingStream* stream) override; +};