diff --git a/src/ObjCommon/SearchPath/IWD.cpp b/src/ObjCommon/SearchPath/IWD.cpp index 0d04354d..7c75c9f7 100644 --- a/src/ObjCommon/SearchPath/IWD.cpp +++ b/src/ObjCommon/SearchPath/IWD.cpp @@ -78,7 +78,8 @@ namespace const auto result = unzReadCurrentFile(m_container, &m_peek_symbol, 1u); - if (result >= 0) + // minizip returns zero at the end of the current archive entry. + if (result > 0) { m_peeked = true; return m_peek_symbol; @@ -96,21 +97,30 @@ namespace } const auto result = unzReadCurrentFile(m_container, &m_peek_symbol, 1u); - return result >= 0 ? m_peek_symbol : EOF; + return result > 0 ? m_peek_symbol : EOF; } std::streamsize xsgetn(char* ptr, std::streamsize count) override { + std::streamsize result{}; + if (m_peeked && count >= 1) { *ptr = static_cast(m_peek_symbol); + m_peeked = false; ptr++; count--; + result++; } - const auto result = unzReadCurrentFile(m_container, ptr, static_cast(count)); + if (count <= 0) + return result; - return result >= 0 ? static_cast(result) : 0; + const auto readResult = unzReadCurrentFile(m_container, ptr, static_cast(count)); + if (readResult > 0) + result += static_cast(readResult); + + return result; } pos_type seekoff(const off_type off, const std::ios_base::seekdir dir, const std::ios_base::openmode mode) override diff --git a/test/ObjCommonTests/SearchPath/IwdTests.cpp b/test/ObjCommonTests/SearchPath/IwdTests.cpp new file mode 100644 index 00000000..f4d62f0e --- /dev/null +++ b/test/ObjCommonTests/SearchPath/IwdTests.cpp @@ -0,0 +1,57 @@ +#include "OatTestPaths.h" +#include "SearchPath/IWD.h" +#include "Utils/FileToZlibWrapper.h" + +#include +#include +#include +#include +#include +#include +#include + +namespace fs = std::filesystem; + +namespace +{ + void WriteIwd(std::ostream& stream, const std::string& entryName, const std::string_view contents) + { + auto zlibFunctions = FileToZlibWrapper::CreateFunctions32ForFile(&stream); + const auto zipHandle = zipOpen2("foo.zip", 0, nullptr, &zlibFunctions); + zipOpenNewFileInZip2(zipHandle, entryName.c_str(), nullptr, nullptr, 0, nullptr, 0, nullptr, Z_DEFLATED, Z_DEFAULT_COMPRESSION, 0); + zipWriteInFileInZip(zipHandle, contents.data(), static_cast(contents.size())); + zipCloseFileInZip(zipHandle); + zipClose(zipHandle, nullptr); + } + + TEST_CASE("Iwd: Reads a peeked entry through end of stream", "[searchpath][iwd]") + { + constexpr auto ENTRY_NAME = "test.txt"; + constexpr auto CONTENTS = "IWD stream test"; + + const auto tempDirectory = oat::paths::GetTempDirectory("iwd"); + fs::create_directories(tempDirectory); + const auto iwdPath = tempDirectory / "test.iwd"; + + { + std::ofstream stream(iwdPath, std::ios::out | std::ios::binary); + WriteIwd(stream, ENTRY_NAME, CONTENTS); + stream.close(); + } + + const auto searchPath = iwd::LoadFromFile(iwdPath.string()); + REQUIRE(searchPath); + + const auto file = searchPath->Open(ENTRY_NAME); + REQUIRE(file.IsOpen()); + + auto& stream = *file.m_stream; + REQUIRE(stream.peek() == CONTENTS[0]); + + std::array buffer{}; + stream.read(buffer.data(), buffer.size()); + REQUIRE(stream.gcount() == static_cast(buffer.size())); + REQUIRE(std::string_view{buffer.data(), buffer.size()} == CONTENTS); + REQUIRE(stream.get() == std::char_traits::eof()); + } +} // namespace