1 // File_Extractor 1.0.0. http://www.slack.net/~ant/
2
3 #include "File_Extractor.h"
4
5 /* Copyright (C) 2005-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
fex_t(fex_type_t t)18 File_Extractor::fex_t( fex_type_t t ) :
19 type_( t )
20 {
21 own_file_ = NULL;
22
23 close_();
24 }
25
26 // Open
27
set_path(const char * path)28 blargg_err_t File_Extractor::set_path( const char* path )
29 {
30 if ( !path )
31 path = "";
32
33 RETURN_ERR( path_.resize( strlen( path ) + 1 ) );
34 memcpy( path_.begin(), path, path_.size() );
35 return blargg_ok;
36 }
37
open(const char path[])38 blargg_err_t File_Extractor::open( const char path [] )
39 {
40 close();
41
42 RETURN_ERR( set_path( path ) );
43
44 blargg_err_t err = open_path_v();
45 if ( err )
46 close();
47 else
48 opened_ = true;
49
50 return err;
51 }
52
open_path_v()53 blargg_err_t File_Extractor::open_path_v()
54 {
55 RETURN_ERR( open_arc_file() );
56
57 return open_v();
58 }
59
60 inline
make_unbuffered(Std_File_Reader * r)61 static void make_unbuffered( Std_File_Reader* r )
62 {
63 r->make_unbuffered();
64 }
65
66 inline
make_unbuffered(void *)67 static void make_unbuffered( void* )
68 { }
69
open_arc_file(bool unbuffered)70 blargg_err_t File_Extractor::open_arc_file( bool unbuffered )
71 {
72 if ( reader_ )
73 return blargg_ok;
74
75 FEX_FILE_READER* in = BLARGG_NEW FEX_FILE_READER;
76 CHECK_ALLOC( in );
77
78 blargg_err_t err = in->open( arc_path() );
79 if ( err )
80 {
81 delete in;
82 }
83 else
84 {
85 reader_ = in;
86 own_file();
87 if ( unbuffered )
88 make_unbuffered( in );
89 }
90
91 return err;
92 }
93
open(File_Reader * input,const char * path)94 blargg_err_t File_Extractor::open( File_Reader* input, const char* path )
95 {
96 close();
97
98 RETURN_ERR( set_path( path ) );
99
100 RETURN_ERR( input->seek( 0 ) );
101
102 reader_ = input;
103 blargg_err_t err = open_v();
104 if ( err )
105 close();
106 else
107 opened_ = true;
108
109 return err;
110 }
111
112 // Close
113
close()114 void File_Extractor::close()
115 {
116 close_v();
117 close_();
118 }
119
close_()120 void File_Extractor::close_()
121 {
122 delete own_file_;
123 own_file_ = NULL;
124
125 tell_ = 0;
126 reader_ = NULL;
127 opened_ = false;
128
129 path_.clear();
130 clear_file();
131 }
132
~fex_t()133 File_Extractor::~fex_t()
134 {
135 check( !opened() ); // fails if derived destructor didn't call close()
136
137 delete own_file_;
138 }
139
140 // Scanning
141
clear_file()142 void File_Extractor::clear_file()
143 {
144 name_ = NULL;
145 wname_ = NULL;
146 done_ = true;
147 stat_called = false;
148 data_ptr_ = NULL;
149
150 set_info( 0 );
151 own_data_.clear();
152 clear_file_v();
153 }
154
set_name(const char new_name[],const wchar_t * new_wname)155 void File_Extractor::set_name( const char new_name [], const wchar_t* new_wname )
156 {
157 name_ = new_name;
158 wname_ = new_wname;
159 done_ = false;
160 }
161
set_info(int new_size,unsigned date,unsigned crc)162 void File_Extractor::set_info( int new_size, unsigned date, unsigned crc )
163 {
164 size_ = new_size;
165 date_ = (date != 0xFFFFFFFF ? date : 0);
166 crc32_ = crc;
167 set_remain( new_size );
168 }
169
next_()170 blargg_err_t File_Extractor::next_()
171 {
172 tell_++;
173 clear_file();
174
175 blargg_err_t err = next_v();
176 if ( err )
177 clear_file();
178
179 return err;
180 }
181
next()182 blargg_err_t File_Extractor::next()
183 {
184 assert( !done() );
185 return next_();
186 }
187
rewind()188 blargg_err_t File_Extractor::rewind()
189 {
190 assert( opened() );
191
192 tell_ = 0;
193 clear_file();
194
195 blargg_err_t err = rewind_v();
196 if ( err )
197 clear_file();
198
199 return err;
200 }
201
stat()202 blargg_err_t File_Extractor::stat()
203 {
204 assert( !done() );
205
206 if ( !stat_called )
207 {
208 RETURN_ERR( stat_v() );
209 stat_called = true;
210 }
211 return blargg_ok;
212 }
213
214 // Tell/seek
215
216 int const pos_offset = 1;
217
tell_arc() const218 fex_pos_t File_Extractor::tell_arc() const
219 {
220 assert( opened() );
221
222 fex_pos_t pos = tell_arc_v();
223 assert( pos >= 0 );
224
225 return pos + pos_offset;
226 }
227
seek_arc(fex_pos_t pos)228 blargg_err_t File_Extractor::seek_arc( fex_pos_t pos )
229 {
230 assert( opened() );
231 assert( pos != 0 );
232
233 clear_file();
234
235 blargg_err_t err = seek_arc_v( pos - pos_offset );
236 if ( err )
237 clear_file();
238
239 return err;
240 }
241
tell_arc_v() const242 fex_pos_t File_Extractor::tell_arc_v() const
243 {
244 return tell_;
245 }
246
seek_arc_v(fex_pos_t pos)247 blargg_err_t File_Extractor::seek_arc_v( fex_pos_t pos )
248 {
249 // >= because seeking to current file should always reset read pointer etc.
250 if ( tell_ >= pos )
251 RETURN_ERR( rewind() );
252
253 while ( tell_ < pos )
254 {
255 RETURN_ERR( next_() );
256
257 if ( done() )
258 {
259 assert( false );
260 return blargg_err_caller;
261 }
262 }
263
264 assert( tell_ == pos );
265
266 return blargg_ok;
267 }
268
269 // Extraction
270
rewind_file()271 blargg_err_t File_Extractor::rewind_file()
272 {
273 RETURN_ERR( stat() );
274
275 if ( tell() > 0 )
276 {
277 if ( data_ptr_ )
278 {
279 set_remain( size() );
280 }
281 else
282 {
283 RETURN_ERR( seek_arc( tell_arc() ) );
284 RETURN_ERR( stat() );
285 }
286 }
287
288 return blargg_ok;
289 }
290
data(const void ** data_out)291 blargg_err_t File_Extractor::data( const void** data_out )
292 {
293 assert( !done() );
294
295 *data_out = NULL;
296 if ( !data_ptr_ )
297 {
298 int old_tell = tell();
299
300 RETURN_ERR( rewind_file() );
301
302 void const* ptr;
303 RETURN_ERR( data_v( &ptr ) );
304 data_ptr_ = ptr;
305
306 // Now that data is in memory, we can seek by simply setting remain
307 set_remain( size() - old_tell );
308 }
309
310 *data_out = data_ptr_;
311 return blargg_ok;
312 }
313
data_v(void const ** out)314 blargg_err_t File_Extractor::data_v( void const** out )
315 {
316 RETURN_ERR( own_data_.resize( size() ) );
317 *out = own_data_.begin();
318
319 blargg_err_t err = extract_v( own_data_.begin(), own_data_.size() );
320 if ( err )
321 own_data_.clear();
322
323 return err;
324 }
325
extract_v(void * out,int count)326 blargg_err_t File_Extractor::extract_v( void* out, int count )
327 {
328 void const* p;
329 RETURN_ERR( data( &p ) );
330 memcpy( out, STATIC_CAST(char const*,p) + (size() - remain()), count );
331
332 return blargg_ok;
333 }
334
read_v(void * out,int count)335 blargg_err_t File_Extractor::read_v( void* out, int count )
336 {
337 if ( data_ptr_ )
338 return File_Extractor::extract_v( out, count );
339
340 return extract_v( out, count );
341 }
342