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