/* * ungzip.cpp * ---------- * Purpose: Implementation file for extracting modules from .gz archives * Notes : (currently none) * Authors: OpenMPT Devs * The OpenMPT source code is released under the BSD license. Read LICENSE for more details. */ #include "stdafx.h" #include "../common/FileReader.h" #include "ungzip.h" #if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ) #if defined(MPT_WITH_ZLIB) #include #elif defined(MPT_WITH_MINIZ) #include #endif #endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ OPENMPT_NAMESPACE_BEGIN #if defined(MPT_WITH_ZLIB) || defined(MPT_WITH_MINIZ) CGzipArchive::CGzipArchive(const FileReader &file) : ArchiveBase(file) { inFile.Rewind(); inFile.ReadStruct(header); // Check header data + file size if(header.magic1 != GZ_HMAGIC1 || header.magic2 != GZ_HMAGIC2 || header.method != GZ_HMDEFLATE || (header.flags & GZ_FRESERVED) != 0 || inFile.GetLength() <= sizeof(GZheader) + sizeof(GZtrailer)) { return; } ArchiveFileInfo info; info.type = ArchiveFileType::Normal; contents.push_back(info); } CGzipArchive::~CGzipArchive() { return; } bool CGzipArchive::ExtractFile(std::size_t index) { if(index >= contents.size()) { return false; } // Read trailer GZtrailer trailer; inFile.Seek(inFile.GetLength() - sizeof(GZtrailer)); inFile.ReadStruct(trailer); // Continue reading header inFile.Seek(sizeof(GZheader)); // Extra block present? (skip the extra data) if(header.flags & GZ_FEXTRA) { inFile.Skip(inFile.ReadUint16LE()); } // Filename present? (ignore) if(header.flags & GZ_FNAME) { while(inFile.ReadUint8() != 0); } // Comment present? (ignore) if(header.flags & GZ_FCOMMENT) { while(inFile.ReadUint8() != 0); } // CRC16 present? (ignore) if(header.flags & GZ_FHCRC) { inFile.Skip(2); } // Well, this is a bit small when deflated. if(!inFile.CanRead(sizeof(GZtrailer))) { return false; } try { data.reserve(inFile.BytesLeft()); } catch(...) { return false; } // Inflate! z_stream strm{}; strm.zalloc = Z_NULL; strm.zfree = Z_NULL; strm.opaque = Z_NULL; strm.avail_in = 0; strm.next_in = Z_NULL; if(inflateInit2(&strm, -15) != Z_OK) return false; int retVal = Z_OK; uint32 crc = 0; auto bytesLeft = inFile.BytesLeft() - sizeof(GZtrailer); do { std::array inBuffer, outBuffer; strm.avail_in = static_cast(std::min(static_cast(inBuffer.size()), bytesLeft)); inFile.ReadStructPartial(inBuffer, strm.avail_in); strm.next_in = reinterpret_cast(inBuffer.data()); bytesLeft -= strm.avail_in; do { strm.avail_out = static_cast(outBuffer.size()); strm.next_out = reinterpret_cast(outBuffer.data()); retVal = inflate(&strm, Z_NO_FLUSH); const auto output = mpt::as_span(outBuffer.data(), outBuffer.data() + outBuffer.size() - strm.avail_out); crc = crc32(crc, reinterpret_cast(output.data()), static_cast(output.size())); data.insert(data.end(), output.begin(), output.end()); } while(strm.avail_out == 0); } while(retVal == Z_OK && bytesLeft); inflateEnd(&strm); // Everything went OK? Check return code, number of written bytes and CRC32. return retVal == Z_STREAM_END && trailer.isize == static_cast(strm.total_out) && trailer.crc32_ == crc; } #endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ OPENMPT_NAMESPACE_END