/* Unmass - unpacker for game archives. Copyright (C) 2002-2007 Mirex This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include #include #include "config.h" #ifdef _WIN32 #include #endif #ifdef _UNIX #include #include #endif #include "massfs.h" #include "utools.h" #include "ma.h" #include "ma_lgp.h" #include "ma_pak.h" #include "ma_wad2.h" #include "ma_ipwad.h" #include "ma_grp.h" #include "ma_lbx.h" #include "ma_vol.h" #include "ma_wtn.h" #include "ma_dune2.h" #include "ma_moor3.h" #include "ma_swine.h" #include "ma_roll.h" #include "ma_umod.h" #include "ma_pbo.h" #include "ma_pbo.h" #include "ma_crism.h" #include "ma_ff8.h" #include "ma_bif.h" #include "ma_eth2.h" #include "ma_oni_d.h" #include "ma_ecou.h" #include "ma_vf1bi.h" #include "ma_mgmnl.h" #include "ma_mgs.h" #include "ma_mea.h" #include "ma_gunme.h" #include "ma_fpk.h" CMassFiles::CMassFiles( void ) { int i; for( i=0; i= FileNameWithPathMaxLen ) { SetErrorStr( "Open: filename too long" ); return 0; } //try if it exists f = fopen( MassFileName, "rb" ); if ( f == NULL ) { SetErrorStr( "Open: could not open file" ); return 0; } fclose( f ); //check out the file type and open it for( i=0; iOpenFile( MassFileName ); if ( Archive[ i ]->CheckFileType() ) break; Archive[ i ]->CloseFile(); } if ( i == massftypes_count ) { SetErrorStr( "Open: unknown filetype" ); return 0; } MassfInfo.type = i; //=== get archive info === strncpy( MassfInfo.filenm, MassFileName, FileNameWithPathMaxLen ); //i'll split file name into filename and file extension //find last slash (or start) and last dot int dot, slash; slash = strlen( MassFileName ) - 1; dot = -1; while ( slash > -1 ) { if (( MassFileName[ slash ] == '\\' ) || ( MassFileName[ slash ] == '/' )) break; if (( MassFileName[ slash ] == '.' ) && ( dot == -1 )) dot = slash; slash --; } ; if ( dot == -1 ) { strncpy( MassfInfo.fileextension, "", 19 ); dot = strlen( MassFileName ); } else strncpy( MassfInfo.fileextension, &MassFileName[ dot + 1 ], 19 ); memcpy( MassfInfo.filedir, MassFileName, FileNameWithPathMaxLen ); MassfInfo.filedir[ slash+1 ] = 0; memcpy( MassfInfo.filename, &MassFileName[ slash + 1 ], dot - slash - 1 ); MassfInfo.filename[ dot - slash - 1 ] = '\0'; UnmassTools::_strlwr( MassfInfo.filename ); UnmassTools::_strlwr( MassfInfo.fileextension ); Archive[ MassfInfo.type ]->GetFileMainInfo(); strncpy( MassfInfo.typestring, Archive[ MassfInfo.type ]->GetIdent(), FileNameWithPathMaxLen ); MassfInfo.files_count = Archive[ MassfInfo.type ]->GetFilesCount(); MassfInfo.flags = Archive[ MassfInfo.type ]->GetFlags(); MassfInfo.set = 1; return 1; } void CMassFiles::Close( void ) { int i; for( i=0; iCloseFile(); MassfInfo.type = massftype_unknown; memset( &MassfInfo, 0, sizeof( MassfInfo ) ); MassfInfo.set = 0; } char *CMassFiles::GetTypeIdentString( int type ) { static char ident[ 256 ]; if (( type < 0 ) || ( type >= massftypes_count )) { ident[0] = 0; return ident; } strncpy( ident, Archive[ type ]->GetIdent(), 255 ); return ident; } char *CMassFiles::GetTypeExt( int type ) { static char ext[ 256 ]; if (( type < 0 ) || ( type >= massftypes_count )) { ext[0] = 0; return ext; } strncpy( ext, Archive[ type ]->GetExtension(), 255 ); return ext; } int CMassFiles::GetRec( long num ) { if ( MassfInfo.type == massftype_unknown ) return 0; Archive[ MassfInfo.type ]->FileRec.Reset(); Archive[ MassfInfo.type ]->GetRec( num ); FileRec = Archive[ MassfInfo.type ]->FileRec; return FileRec.set; } int CMassFiles::ReadRecords( void ) { return Archive[ MassfInfo.type ]->ReadRecords(); } int CMassFiles::ReadAllRecords( void ) { int res = 1; for( int i=0; iReadRecords(); return res; } int CMassFiles::Extract( void ) { enum { Massfs_BufSize = 20000 }; int ei, i; char newname[ FileNameWithPathMaxLen ], newdir[ FileNameWithPathMaxLen ]; FILE *newf; char str[ FileNameWithPathMaxLen ]; unsigned char buf[ Massfs_BufSize ]; unsigned long pos, size; if ( MassfInfo.type == massftype_unknown ) { SetErrorStr( "Unknown file type" ); return 0; } Archive[ MassfInfo.type ]->FileRec = FileRec; strncpy( newname, FileRec.name, FileNameWithPathMaxLen-1 ); for( ei=0; ei<(int)strlen( newname ); ei++ ) { if ( newname[ ei ] == '/' ) newname[ ei ] = '\\'; if ( newname[ ei ] == ' ' ) newname[ ei ] = '_'; } //dir strncpy( newdir, newname, FileNameWithPathMaxLen-1 ); ei = strlen( newdir ); while (( newdir[ ei ] != '\\' ) && ( ei > 0 )) ei--; newdir[ ei ] = '\0'; if ( strlen( newdir ) != 0 ) MakeDir( newdir ); //create new file sprintf( str, "Creating [%s] ...", newname ); // Msg( str ); newf = fopen( newname, "wb" ); //?? check for existing !! if ( newf == NULL ) { sprintf( error, "Error creating [%s].", newname ); return 0; } if ( MassfInfo.flags & CMassArchive::ma_flag_extract_only_whole_file ) { i = Archive[ MassfInfo.type ]->ExtractWholeFile( newf ); if ( i == 0 ) { fclose( newf ); return 0; } } else { pos = 0; size = Massfs_BufSize; while ( pos < FileRec.size ) { if ( pos + size > FileRec.size ) size = FileRec.size - pos; i = Archive[ MassfInfo.type ]->Extract ( pos, size, buf ); if ( i == 0 ) { fclose( newf ); return 0; } pos += i; fwrite( buf, i, 1, newf ); } } fclose( newf ); sprintf( str, "Extracted [%s]", newname ); return 1; }//Extract //if there is no [mpath] directory, proc creates it //watch out, it will return error on double slashes, so avoid them ! void CMassFiles::MakeDir( char *path ) { char oldPath[301]; //drive:\path before mk, chdir ... char str[301]; int mi, mj; int pos; //pointer into mpath, where am i in process int oldDisk = -1; char c; if ( path[0] == '\0' ) //if no dir return; //#ifdef WIN32 getcwd( oldPath, 300 ); //#else // getcurdir( 0, str ); //#endif // sprintf( oldPath, "\\%s", str ); //try first, if it is not present already if ( chdir( path ) == 0 ) { //fine, path is present chdir( oldPath ); return; } //if not, create path. sprintf( str, "Creating dir [%s]", path ); // Msg( str ); pos = 0; #ifdef _WIN32 // you can change drives on WIN32 or DOS systems //check if drive is included if ( path[1] == ':' ) { oldDisk = _getdrive(); c = path[0]; if (( 'a' <= c ) && ( c <= 'z' )) c += 'A' - 'a'; mi = c - 'A' + 1; // mj = setdrive( mi ); mj = _chdrive( mi ); if ( mj == -1 ) { sprintf( str, "Bad drive [%c%c]", path[0], path[1] ); // Msg( str ); } pos = 2; } #endif //change to root if ( path[pos] == '\\') { chdir( "\\" ); pos++; } //go as far as possible do { mj = pos; //start of dir = now while ( (pos < (int)strlen(path)) && (path[pos] != '/') && (path[pos] != '\\') ) pos++; strncpy( str, &path[mj], 300 ); str[pos-mj] = '\0'; mi = chdir( str ); pos++; } while ( mi == 0 ); //do while chdir is ok //can't go further, create. Allways have to create something, if im here pos = mj; //pos to first bad dir do { mj = pos; //start of dir = now while ( (pos < (int)strlen(path)) && (path[pos] != '/' ) && ( path[pos] != '\\' )) pos++; strncpy( str, &path[mj], 300 ); str[pos-mj] = '\0'; #ifdef _WIN32 if ( mkdir( str ) != 0 ) return; #endif #ifdef _UNIX // oct 666 if ( mkdir( str, 438 ) != 0 ) return; #endif // Msg( "MakeDir: Creating dir" ); chdir( str ); pos++; } while ( pos < (int)strlen( path ) ); // Msg( "Created." ); #ifdef _WIN32 if ( oldDisk != -1 ) // setdisk( oldDisk ); _chdrive( oldDisk ); #endif // if ( chdir( oldPath ) != 0 ) // Msg( "MakeDir: error change old path" ); }//MakeDir //return index of added file, -1 on error int CMassFiles::AddFile( char* filename, char* archive_name ) { FILE *fi = NULL; unsigned long fs, p, bs; char *buffer = NULL; int res; #define BUFFER_SIZE 64000 if ( MassfInfo.type == massftype_unknown ) { SetErrorStr( "Unknown file type" ); return 0; } if (( MassfInfo.flags & CMassArchive::ma_flag_add_file ) == 0 ) { assert( false ); SetErrorStr( "This filetype does not support adding files" ); goto AddFile_ret_error; } buffer = (char*) malloc( BUFFER_SIZE ); if ( buffer == NULL ) { SetErrorStr( "Not enough memory" ); goto AddFile_ret_error; } fi = fopen( filename, "rb" ); if ( fi == NULL ) { SetErrorStr( "File not found" ); goto AddFile_ret_error; } fseek( fi, 0, SEEK_END ); fs = ftell( fi ); fseek( fi, 0, SEEK_SET ); strncpy( Archive[ MassfInfo.type ]->FileRec.name, archive_name, CMassArchive::FileNameWithPathMaxLen-1 ); Archive[ MassfInfo.type ]->FileRec.size = fs; res = Archive[ MassfInfo.type ]->AddFileRecord(); if ( res == 0 ) { SetErrorStr( "Error adding file record" ); goto AddFile_ret_error; } MassfInfo.files_count++; bs = BUFFER_SIZE; p = 0; while( p < fs ) { if ( p + bs > fs ) bs = fs - p; fread( buffer, bs, 1, fi ); Archive[ MassfInfo.type ]->AddFileData( p, bs, buffer ); p += bs; } p = Archive[ MassfInfo.type ]->FileRec.index; AddFile_ret: if ( fi != NULL ) fclose( fi ); if ( buffer != NULL ) free( buffer ); return p; AddFile_ret_error: p = -1; goto AddFile_ret; } int CMassFiles::ReopenForWriting( void ) { if ( MassfInfo.type == massftype_unknown ) return 0; return Archive[ MassfInfo.type ]->ReopenForWriting(); } int CMassFiles::UnlinkFile( unsigned long index ) { if ( MassfInfo.type == massftype_unknown ) { SetErrorStr( "Unknown file type" ); return 0; } if ( index >= MassfInfo.files_count ) { assert( false ); SetErrorStr( "Index out of bounds" ); return 0; } if ( Archive[ MassfInfo.type ]->UnlinkFile( index ) == 0 ) { SetErrorStr( Archive[ MassfInfo.type ]->GetErrorStr() ); return 0; } MassfInfo.files_count--; return 1; } int CMassFiles::CreateNewArchive( char* newfilename, int type ) { FILE *nf; int i; if (( type < 0 ) || ( type >= massftypes_count )) { SetErrorStr( "type out of bounds" ); return 0; } nf = fopen( newfilename, "wb" ); if ( nf == NULL ) { SetErrorStr( "Could not create new file" ); return 0; } i = Archive[ type ]->CreateNewArchive( nf ); if ( i == 0 ) SetErrorStr( Archive[ type ]->GetErrorStr() ); fclose( nf ); return i; } const char* CMassFiles::TypeExtToWinDlgExt( int type ) { if (( type < 0 ) || ( type >= massftypes_count )) { SetErrorStr( "type out of bounds" ); return NULL; } char s[ 1024 ], *chp; int i, sl, sp; //get ext chp = Archive[ type ]->GetExtension(); sl = strlen( chp ); strncpy( s, chp, 1000 ); i = 0; sp = 0; do { strncpy( &str[ sp ], "*.", 1000 - sp ); sp += 2; while (( s[ i ] != 0 ) && ( s[ i ] != ',' )) { str[ sp++ ] = s[ i++ ]; } if ( s[ i ] == ',' ) strcat( str, "; " ); } while ( s[ i ] != 0 ); str[ sp++ ] = 0; return str; } const char* CMassFiles::GetTypeFirstExt( unsigned long type ) { if ( type >= massftypes_count ) { SetErrorStr( "type out of bounds" ); return NULL; } char *chp; int i, sl, sp; chp = Archive[ type ]->GetExtension(); sl = strlen( chp ); strncpy( str, chp, 1023 ); i = 0; while (( str[ i ] != 0 ) && ( str[ i ] != ',' )) i++; str[ i ] = 0; return str; }