1 // File_Extractor 1.0.0. http://www.slack.net/~ant/
2
3 #include "fex.h"
4
5 #include "File_Extractor.h"
6 #include "blargg_endian.h"
7 #include <string.h>
8 #include <ctype.h>
9
10 /* Copyright (C) 2005-2009 Shay Green. This module is free software; you
11 can redistribute it and/or modify it under the terms of the GNU Lesser
12 General Public License as published by the Free Software Foundation; either
13 version 2.1 of the License, or (at your option) any later version. This
14 module is distributed in the hope that it will be useful, but WITHOUT ANY
15 WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
16 FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
17 details. You should have received a copy of the GNU Lesser General Public
18 License along with this module; if not, write to the Free Software Foundation,
19 Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */
20
21 #include "blargg_source.h"
22
23
24 //// Types
25
fex_type_list(void)26 BLARGG_EXPORT const fex_type_t* fex_type_list( void )
27 {
28 static fex_type_t const fex_type_list_ [] =
29 {
30 #ifdef FEX_TYPE_LIST
31 FEX_TYPE_LIST
32 #else
33 // Modify blargg_config.h to change type list, NOT this file
34 fex_7z_type,
35 fex_gz_type,
36 #if FEX_ENABLE_RAR
37 fex_rar_type,
38 #endif
39 fex_zip_type,
40 #endif
41 fex_bin_type,
42 NULL
43 };
44
45 return fex_type_list_;
46 }
47
fex_init(void)48 BLARGG_EXPORT fex_err_t fex_init( void )
49 {
50 static bool inited;
51 if ( !inited )
52 {
53 for ( fex_type_t const* t = fex_type_list(); *t != NULL; ++t )
54 {
55 if ( (*t)->init )
56 RETURN_ERR( (*t)->init() );
57 }
58 inited = true;
59 }
60 return blargg_ok;
61 }
62
fex_identify_header(void const * header)63 BLARGG_EXPORT const char* fex_identify_header( void const* header )
64 {
65 unsigned four = get_be32( header );
66 switch ( four )
67 {
68 case 0x52457E5E:
69 case 0x52617221: return ".rar";
70
71 case 0x377ABCAF: return ".7z";
72
73 case 0x504B0304:
74 case 0x504B0506: return ".zip";
75
76 case 0x53495421: return ".sit";
77 case 0x41724301: return ".arc";
78 case 0x4D534346: return ".cab";
79 case 0x5A4F4F20: return ".zoo";
80 }
81
82 unsigned three = four >> 8;
83 switch ( three )
84 {
85 case 0x425A68: return ".bz2";
86 }
87
88 unsigned two = four >> 16;
89 switch ( two )
90 {
91 case 0x1F8B: return ".gz";
92 case 0x60EA: return ".arj";
93 }
94
95 unsigned skip_first_two = four & 0xFFFF;
96 if ( skip_first_two == 0x2D6C )
97 return ".lha";
98
99 return "";
100 }
101
fex_has_extension_(const char str[],const char suffix[],size_t str_len)102 static int fex_has_extension_( const char str [], const char suffix [], size_t str_len )
103 {
104 size_t suffix_len = strlen( suffix );
105 if ( str_len >= suffix_len )
106 {
107 str += str_len - suffix_len;
108 while ( *str && tolower( (unsigned char) *str ) == *suffix )
109 {
110 str++;
111 suffix++;
112 }
113 }
114 return *suffix == 0;
115 }
116
fex_has_extension(const char str[],const char suffix[])117 BLARGG_EXPORT int fex_has_extension( const char str [], const char suffix [] )
118 {
119 return fex_has_extension_( str, suffix, strlen( str ) );
120 }
121
is_archive_extension(const char str[])122 static int is_archive_extension( const char str [] )
123 {
124 static const char exts [] [6] = {
125 ".7z",
126 ".arc",
127 ".arj",
128 ".bz2",
129 ".cab",
130 ".dmg",
131 ".gz",
132 ".lha",
133 ".lz",
134 ".lzh",
135 ".lzma",
136 ".lzo",
137 ".lzx",
138 ".pea",
139 ".rar",
140 ".sit",
141 ".sitx",
142 ".tgz",
143 ".tlz",
144 ".z",
145 ".zip",
146 ".zoo",
147 ""
148 };
149
150 size_t str_len = strlen( str );
151 const char (*ext) [6] = exts;
152 for ( ; **ext; ext++ )
153 {
154 if ( fex_has_extension_( str, *ext, str_len ) )
155 return 1;
156 }
157 return 0;
158 }
159
fex_identify_extension(const char str[])160 BLARGG_EXPORT fex_type_t fex_identify_extension( const char str [] )
161 {
162 size_t str_len = strlen( str );
163 for ( fex_type_t const* types = fex_type_list(); *types; types++ )
164 {
165 if ( fex_has_extension_( str, (*types)->extension, str_len ) )
166 {
167 // Avoid treating known archive type as binary
168 if ( *(*types)->extension || !is_archive_extension( str ) )
169 return *types;
170 }
171 }
172 return NULL;
173 }
174
fex_identify_file(fex_type_t * type_out,const char path[])175 BLARGG_EXPORT fex_err_t fex_identify_file( fex_type_t* type_out, const char path [] )
176 {
177 *type_out = NULL;
178
179 fex_type_t type = fex_identify_extension( path );
180
181 // Unsupported extension?
182 if ( !type )
183 return blargg_ok; // reject
184
185 // Unknown/no extension?
186 if ( !*(type->extension) )
187 {
188 // Examine header
189 FEX_FILE_READER in;
190 RETURN_ERR( in.open( path ) );
191 if ( in.remain() >= fex_identify_header_size )
192 {
193 char h [fex_identify_header_size];
194 RETURN_ERR( in.read( h, sizeof h ) );
195
196 type = fex_identify_extension( fex_identify_header( h ) );
197 }
198 }
199
200 *type_out = type;
201 return blargg_ok;
202 }
203
fex_open_type(fex_t ** fe_out,const char path[],fex_type_t type)204 BLARGG_EXPORT fex_err_t fex_open_type( fex_t** fe_out, const char path [], fex_type_t type )
205 {
206 *fe_out = NULL;
207
208 if ( !type )
209 return blargg_err_file_type;
210
211 fex_t* fe = type->new_fex();
212 CHECK_ALLOC( fe );
213
214 fex_err_t err = fe->open( path );
215 if ( err )
216 {
217 delete fe;
218 return err;
219 }
220
221 *fe_out = fe;
222 return blargg_ok;
223 }
224
fex_open(fex_t ** fe_out,const char path[])225 BLARGG_EXPORT fex_err_t fex_open( fex_t** fe_out, const char path [] )
226 {
227 *fe_out = NULL;
228
229 fex_type_t type;
230 RETURN_ERR( fex_identify_file( &type, path ) );
231
232 return fex_open_type( fe_out, path, type );
233 }
234
235
236 //// Wide paths
237
fex_wide_to_path(const wchar_t * wide)238 char* fex_wide_to_path( const wchar_t* wide )
239 {
240 return blargg_to_utf8( wide );
241 }
242
fex_free_path(char * path)243 void fex_free_path( char* path )
244 {
245 free( path );
246 }
247
248
249 //// Errors
250
251 #define ENTRY( name ) { blargg_err_##name, fex_err_##name }
252 static blargg_err_to_code_t const fex_codes [] =
253 {
254 ENTRY( generic ),
255 ENTRY( memory ),
256 ENTRY( caller ),
257 ENTRY( internal ),
258 ENTRY( limitation ),
259
260 ENTRY( file_missing ),
261 ENTRY( file_read ),
262 ENTRY( file_io ),
263 ENTRY( file_eof ),
264
265 ENTRY( file_type ),
266 ENTRY( file_feature ),
267 ENTRY( file_corrupt ),
268
269 { 0, -1 }
270 };
271 #undef ENTRY
272
err_code(fex_err_t err)273 static int err_code( fex_err_t err )
274 {
275 return blargg_err_to_code( err, fex_codes );
276 }
277
fex_err_code(fex_err_t err)278 BLARGG_EXPORT int fex_err_code( fex_err_t err )
279 {
280 int code = err_code( err );
281 return (code >= 0 ? code : fex_err_generic);
282 }
283
fex_code_to_err(int code)284 BLARGG_EXPORT fex_err_t fex_code_to_err( int code )
285 {
286 return blargg_code_to_err( code, fex_codes );
287 }
288
fex_err_details(fex_err_t err)289 BLARGG_EXPORT const char* fex_err_details( fex_err_t err )
290 {
291 // If we don't have error code assigned, return entire string
292 return (err_code( err ) >= 0 ? blargg_err_details( err ) : blargg_err_str( err ));
293 }
294
295
296 //// Wrappers
297
fex_read(fex_t * fe,void * out,int count)298 BLARGG_EXPORT fex_err_t fex_read( fex_t* fe, void* out, int count )
299 {
300 RETURN_ERR( fe->stat() );
301 return fe->reader().read( out, count );
302 }
303
fex_close(fex_t * fe)304 BLARGG_EXPORT void fex_close ( fex_t* fe ) { delete fe; }
fex_type(const fex_t * fe)305 BLARGG_EXPORT fex_type_t fex_type ( const fex_t* fe ) { return fe->type(); }
fex_done(const fex_t * fe)306 BLARGG_EXPORT int fex_done ( const fex_t* fe ) { return fe->done(); }
fex_name(const fex_t * fe)307 BLARGG_EXPORT const char* fex_name ( const fex_t* fe ) { return fe->name(); }
fex_wname(const fex_t * fe)308 BLARGG_EXPORT const wchar_t* fex_wname ( const fex_t* fe ) { return fe->wname(); }
fex_size(const fex_t * fe)309 BLARGG_EXPORT int fex_size ( const fex_t* fe ) { return fe->size(); }
fex_dos_date(const fex_t * fe)310 BLARGG_EXPORT unsigned fex_dos_date ( const fex_t* fe ) { return fe->dos_date(); }
fex_crc32(const fex_t * fe)311 BLARGG_EXPORT unsigned fex_crc32 ( const fex_t* fe ) { return fe->crc32(); }
fex_stat(fex_t * fe)312 BLARGG_EXPORT fex_err_t fex_stat ( fex_t* fe ) { return fe->stat(); }
fex_next(fex_t * fe)313 BLARGG_EXPORT fex_err_t fex_next ( fex_t* fe ) { return fe->next(); }
fex_rewind(fex_t * fe)314 BLARGG_EXPORT fex_err_t fex_rewind ( fex_t* fe ) { return fe->rewind(); }
fex_tell(const fex_t * fe)315 BLARGG_EXPORT int fex_tell ( const fex_t* fe ) { return fe->tell(); }
fex_tell_arc(const fex_t * fe)316 BLARGG_EXPORT fex_pos_t fex_tell_arc ( const fex_t* fe ) { return fe->tell_arc(); }
fex_seek_arc(fex_t * fe,fex_pos_t pos)317 BLARGG_EXPORT fex_err_t fex_seek_arc ( fex_t* fe, fex_pos_t pos ) { return fe->seek_arc( pos ); }
fex_type_extension(fex_type_t t)318 BLARGG_EXPORT const char* fex_type_extension ( fex_type_t t ) { return t->extension; }
fex_type_name(fex_type_t t)319 BLARGG_EXPORT const char* fex_type_name ( fex_type_t t ) { return t->name; }
fex_data(fex_t * fe,const void ** data_out)320 BLARGG_EXPORT fex_err_t fex_data ( fex_t* fe, const void** data_out ) { return fe->data( data_out ); }
fex_err_str(fex_err_t err)321 BLARGG_EXPORT const char* fex_err_str ( fex_err_t err ) { return blargg_err_str( err ); }
322