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