1 /*
2 Unmass - unpacker for game archives.
3 Copyright (C) 2002-2007 Mirex
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
9
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
14
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 #include <stdarg.h>
21 #include <stdio.h>
22
23 #include <string.h>
24
25 #include "config.h"
26
27 #ifdef _UNIX
28 #include <ctype.h>
29 #include <strings.h>
30 #include <unistd.h>
31 #include <sys/stat.h>
32 #endif
33
34 #include "utools.h"
35
36 #include <string>
37
38 UnmassTools::s_params UnmassTools::param_def[ UnmassTools::param_def_count ] =
39 {
40 { param_help, "-help", 0 },
41 { param_help, "--help", 0 },
42 { param_help, "/?", 0 },
43
44 { param_extract_files, "-e", -2 },
45 { param_list_of_modules, "-modules", 0 },
46 { param_print_file_list, "-list", 1 },
47 { param_print_file_list_full, "-list-full", 1 },
48
49
50 } ;
51
52 char UnmassTools::version_string[ 20 ] = "0.9 console";
53 char UnmassTools::executable_name_string[ 20 ] = "unmass";
54
55
Log(int severity,const char * format,...)56 void UnmassTools::Log( int severity, const char* format, ... )
57 {
58 va_list argptr;
59 va_start( argptr, format );
60
61 vprintf( format, argptr );
62
63 va_end( argptr );
64 }
65
Start(int argc,char * argv[])66 int UnmassTools::Start( int argc, char* argv[] )
67 {
68 int action = param_help;
69 int i, c, required_params_number;
70 bool or_more = false;
71 char str1[ 301 ];
72
73 UnmassTools::array_of_filenames names_array;
74
75 if ( argc <= 1 ) {
76 action = param_none;
77 } else {
78
79 for( i=0; i<param_def_count; i++ )
80 if ( _strcasecmp( argv[ 1 ], param_def[ i ].string ) == 0 ) {
81 action = param_def[ i ].ident;
82
83 required_params_number = param_def[ i ].param_count;
84 if ( required_params_number < 0 ) {
85 required_params_number = - required_params_number;
86 or_more = true;
87 }
88
89 c = argc - 2;
90
91 if (( c > required_params_number ) && ( or_more == false )) {
92 Log( 0, "Supplied more than enough parameters.\n" );
93 }
94
95 if ( c < required_params_number ) {
96 Log( 0, "Not enough parameters.\n" );
97 action = param_none;
98 }
99
100 break;
101 }
102
103 }
104
105
106 switch( action ) {
107 case param_none:
108 PrintProgramInfo();
109 return 0;
110
111 case param_help:
112 PrintHelp();
113 return 0;
114
115 case param_list_of_modules:
116 PrintModules();
117 return 0;
118
119 case param_print_file_list:
120
121 return PrintMassFileList( argv[ 2 ], true );
122
123 case param_print_file_list_full:
124
125 return PrintMassFileList( argv[ 2 ], false );
126
127 case param_extract_files:
128
129 names_array.clear();
130 for( i=0; i<c-1; i++ )
131 names_array.push_back( argv[ 3 + i ] );
132
133 // set output directory - current working directory
134 getcwd( str1, 300 );
135 SetOutputDirectory( str1 );
136
137 return ExtractMultipleFiles( argv[ 2 ], names_array );
138
139 default:
140 Log( 1, "Incorrect action\n" );
141 break;
142
143 }
144
145 return 1;
146 }
147
PrintProgramInfo(void)148 void UnmassTools::PrintProgramInfo( void )
149 {
150 Log( 0, "-*= Unmass =*-\n" );
151 Log( 0, "version %s\n\n", version_string );
152 Log( 0, "Unmass is archive unpacker for game archives.\n" );
153 Log( 0, "Use parameter /? to see help.\n" );
154 }
155
PrintHelp(void)156 void UnmassTools::PrintHelp( void )
157 {
158 PrintProgramInfo();
159 Log( 0, "\n" );
160
161 int i, j;
162
163 for( i=0; i<param_count; i++ ) {
164
165 for( j=0; j<param_def_count; j++ ) {
166 if ( param_def[ j ].ident == i ) {
167
168 Log( 0, " %s\n", param_def[ j ].string );
169
170 }
171 }
172
173 switch( i ) {
174 case param_help:
175 Log( 0, "Prints out this help\n" );
176 break;
177 case param_list_of_modules:
178 Log( 0, "Prints out list of modules for archive loading\n" );
179 break;
180 case param_print_file_list:
181 Log( 0, "Parameters: <archive_file>\n" );
182 Log( 0, "Opens the archive and prints list of the files inside.\n" );
183 break;
184 case param_print_file_list_full:
185 Log( 0, "Parameters: <archive_file>\n" );
186 Log( 0, "Opens the archive and prints list of the files inside with offsets, sizes and number of files.\n" );
187 break;
188 case param_extract_files:
189 Log( 0, "Parameters: <archive file> <file name> [...<file name>]\n" );
190 Log( 0, "Opens the archive and extract files.\n" );
191 Log( 0, " file name can be specified also with * wildcard in one of these forms:\n");
192 Log( 0, " 'name', 'prefix*', '*suffix' or 'prefix*suffix' \n");
193 Log( 0, " so 'level*.txt' should work well.\n");
194 Log( 0, " Enclose the filenames with wildcards into ' ' so unix system won't replace\n" );
195 Log( 0, " those names with currently existing files names.\n" );
196 break;
197 }
198
199 Log( 0, "\n" );
200 }
201
202 Log( 0, "Example:\n" );
203 Log( 0, " %s -list battle.lgp\n", executable_name_string );
204 Log( 0, " Displays list of files contained inside archive 'battle.lgp'\n\n" );
205 Log( 0, " %s -e battle.lgp aabc.txt *dat\n", executable_name_string );
206 Log( 0, " Opens 'battle.lgp' and extracts file 'aabc.txt' and all files ending\n" );
207 Log( 0, " with 'dat' into current directory\n\n" );
208
209 Log( 0, "\n" );
210
211 return;
212 }
213
PrintModules(void)214 void UnmassTools::PrintModules( void )
215 {
216 int i, c;
217 CMassArchive* pArchive;
218
219 Log( 0, "Plugins and their supported types:\n" );
220
221 for( i=0; i<CMassFiles::massftypes_count; i++ ) {
222 pArchive = massfs.Archive[ i ];
223 if ( pArchive == NULL )
224 Log( 0, "null\n" );
225 else {
226 Log( 0, "%10s %s\n", pArchive->GetExtension(), pArchive->GetIdent() );
227 }
228 }
229
230 }
231
PrintMassFileList(const char * mass_filename,bool filenames_only)232 int UnmassTools::PrintMassFileList( const char* mass_filename, bool filenames_only )
233 {
234 if ( massfs.Open( mass_filename ) == 0 ) {
235 Log( 1, "Error, could not open file (%s)\n", massfs.GetErrorStr() );
236 return 0;
237 }
238
239 int i, c;
240 c = massfs.MassfInfo.files_count;
241
242 massfs.ReadAllRecords();
243
244 if ( ! filenames_only ) {
245 printf( "Files: %i\n", c );
246 printf( "%9s %9s %s\n", "offset", "size", "name" );
247 }
248
249 for( i=0; i<c; i++ ) {
250 massfs.GetRec( i );
251
252 if ( filenames_only )
253 printf( "%s\n", massfs.FileRec.name );
254 else
255 printf( "%9i %9i %s\n", massfs.FileRec.offset, massfs.FileRec.size, massfs.FileRec.name );
256 }
257
258 massfs.Close();
259
260 }
261
262 // const char* filename
ExtractMultipleFiles(const char * mass_filename,UnmassTools::array_of_filenames names)263 int UnmassTools::ExtractMultipleFiles(
264 const char* mass_filename, UnmassTools::array_of_filenames names )
265 {
266 // first try to open the file
267 if ( massfs.Open( mass_filename ) == 0 ) {
268 Log( 1, "Error, could not open file (%s)\n", massfs.GetErrorStr() );
269 return 0;
270 }
271
272 int i, j, c, extracted_files = 0;
273 bool extract, prefix_match, suffix_match;
274 c = massfs.MassfInfo.files_count;
275 massfs.ReadAllRecords();
276
277 // second, parse the parameter list and try to extract the files
278 int number_of_files = names.size();
279 const char* ccp;
280
281 // filename can be either "name" or "nam*" or "*me" or "na*me" or "*"
282 //! structure is named [s_filespec] because MSVC6 complains if
283 //! unnamed struct contains function
284 struct s_filespec {
285
286 enum {
287 name_len = 512,
288 } ;
289
290 char name[ name_len ];
291 bool valid;
292 bool all; // extract all files ?
293 char prefix[ name_len ];
294 char suffix[ name_len ];
295
296 int prefix_length, suffix_length;
297
298 void Reset( void ) {
299 valid = false;
300 all = false;
301 name[ 0 ] = 0;
302 prefix[ 0 ] = 0;
303 suffix[ 0 ] = 0;
304 prefix_length = suffix_length = 0;
305 }
306
307 void Parse( const char* fname ) {
308 int stars = 0, c;
309 const char* ccp = fname;
310
311 Reset();
312
313 // is it a single star ? then it means 'all files'
314 if (( fname[ 0 ] == '*' ) && ( fname[ 1 ] == 0 )) {
315 all = true;
316 valid = true;
317 return;
318 }
319
320 while( *ccp != 0 ) {
321
322 if ( *ccp == '*' ) {
323
324 // count the star characters. there can be only one in the filename
325 stars++;
326 if ( stars > 1 )
327 break;
328
329 // treat all characters before star as prefix
330 if ( ccp != fname ) {
331 c = ( ccp - fname );
332 if ( c >= name_len )
333 c = name_len - 1;
334 memcpy( prefix, fname, c );
335 prefix[ c ] = 0;
336 prefix_length = c;
337 }
338
339 } else {
340 // if there was star already, and suffix is empty then create it
341 if (( stars > 0 ) && ( suffix[ 0 ] == 0 )) {
342 strncpy( suffix, ccp, name_len-1 );
343 suffix_length = strlen( suffix );
344 }
345
346 }
347
348 ccp++;
349 }
350
351 // too many stars ? invalid mask !
352 if ( stars > 1 ) {
353 Reset();
354 valid = false;
355
356 } else {
357
358 strncpy( name, fname, name_len-1 );
359 valid = true;
360
361 }
362 } // Parse
363
364 } filespec;
365
366 for( i=0; i<number_of_files; i++ ) {
367 ccp = names[ i ];
368
369 filespec.Parse( ccp );
370
371 if ( filespec.valid == false ) {
372 Log( 1, "Error: filename specification '%s' is invalid.", ccp );
373 continue;
374 }
375
376 // loop through all files, and see if the filename matches the search pattern
377 for( j=0; j<massfs.MassfInfo.files_count; j++ ) {
378
379 extract = false;
380 prefix_match = false;
381 suffix_match = false;
382 massfs.GetRec( j );
383
384 if ( filespec.all )
385 extract = true;
386
387 // whole name
388 if (( extract == false ) && ( _strcasecmp( ccp, massfs.FileRec.name ) == 0 ))
389 extract = true;
390
391 // check for prefix
392 if (( extract == false ) && ( filespec.prefix_length > 0 ) &&
393 ( _strncasecmp( filespec.prefix, massfs.FileRec.name, filespec.prefix_length ) == 0 ))
394 prefix_match = true;
395
396 // check for suffix
397 if (( extract == false ) && ( filespec.suffix_length > 0 ) &&
398 ( _strcasecmp( filespec.suffix,
399 massfs.FileRec.name + strlen( massfs.FileRec.name ) - filespec.suffix_length ) == 0 ))
400 suffix_match = true;
401
402 // if there is both prefix and suffix defined, then extract only if both match
403 if (( filespec.prefix_length > 0 ) && ( filespec.suffix_length > 0 )) {
404 if (( prefix_match) && ( suffix_match ))
405 extract = true;
406 } else {
407 if (( prefix_match ) || ( suffix_match ))
408 extract = true;
409 }
410
411 if ( extract ) {
412 extracted_files++;
413 Log( 0, "Extracting file '%s'\n", massfs.FileRec.name );
414 massfs.Extract();
415 }
416 }
417
418 // if I extract all files its not needed to extract any more
419 if ( filespec.all ) {
420 break;
421 }
422
423 }
424
425 Log( 0, "Extracted %i files.\n", extracted_files );
426
427 massfs.Close();
428
429 return 1;
430 }
431
_strlwr(char * string)432 void UnmassTools::_strlwr( char* string )
433 {
434 #ifdef _UNIX
435 char* cp = string;
436
437 while( *cp != 0 ) {
438 *cp = tolower( *cp );
439 cp++;
440 }
441 return;
442 #else
443 strlwr( string );
444 return;
445 #endif
446 }
447
_strcasecmp(const char * s1,const char * s2)448 int UnmassTools::_strcasecmp( const char* s1, const char* s2 )
449 {
450 #ifdef _UNIX
451 return strcasecmp( s1, s2 );
452 #else
453 return strcmp( s1, s2 );
454 #endif
455 }
456
_strncasecmp(const char * s1,const char * s2,int n)457 int UnmassTools::_strncasecmp( const char* s1, const char* s2, int n )
458 {
459 #ifdef _UNIX
460 return strncasecmp( s1, s2, n );
461 #else
462 return stricmp( s1, s2 );
463 #endif
464 }
465
SetOutputDirectory(const char * s)466 void UnmassTools::SetOutputDirectory( const char* s )
467 {
468 massfs.SetOutputDirectory( s );
469 }
470