1 #include "../common/pch.h"
6 feFileTool::feFileTool( int argc
, TCHAR
*argv
[] )
16 feFileTool::~feFileTool()
18 std::vector
< feRegexp
* >::iterator itIgnore
;
19 for ( itIgnore
= mIgnoreList
.begin(); itIgnore
!= mIgnoreList
.end(); itIgnore
++ )
21 std::map
< feRegexp
*, feCStr
>::iterator itRules
;
22 for ( itRules
= mRulesList
.begin(); itRules
!= mRulesList
.end(); itRules
++ )
23 delete ( *itRules
).first
;
42 if ( mCommand
== cmdRebuild
)
45 // no commands were specified
47 catch ( feFileToolErr e
)
49 _tprintf( _T( "%s\n" ), e
.msg().c_str() );
55 void feFileTool::rebuild( void )
57 if ( mOutFile
.empty() )
59 _tprintf( _T( "rebuild command requires output filename. use -o option.\n" ) );
66 mpRoot
->blocktype
= feFSBlockType_Folder
;
67 createFSStructure( mpRoot
);
68 _tprintf( _T( "total number of files: %d\n" ), mNumFiles
);
71 dbgPrintTree( mpRoot
);
74 _tprintf( _T( "opening '%s' for writing...\n" ), mOutFile
.c_str() );
75 FILE *fp
= _tfopen( mOutFile
.c_str(), _T( "w+b" ) );
78 _tprintf( _T( "cannot open file. exiting.\n" ) );
82 _tprintf( _T( "writing magic...\n" ) );
83 char magic
[] = "FEPAK";
84 fwrite( magic
, strlen( magic
), 1, fp
);
85 fwrite( &mTableOffset
, sizeof( mTableOffset
), 1, fp
);
87 mStartTime
= (double)clock() / CLOCKS_PER_SEC
;
90 processFiles( fp
, mpRoot
, "" );
91 _tprintf( _T( "\n" ) );
93 _tprintf( _T( "approximated filesize = %.3lf mb\n" ), mTotalSize
/ ( 1024.0 * 1024.0 ) );
95 mTableOffset
= ftell( fp
);
96 writeTable( fp
, mpRoot
);
97 fseek( fp
, (long)strlen( magic
), SEEK_SET
);
98 fwrite( &mTableOffset
, sizeof( mTableOffset
), 1, fp
);
99 _tprintf( _T( "closing output file...\n" ) );
101 _tprintf( _T( "done!\n" ) );
104 void feFileTool::printUsage( void )
106 _tprintf( _T( "usage: filetool.exe [options] command [command-options-and-arguments]\n" ) );
107 _tprintf( _T( " where options are -o, -c, etc.\n" ) );
108 _tprintf( _T( " (specify --help-options for a list of options)\n" ) );
109 _tprintf( _T( " where command are add, update, remove, etc.\n" ) );
110 _tprintf( _T( " (specify --help-commands for a list of commands)\n" ) );
111 _tprintf( _T( " where command-options-and-arguments depend on the specific command\n" ) );
112 _tprintf( _T( " (specify --help followed by a command name for command-specific help)\n" ) );
113 _tprintf( _T( " specify --help to recieve this message\n" ) );
114 _tprintf( _T( "\nfe file processing tool v.0.1\n" ) );
117 bool feFileTool::parseArgs( void )
119 for ( int i
= 1; i
< mArgc
; i
++ )
122 if ( !_tcsicmp( mArgv
[i
], _T( "--help" ) ) )
132 printCommandHelp( i
);
136 else if ( !_tcsicmp( mArgv
[i
], _T( "--help-options" ) ) )
141 else if ( !_tcsicmp( mArgv
[i
], _T( "--help-commands" ) ) )
147 else if ( !_tcsnicmp( mArgv
[i
], _T( "-o" ), 2 ) ) // outfile
149 mOutFile
= mArgv
[i
]+2;
150 if ( mOutFile
.empty() )
152 _tprintf( _T( "empty filename specified with -o\n" ) );
156 else if ( !_tcsicmp( mArgv
[i
], _T( "rebuild" ) ) ) // rebuild all
158 mCommand
= cmdRebuild
;
164 _tprintf( _T( "unknown keyword: %s\n" ), mArgv
[i
] );
171 void feFileTool::printOptionsHelp( void )
173 _tprintf( _T( "filetool global options (specified before command name) are:\n" ) );
174 _tprintf( _T( "\t-o filename\t\tuse 'filename' as output file.\n" ) );
175 _tprintf( _T( "\n(specify the --help global option for a list of other help options)\n" ) );
178 void feFileTool::printCommandsHelp( void )
180 _tprintf( _T( "filetool commands are:\n" ) );
181 _tprintf( _T( "\trebuild\t\trebuilds entire package from scratch.\n" ) );
182 _tprintf( _T( "\n(specify the --help global option for a list of other help options)\n" ) );
185 void feFileTool::printCommandHelp( int argIndex
)
187 if ( !_tcsicmp( mArgv
[argIndex
], _T( "rebuild" ) ) )
189 _tprintf( _T( "usage: filetool rebuild\n" ) );
191 _tprintf( _T( "(specify the --help global option for a list of other help options)\n" ) );
194 void feFileTool::loadIgnoreList()
196 _tprintf( _T( "reading '.ftignore'...\n" ) );
199 feCharParser
p( ".ftignore", true );
205 feRegexp
*r
= new feRegexp( p
.token() );
206 mIgnoreList
.push_back( r
);
210 catch ( feParserException e
)
212 if ( e
== feParserException::FILE_NOT_FOUND
)
214 _tprintf( _T( "'.ftignore' does not exist. all contents will be processed.\n" ) );
216 else if ( e
== feParserException::END_OF_FILE
&& false == expEof
)
218 throw feFileToolErr( _T( "unexpected eof while reading '.ftignore'" ) );
220 else if ( e
!= feParserException::END_OF_FILE
)
221 _tprintf( _T( "parser error: %s" ), feStr (e
) );
224 // add always-ignored items
225 mIgnoreList
.push_back( new feRegexp( "\\.ftignore$" ) );
226 mIgnoreList
.push_back( new feRegexp( "\\.ftrules$" ) );
227 mIgnoreList
.push_back( new feRegexp( "filetool\\.exe$" ) );
230 void feFileTool::loadRulesList()
232 _tprintf( _T( "reading '.ftrules'...\n" ) );
235 feCharParser
p( ".ftrules", true );
240 feCStr re
= p
.token();
243 feRegexp
*r
= new feRegexp( re
);
244 mRulesList
[r
] = p
.token();
248 catch ( feParserException e
)
250 if ( e
== feParserException::FILE_NOT_FOUND
)
252 _tprintf( _T( "'.ftrules' does not exist. all files will be converted to compressed binaries ( feFSBlockType_BinaryCompressed ).\n" ) );
255 else if ( e
== feParserException::END_OF_FILE
&& false == expEof
)
257 throw feFileToolErr( _T( "unexpected eof while reading '.ftrules'" ) );
259 else if ( e
!= feParserException::END_OF_FILE
)
260 _tprintf( _T( "parser error: %s" ), feStr (e
));
264 void feFileTool::createFSStructure( file
*f
)
267 _tprintf( _T( "creating filesystem structure...\n" ) );
269 memset( &fd
, 0, sizeof( fd
) );
270 HANDLE hFind
= ::FindFirstFile( _T( "*.*" ), &fd
);
271 if ( hFind
!= INVALID_HANDLE_VALUE
)
275 feStr fname
= fd
.cFileName
;
277 if ( !testIgnoreList( fname
) )
279 if ( fname
!= _T( ".." ) && fname
!= _T( "." ) && _tcslen( fname
) <= 32 )
284 strcpy( nf
->name
, feCStr( fname
).c_str() );
287 if ( fd
.dwFileAttributes
& FILE_ATTRIBUTE_DIRECTORY
)
290 TCHAR rmbFolder
[_MAX_PATH
];
291 _tgetcwd( rmbFolder
, _MAX_PATH
);
293 createFSStructure( nf
);
294 _tchdir( rmbFolder
);
295 nf
->blocktype
= feFSBlockType_Folder
;
299 // find rule, default is 'c'
300 feCStr rule
= findRule( fname
);
302 nf
->blocktype
= feFSBlockType_BinaryCompressed
;
303 else if ( rule
== "b" )
304 nf
->blocktype
= feFSBlockType_BinaryRawData
;
308 s
.printf( _T( "invalid rule: '%s'" ), rule
.c_str() );
309 throw feFileToolErr( s
);
312 if ( fd
.nFileSizeHigh
!= 0 )
315 s
.printf( _T( "file size is too big: '%s'" ), fname
.c_str() );
316 throw feFileToolErr( s
);
319 nf
->size
= fd
.nFileSizeLow
;
321 f
->children
.push_back( nf
);
324 } while ( FindNextFile( hFind
, &fd
) );
328 bool feFileTool::testIgnoreList( const feStr
&s
)
331 std::vector
< feRegexp
* >::iterator it
;
332 for ( it
= mIgnoreList
.begin(); it
!= mIgnoreList
.end(); it
++ )
334 char *str
= new char[cs
.size() + 1];
335 strcpy( str
, cs
.c_str() );
336 int r
= regexec( ( *it
)->getRegexp(), str
);
344 feCStr
feFileTool::findRule( const feStr
&s
)
347 std::map
< feRegexp
*, feCStr
>::iterator it
;
348 for ( it
= mRulesList
.begin(); it
!= mRulesList
.end(); it
++ )
350 char *str
= new char[cs
.size() + 1];
351 strcpy( str
, cs
.c_str() );
352 int r
= regexec( ( *it
).first
->getRegexp(), str
);
355 return ( *it
).second
;
361 void feFileTool::dbgPrintTree( file
*f
, int indent
)
364 for ( i
= 0; i
< indent
; i
++ )
365 _tprintf( _T( "--" ) );
366 if ( false == f
->children
.empty() )
367 _tprintf( _T( "[" ) );
368 _tprintf( feStr( f
->name
).c_str() );
369 if ( false == f
->children
.empty() )
370 _tprintf( _T( "]" ) );
371 _tprintf( _T( "\n" ) );
372 for ( i
= 0; i
< (int)f
->children
.size(); i
++ )
373 dbgPrintTree( f
->children
[i
], indent
+1 );
376 void feFileTool::processFiles( FILE *fp
, file
*f
, const feCStr
&dir
)
379 _tprintf( _T( "writing files...\n" ) );
380 if ( f
->blocktype
!= feFSBlockType_Folder
) // folder is dummy
382 f
->offset
= ftell( fp
);
384 uchar
*buffer
= new uchar
[f
->size
];
386 // maxname is 32 chars, terminating 0 is not included
387 // copy string and append with 0
389 memset( str
, 0, 33 );
390 memcpy( str
, f
->name
, 32 );
391 s
.printf( "%s%s", dir
.c_str(), str
);
392 FILE *in
= fopen( s
.c_str(), "rb" );
395 _tprintf( _T( "*** warning: cannot open file '%s', filling with zeros.\n" ), feStr( s
.c_str() ).c_str() );
396 _tprintf( _T( "*** hint: file name may contain non-OEM characters.\n" ) );
397 memset( buffer
, 0, f
->size
);
401 memset( buffer
, 0, f
->size
);
402 fread( buffer
, f
->size
, 1, in
);
406 if ( f
->blocktype
== feFSBlockType_BinaryRawData
)
408 fwrite( buffer
, f
->size
, 1, fp
);
409 mTotalSize
+= f
->size
;
410 f
->comprsize
= f
->size
;
412 else if ( f
->blocktype
== feFSBlockType_BinaryCompressed
)
414 f
->comprsize
= (ulong
)(f
->size
* 1.3);
415 uchar
*compr
= new uchar
[f
->comprsize
];
416 compress2( compr
, &f
->comprsize
, buffer
, f
->size
, 9 );
417 fwrite( compr
, f
->comprsize
, 1, fp
);
418 mTotalSize
+= f
->comprsize
;
422 __asm
int 3; // break execution: something wrong
426 double elapsedtime
= (double)clock() / CLOCKS_PER_SEC
- mStartTime
;
427 double timeperfile
= elapsedtime
/ ( mCurrentFile
? mCurrentFile
: 1 );
428 double totaltime
= timeperfile
* mNumFiles
;
432 printf( "%d%% (%d/%d); elapsed time %.2lfm, estimated time left: %.2lfm, avg time per file: %.5lfm \r"
433 , (int)( 100.0 * ( mCurrentFile
+ 1 ) / mNumFiles
)
434 , mCurrentFile
+1, mNumFiles
436 , totaltime
- elapsedtime
444 for ( size_t i
= 0; i
< f
->children
.size(); i
++ )
446 feCStr newDir
= f
== mpRoot
? "" : dir
+ feCStr(f
->name
) + feCStr("/");
447 processFiles( fp
, f
->children
[i
], newDir
);
452 void feFileTool::writeTable( FILE *fp
, file
*f
)
455 _tprintf( _T( "writing file table...\n" ) );
456 fwrite( f
, 48, 1, fp
);
457 if ( f
->blocktype
== feFSBlockType_Folder
)
459 ulong nc
= ( ulong
)f
->children
.size();
460 fwrite( &nc
, sizeof( nc
), 1, fp
);
461 for ( ulong i
= 0; i
< nc
; i
++ )
462 writeTable( fp
, f
->children
[i
] );