mirror of
https://github.com/Laupetin/OpenAssetTools.git
synced 2025-09-10 02:27:27 +00:00
feat: dump t6 xbox fastfile data
This commit is contained in:
@@ -20,6 +20,7 @@ function ZoneCommon:link(links)
|
||||
links:linkto(ObjCommon)
|
||||
links:linkto(Parser)
|
||||
links:linkto(Utils)
|
||||
links:linkto(lzx)
|
||||
end
|
||||
|
||||
function ZoneCommon:use()
|
||||
@@ -45,5 +46,6 @@ function ZoneCommon:project()
|
||||
path.join(folder, "ZoneCommon/**.cpp")
|
||||
}
|
||||
|
||||
lzx:include(includes)
|
||||
self:include(includes)
|
||||
end
|
||||
|
@@ -1,12 +1,132 @@
|
||||
#include "XChunkProcessorLzxDecompress.h"
|
||||
|
||||
#include <cstring>
|
||||
#include <format>
|
||||
#include <iostream>
|
||||
#include <lzx.h>
|
||||
|
||||
size_t
|
||||
XChunkProcessorLzxDecompress::Process(unsigned streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize)
|
||||
namespace
|
||||
{
|
||||
// TODO: Implement
|
||||
uint8_t NextByte(const uint8_t* input, size_t& offset, size_t& remainingSize)
|
||||
{
|
||||
const auto value = input[offset];
|
||||
offset++;
|
||||
remainingSize--;
|
||||
return value;
|
||||
}
|
||||
|
||||
memcpy(output, input, inputLength);
|
||||
return inputLength;
|
||||
uint16_t CombineHighLow(const uint8_t highByte, const uint8_t lowByte)
|
||||
{
|
||||
return static_cast<uint16_t>(static_cast<uint16_t>(static_cast<uint16_t>(highByte) << 8u) | static_cast<uint16_t>(lowByte));
|
||||
}
|
||||
|
||||
void LogErrorHeaderSpace(size_t remainingInputSize)
|
||||
{
|
||||
std::cerr << std::format("XMemCompress: Not enough data for header: {}\n", remainingInputSize);
|
||||
}
|
||||
} // namespace
|
||||
|
||||
XChunkProcessorLzxDecompress::XChunkProcessorLzxDecompress(const unsigned streamCount)
|
||||
: m_lzx_states(streamCount)
|
||||
{
|
||||
// T6 uses 17 for window bits
|
||||
for (auto& lzxState : m_lzx_states)
|
||||
lzxState = lzx_init(17);
|
||||
}
|
||||
|
||||
XChunkProcessorLzxDecompress::~XChunkProcessorLzxDecompress()
|
||||
{
|
||||
for (auto* lzxState : m_lzx_states)
|
||||
lzx_teardown(static_cast<lzx_state*>(lzxState));
|
||||
}
|
||||
|
||||
size_t XChunkProcessorLzxDecompress::Process(
|
||||
const unsigned streamNumber, const uint8_t* input, const size_t inputLength, uint8_t* output, const size_t outputBufferSize)
|
||||
{
|
||||
auto* state = static_cast<lzx_state*>(m_lzx_states[streamNumber]);
|
||||
|
||||
// lzx state is reset before each chunk
|
||||
lzx_reset(state);
|
||||
|
||||
size_t curInputOffset = 0uz;
|
||||
size_t curInputSize = inputLength;
|
||||
|
||||
size_t curOutputOffset = 0uz;
|
||||
size_t curOutputSize = outputBufferSize;
|
||||
|
||||
uint8_t lowByte;
|
||||
uint16_t dstSize, srcSize;
|
||||
|
||||
while (curInputSize > 0)
|
||||
{
|
||||
uint8_t highByte = NextByte(input, curInputOffset, curInputSize);
|
||||
|
||||
uint8_t suffixSize;
|
||||
if (highByte == 0xFF) // magic number: output is smaller than 0x8000
|
||||
{
|
||||
if (curInputSize < 4)
|
||||
{
|
||||
LogErrorHeaderSpace(curInputSize);
|
||||
return curOutputOffset;
|
||||
}
|
||||
|
||||
highByte = NextByte(input, curInputOffset, curInputSize);
|
||||
lowByte = NextByte(input, curInputOffset, curInputSize);
|
||||
dstSize = CombineHighLow(highByte, lowByte);
|
||||
|
||||
highByte = NextByte(input, curInputOffset, curInputSize);
|
||||
lowByte = NextByte(input, curInputOffset, curInputSize);
|
||||
srcSize = CombineHighLow(highByte, lowByte);
|
||||
|
||||
// The game seems to skip a 5 byte suffix after these blocks, not sure why.
|
||||
suffixSize = 5u;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (curInputSize < 1)
|
||||
{
|
||||
LogErrorHeaderSpace(curInputSize);
|
||||
return curOutputOffset;
|
||||
}
|
||||
|
||||
dstSize = 0x8000u;
|
||||
lowByte = NextByte(input, curInputOffset, curInputSize);
|
||||
srcSize = CombineHighLow(highByte, lowByte);
|
||||
|
||||
suffixSize = 0u;
|
||||
}
|
||||
|
||||
if (srcSize == 0 || dstSize == 0)
|
||||
{
|
||||
// Other implementations do not handle this as a failure, game code suggests otherwise though
|
||||
std::cerr << std::format("XMemCompress: EOF: {} {}, {}\n", srcSize, dstSize, curInputSize);
|
||||
return curOutputOffset;
|
||||
}
|
||||
|
||||
if (static_cast<size_t>(srcSize) + suffixSize > curInputSize)
|
||||
{
|
||||
std::cerr << std::format("XMemCompress: block size bigger than remaining data: {} > {}\n", srcSize, curInputSize);
|
||||
return curOutputOffset;
|
||||
}
|
||||
|
||||
if (dstSize > curOutputSize)
|
||||
{
|
||||
std::cerr << std::format("XMemCompress: output size bigger than remaining data: {} > {}\n", dstSize, curOutputSize);
|
||||
return curOutputOffset;
|
||||
}
|
||||
|
||||
auto ret = lzx_decompress(state, &input[curInputOffset], &output[curOutputOffset], srcSize, dstSize);
|
||||
curInputOffset += srcSize + suffixSize;
|
||||
curInputSize -= (srcSize + suffixSize);
|
||||
curOutputOffset += dstSize;
|
||||
curOutputSize -= srcSize;
|
||||
|
||||
if (ret != DECR_OK)
|
||||
{
|
||||
std::cerr << std::format("XMemCompress: lzx decompression failed: {}\n", ret);
|
||||
return curOutputOffset;
|
||||
}
|
||||
}
|
||||
|
||||
return curOutputOffset;
|
||||
}
|
||||
|
@@ -1,8 +1,16 @@
|
||||
#pragma once
|
||||
|
||||
#include "IXChunkProcessor.h"
|
||||
|
||||
#include <vector>
|
||||
|
||||
class XChunkProcessorLzxDecompress final : public IXChunkProcessor
|
||||
{
|
||||
public:
|
||||
explicit XChunkProcessorLzxDecompress(unsigned streamCount);
|
||||
~XChunkProcessorLzxDecompress();
|
||||
size_t Process(unsigned streamNumber, const uint8_t* input, size_t inputLength, uint8_t* output, size_t outputBufferSize) override;
|
||||
|
||||
private:
|
||||
std::vector<void*> m_lzx_states;
|
||||
};
|
||||
|
@@ -14,6 +14,7 @@ function ZoneLoading:link(links)
|
||||
links:linkto(Cryptography)
|
||||
links:linkto(Utils)
|
||||
links:linkto(ZoneCommon)
|
||||
links:linkto(lzx)
|
||||
links:linkto(zlib)
|
||||
|
||||
if os.host() == "linux" then
|
||||
@@ -55,6 +56,7 @@ function ZoneLoading:project()
|
||||
self:include(includes)
|
||||
Cryptography:include(includes)
|
||||
Utils:include(includes)
|
||||
lzx:include(includes)
|
||||
zlib:include(includes)
|
||||
ZoneCode:include(includes)
|
||||
|
||||
|
@@ -172,8 +172,18 @@ namespace
|
||||
AddXChunkProcessor(const bool isBigEndian, const bool isEncrypted, const bool isLzxCompressed, ZoneLoader& zoneLoader, std::string& fileName)
|
||||
{
|
||||
ICapturedDataProvider* result = nullptr;
|
||||
auto xChunkProcessor = processor::CreateProcessorXChunks(
|
||||
ZoneConstants::STREAM_COUNT, ZoneConstants::XCHUNK_SIZE, isBigEndian ? GameEndianness::BE : GameEndianness::LE, ZoneConstants::VANILLA_BUFFER_SIZE);
|
||||
std::unique_ptr<processor::IProcessorXChunks> xChunkProcessor;
|
||||
|
||||
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] = isBigEndian ? ZoneConstants::SALSA20_KEY_TREYARCH_XENON : ZoneConstants::SALSA20_KEY_TREYARCH_PC;
|
||||
|
||||
@@ -189,7 +199,7 @@ namespace
|
||||
if (isLzxCompressed)
|
||||
{
|
||||
// Decompress the chunks using lzx
|
||||
xChunkProcessor->AddChunkProcessor(std::make_unique<XChunkProcessorLzxDecompress>());
|
||||
xChunkProcessor->AddChunkProcessor(std::make_unique<XChunkProcessorLzxDecompress>(ZoneConstants::STREAM_COUNT));
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@@ -223,19 +223,18 @@ namespace
|
||||
}
|
||||
|
||||
const size_t readSize = m_base_stream->Load(&chunkSize, sizeof(chunkSize));
|
||||
if (m_endianness == GameEndianness::LE)
|
||||
chunkSize = endianness::FromLittleEndian(chunkSize);
|
||||
else
|
||||
chunkSize = endianness::FromBigEndian(chunkSize);
|
||||
|
||||
if (readSize == 0)
|
||||
if (readSize < sizeof(chunkSize) || chunkSize == 0)
|
||||
{
|
||||
m_eof_reached = true;
|
||||
m_eof_stream = streamNum;
|
||||
return;
|
||||
}
|
||||
|
||||
if (m_endianness == GameEndianness::LE)
|
||||
chunkSize = endianness::FromLittleEndian(chunkSize);
|
||||
else
|
||||
chunkSize = endianness::FromBigEndian(chunkSize);
|
||||
|
||||
if (chunkSize > m_chunk_size)
|
||||
{
|
||||
throw InvalidChunkSizeException(chunkSize, m_chunk_size);
|
||||
|
Reference in New Issue
Block a user