148 lines
3.3 KiB
C++
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
|