winamp/Src/external_dependencies/openmpt-trunk/unarchiver/ungzip.cpp

148 lines
3.3 KiB
C++

/*
* 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