diff options
Diffstat (limited to 'source/blender/inflate/intern/BLO_inflate.c')
-rw-r--r-- | source/blender/inflate/intern/BLO_inflate.c | 431 |
1 files changed, 431 insertions, 0 deletions
diff --git a/source/blender/inflate/intern/BLO_inflate.c b/source/blender/inflate/intern/BLO_inflate.c new file mode 100644 index 00000000000..62c90fcb06a --- /dev/null +++ b/source/blender/inflate/intern/BLO_inflate.c @@ -0,0 +1,431 @@ +/** + * $Id$ + * + * ***** BEGIN GPL/BL DUAL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. The Blender + * Foundation also sells licenses for use in proprietary software under + * the Blender License. See http://www.blender.org/BL/ for information + * about this. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL/BL DUAL LICENSE BLOCK ***** + * zlib inflate decompression wrapper library + */ + +#include <stdio.h> +#include <stdlib.h> + +#include "zlib.h" + +#include "GEN_messaging.h" + +#include "BLO_readStreamGlue.h" +#include "BLO_in_de_flateHeader.h" /* used by deflate and inflate */ + +#include "BLO_inflate.h" + +// TODO use other error function +static int CHECK_ERR(int err, char *msg); + +static int CHECK_ERR(int err, char *msg) +{ + if (err != Z_OK) { +#ifndef NDEBUG + fprintf(GEN_errorstream, + "%s error: %d\n", + msg, + err); +#endif + return 1; + } + return 0; +} + +struct inflateStructType { + uInt compresSize; /* fixed compresBuf size in bytes */ + Bytef *compresBuf; /* reusable fixed size output buffer for inflate */ + struct readStreamGlueStruct *streamGlue; + struct BLO_in_de_flateHeaderStruct *streamHeader; + unsigned int streamDone; + unsigned char headerbuffer[IN_DE_FLATEHEADERSTRUCTSIZE]; + z_stream d_stream; /* decompression stream */ + char dictionary[50]; + uLong dictId; /* Adler32 value of the dictionary */ + void *endControl; +}; + +/** + * zlib inflate decompression initializer + * @retval pointer to inflate control structure + */ + BLO_inflateStructHandle +BLO_inflate_begin( + void *endControl) +{ + int err = 0; /* our own error */ + char *errmessage = "inflateInit"; + + struct inflateStructType *control; + control = malloc(sizeof(struct inflateStructType)); + if (!control) return NULL; + + control->compresSize = (100000 * 1.1) + 12; + control->compresBuf = (Bytef *)malloc(control->compresSize); + if (!control->compresBuf) { + free(control); + return NULL; + } + + control->streamGlue = NULL; + control->streamHeader = malloc(IN_DE_FLATEHEADERSTRUCTSIZE); + if (!control->streamHeader) { + free(control->compresBuf); + free(control); + return NULL; + } + + control->streamHeader->magic = 0; + control->streamHeader->compressedLength = 0; + control->streamHeader->uncompressedLength = 0; + control->streamHeader->dictionary_id = 0; + control->streamHeader->dictId = 0; + control->streamHeader->crc = 0; + control->streamDone = 0; + memset(control->headerbuffer, 0, IN_DE_FLATEHEADERSTRUCTSIZE); + control->d_stream.zalloc = (alloc_func)0; + control->d_stream.zfree = (free_func)0; + control->d_stream.opaque = (voidpf)0; + // TODO use dictionary index, this is id = 1 : + strcpy(control->dictionary, "sure this is not a number"); + + /* we need to rewire this to also return err */ + err = inflateInit(&(control->d_stream)); + err = CHECK_ERR(err, errmessage); + if (err) { + free(control->compresBuf); + free(control->streamHeader); + free(control); + return NULL; + } + + control->dictId = control->d_stream.adler; + + control->d_stream.next_out = control->compresBuf; + control->d_stream.avail_out = control->compresSize; + + control->d_stream.next_in = NULL; + control->d_stream.avail_in = 0; + + control->endControl = endControl; + return((BLO_inflateStructHandle) control); +} + +/** + * zlib inflate dataprocessor wrapper + * @param BLO_inflate Pointer to inflate control structure + * @param data Pointer to new data + * @param dataIn New data amount + * @retval streamGlueRead return value + */ + int +BLO_inflate_process( + BLO_inflateStructHandle BLO_inflate_handle, + unsigned char *data, + unsigned int dataIn) +{ + int zlib_err = 0; + int err = 0; + char *errmsg1 = "inflateSetDictionary"; + + struct inflateStructType *BLO_inflate = + (struct inflateStructType *) BLO_inflate_handle; + + if (!BLO_inflate) { + err = BRS_SETFUNCTION(BRS_INFLATE) | + BRS_SETGENERR(BRS_NULL); + return err; + } + + /* First check if we have our header filled in yet */ + if (BLO_inflate->streamHeader->compressedLength == 0) { + unsigned int processed; + if (dataIn == 0) return err; /* really need data to do anything */ + processed = ((dataIn + BLO_inflate->streamDone) <= + IN_DE_FLATEHEADERSTRUCTSIZE) + ? dataIn : IN_DE_FLATEHEADERSTRUCTSIZE; + memcpy(BLO_inflate->headerbuffer + BLO_inflate->streamDone, + data, processed); + BLO_inflate->streamDone += processed; + dataIn -= processed; + data += processed; + if (BLO_inflate->streamDone == IN_DE_FLATEHEADERSTRUCTSIZE) { + /* we have the whole header, absorb it */ + struct BLO_in_de_flateHeaderStruct *header; + uint32_t crc; + header = (struct BLO_in_de_flateHeaderStruct *) + BLO_inflate->headerbuffer; + BLO_inflate->streamHeader->compressedLength = + ntohl(header->compressedLength); + BLO_inflate->streamHeader->uncompressedLength = + ntohl(header->uncompressedLength); + BLO_inflate->streamHeader->dictId = + ntohl(header->dictId); + BLO_inflate->streamHeader->dictionary_id = + ntohl(header->dictionary_id); + crc = crc32(0L, (const Bytef *) header, + IN_DE_FLATEHEADERSTRUCTSIZE - 4); + + if (header->magic == 'B') { +#ifndef NDEBUG + fprintf(GEN_errorstream, + "BLO_in_de_flateHeaderStruct Magic confirmed\n"); +#endif + } else { +#ifndef NDEBUG + fprintf(GEN_errorstream, + "ERROR BLO_in_de_flateHeaderStruct Magic NOT confirmed\n"); +#endif + err = BRS_SETFUNCTION(BRS_INFLATE) | + BRS_SETGENERR(BRS_MAGIC); + if (BLO_inflate->streamGlue) free(BLO_inflate->streamGlue); + if (BLO_inflate->streamHeader) free(BLO_inflate->streamHeader); + if (BLO_inflate->compresBuf) free(BLO_inflate->compresBuf); + free(BLO_inflate); + return err; + } + + if (crc == ntohl(header->crc)) { +#ifndef NDEBUG + fprintf(GEN_errorstream, + "BLO_in_de_flateHeader CRC correct\n"); +#endif + } else { +#ifndef NDEBUG + fprintf(GEN_errorstream, + "ERROR BLO_in_de_flateHeader CRC NOT correct\n"); +#endif + err = BRS_SETFUNCTION(BRS_INFLATE) | + BRS_SETGENERR(BRS_CRCHEADER); + if (BLO_inflate->streamGlue) free(BLO_inflate->streamGlue); + if (BLO_inflate->streamHeader) free(BLO_inflate->streamHeader); + if (BLO_inflate->compresBuf) free(BLO_inflate->compresBuf); + free(BLO_inflate); + return err; + } + +#ifndef NDEBUG + fprintf(GEN_errorstream, + "BLO_inflate_process gets %u compressed bytes, will be %u uncompressed\n", + (unsigned int) BLO_inflate->streamHeader->compressedLength, + (unsigned int) BLO_inflate->streamHeader->uncompressedLength); +#endif + + } + } + + /* Is there really (still) new data available ? */ + if (dataIn > 0) { + int inflateWantsToLoopAgain = 0; + BLO_inflate->d_stream.next_in = data; + BLO_inflate->d_stream.avail_in = dataIn; + do { + zlib_err = inflate(&(BLO_inflate->d_stream), Z_SYNC_FLUSH); + if (zlib_err == Z_NEED_DICT) { + // TODO we can use BLO_inflate->d_stream.adler (it has + // multiple uses) to select the dictionary to use. This is id=1 + zlib_err = inflateSetDictionary(&(BLO_inflate->d_stream), + (const Bytef*)BLO_inflate->dictionary, + strlen(BLO_inflate->dictionary)); + err = CHECK_ERR(zlib_err, errmsg1); + if (err) { + err = BRS_SETFUNCTION(BRS_INFLATE) | + BRS_SETSPECERR(BRS_INFLATEERROR); + if (BLO_inflate->streamGlue) free(BLO_inflate->streamGlue); + if (BLO_inflate->streamHeader) free(BLO_inflate->streamHeader); + if (BLO_inflate->compresBuf) free(BLO_inflate->compresBuf); + free(BLO_inflate); + return err; + } + + // go again + zlib_err = inflate(&(BLO_inflate->d_stream), Z_SYNC_FLUSH); + } + if (zlib_err == Z_STREAM_END) { +#ifndef NDEBUG + fprintf(GEN_errorstream, + "Note: inflate returned Z_STREAM_END\n"); +#endif + } else if (zlib_err != Z_OK) { +#ifndef NDEBUG + fprintf(GEN_errorstream, "Error: inflate should return Z_OK, not %d\n", zlib_err); +#endif + err = BRS_SETFUNCTION(BRS_INFLATE) | + BRS_SETSPECERR(BRS_INFLATEERROR); + if (BLO_inflate->streamGlue) free(BLO_inflate->streamGlue); + if (BLO_inflate->streamHeader) free(BLO_inflate->streamHeader); + if (BLO_inflate->compresBuf) free(BLO_inflate->compresBuf); + free(BLO_inflate); + return err; + } + if (BLO_inflate->d_stream.avail_out == 0) { +#ifndef NDEBUG + fprintf(GEN_errorstream, + "Note: inflate needs more output space, loop again %u\n", + (unsigned int) BLO_inflate->d_stream.avail_in); +#endif + inflateWantsToLoopAgain = 1; + } else { +#ifndef NDEBUG + if (inflateWantsToLoopAgain == 1) + fprintf(GEN_errorstream, + "Note: this is inflates last loop\n"); +#endif + inflateWantsToLoopAgain = 0; + +#ifndef NDEBUG + fprintf(GEN_errorstream, "inflated %u to %u (flushes) err=%d\n", + dataIn, + (unsigned int) (BLO_inflate->compresSize - BLO_inflate->d_stream.avail_out), + err); +#endif + } + + // give data to streamGlueRead, it will find out what to do next + err = readStreamGlue( + BLO_inflate->endControl, + &(BLO_inflate->streamGlue), + BLO_inflate->compresBuf, + BLO_inflate->compresSize - BLO_inflate->d_stream.avail_out); + BLO_inflate->d_stream.next_out = BLO_inflate->compresBuf; + BLO_inflate->d_stream.avail_out = BLO_inflate->compresSize; + } while (inflateWantsToLoopAgain == 1); + } + return err; +} + +/** + * zlib inflate final call and cleanup + * @param BLO_inflate Pointer to inflate control structure + * @retval streamGlueRead return value + */ + int +BLO_inflate_end( + BLO_inflateStructHandle BLO_inflate_handle) +{ + char *errmsg2 = "inflateEnd"; + int err = 0; + int zlib_err = 0; + struct inflateStructType *BLO_inflate = + (struct inflateStructType *) BLO_inflate_handle; + // TODO perhaps check streamHeader->totalStreamLength + + if (!BLO_inflate) { + err = BRS_SETFUNCTION(BRS_INFLATE) | + BRS_SETGENERR(BRS_NULL); + return err; + } + + BLO_inflate->d_stream.avail_in = 0; + // Note: do not also set BLO_inflate->d_stream.next_in to NULL, it + // is illegal (zlib.h:374) and causes a Z_STREAM_ERROR + + zlib_err = inflate(&(BLO_inflate->d_stream), Z_FINISH); + if (zlib_err != Z_STREAM_END) { +#ifdef NDEBUG + fprintf(GEN_errorstream, + "inflate should report Z_STREAM_END, not %d\n", + err); + + if (BLO_inflate->d_stream.avail_out == 0) { + fprintf(GEN_errorstream, + "Error: inflate wanted more output buffer space\n"); + // Note that we CANNOT inflate-loop again ! + // But this should never happen because we Z_SYNC_FLUSH + } +#endif + err = BRS_SETFUNCTION(BRS_INFLATE) | + BRS_SETSPECERR(BRS_INFLATEERROR); + if (BLO_inflate->streamGlue) free(BLO_inflate->streamGlue); + if (BLO_inflate->streamHeader) free(BLO_inflate->streamHeader); + if (BLO_inflate->compresBuf) free(BLO_inflate->compresBuf); + free(BLO_inflate); + return err; + } + + zlib_err = inflateEnd(&(BLO_inflate->d_stream)); + err = CHECK_ERR(zlib_err, errmsg2); + if (err) { + err = BRS_SETFUNCTION(BRS_INFLATE) | + BRS_SETSPECERR(BRS_INFLATEERROR); + if (BLO_inflate->streamGlue) free(BLO_inflate->streamGlue); + if (BLO_inflate->streamHeader) free(BLO_inflate->streamHeader); + if (BLO_inflate->compresBuf) free(BLO_inflate->compresBuf); + free(BLO_inflate); + return err; + } + + if (BLO_inflate->d_stream.adler != BLO_inflate->dictId) { + // data was corrupted +#ifndef NDEBUG + fprintf(GEN_errorstream, + "Failed adler checksum\n"); +#endif + err = BRS_SETFUNCTION(BRS_INFLATE) | + BRS_SETGENERR(BRS_CRCDATA); + if (BLO_inflate->streamGlue) free(BLO_inflate->streamGlue); + if (BLO_inflate->streamHeader) free(BLO_inflate->streamHeader); + if (BLO_inflate->compresBuf) free(BLO_inflate->compresBuf); + free(BLO_inflate); + return err; + } else { +#ifndef NDEBUG + fprintf(GEN_errorstream, + "Passed adler checksum\n"); +#endif + } + + /* ready decompressing */ +#ifndef NDEBUG + fprintf(GEN_errorstream, + "DeCompressed %ld bytes to %ld (%.0f%%)\n", + BLO_inflate->d_stream.total_in, BLO_inflate->d_stream.total_out, + 100. * (float)BLO_inflate->d_stream.total_out / + (float)BLO_inflate->d_stream.total_in); +#endif + + err = readStreamGlue( + BLO_inflate->endControl, + &(BLO_inflate->streamGlue), + BLO_inflate->compresBuf, + BLO_inflate->compresSize - BLO_inflate->d_stream.avail_out); + + BLO_inflate->d_stream.next_out = BLO_inflate->compresBuf; + BLO_inflate->d_stream.avail_out = BLO_inflate->compresSize; + + if (BLO_inflate->streamGlue) free(BLO_inflate->streamGlue); + if (BLO_inflate->streamHeader) free(BLO_inflate->streamHeader); + if (BLO_inflate->compresBuf) free(BLO_inflate->compresBuf); + free(BLO_inflate); + + return err; +} + |