Skip to content

Commit

Permalink
COMMON: Add the possibility to deflate without knowing the output size
Browse files Browse the repository at this point in the history
This is needed for scaleform gfx files
  • Loading branch information
Nostritius committed May 12, 2018
1 parent 8ddab02 commit 04ce40c
Show file tree
Hide file tree
Showing 2 changed files with 96 additions and 12 deletions.
81 changes: 69 additions & 12 deletions src/common/deflate.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -29,43 +29,48 @@
#include "src/common/deflate.h"
#include "src/common/error.h"
#include "src/common/scopedptr.h"
#include "src/common/ptrvector.h"
#include "src/common/memreadstream.h"

namespace Common {

byte *decompressDeflate(const byte *data, size_t inputSize,
size_t outputSize, int windowBits) {

ScopedArray<byte> decompressedData(new byte[outputSize]);

static void initZStream(z_stream &strm, int windowBits, size_t size, const byte *data) {
/* Initialize the zlib data stream for decompression with our input data.
*
* This ugly const cast is necessary because the zlib API wants a non-const
* next_in pointer by default. Unless we define ZLIB_CONST, but that only
* appeared in zlib 1.2.5.3. Not really worth bumping our required zlib
* version for, IMHO. */

z_stream strm;
strm.zalloc = Z_NULL;
strm.zfree = Z_NULL;
strm.opaque = Z_NULL;
strm.avail_in = inputSize;

strm.avail_in = size;
strm.next_in = const_cast<byte *>(data);

int zResult = inflateInit2(&strm, windowBits);
if (zResult != Z_OK)
throw Exception("Could not initialize zlib inflate: %s (%d)", zError(zResult), zResult);
}

byte *decompressDeflate(const byte *data, size_t inputSize,
size_t outputSize, int windowBits) {

ScopedArray<byte> decompressedData(new byte[outputSize]);

z_stream strm;
BOOST_SCOPE_EXIT( (&strm) ) {
inflateEnd(&strm);
inflateEnd(&strm);
} BOOST_SCOPE_EXIT_END

if (zResult != Z_OK)
throw Exception("Could not initialize zlib inflate: %s (%d)", zError(zResult), zResult);
initZStream(strm, windowBits, inputSize, data);

// Set the output data pointer and size
strm.avail_out = outputSize;
strm.next_out = decompressedData.get();

// Decompress. Z_FINISH, because we want to decompress the whole thing in one go.
zResult = inflate(&strm, Z_FINISH);
int zResult = inflate(&strm, Z_FINISH);

// Was the end of the input stream correctly reached?
if ((zResult != Z_STREAM_END) || (strm.avail_out != 0)) {
Expand All @@ -81,6 +86,46 @@ byte *decompressDeflate(const byte *data, size_t inputSize,
return decompressedData.release();
}

byte *decompressDeflateWithoutOutputSize(const byte *data, size_t inputSize, size_t &outputSize,
int windowBits, unsigned int frameSize) {
z_stream strm;
BOOST_SCOPE_EXIT( (&strm) ) {
inflateEnd(&strm);
} BOOST_SCOPE_EXIT_END

initZStream(strm, windowBits, inputSize, data);

Common::PtrVector<byte, Common::DeallocatorArray> buffers;

int zResult = 0;
do {
buffers.push_back(new byte[frameSize]);

// Set the output data pointer and size
strm.avail_out = frameSize;
strm.next_out = buffers.back();

// Decompress. Z_FULL_FLUSH, because we want to decompress partwise.
zResult = inflate(&strm, Z_FULL_FLUSH);
if (zResult != Z_STREAM_END && zResult != Z_OK)
throw Exception("Failed to inflate: %s (%d)", zError(zResult), zResult);
} while (strm.avail_in != 0);

if (zResult != Z_STREAM_END)
throw Exception("Failed to inflate: %s (%d)", zError(zResult), zResult);

ScopedArray<byte> decompressedData(new byte[strm.total_out]);
for (size_t i = 0; i < buffers.size(); ++i) {
if (i == buffers.size() - 1)
std::memcpy(decompressedData.get() + i * frameSize, buffers[i], strm.total_out % frameSize);
else
std::memcpy(decompressedData.get() + i * frameSize, buffers[i], frameSize);
}

outputSize = strm.total_out;
return decompressedData.release();
}

SeekableReadStream *decompressDeflate(ReadStream &input, size_t inputSize,
size_t outputSize, int windowBits) {

Expand All @@ -93,4 +138,16 @@ SeekableReadStream *decompressDeflate(ReadStream &input, size_t inputSize,
return new MemoryReadStream(decompressedData, outputSize, true);
}

SeekableReadStream *decompressDeflateWithoutOutputSize(ReadStream &input, size_t inputSize,
int windowBits, unsigned int frameSize) {
ScopedArray<byte> compressedData(new byte[inputSize]);
if (input.read(compressedData.get(), inputSize) != inputSize)
throw Exception(kReadError);

size_t size = 0;
byte *decompressedData = decompressDeflateWithoutOutputSize(compressedData.get(), inputSize, size, windowBits, frameSize);

return new MemoryReadStream(decompressedData, size, true);
}

} // End of namespace Common
27 changes: 27 additions & 0 deletions src/common/deflate.h
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,20 @@ static const int kWindowBitsMaxRaw = -kWindowBitsMax;
byte *decompressDeflate(const byte *data, size_t inputSize,
size_t outputSize, int windowBits);

/** Decompress (inflate) using zlib's DEFLATE algorithm without knowing the output size.
*
* @param data The compressed input data.
* @param inputSize The size of the input data in bytes.
* @param outputSize The size of the decompressed output data.
* @param windowBits The base two logarithm of the window size (the size of
* the history buffer). See the zlib documentation on
* inflateInit2() for details.
* @param frameSize The size of the extracted frames, defaults to 4096.
* @return The decompressed data
*/
byte *decompressDeflateWithoutOutputSize(const byte *data, size_t inputSize, size_t &outputSize,
int windowBits, unsigned int frameSize = 4096);

/** Decompress (inflate) using zlib's DEFLATE algorithm.
*
* @param input The compressed input data.
Expand All @@ -73,6 +87,19 @@ byte *decompressDeflate(const byte *data, size_t inputSize,
SeekableReadStream *decompressDeflate(ReadStream &input, size_t inputSize,
size_t outputSize, int windowBits);

/** Decompress (inflate) using zlib's DEFLATE algorithm without knowing the output size.
*
* @param input The compressed input data.
* @param inputSize The size of the input data to read in bytes.
* @param windowBits The base two logarithm of the window size (the size of
* the history buffer). See the zlib documentation on
* inflateInit2() for details.
* @param frameSize The size of the extracted frames, defaults to 4096.
* @return A stream of the decompressed data.
*/
SeekableReadStream *decompressDeflateWithoutOutputSize(ReadStream &input, size_t inputSize,
int windowBits, unsigned int frameSize = 4096);

} // End of namespace Common

#endif // COMMON_DEFLATE_H

0 comments on commit 04ce40c

Please sign in to comment.