ObjLoading: Fix loading the wrong chunkbuffer window data when trying to shift chunkbuffer

This commit is contained in:
Jan 2020-02-10 14:17:28 +01:00
parent 76712328b2
commit 53b03c1fdb
3 changed files with 81 additions and 33 deletions

View File

@ -126,7 +126,9 @@ void ObjLoaderT6::LoadImageFromIwi(T6::GfxImage* image, ISearchPath* searchPath,
delete ipakStream; delete ipakStream;
if (loadedTexture != nullptr) if (loadedTexture != nullptr)
{
break; break;
}
} }
} }
} }

View File

@ -38,33 +38,64 @@ IPakEntryReadStream::~IPakEntryReadStream()
Close(); 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 // Cannot load more than IPAK_CHUNK_COUNT_PER_READ chunks without overflowing the buffer
assert(chunkCount <= IPAK_CHUNK_COUNT_PER_READ); 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 // The start position must be aligned to IPAK_CHUNK_SIZE
assert(startPos % IPAK_CHUNK_SIZE == 0); assert(startPos % IPAK_CHUNK_SIZE == 0);
const int64_t endPos = startPos + static_cast<int64_t>(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; const int64_t endPos = startPos + static_cast<int64_t>(chunkCount) * IPAK_CHUNK_SIZE;
size_t readChunkCount = chunkCount;
if (startPos >= m_buffer_start_pos && startPos < m_buffer_end_pos) 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, memmove_s(m_chunk_buffer,
IPAK_CHUNK_SIZE * IPAK_CHUNK_COUNT_PER_READ, IPAK_CHUNK_SIZE * IPAK_CHUNK_COUNT_PER_READ,
&m_chunk_buffer[startPos - m_buffer_start_pos], &m_chunk_buffer[startPos - m_buffer_start_pos],
static_cast<size_t>(m_buffer_end_pos - startPos)); static_cast<size_t>(moveEnd - startPos));
readBuffer = &m_chunk_buffer[m_buffer_end_pos - startPos]; m_buffer_start_pos = startPos;
} }
readChunkCount = endPos > m_buffer_end_pos if (endPos > m_buffer_end_pos)
? static_cast<size_t>(endPos - m_buffer_end_pos) / IPAK_CHUNK_SIZE {
: 0; const size_t readChunkCount = ReadChunks(&m_chunk_buffer[m_buffer_end_pos - startPos],
m_buffer_end_pos,
static_cast<size_t>(endPos - m_buffer_end_pos) / IPAK_CHUNK_SIZE);
m_buffer_end_pos += static_cast<int64_t>(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) 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, m_chunk_buffer,
static_cast<size_t>(endPos - m_buffer_start_pos)); static_cast<size_t>(endPos - m_buffer_start_pos));
readChunkCount = static_cast<size_t>(m_buffer_start_pos - startPos) / IPAK_CHUNK_SIZE; const size_t readChunkCount = ReadChunks(m_chunk_buffer,
startPos,
static_cast<size_t>(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<int64_t>(readChunkCount) * IPAK_CHUNK_SIZE;
return m_buffer_end_pos == endPos;
} }
if (readChunkCount == 0) const size_t readChunkCount = ReadChunks(m_chunk_buffer,
return true; startPos,
chunkCount);
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();
m_buffer_start_pos = startPos; m_buffer_start_pos = startPos;
m_buffer_end_pos = AlignBackwards<int64_t>(m_buffer_start_pos + readSize, IPAK_CHUNK_SIZE); m_buffer_end_pos = startPos + static_cast<int64_t>(readChunkCount) * IPAK_CHUNK_SIZE;
return readSize == readChunkCount * IPAK_CHUNK_SIZE; return chunkCount == readChunkCount;
} }
bool IPakEntryReadStream::ValidateBlockHeader(IPakDataBlockHeader* blockHeader) const 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); printf("IPak block has more than 31 commands: %u -> Invalid\n", blockHeader->count);
return false; 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, // A matching offset is only relevant if a command contains data.
m_entry_size); for(unsigned currentCommand = 0; currentCommand < blockHeader->count; currentCommand++)
return false; {
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; return true;
@ -148,7 +191,7 @@ bool IPakEntryReadStream::NextBlock()
const auto blockOffsetInChunk = static_cast<size_t>(m_pos - chunkStartPos); const auto blockOffsetInChunk = static_cast<size_t>(m_pos - chunkStartPos);
const size_t sizeLeftToRead = m_entry_size - m_file_head; 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<size_t>(m_pos - m_base_pos), IPAK_CHUNK_SIZE)
/ IPAK_CHUNK_SIZE; / IPAK_CHUNK_SIZE;
if (estimatedChunksToRead > IPAK_CHUNK_COUNT_PER_READ) 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; lzo_uint outputSize = sizeof m_decompress_buffer;
const auto result = lzo1x_decompress_safe(&m_chunk_buffer[m_pos - m_buffer_start_pos], commandSize, 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) if (result != LZO_E_OK)
{ {
@ -207,13 +250,14 @@ bool IPakEntryReadStream::ProcessCommand(const size_t commandSize, const int com
bool IPakEntryReadStream::AdvanceStream() 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()) if (!NextBlock())
return false; 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++; m_next_command++;
return true; 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) if (sizeToRead > m_current_command_length - m_current_command_offset)
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; countRead += sizeToRead;
m_current_command_offset += sizeToRead; m_current_command_offset += sizeToRead;
m_file_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<size_t>(amount); const size_t targetOffset = m_file_offset + static_cast<size_t>(amount);
while(m_file_head < targetOffset) while (m_file_head < targetOffset)
{ {
if (!AdvanceStream()) if (!AdvanceStream())
break; break;
} }
if(targetOffset <= m_file_head) if (targetOffset <= m_file_head)
{ {
m_current_command_offset = m_current_command_length - (m_file_head - targetOffset); m_current_command_offset = m_current_command_length - (m_file_head - targetOffset);
m_file_offset = targetOffset; m_file_offset = targetOffset;
@ -300,7 +345,7 @@ int64_t IPakEntryReadStream::Pos()
void IPakEntryReadStream::Goto(const int64_t pos) void IPakEntryReadStream::Goto(const int64_t pos)
{ {
if(pos == 0) if (pos == 0)
{ {
m_pos = m_base_pos; m_pos = m_base_pos;

View File

@ -44,6 +44,7 @@ class IPakEntryReadStream final : public FileAPI::IFile
return num / alignTo * alignTo; 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 SetChunkBufferWindow(int64_t startPos, size_t chunkCount);
bool ValidateBlockHeader(IPakDataBlockHeader* blockHeader) const; bool ValidateBlockHeader(IPakDataBlockHeader* blockHeader) const;
bool AdjustChunkBufferWindowForBlockHeader(IPakDataBlockHeader* blockHeader, size_t blockOffsetInChunk); bool AdjustChunkBufferWindowForBlockHeader(IPakDataBlockHeader* blockHeader, size_t blockOffsetInChunk);