From 53b03c1fdb6f640257e2d2cfa6066f14766446ad Mon Sep 17 00:00:00 2001 From: Jan Date: Mon, 10 Feb 2020 14:17:28 +0100 Subject: [PATCH] ObjLoading: Fix loading the wrong chunkbuffer window data when trying to shift chunkbuffer --- src/ObjLoading/Game/T6/ObjLoaderT6.cpp | 2 + .../ObjContainer/IPak/IPakEntryReadStream.cpp | 111 ++++++++++++------ .../ObjContainer/IPak/IPakEntryReadStream.h | 1 + 3 files changed, 81 insertions(+), 33 deletions(-) diff --git a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp index da4cecef..61a75c36 100644 --- a/src/ObjLoading/Game/T6/ObjLoaderT6.cpp +++ b/src/ObjLoading/Game/T6/ObjLoaderT6.cpp @@ -126,7 +126,9 @@ void ObjLoaderT6::LoadImageFromIwi(T6::GfxImage* image, ISearchPath* searchPath, delete ipakStream; if (loadedTexture != nullptr) + { break; + } } } } diff --git a/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.cpp b/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.cpp index 6660cdf0..9d7ef7ae 100644 --- a/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.cpp +++ b/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.cpp @@ -38,33 +38,64 @@ IPakEntryReadStream::~IPakEntryReadStream() Close(); } -bool IPakEntryReadStream::SetChunkBufferWindow(const int64_t startPos, const size_t chunkCount) +size_t IPakEntryReadStream::ReadChunks(uint8_t* buffer, const int64_t startPos, const size_t chunkCount) const +{ + m_stream_manager_actions->StartReading(); + m_file->Goto(startPos); + const auto readSize = m_file->Read(buffer, 1, chunkCount * IPAK_CHUNK_SIZE); + m_stream_manager_actions->StopReading(); + + return readSize / IPAK_CHUNK_SIZE; +} + +bool IPakEntryReadStream::SetChunkBufferWindow(const int64_t startPos, size_t chunkCount) { // Cannot load more than IPAK_CHUNK_COUNT_PER_READ chunks without overflowing the buffer assert(chunkCount <= IPAK_CHUNK_COUNT_PER_READ); + if (chunkCount > IPAK_CHUNK_COUNT_PER_READ) + chunkCount = IPAK_CHUNK_COUNT_PER_READ; + // The start position must be aligned to IPAK_CHUNK_SIZE assert(startPos % IPAK_CHUNK_SIZE == 0); - const int64_t endPos = startPos + static_cast(chunkCount) * IPAK_CHUNK_SIZE; + if (chunkCount == 0) + { + m_buffer_start_pos = startPos; + m_buffer_end_pos = startPos; + return true; + } - uint8_t* readBuffer = m_chunk_buffer; - size_t readChunkCount = chunkCount; + const int64_t endPos = startPos + static_cast(chunkCount) * IPAK_CHUNK_SIZE; if (startPos >= m_buffer_start_pos && startPos < m_buffer_end_pos) { - if (startPos != m_buffer_start_pos) + if (m_buffer_start_pos != startPos) { + const int64_t moveEnd = endPos < m_buffer_end_pos ? endPos : m_buffer_end_pos; memmove_s(m_chunk_buffer, IPAK_CHUNK_SIZE * IPAK_CHUNK_COUNT_PER_READ, &m_chunk_buffer[startPos - m_buffer_start_pos], - static_cast(m_buffer_end_pos - startPos)); - readBuffer = &m_chunk_buffer[m_buffer_end_pos - startPos]; + static_cast(moveEnd - startPos)); + m_buffer_start_pos = startPos; } - readChunkCount = endPos > m_buffer_end_pos - ? static_cast(endPos - m_buffer_end_pos) / IPAK_CHUNK_SIZE - : 0; + if (endPos > m_buffer_end_pos) + { + const size_t readChunkCount = ReadChunks(&m_chunk_buffer[m_buffer_end_pos - startPos], + m_buffer_end_pos, + static_cast(endPos - m_buffer_end_pos) / IPAK_CHUNK_SIZE); + + m_buffer_end_pos += static_cast(readChunkCount) * IPAK_CHUNK_SIZE; + + return m_buffer_end_pos == endPos; + } + else + { + m_buffer_end_pos = endPos; + + return true; + } } else if (endPos > m_buffer_start_pos && endPos <= m_buffer_end_pos) { @@ -73,21 +104,26 @@ bool IPakEntryReadStream::SetChunkBufferWindow(const int64_t startPos, const siz m_chunk_buffer, static_cast(endPos - m_buffer_start_pos)); - readChunkCount = static_cast(m_buffer_start_pos - startPos) / IPAK_CHUNK_SIZE; + const size_t readChunkCount = ReadChunks(m_chunk_buffer, + startPos, + static_cast(m_buffer_start_pos - startPos) / IPAK_CHUNK_SIZE); + + m_buffer_start_pos = startPos; + m_buffer_end_pos = readChunkCount == (m_buffer_start_pos - startPos) / IPAK_CHUNK_SIZE + ? endPos + : startPos + static_cast(readChunkCount) * IPAK_CHUNK_SIZE; + + return m_buffer_end_pos == endPos; } - if (readChunkCount == 0) - return true; - - m_stream_manager_actions->StartReading(); - m_file->Goto(startPos); - const auto readSize = m_file->Read(readBuffer, 1, readChunkCount * IPAK_CHUNK_SIZE); - m_stream_manager_actions->StopReading(); + const size_t readChunkCount = ReadChunks(m_chunk_buffer, + startPos, + chunkCount); m_buffer_start_pos = startPos; - m_buffer_end_pos = AlignBackwards(m_buffer_start_pos + readSize, IPAK_CHUNK_SIZE); + m_buffer_end_pos = startPos + static_cast(readChunkCount) * IPAK_CHUNK_SIZE; - return readSize == readChunkCount * IPAK_CHUNK_SIZE; + return chunkCount == readChunkCount; } bool IPakEntryReadStream::ValidateBlockHeader(IPakDataBlockHeader* blockHeader) const @@ -97,11 +133,18 @@ bool IPakEntryReadStream::ValidateBlockHeader(IPakDataBlockHeader* blockHeader) printf("IPak block has more than 31 commands: %u -> Invalid\n", blockHeader->count); return false; } - if (blockHeader->offset > m_entry_size) + if (blockHeader->offset != m_file_head) { - printf("IPak block offset is larger than the entry itself: %u > %u -> Invalid\n", blockHeader->offset, - m_entry_size); - return false; + // A matching offset is only relevant if a command contains data. + for(unsigned currentCommand = 0; currentCommand < blockHeader->count; currentCommand++) + { + if(blockHeader->_commands[currentCommand].compressed == 0 + || blockHeader->_commands[currentCommand].compressed == 1) + { + printf("IPak block offset is not the file head: %u != %u -> Invalid\n", blockHeader->offset, m_file_head); + return false; + } + } } return true; @@ -148,7 +191,7 @@ bool IPakEntryReadStream::NextBlock() const auto blockOffsetInChunk = static_cast(m_pos - chunkStartPos); const size_t sizeLeftToRead = m_entry_size - m_file_head; - size_t estimatedChunksToRead = AlignForward(blockOffsetInChunk + sizeLeftToRead, IPAK_CHUNK_SIZE) + size_t estimatedChunksToRead = AlignForward(m_entry_size - static_cast(m_pos - m_base_pos), IPAK_CHUNK_SIZE) / IPAK_CHUNK_SIZE; if (estimatedChunksToRead > IPAK_CHUNK_COUNT_PER_READ) @@ -179,7 +222,7 @@ bool IPakEntryReadStream::ProcessCommand(const size_t commandSize, const int com { lzo_uint outputSize = sizeof m_decompress_buffer; const auto result = lzo1x_decompress_safe(&m_chunk_buffer[m_pos - m_buffer_start_pos], commandSize, - m_decompress_buffer, &outputSize, nullptr); + m_decompress_buffer, &outputSize, nullptr); if (result != LZO_E_OK) { @@ -207,13 +250,14 @@ bool IPakEntryReadStream::ProcessCommand(const size_t commandSize, const int com bool IPakEntryReadStream::AdvanceStream() { - if(m_current_block == nullptr || m_next_command >= m_current_block->count) + if (m_current_block == nullptr || m_next_command >= m_current_block->count) { if (!NextBlock()) return false; } - ProcessCommand(m_current_block->_commands[m_next_command].size, m_current_block->_commands[m_next_command].compressed); + ProcessCommand(m_current_block->_commands[m_next_command].size, + m_current_block->_commands[m_next_command].compressed); m_next_command++; return true; @@ -242,9 +286,10 @@ size_t IPakEntryReadStream::Read(void* buffer, const size_t elementSize, const s if (sizeToRead > m_current_command_length - m_current_command_offset) sizeToRead = m_current_command_length - m_current_command_offset; - if(sizeToRead > 0) + if (sizeToRead > 0) { - memcpy_s(&destBuffer[countRead], bufferSize - countRead, &m_current_command_buffer[m_current_command_offset], sizeToRead); + memcpy_s(&destBuffer[countRead], bufferSize - countRead, + &m_current_command_buffer[m_current_command_offset], sizeToRead); countRead += sizeToRead; m_current_command_offset += sizeToRead; m_file_offset += sizeToRead; @@ -267,13 +312,13 @@ void IPakEntryReadStream::Skip(const int64_t amount) { const size_t targetOffset = m_file_offset + static_cast(amount); - while(m_file_head < targetOffset) + while (m_file_head < targetOffset) { if (!AdvanceStream()) break; } - if(targetOffset <= m_file_head) + if (targetOffset <= m_file_head) { m_current_command_offset = m_current_command_length - (m_file_head - targetOffset); m_file_offset = targetOffset; @@ -300,7 +345,7 @@ int64_t IPakEntryReadStream::Pos() void IPakEntryReadStream::Goto(const int64_t pos) { - if(pos == 0) + if (pos == 0) { m_pos = m_base_pos; diff --git a/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.h b/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.h index 47285334..b89ca8a8 100644 --- a/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.h +++ b/src/ObjLoading/ObjContainer/IPak/IPakEntryReadStream.h @@ -44,6 +44,7 @@ class IPakEntryReadStream final : public FileAPI::IFile return num / alignTo * alignTo; } + size_t ReadChunks(uint8_t* buffer, int64_t startPos, size_t chunkCount) const; bool SetChunkBufferWindow(int64_t startPos, size_t chunkCount); bool ValidateBlockHeader(IPakDataBlockHeader* blockHeader) const; bool AdjustChunkBufferWindowForBlockHeader(IPakDataBlockHeader* blockHeader, size_t blockOffsetInChunk);