1 // ==========================================================
2 // ZLib library interface
3 //
4 // Design and implementation by
5 // - Floris van den Berg (flvdberg@wxs.nl)
6 //
7 // This file is part of FreeImage 3
8 //
9 // COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, WITHOUT WARRANTY
10 // OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, WITHOUT LIMITATION, WARRANTIES
11 // THAT THE COVERED CODE IS FREE OF DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE
12 // OR NON-INFRINGING. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED
13 // CODE IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, YOU (NOT
14 // THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE COST OF ANY NECESSARY
15 // SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER OF WARRANTY CONSTITUTES AN ESSENTIAL
16 // PART OF THIS LICENSE. NO USE OF ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER
17 // THIS DISCLAIMER.
18 //
19 // Use at your own risk!
20 // ==========================================================
21 
22 #include "../ZLib/zlib.h"
23 #include "FreeImage.h"
24 #include "Utilities.h"
25 #include "../ZLib/zutil.h"	/* must be the last header because of error C3163 in VS2008 (_vsnprintf defined in stdio.h) */
26 
27 /**
28 Compresses a source buffer into a target buffer, using the ZLib library.
29 Upon entry, target_size is the total size of the destination buffer,
30 which must be at least 0.1% larger than source_size plus 12 bytes.
31 
32 @param target Destination buffer
33 @param target_size Size of the destination buffer, in bytes
34 @param source Source buffer
35 @param source_size Size of the source buffer, in bytes
36 @return Returns the actual size of the compressed buffer, returns 0 if an error occured
37 @see FreeImage_ZLibUncompress
38 */
39 DWORD DLL_CALLCONV
FreeImage_ZLibCompress(BYTE * target,DWORD target_size,BYTE * source,DWORD source_size)40 FreeImage_ZLibCompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
41 	uLongf dest_len = (uLongf)target_size;
42 
43 	int zerr = compress(target, &dest_len, source, source_size);
44 	switch(zerr) {
45 		case Z_MEM_ERROR:	// not enough memory
46 		case Z_BUF_ERROR:	// not enough room in the output buffer
47 			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
48 			return 0;
49 		case Z_OK:
50 			return dest_len;
51 	}
52 
53 	return 0;
54 }
55 
56 /**
57 Decompresses a source buffer into a target buffer, using the ZLib library.
58 Upon entry, target_size is the total size of the destination buffer,
59 which must be large enough to hold the entire uncompressed data.
60 The size of the uncompressed data must have been saved previously by the compressor
61 and transmitted to the decompressor by some mechanism outside the scope of this
62 compression library.
63 
64 @param target Destination buffer
65 @param target_size Size of the destination buffer, in bytes
66 @param source Source buffer
67 @param source_size Size of the source buffer, in bytes
68 @return Returns the actual size of the uncompressed buffer, returns 0 if an error occured
69 @see FreeImage_ZLibCompress
70 */
71 DWORD DLL_CALLCONV
FreeImage_ZLibUncompress(BYTE * target,DWORD target_size,BYTE * source,DWORD source_size)72 FreeImage_ZLibUncompress(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
73 	uLongf dest_len = (uLongf)target_size;
74 
75 	int zerr = uncompress(target, &dest_len, source, source_size);
76 	switch(zerr) {
77 		case Z_MEM_ERROR:	// not enough memory
78 		case Z_BUF_ERROR:	// not enough room in the output buffer
79 		case Z_DATA_ERROR:	// input data was corrupted
80 			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
81 			return 0;
82 		case Z_OK:
83 			return dest_len;
84 	}
85 
86 	return 0;
87 }
88 
89 /**
90 Compresses a source buffer into a target buffer, using the ZLib library.
91 On success, the target buffer contains a GZIP compatible layout.
92 Upon entry, target_size is the total size of the destination buffer,
93 which must be at least 0.1% larger than source_size plus 24 bytes.
94 
95 @param target Destination buffer
96 @param target_size Size of the destination buffer, in bytes
97 @param source Source buffer
98 @param source_size Size of the source buffer, in bytes
99 @return Returns the actual size of the compressed buffer, returns 0 if an error occured
100 @see FreeImage_ZLibCompress
101 */
102 DWORD DLL_CALLCONV
FreeImage_ZLibGZip(BYTE * target,DWORD target_size,BYTE * source,DWORD source_size)103 FreeImage_ZLibGZip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
104 	uLongf dest_len = (uLongf)target_size - 12;
105 	DWORD crc = crc32(0L, NULL, 0);
106 
107     // set up header (stolen from zlib/gzio.c)
108     sprintf((char *)target, "%c%c%c%c%c%c%c%c", 0x1f, 0x8b,
109          Z_DEFLATED, 0 /*flags*/, 0,0,0,0 /*time*/);
110     int zerr = compress2(target + 8, &dest_len, source, source_size, Z_BEST_COMPRESSION);
111 	switch(zerr) {
112 		case Z_MEM_ERROR:	// not enough memory
113 		case Z_BUF_ERROR:	// not enough room in the output buffer
114 			FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
115 			return 0;
116         case Z_OK: {
117             // patch header, setup crc and length (stolen from mod_trace_output)
118             BYTE *p = target + 8; *p++ = 2; *p = OS_CODE; // xflags, os_code
119  	        crc = crc32(crc, source, source_size);
120 	        memcpy(target + 4 + dest_len, &crc, 4);
121 	        memcpy(target + 8 + dest_len, &source_size, 4);
122             return dest_len + 12;
123         }
124 	}
125 	return 0;
126 }
127 
128 /**
129 Decompresses a gzipped source buffer into a target buffer, using the ZLib library.
130 Upon entry, target_size is the total size of the destination buffer,
131 which must be large enough to hold the entire uncompressed data.
132 The size of the uncompressed data must have been saved previously by the compressor
133 and transmitted to the decompressor by some mechanism outside the scope of this
134 compression library.
135 
136 @param target Destination buffer
137 @param target_size Size of the destination buffer, in bytes
138 @param source Source buffer
139 @param source_size Size of the source buffer, in bytes
140 @return Returns the actual size of the uncompressed buffer, returns 0 if an error occured
141 @see FreeImage_ZLibGZip
142 */
143 
get_byte(z_stream * stream)144 static int get_byte(z_stream *stream) {
145     if (stream->avail_in <= 0) return EOF;
146     stream->avail_in--;
147     return *(stream->next_in)++;
148 }
149 
checkheader(z_stream * stream)150 static int checkheader(z_stream *stream) {
151     int flags, c;
152     DWORD len;
153 
154     if (get_byte(stream) != 0x1f || get_byte(stream) != 0x8b)
155         return Z_DATA_ERROR;
156     if (get_byte(stream) != Z_DEFLATED || ((flags = get_byte(stream)) & 0xE0) != 0)
157         return Z_DATA_ERROR;
158     for (len = 0; len < 6; len++) (void)get_byte(stream);
159 
160     if ((flags & 0x04) != 0) { /* skip the extra field */
161         len  =  (DWORD)get_byte(stream);
162         len += ((DWORD)get_byte(stream)) << 8;
163         /* len is garbage if EOF but the loop below will quit anyway */
164         while (len-- != 0 && get_byte(stream) != EOF) ;
165     }
166     if ((flags & 0x08) != 0) { /* skip the original file name */
167         while ((c = get_byte(stream)) != 0 && c != EOF) ;
168     }
169     if ((flags & 0x10) != 0) {   /* skip the .gz file comment */
170         while ((c = get_byte(stream)) != 0 && c != EOF) ;
171     }
172     if ((flags & 0x02) != 0) {  /* skip the header crc */
173         for (len = 0; len < 2; len++) (void)get_byte(stream);
174     }
175     return Z_OK;
176 }
177 
178 DWORD DLL_CALLCONV
FreeImage_ZLibGUnzip(BYTE * target,DWORD target_size,BYTE * source,DWORD source_size)179 FreeImage_ZLibGUnzip(BYTE *target, DWORD target_size, BYTE *source, DWORD source_size) {
180     DWORD src_len  = source_size;
181     DWORD dest_len = target_size;
182     int   zerr     = Z_DATA_ERROR;
183 
184     if (src_len > 0) {
185         z_stream stream;
186         memset(&stream, 0, sizeof (stream));
187         if ((zerr = inflateInit2(&stream, -MAX_WBITS)) == Z_OK) {
188             stream.next_in  = source;
189             stream.avail_in = source_size;
190 
191             stream.next_out  = target;
192             stream.avail_out = target_size;
193 
194             if ((zerr = checkheader(&stream)) == Z_OK) {
195                 zerr = inflate (&stream, Z_NO_FLUSH);
196                 dest_len = target_size - stream.avail_out;
197 
198                 if (zerr == Z_OK || zerr == Z_STREAM_END)
199                     inflateEnd(&stream);
200             }
201         }
202     }
203     if (zerr != Z_OK && zerr != Z_STREAM_END) {
204         FreeImage_OutputMessageProc(FIF_UNKNOWN, "Zlib error : %s", zError(zerr));
205         return 0;
206     }
207     return dest_len;
208 }
209 
210 /**
211 Update a running crc from source and return the updated crc, using the ZLib library.
212 If source is NULL, this function returns the required initial value for the crc.
213 
214 @param crc Running crc value
215 @param source Source buffer
216 @param source_size Size of the source buffer, in bytes
217 @return Returns the new crc value
218 */
219 DWORD DLL_CALLCONV
FreeImage_ZLibCRC32(DWORD crc,BYTE * source,DWORD source_size)220 FreeImage_ZLibCRC32(DWORD crc, BYTE *source, DWORD source_size) {
221 
222     return crc32(crc, source, source_size);
223 }
224