123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147 |
- /*
- * 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 <zlib.h>
- #elif defined(MPT_WITH_MINIZ)
- #include <miniz/miniz.h>
- #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<char, mpt::IO::BUFFERSIZE_SMALL> inBuffer, outBuffer;
- strm.avail_in = static_cast<uInt>(std::min(static_cast<FileReader::pos_type>(inBuffer.size()), bytesLeft));
- inFile.ReadStructPartial(inBuffer, strm.avail_in);
- strm.next_in = reinterpret_cast<Bytef *>(inBuffer.data());
- bytesLeft -= strm.avail_in;
- do
- {
- strm.avail_out = static_cast<uInt>(outBuffer.size());
- strm.next_out = reinterpret_cast<Bytef *>(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<Bytef *>(output.data()), static_cast<uInt>(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<uint32>(strm.total_out) && trailer.crc32_ == crc;
- }
- #endif // MPT_WITH_ZLIB || MPT_WITH_MINIZ
- OPENMPT_NAMESPACE_END
|