1
0
mirror of https://github.com/vcmi/vcmi.git synced 2024-12-18 17:40:48 +02:00
vcmi/lib/Filesystem/CLodStream.cpp

156 lines
3.3 KiB
C++

#include "StdInc.h"
#include "CLodStream.h"
#include "CLodArchiveLoader.h"
#include "CFileInputStream.h"
#include <zlib.h>
CLodStream::CLodStream()
{
}
CLodStream::CLodStream(const CLodArchiveLoader * loader, const std::string & resourceName)
{
open(loader, resourceName);
}
void CLodStream::open(const CLodArchiveLoader * loader, const std::string & resourceName)
{
close();
const ArchiveEntry * archiveEntry = loader->getArchiveEntry(resourceName);
if(archiveEntry == nullptr)
{
throw std::runtime_error("Archive entry " + resourceName + " wasn't found in the archive " + loader->getOrigin());
}
this->archiveEntry = archiveEntry;
// Open the archive and set the read pointer to the correct position
fileStream.open(loader->getOrigin());
fileStream.seek(this->archiveEntry->offset);
}
si64 CLodStream::read(ui8 * data, si64 size)
{
// Test whether the file has to be decompressed
if(archiveEntry->size == 0)
{
// No decompression
return fileStream.read(data, size);
}
else
{
// Decompress file
// We can't decompress partially, so the size of the output buffer has to be minimum the
// size of the decompressed lod entry. If this isn't the case, it is a programming fault.
assert(size >= archiveEntry->realSize);
// Read the compressed data into a buffer
ui8 * comp = new ui8[archiveEntry->size];
fileStream.read(comp, archiveEntry->size);
// Decompress the file
if(!decompressFile(comp, archiveEntry->size, archiveEntry->realSize, data))
{
throw std::runtime_error("File decompression wasn't successful. Resource name: " + archiveEntry->name);
}
delete[] comp;
// We're reading the total size always
return archiveEntry->realSize;
}
}
bool CLodStream::decompressFile(ui8 * in, int size, int realSize, ui8 * out)
{
const int WBITS = 15;
const int FCHUNK = 50000;
int ret;
unsigned have;
z_stream strm;
int latPosOut = 0;
// Allocate inflate state
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = 0;
strm.next_in = Z_NULL;
ret = inflateInit2(&strm, WBITS);
if (ret != Z_OK)
return false;
int chunkNumber = 0;
do
{
if(size < chunkNumber * FCHUNK)
break;
strm.avail_in = std::min(FCHUNK, size - chunkNumber * FCHUNK);
if (strm.avail_in == 0)
break;
strm.next_in = in + chunkNumber * FCHUNK;
// Run inflate() on input until output buffer not full
do
{
strm.avail_out = realSize - latPosOut;
strm.next_out = out + latPosOut;
ret = inflate(&strm, Z_NO_FLUSH);
bool breakLoop = false;
switch (ret)
{
case Z_STREAM_END:
breakLoop = true;
break;
case Z_NEED_DICT:
ret = Z_DATA_ERROR;
case Z_DATA_ERROR:
case Z_MEM_ERROR:
(void)inflateEnd(&strm);
return false;
}
if(breakLoop)
break;
have = realSize - latPosOut - strm.avail_out;
latPosOut += have;
} while (strm.avail_out == 0);
++chunkNumber;
} while (ret != Z_STREAM_END);
// Clean up and return
(void)inflateEnd(&strm);
return ret == Z_STREAM_END ? true : false;
}
si64 CLodStream::seek(si64 position)
{
return fileStream.seek(archiveEntry->offset + position);
}
si64 CLodStream::tell()
{
return fileStream.tell() - archiveEntry->offset;
}
si64 CLodStream::skip(si64 delta)
{
return fileStream.skip(delta);
}
si64 CLodStream::getSize()
{
return archiveEntry->realSize;
}
void CLodStream::close()
{
fileStream.close();
}