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