| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636 |
- /*
- ** Command & Conquer Renegade(tm)
- ** Copyright 2025 Electronic Arts Inc.
- **
- ** 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 3 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, see <http://www.gnu.org/licenses/>.
- */
- /***********************************************************************************************
- *** Confidential - Westwood Studios ***
- ***********************************************************************************************
- * *
- * Project Name : Commando *
- * *
- * $Archive:: /Commando/Code/wwlib/mixfile.cpp $*
- * *
- * $Author:: Patrick $*
- * *
- * $Modtime:: 9/12/01 7:39p $*
- * *
- * $Revision:: 4 $*
- * *
- *---------------------------------------------------------------------------------------------*
- * Functions: *
- * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
- #include "mixfile.h"
- #include "wwdebug.h"
- #include "ffactory.h"
- #include "wwfile.h"
- #include "realcrc.h"
- #include "rawfile.h"
- #include "win.h"
- #include "bittype.h"
- /*
- **
- */
- typedef struct
- {
- char signature[4];
- long header_offset;
- long names_offset;
- } MIXFILE_HEADER;
- typedef struct
- {
- long file_count;
- } MIXFILE_DATA_HEADER;
- /*
- **
- */
- MixFileFactoryClass::MixFileFactoryClass( const char * mix_filename, FileFactoryClass * factory ) :
- FileCount (0),
- NamesOffset (0),
- IsValid (false),
- BaseOffset (0),
- Factory (NULL),
- IsModified (false)
- {
- // WWDEBUG_SAY(( "MixFileFactory( %s )\n", mix_filename ));
- MixFilename = mix_filename;
- Factory = factory;
- FilenameList.Set_Growth_Step (1000);
- // First, open the mix file
- FileClass * file = factory->Get_File( mix_filename );
- // WWASSERT( file );
- if ( file && file->Is_Available() ) {
- file->Open();
- IsValid = true;
- //
- // Read the file header
- //
- MIXFILE_HEADER header = { 0 };
- IsValid = (file->Read( &header, sizeof( header ) ) == sizeof( header ));
- //
- // Validate the file header
- //
- if ( IsValid ) {
- IsValid = (::memcmp( header.signature, "MIX1", sizeof ( header.signature ) ) == 0);
- }
- //
- // Seek to the data start
- //
- FileCount = 0;
- if ( IsValid ) {
- file->Seek( header.header_offset, SEEK_SET );
- IsValid = ( file->Read( &FileCount, sizeof( FileCount ) ) == sizeof( FileCount ) );
- }
-
- //
- // Read the array of data headers
- //
- if ( IsValid ) {
- FileInfo.Resize( FileCount );
- int size = FileCount * sizeof( FileInfoStruct );
- IsValid = ( file->Read( &FileInfo[0], size ) == size );
- }
- //
- // Check for success
- //
- if ( IsValid ) {
- BaseOffset = 0;
- NamesOffset = header.names_offset;
- WWDEBUG_SAY(( "MixFileFactory( %s ) loaded successfully %d files\n", MixFilename, FileInfo.Length() ));
- } else {
- FileInfo.Resize(0);
- }
- factory->Return_File( file );
- } else {
- WWDEBUG_SAY(( "MixFileFactory( %s ) FAILED\n", mix_filename ));
- }
- }
- MixFileFactoryClass::~MixFileFactoryClass( void )
- {
- FileInfo.Resize(0);
- }
- bool MixFileFactoryClass::Build_Filename_List (DynamicVectorClass<StringClass> &list)
- {
- if (IsValid == false) {
- return false;
- }
- bool retval = false;
- //
- // Attempt to open the file
- //
- RawFileClass *file = (RawFileClass *)Factory->Get_File( MixFilename );
- if ( file != NULL && file->Open ( RawFileClass::READ ) ) {
- //
- // Seek to the names offset header
- //
- file->Seek (NamesOffset, SEEK_SET);
- retval = true;
-
- //
- // Read the count of files
- //
- int file_count = 0;
- if (file->Read( &file_count, sizeof( file_count) ) == sizeof( file_count )) {
- //
- // Loop over each saved filename
- //
- bool keep_going = true;
- for (int index = 0; index < file_count && keep_going; index ++) {
- keep_going = false;
-
- //
- // Get the length of the filename
- //
- uint8 name_len = 0;
- if (file->Read( &name_len, sizeof( name_len ) ) == sizeof( name_len )) {
-
- //
- // Read the filename
- //
- StringClass filename;
- if (file->Read( filename.Get_Buffer( name_len ), name_len ) == name_len ) {
-
- //
- // Add the filename to our list
- //
- list.Add( filename );
- keep_going = true;
- }
- }
- }
- }
- //
- // Close the file
- //
- Factory->Return_File( file );
- }
- return retval;
- }
- FileClass * MixFileFactoryClass::Get_File( char const *filename )
- {
- if ( FileInfo.Length() == 0 ) {
- return NULL;
- }
- // WWDEBUG_SAY(( "MixFileFactoryClass::Get_File( %s )\n", filename ));
- RawFileClass *file = NULL;
- // Create the key block that will be used to binary search for the file.
- unsigned long crc = CRC_Stringi( filename );
- // Binary search for the file in this mixfile. If it is found, then create the file
- FileInfoStruct * info = NULL;
- FileInfoStruct * base = &FileInfo[0];
- int stride = FileInfo.Length();
- while (stride > 0) {
- int pivot = stride / 2;
- FileInfoStruct * tryptr = base + pivot;
- if (crc < tryptr->CRC) {
- stride = pivot;
- } else {
- if (tryptr->CRC == crc) {
- info = tryptr;
- break;
- }
- base = tryptr + 1;
- stride -= pivot + 1;
- }
- }
- if ( info != NULL) {
- // WWDEBUG_SAY(( "MixFileFactoryClass::Get_File( %s ) FOUND\n", filename ));
- file = (RawFileClass *)Factory->Get_File( MixFilename );
- if ( file ) {
- file->Bias( BaseOffset + info->Offset, info->Size );
- }
- // WWDEBUG_SAY(( "MixFileFactoryClass::Get_File( %s ) FOUND\n", filename ));
- } else {
- // WWDEBUG_SAY(( "MixFileFactoryClass::Get_File( %s ) NOT FOUND\n", filename ));
- }
- return file;
- }
- void MixFileFactoryClass::Return_File( FileClass * file )
- {
- if ( file != NULL ) {
- Factory->Return_File( file );
- }
- }
- /*
- **
- */
- void
- MixFileFactoryClass::Add_File (const char *full_path, const char *filename)
- {
- AddInfoStruct info;
- info.FullPath = full_path;
- info.Filename = filename;
- PendingAddFileList.Add (info);
- IsModified = true;
- return ;
- }
- /*
- **
- */
- void
- MixFileFactoryClass::Delete_File (const char *filename)
- {
- //
- // Remove this file (if it exists) from our filename list
- //
- for (int list_index = 0; list_index < FilenameList.Count (); list_index ++) {
- if (FilenameList[list_index].Compare_No_Case (filename) == 0) {
- FilenameList.Delete (list_index);
- IsModified = true;
- break;
- }
- }
- return ;
- }
- /*
- **
- */
- void
- MixFileFactoryClass::Flush_Changes (void)
- {
- //
- // Exit if there's nothing to do.
- //
- if (IsModified == false) {
- return ;
- }
- //
- // Get the path of the mix file
- //
- char drive[_MAX_DRIVE] = { 0 };
- char dir[_MAX_DIR] = { 0 };
- ::_splitpath (MixFilename, drive, dir, NULL, NULL);
- StringClass path = drive;
- path += dir;
- //
- // Try to find a temp filename
- //
- StringClass full_path;
- if (Get_Temp_Filename (path, full_path)) {
- MixFileCreator new_mix_file (full_path);
- //
- // Add all the remaining files from our file set
- //
- for (int index = 0; index < FilenameList.Count (); index ++) {
- StringClass &filename = FilenameList[index];
- //
- // Copy this file data to the mix file
- //
- FileClass *file_data = Get_File (filename);
- if (file_data != NULL) {
- file_data->Open ();
- new_mix_file.Add_File (filename, file_data);
- Return_File (file_data);
- //
- // Remove this file from the pending list (if necessary)
- //
- for (int temp_index = 0; temp_index < PendingAddFileList.Count (); temp_index ++) {
- if (filename.Compare_No_Case (PendingAddFileList[temp_index].Filename) == 0) {
- PendingAddFileList.Delete (temp_index);
- break;
- }
- }
- }
- }
- //
- // Add the new files that are pending
- //
- for (index = 0; index < PendingAddFileList.Count (); index ++) {
- new_mix_file.Add_File (PendingAddFileList[index].FullPath, PendingAddFileList[index].Filename);
- }
- }
- //
- // Delete the old mix file and rename the new one
- //
- ::DeleteFile (MixFilename);
- ::MoveFile (full_path, MixFilename);
- //
- // Reset the lists
- //
- IsModified = false;
- PendingAddFileList.Delete_All ();
- return ;
- }
- /*
- **
- */
- bool
- MixFileFactoryClass::Get_Temp_Filename (const char *path, StringClass &full_path)
- {
- bool retval = false;
- StringClass temp_path = path;
- temp_path += "_tmpmix";
-
- //
- // Try to find a unique temp filename
- //
- for (int index = 0; index < 20; index ++) {
- full_path.Format ("%s%.2d.dat", (const char *)temp_path, index + 1);
- if (GetFileAttributes (full_path) == 0xFFFFFFFF) {
- retval = true;
- break;
- }
- }
- return retval;
- }
- /*
- **
- */
- SimpleFileFactoryClass _SimpleFileFactory;
- MixFileCreator::MixFileCreator( const char * filename )
- {
- WWDEBUG_SAY(( "Creating Mix File %s\n", filename ));
- MixFile = _SimpleFileFactory.Get_File(filename);
- if ( MixFile != NULL ) {
- MixFile->Open( FileClass::WRITE );
- MixFile->Write( "MIX1", 4 );
- long header_offset = 0;
- MixFile->Write( &header_offset, sizeof( header_offset ) );
- long names_offset = 0;
- MixFile->Write( &names_offset, sizeof( names_offset ) );
- long unused = 0;
- MixFile->Write( &unused, sizeof( unused ) );
- }
- }
- int MixFileCreator::File_Info_Compare(const void * a, const void * b)
- {
- unsigned int CRCA = ((FileInfoStruct*)a)->CRC;
- unsigned int CRCB = ((FileInfoStruct*)b)->CRC;
- if ( CRCA < CRCB ) return -1;
- if ( CRCA > CRCB ) return 1;
- return 0;
- // return ((FileInfoStruct*)a)->CRC - ((FileInfoStruct*)b)->CRC;
- }
- MixFileCreator::~MixFileCreator( void )
- {
- if ( MixFile != NULL ) {
- // Save Header Data
- int header_offset = MixFile->Tell();
- // Save file count
- int i,num_files = FileInfo.Count();
- WWDEBUG_SAY(( "Closing with %d files\n", num_files ));
- MixFile->Write( &num_files, sizeof( num_files ) );
- if ( num_files > 1 ) {
- qsort( &FileInfo[0], num_files, sizeof(FileInfo[0]), &File_Info_Compare);
- }
- // Save file info (CRC, Offset, Size )
- for ( i = 0; i < num_files; i++ ) {
- MixFile->Write( &FileInfo[i].CRC, 4 );
- MixFile->Write( &FileInfo[i].Offset, 4 );
- MixFile->Write( &FileInfo[i].Size, 4 );
- // WWDEBUG_SAY(( "Write CRC %08X\n", FileInfo[i].CRC ));
- }
- // ---------------------------------------
- // Save Names Data
- int names_offset = MixFile->Tell();
- // Save file count
- MixFile->Write( &num_files, sizeof( num_files ) );
- // Save file info
- for ( i = 0; i < num_files; i++ ) {
- const char * filename = FileInfo[i].Filename;
- int size = FileInfo[i].Filename.Get_Length()+1;
- WWASSERT( size < 255 );
- unsigned char csize = size;
- MixFile->Write( &csize, 1 );
- MixFile->Write( filename, size );
- }
- // ---------------------------------------
- MixFile->Seek( 4, SEEK_SET );
- // Save header offset
- WWDEBUG_SAY(( "Writing header offset %d (%08X)\n", header_offset, header_offset ));
- MixFile->Write( &header_offset, sizeof( header_offset ) );
- // Save names offset
- WWDEBUG_SAY(( "Writing names offset %d (%08X)\n", names_offset, names_offset ));
- MixFile->Write( &names_offset, sizeof( names_offset ) );
- // ---------------------------------------
- MixFile->Close();
- _SimpleFileFactory.Return_File(MixFile);
- }
- }
- void MixFileCreator::Add_File( const char * source_filename, const char * saved_filename )
- {
- if ( saved_filename == NULL ) {
- saved_filename = source_filename;
- }
- if ( MixFile != NULL ) {
- FileClass * file = _SimpleFileFactory.Get_File( source_filename );
- if ( file && file->Is_Available() ) {
- file->Open();
- MixFileCreator::FileInfoStruct info;
- info.CRC = CRC_Stringi( saved_filename );
- info.Offset = MixFile->Tell();
- info.Size = file->Size();
- FileInfo.Add( info );
- FileInfo[ FileInfo.Count()-1 ].Filename = saved_filename;
- WWDEBUG_SAY(( "Saving File %s CRC %08X Offset %d (0x%08X) Size %d (0x%08X)\n",
- saved_filename, info.CRC, info.Offset, info.Offset, info.Size, info.Size ));
- int size = file->Size();
- while ( size ) {
- char buffer[ 4096 ];
- int amount = MIN( sizeof( buffer ), size );
- size -= amount;
- file->Read( buffer, amount );
- if ( MixFile->Write( buffer, amount ) != amount ) {
- WWDEBUG_SAY(( "Failed to write MixFile\n" ));
- }
- }
- // Pad the MixFile to make DWord Aligned
- int offset = MixFile->Tell();
- offset = (8-(offset & 7)) & 7;
- if ( offset != 0 ) {
- char zeros[8] = {0,0,0,0,0,0,0,0};
- if ( MixFile->Write( zeros, offset ) != offset ) {
- WWDEBUG_SAY(( "Failed to write padding\n" ));
- }
- }
- file->Close();
- _SimpleFileFactory.Return_File( file );
- } else {
- WWDEBUG_SAY(( "MixFileCreator::Failed to open \"%s\"\n", source_filename ));
- }
- }
- }
- void MixFileCreator::Add_File( const char * filename, FileClass *file )
- {
- if ( MixFile != NULL ) {
- MixFileCreator::FileInfoStruct info;
- info.CRC = CRC_Stringi( filename );
- info.Offset = MixFile->Tell();
- info.Size = file->Size();
- FileInfo.Add( info );
- FileInfo[ FileInfo.Count()-1 ].Filename = filename;
- WWDEBUG_SAY(( "Saving File %s CRC %08X Offset %d (0x%08X) Size %d (0x%08X)\n",
- filename, info.CRC, info.Offset, info.Offset, info.Size, info.Size ));
- int size = file->Size();
- while ( size ) {
- char buffer[ 4096 ];
- int amount = MIN( sizeof( buffer ), size );
- size -= amount;
- file->Read( buffer, amount );
- if ( MixFile->Write( buffer, amount ) != amount ) {
- WWDEBUG_SAY(( "Failed to write MixFile\n" ));
- }
- }
- // Pad the MixFile to make DWord Aligned
- int offset = MixFile->Tell();
- offset = (8-(offset & 7)) & 7;
- if ( offset != 0 ) {
- char zeros[8] = {0,0,0,0,0,0,0,0};
- if ( MixFile->Write( zeros, offset ) != offset ) {
- WWDEBUG_SAY(( "Failed to write padding\n" ));
- }
- }
- }
- return ;
- }
- /*
- **
- */
- void Add_Files( const char * dir, MixFileCreator & mix )
- {
- BOOL bcontinue = TRUE;
- HANDLE hfile_find;
- WIN32_FIND_DATA find_info = {0};
- StringClass path;
- path.Format( "data\\makemix\\%s*.*", dir );
- WWDEBUG_SAY(( "Adding files from %s\n", path ));
- for (hfile_find = ::FindFirstFile( path, &find_info);
- (hfile_find != INVALID_HANDLE_VALUE) && bcontinue;
- bcontinue = ::FindNextFile(hfile_find, &find_info)) {
- if ( find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) {
- if ( find_info.cFileName[0] != '.' ) {
- StringClass path;
- path.Format( "%s%s\\", dir, find_info.cFileName );
- Add_Files( path, mix );
- }
- } else {
- StringClass name;
- name.Format( "%s%s", dir, find_info.cFileName );
- StringClass source;
- source.Format( "makemix\\%s", name );
- mix.Add_File( source, name );
- // WWDEBUG_SAY(( "Adding file from %s %s\n", source, name ));
- }
- }
- }
- void Setup_Mix_File( void )
- {
- _SimpleFileFactory.Set_Sub_Directory( "DATA\\" );
- // _SimpleFileFactory.Set_Strip_Path( true );
- WWDEBUG_SAY(( "Mix File Create .....\n" ));
- {
- MixFileCreator mix( "MAKEMIX.MIX" );
- Add_Files( "", mix );
- }
- }
|