mirror of
https://github.com/electronicarts/CnC_Renegade.git
synced 2025-12-16 23:51:41 -05:00
Initial commit of Command & Conquer Renegade source code.
This commit is contained in:
592
Code/Tools/MixViewer/duplicatecombiner.cpp
Normal file
592
Code/Tools/MixViewer/duplicatecombiner.cpp
Normal file
@@ -0,0 +1,592 @@
|
||||
/*
|
||||
** 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/>.
|
||||
*/
|
||||
|
||||
/***********************************************************************************************
|
||||
*** 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<MixFileFactoryClass *> 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<StringClass> 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<int> 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<MixFileFactoryClass *> &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<MixFileFactoryClass *> &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<StringClass> 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<StringClass> *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;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user