/* ** 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 . */ /*********************************************************************************************** *** C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *** *********************************************************************************************** * * * Project Name : WWAudio * * * * $Archive:: /Commando/Code/Tools/MixViewer/duplicatecombiner.cpp $* * * * Author:: Patrick Smith * * * * $Modtime:: 2/21/02 5:25p $* * * * $Revision:: 3 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "stdafx.h" #include "duplicatecombiner.h" #include "ffactory.h" #include "mixfile.h" #include "rawfile.h" #include "bittype.h" #include "mixcombiningdialog.h" ////////////////////////////////////////////////////////////// // // DuplicateRemoverClass // ////////////////////////////////////////////////////////////// DuplicateRemoverClass::DuplicateRemoverClass (void) : TempFilenameStart (0), Dialog (NULL) { return ; } ////////////////////////////////////////////////////////////// // // ~DuplicateRemoverClass // ////////////////////////////////////////////////////////////// DuplicateRemoverClass::~DuplicateRemoverClass (void) { return ; } ////////////////////////////////////////////////////////////// // // Process // ////////////////////////////////////////////////////////////// void DuplicateRemoverClass::Process (void) { // // Kick off the worker thread... // ::AfxBeginThread (fnThreadProc, (LPVOID)this); // // Show the UI // MixCombiningDialogClass dialog; Dialog = &dialog; dialog.DoModal (); return ; } //////////////////////////////////////////////////////////////////////////// // // fnThreadProc // //////////////////////////////////////////////////////////////////////////// UINT DuplicateRemoverClass::fnThreadProc (LPVOID pParam) { // // Simply ask the combiner to start processing // DuplicateRemoverClass *remover = (DuplicateRemoverClass *)pParam; if (remover != NULL) { remover->Internal_Process (); } return 1; } ////////////////////////////////////////////////////////////// // // Internal_Process // ////////////////////////////////////////////////////////////// void DuplicateRemoverClass::Internal_Process (void) { Make_Temp_Directory (); // // Open the source mix files // DynamicVectorClass mix_file_list; Open_Mix_Files (mix_file_list); // // Open the destination mix factory // MixFileFactoryClass dest_factory (DestinationMixFilename, _TheFileFactory); if (dest_factory.Is_Valid () && dest_factory.Build_Internal_Filename_List ()) { Dialog->Set_Status_Text ("Building temporary files..."); Dialog->Set_Progress_Percent (0); // // Loop over each mix file... // for (int mix_index = 0; mix_index < mix_file_list.Count (); mix_index ++) { // // Get the list of filenames inside this mix file // DynamicVectorClass filename_list; mix_file_list[mix_index]->Get_Filename_List (filename_list); for (int file_index = 0; file_index < filename_list.Count (); file_index ++) { StringClass filename = ::strlwr (filename_list[file_index].Peek_Buffer ()); DynamicVectorClass dup_indices; dup_indices.Add (mix_index); // // Try to find this file in any of the other mix files // for (int test_mix_index = 0; test_mix_index < mix_file_list.Count (); test_mix_index ++) { if (test_mix_index != mix_index && Is_File_In_Factory (filename, mix_file_list[test_mix_index])) { dup_indices.Add (test_mix_index); } } // // Check to see if this file is already in the "destination" file // bool is_in_dest = Is_File_In_Factory (filename, &dest_factory); // // Were there any duplicates? // if (dup_indices.Count () > 1 || is_in_dest) { // // Copy the file from the source to the destination factory // if (is_in_dest == false) { Copy_File (mix_file_list[mix_index], &dest_factory, filename); } // // Remove all the duplicates from the mix files // for (int dup_index = 0; dup_index < dup_indices.Count (); dup_index ++) { mix_file_list[dup_indices[dup_index]]->Delete_File (filename); } } } // // Update the UI // Dialog->Set_Progress_Percent ((float)mix_index / float(mix_file_list.Count () + 1)); } // // Save the changes // Dialog->Set_Status_Text ("Reconstructing mix files..."); Dialog->Set_Progress_Percent (0); dest_factory.Flush_Changes (); Dialog->Set_Progress_Percent (1.0F / float(mix_file_list.Count () + 1)); for (int index = 0; index < mix_file_list.Count (); index ++) { mix_file_list[index]->Flush_Changes (); Dialog->Set_Progress_Percent ((float)index / float(mix_file_list.Count () + 1)); } Dialog->Set_Progress_Percent (1.0F); } // // Free each of the mix file factories // Close_Mix_Files (mix_file_list); // // Remove the temporary directory we created // Delete_Temp_Directory (); // // Close the dialog // Dialog->PostMessage (WM_COMMAND, MAKELPARAM (IDOK, BN_CLICKED)); return ; } ////////////////////////////////////////////////////////////// // // Copy_File // ////////////////////////////////////////////////////////////// void DuplicateRemoverClass::Copy_File ( MixFileFactoryClass * src_mix, MixFileFactoryClass * dest_mix, const char * filename ) { // // Get the file data from the source mix // FileClass *src_file = src_mix->Get_File (filename); src_file->Open (); // // Create a destination file for the data // StringClass full_path; Get_Temp_Filename (full_path); RawFileClass dest_file; dest_file.Set_Name (full_path); if (dest_file.Open (RawFileClass::WRITE)) { // // Copy the data from the source mix file to the destination file // int file_size = src_file->Size (); uint8 buffer[4096]; while (file_size > 0) { // // Read the data from the source file // int bytes = min (file_size, (int)sizeof (buffer)); int copied_size = src_file->Read (buffer, bytes); file_size -= copied_size; if (copied_size <= 0) { break; } // // Copy the data to the dest file // dest_file.Write (buffer, copied_size); } // // Add the file to the destination mix file // dest_mix->Add_File (full_path, filename); // // Close the temporary data file // dest_file.Close (); } // // Return the file // src_mix->Return_File (src_file); return ; } ////////////////////////////////////////////////////////////// // // Open_Mix_Files // ////////////////////////////////////////////////////////////// void DuplicateRemoverClass::Open_Mix_Files (DynamicVectorClass &list) { // // Loop over all the mix filenames in our list // for (int index = 0; index < MixFileList.Count (); index ++) { // // Create this mix file factory and add it to our list // MixFileFactoryClass *mix_factory = new MixFileFactoryClass (MixFileList[index], _TheFileFactory); if (mix_factory->Is_Valid () && mix_factory->Build_Internal_Filename_List ()) { list.Add (mix_factory); } else { delete mix_factory; mix_factory = NULL; } // // Update the UI // Dialog->Set_Progress_Percent ((float)index / float(MixFileList.Count () + 1)); } return ; } ////////////////////////////////////////////////////////////// // // Close_Mix_Files // ////////////////////////////////////////////////////////////// void DuplicateRemoverClass::Close_Mix_Files (DynamicVectorClass &list) { // // Simply free each entry in the list // for (int index = 0; index < list.Count (); index ++) { delete list[index]; } list.Delete_All (); return ; } ///////////////////////////////////////////////////////// // // Make_Temp_Directory // ///////////////////////////////////////////////////////// void DuplicateRemoverClass::Make_Temp_Directory (void) { // // Get the path of the temp directory // char temp_dir[MAX_PATH] = { 0 }; ::GetTempPath (sizeof (temp_dir), temp_dir); CString temp_path = Make_Path (temp_dir, "mixcombiner"); // // Try to find a unique temp directory to store our data // int index = 0; do { TempDirectory.Format ("%s%.2d.DIR", (const char *)temp_path, index++); } while (GetFileAttributes (TempDirectory) != 0xFFFFFFFF); // // Create the directory // ::CreateDirectory (TempDirectory, NULL); ::SetCurrentDirectory (TempDirectory); return ; } ///////////////////////////////////////////////////////// // // Delete_Temp_Directory // ///////////////////////////////////////////////////////// void DuplicateRemoverClass::Delete_Temp_Directory (void) { // // Change the current directory so we can remove the temporary one // ::SetCurrentDirectory ("c:\\"); // // Remove the temporary directory // Clean_Directory (TempDirectory); ::RemoveDirectory (TempDirectory); return ; } ////////////////////////////////////////////////////////////////////////////////// // // Clean_Directory // ////////////////////////////////////////////////////////////////////////////////// bool DuplicateRemoverClass::Clean_Directory (LPCTSTR local_dir) { bool retval = true; // // Build a search mask from the directory // StringClass search_mask = StringClass (local_dir) + "\\*.*"; // // Loop through all the files in this directory and add them // to our list // DynamicVectorClass file_list; BOOL keep_going = TRUE; WIN32_FIND_DATA find_info = { 0 }; for (HANDLE hfind = ::FindFirstFile (search_mask, &find_info); (hfind != INVALID_HANDLE_VALUE) && keep_going; keep_going = ::FindNextFile (hfind, &find_info)) { // // If this file isn't a directory, add it to the list // if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) { StringClass filename = find_info.cFileName; file_list.Add (filename); } else if (find_info.cFileName[0] != '.') { // // Recurse into this subdirectory // StringClass full_path = Make_Path (local_dir, find_info.cFileName); Clean_Directory (full_path); // // Add this directory to the list so it will get // deleted with the files... // StringClass filename = find_info.cFileName; file_list.Add (filename); } } // // Close the search handle // if (hfind != NULL) { ::FindClose (hfind); } // // Now loop through all the files and delete them // for (int index = 0; index < file_list.Count (); index ++) { StringClass &filename = file_list[index]; StringClass full_path = Make_Path (local_dir, filename); Delete_File (full_path); } return retval; } ////////////////////////////////////////////////////////////////////////////////// // // Make_Path // ////////////////////////////////////////////////////////////////////////////////// StringClass DuplicateRemoverClass::Make_Path (LPCTSTR path, LPCTSTR filename) { StringClass full_path = path; // // Delimit the path if necessary // if (full_path[full_path.Get_Length () - 1] != '\\') { full_path += "\\"; } // // Concatenate the filename onto the path // full_path += filename; return full_path; } ///////////////////////////////////////////////////////// // // Get_Temp_Filename // ///////////////////////////////////////////////////////// void DuplicateRemoverClass::Get_Temp_Filename (StringClass &full_path) { CString temp_path = Make_Path (TempDirectory, "tempfile"); // // Try to find a unique temp filename // do { full_path.Format ("%s%.5d.dat", (const char *)temp_path, TempFilenameStart++); } while (GetFileAttributes (full_path) != 0xFFFFFFFF); return ; } //////////////////////////////////////////////////////////////////////////// // // Delete_File // //////////////////////////////////////////////////////////////////////////// bool DuplicateRemoverClass::Delete_File (LPCTSTR filename) { bool retval = false; ASSERT (filename != NULL); if (filename != NULL) { // // Strip the readonly bit off if necessary // DWORD attributes = ::GetFileAttributes (filename); if ((attributes != 0xFFFFFFFF) && ((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY)) { ::SetFileAttributes (filename, attributes & (~FILE_ATTRIBUTE_READONLY)); } // // Perform the delete operation! // if ((attributes != 0xFFFFFFFF) && ((attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY)) { retval = (::RemoveDirectory (filename) == TRUE); } else { retval = (::DeleteFile (filename) == TRUE); } } return retval; } //////////////////////////////////////////////////////////////////////////// // // Is_File_In_Factory // //////////////////////////////////////////////////////////////////////////// bool DuplicateRemoverClass::Is_File_In_Factory (const StringClass &filename, MixFileFactoryClass *factory) { bool retval = false; // // Get the list of filenames inside this mix file // DynamicVectorClass *test_filename_list = NULL; factory->Get_Filename_List (&test_filename_list); // // Try to find the file inside this mix file // for (int index = 0; index < test_filename_list->Count (); index ++) { if (filename.Compare_No_Case ((*test_filename_list)[index]) == 0) { retval = true; break; } } return retval; }