1 // File_Extractor 1.0.0. http://www.slack.net/~ant/
2 
3 #include "Zlib_Inflater.h"
4 
5 /* Copyright (C) 2006-2009 Shay Green. This module is free software; you
6 can redistribute it and/or modify it under the terms of the GNU Lesser
7 General Public License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version. This
9 module is distributed in the hope that it will be useful, but WITHOUT ANY
10 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
11 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
12 details. You should have received a copy of the GNU Lesser General Public
13 License along with this module; if not, write to the Free Software Foundation,
14 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
15 
16 #include "blargg_source.h"
17 
18 int const block_size = 4096;
19 
get_zlib_err(int code)20 static const char* get_zlib_err( int code )
21 {
22 	assert( code != Z_OK );
23 	switch ( code )
24 	{
25 	case Z_MEM_ERROR:   return blargg_err_memory;
26 	case Z_DATA_ERROR:  return blargg_err_file_corrupt;
27 	// TODO: handle more error codes
28 	}
29 
30 	const char* str = zError( code );
31 	if ( !str )
32 		str = BLARGG_ERR( BLARGG_ERR_GENERIC, "problem unzipping data" );
33 
34 	return str;
35 }
36 
end()37 void Zlib_Inflater::end()
38 {
39 	if ( deflated_ )
40 	{
41 		deflated_ = false;
42 		if ( inflateEnd( &zbuf ) )
43 			check( false );
44 	}
45 	buf.clear();
46 
47 	static z_stream const empty = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
48 	memcpy( &zbuf, &empty, sizeof zbuf );
49 }
50 
Zlib_Inflater()51 Zlib_Inflater::Zlib_Inflater()
52 {
53 	deflated_ = false;
54 	end(); // initialize things
55 }
56 
~Zlib_Inflater()57 Zlib_Inflater::~Zlib_Inflater()
58 {
59 	end();
60 }
61 
fill_buf(int count)62 blargg_err_t Zlib_Inflater::fill_buf( int count )
63 {
64 	byte* out = buf.end() - count;
65 	RETURN_ERR( callback( user_data, out, &count ) );
66 	zbuf.avail_in = count;
67 	zbuf.next_in  = out;
68 	return blargg_ok;
69 }
70 
begin(callback_t new_callback,void * new_user_data,int new_buf_size,int initial_read)71 blargg_err_t Zlib_Inflater::begin( callback_t new_callback, void* new_user_data,
72 		int new_buf_size, int initial_read )
73 {
74 	callback  = new_callback;
75 	user_data = new_user_data;
76 
77 	end();
78 
79 	// TODO: decide whether using different size on alloc failure is a good idea
80 	//RETURN_ERR( buf.resize( new_buf_size ? new_buf_size : 4 * block_size ) );
81 	if ( new_buf_size && buf.resize( new_buf_size ) )
82 	{
83 		ACK_FAILURE();
84 		new_buf_size = 0;
85 	}
86 
87 	if ( !new_buf_size )
88 	{
89 		RETURN_ERR( buf.resize( 4 * block_size ) );
90 		initial_read = 0;
91 	}
92 
93 	// Fill buffer with some data, less than normal buffer size since caller might
94 	// just be examining beginning of file.
95 	return fill_buf( initial_read ? initial_read : block_size );
96 }
97 
set_mode(mode_t mode,int data_offset)98 blargg_err_t Zlib_Inflater::set_mode( mode_t mode, int data_offset )
99 {
100 	zbuf.next_in  += data_offset;
101 	zbuf.avail_in -= data_offset;
102 
103 	if ( mode == mode_auto )
104 	{
105 		// examine buffer for gzip header
106 		mode = mode_copy;
107 		unsigned const min_gzip_size = 2 + 8 + 8;
108 		if ( zbuf.avail_in >= min_gzip_size &&
109 				zbuf.next_in [0] == 0x1F && zbuf.next_in [1] == 0x8B )
110 			mode = mode_ungz;
111 	}
112 
113 	if ( mode != mode_copy )
114 	{
115 		int wb = MAX_WBITS + 16; // have zlib handle gzip header
116 		if ( mode == mode_raw_deflate )
117 			wb = -MAX_WBITS;
118 
119 		int zerr = inflateInit2( &zbuf, wb );
120 		if ( zerr )
121 		{
122 			zbuf.next_in = NULL;
123 			return get_zlib_err( zerr );
124 		}
125 
126 		deflated_ = true;
127 	}
128 	return blargg_ok;
129 }
130 
131 /*
132 // Reads/inflates entire stream. All input must be in buffer, and count must be total
133 // of all output.
134 blargg_err_t read_all( void* out, int count );
135 
136 
137 // zlib automatically applies this optimization (uses inflateFast)
138 // TODO: remove
139 blargg_err_t Zlib_Inflater::read_all( void* out, int count )
140 {
141 	if ( deflated_ )
142 	{
143 		zbuf.next_out  = (Bytef*) out;
144 		zbuf.avail_out = count;
145 
146 		int err = inflate( &zbuf, Z_FINISH );
147 
148 		if ( zbuf.avail_out || err != Z_STREAM_END )
149 			return blargg_err_file_corrupt;
150 	}
151 	else
152 	{
153 		if ( zbuf.avail_in < count )
154 			return blargg_err_file_corrupt;
155 
156 		memcpy( out, zbuf.next_in, count );
157 
158 		zbuf.next_in  += count;
159 		zbuf.avail_in -= count;
160 	}
161 
162 	return blargg_ok;
163 }
164 */
165 
read(void * out,int * count_io)166 blargg_err_t Zlib_Inflater::read( void* out, int* count_io )
167 {
168 	int remain = *count_io;
169 	if ( remain && zbuf.next_in )
170 	{
171 		if ( deflated_ )
172 		{
173 			zbuf.next_out  = (Bytef*) out;
174 			zbuf.avail_out = remain;
175 
176 			while ( 1 )
177 			{
178 				uInt old_avail_in = zbuf.avail_in;
179 				int err = inflate( &zbuf, Z_NO_FLUSH );
180 				if ( err == Z_STREAM_END )
181 				{
182 					remain = zbuf.avail_out;
183 					end();
184 					break; // no more data to inflate
185 				}
186 
187 				if ( err && (err != Z_BUF_ERROR || old_avail_in) )
188 					return get_zlib_err( err );
189 
190 				if ( !zbuf.avail_out )
191 				{
192 					remain = 0;
193 					break; // requested number of bytes inflated
194 				}
195 
196 				if ( zbuf.avail_in )
197 				{
198 					// inflate() should never leave input if there's still space for output
199 					check( false );
200 					return blargg_err_file_corrupt;
201 				}
202 
203 				RETURN_ERR( fill_buf( buf.size() ) );
204 				if ( !zbuf.avail_in )
205 					return blargg_err_file_corrupt; // stream didn't end but there's no more data
206 			}
207 		}
208 		else
209 		{
210 			while ( 1 )
211 			{
212 				// copy buffered data
213 				if ( zbuf.avail_in )
214 				{
215 					long count = zbuf.avail_in;
216 					if ( count > remain )
217 						count = remain;
218 					memcpy( out, zbuf.next_in, count );
219 					zbuf.total_out += count;
220 					out = (char*) out + count;
221 					remain        -= count;
222 					zbuf.next_in  += count;
223 					zbuf.avail_in -= count;
224 				}
225 
226 				if ( !zbuf.avail_in && zbuf.next_in < buf.end() )
227 				{
228 					end();
229 					break;
230 				}
231 
232 				// read large request directly
233 				if ( remain + zbuf.total_out % block_size >= buf.size() )
234 				{
235 					int count = remain;
236 					RETURN_ERR( callback( user_data, out, &count ) );
237 					zbuf.total_out += count;
238 					out = (char*) out + count;
239 					remain -= count;
240 
241 					if ( remain )
242 					{
243 						end();
244 						break;
245 					}
246 				}
247 
248 				if ( !remain )
249 					break;
250 
251 				RETURN_ERR( fill_buf( buf.size() - zbuf.total_out % block_size ) );
252 			}
253 		}
254 	}
255 	*count_io -= remain;
256 	return blargg_ok;
257 }
258