451 lines
No EOL
14 KiB
C++
451 lines
No EOL
14 KiB
C++
/*
|
|
** 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/>.
|
|
*/
|
|
|
|
#include "stdafx.h"
|
|
#include "mixpatchmaker.h"
|
|
#include "ffactory.h"
|
|
#include "mixfile.h"
|
|
#include "rawfile.h"
|
|
#include "bittype.h"
|
|
#include "mixcombiningdialog.h"
|
|
|
|
|
|
|
|
|
|
MixPatchMakerClass::MixPatchMakerClass(void) :
|
|
Dialog(NULL)
|
|
{
|
|
}
|
|
|
|
|
|
|
|
void MixPatchMakerClass::Make_Patch(char *old_input_file, char *new_input_file, char *output_file, char *old_art_dir, char *new_art_dir)
|
|
{
|
|
strcpy(OldInputFile, old_input_file);
|
|
strcpy(NewInputFile, new_input_file);
|
|
strcpy(OutputFile, output_file);
|
|
strcpy(OldArtDir, old_art_dir);
|
|
strcpy(NewArtDir, new_art_dir);
|
|
|
|
//
|
|
// Kick off the worker thread...
|
|
//
|
|
::AfxBeginThread(Do_Stuff, (LPVOID)this);
|
|
|
|
//
|
|
// Show the UI
|
|
//
|
|
MixCombiningDialogClass dialog;
|
|
Dialog = &dialog;
|
|
dialog.DoModal();
|
|
}
|
|
|
|
|
|
|
|
|
|
unsigned int MixPatchMakerClass::Do_Stuff(void *param)
|
|
{
|
|
((MixPatchMakerClass*)param)->Thread_Make();
|
|
return(1);
|
|
}
|
|
|
|
|
|
void MixPatchMakerClass::Thread_Make(void)
|
|
{
|
|
char name[_MAX_PATH];
|
|
char name_compare[_MAX_PATH];
|
|
|
|
while (Dialog == NULL) {
|
|
Sleep(0);
|
|
}
|
|
|
|
/*
|
|
** Get just the file name.
|
|
*/
|
|
char justname[_MAX_PATH];
|
|
char justname_new[_MAX_PATH];
|
|
_splitpath(OldInputFile, NULL, NULL, justname, NULL);
|
|
_splitpath(NewInputFile, NULL, NULL, justname_new, NULL);
|
|
char text[_MAX_PATH + 128];
|
|
sprintf(text, "Looking for differences between %s and %s...", justname, justname_new);
|
|
|
|
Dialog->Set_Title("Building patch mixfile...");
|
|
Dialog->Set_Status_Text(text);
|
|
Dialog->Set_Progress_Percent (0);
|
|
|
|
MixFileFactoryClass old_mix(OldInputFile, _TheFileFactory);
|
|
MixFileFactoryClass new_mix(NewInputFile, _TheFileFactory);
|
|
if (new_mix.Is_Valid() && new_mix.Build_Internal_Filename_List()) {
|
|
if (old_mix.Is_Valid() && old_mix.Build_Internal_Filename_List()) {
|
|
|
|
/*
|
|
** Get the list of files in the new source mix file.
|
|
*/
|
|
DynamicVectorClass<StringClass> new_filename_list;
|
|
new_mix.Get_Filename_List(new_filename_list);
|
|
|
|
/*
|
|
** Get the list of files in the old source mix file.
|
|
*/
|
|
DynamicVectorClass<StringClass> old_filename_list;
|
|
old_mix.Get_Filename_List(old_filename_list);
|
|
|
|
/*
|
|
** Create the output mix file.
|
|
*/
|
|
MixFileCreator mix_out(OutputFile);
|
|
|
|
/*
|
|
** Loop through all the files in the new source mix. If a file doesn't exist in the old one, or it does exist but is
|
|
** different, then copy it into the output mix file.
|
|
*/
|
|
for (int i=0 ; i<new_filename_list.Count() ; i++) {
|
|
|
|
strcpy(name, new_filename_list[i].Peek_Buffer());
|
|
strupr(name);
|
|
bool copy = false;
|
|
|
|
if (stricmp(name, "STRINGS.TDB") == 0) {
|
|
continue;
|
|
}
|
|
|
|
/*
|
|
** Search for the file in the old mixfile.
|
|
*/
|
|
bool found = false;
|
|
for (int j=0 ; j<old_filename_list.Count() ; j++) {
|
|
strcpy(name_compare, old_filename_list[j].Peek_Buffer());
|
|
if (stricmp(name, name_compare) == 0) {
|
|
found = true;
|
|
|
|
/*
|
|
** There's a file with the same name in the old mix file. See if it's byte for byte identical. If it's not
|
|
** then we need to use the newer file in the patch mix.
|
|
*/
|
|
bool same = Compare_File(&new_mix, &old_mix, new_filename_list[i].Peek_Buffer());
|
|
if (!same) {
|
|
|
|
/*
|
|
** If it's a .dds (texture) file, compare the source art for changes. The .dds can change even when the
|
|
** source art doesn't.
|
|
*/
|
|
if (strstr(name, ".DDS")) {
|
|
char targa_name[_MAX_PATH];
|
|
strcpy(targa_name, name);
|
|
char *ext = strstr(targa_name, ".DDS");
|
|
if (ext) {
|
|
strcpy(ext, ".TGA");
|
|
|
|
/*
|
|
** If the two source art files are the same then break out.
|
|
*/
|
|
if (Compare_Source_Art_File(targa_name)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
copy = true;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*
|
|
** If the file doesn't exist at all in the older mixfile then it needs to be in the patch mix.
|
|
*/
|
|
if (!found) {
|
|
copy = true;
|
|
}
|
|
|
|
if (copy) {
|
|
Copy_File(&new_mix, &mix_out, new_filename_list[i].Peek_Buffer());
|
|
}
|
|
|
|
Dialog->Set_Progress_Percent((float)i / float(new_filename_list.Count() + 1));
|
|
}
|
|
}
|
|
}
|
|
Dialog->PostMessage(WM_COMMAND, MAKELPARAM(IDOK, BN_CLICKED));
|
|
}
|
|
|
|
|
|
|
|
|
|
void MixPatchMakerClass::Copy_File(MixFileFactoryClass *src_mix, MixFileCreator *dest_mix, char *filename)
|
|
{
|
|
//
|
|
// Get the file data from the source mix
|
|
//
|
|
FileClass *src_file = src_mix->Get_File (filename);
|
|
src_file->Open ();
|
|
int size = src_file->Size();
|
|
|
|
//
|
|
// Create a temporary destination file for the data
|
|
//
|
|
char temp_file_name[_MAX_PATH];
|
|
char temp_path[_MAX_PATH];
|
|
int chars = GetTempPath(_MAX_PATH, temp_path);
|
|
if (chars) {
|
|
int res = GetTempFileName(temp_path, "MIX", 0, temp_file_name);
|
|
if (res == 0) {
|
|
WWDEBUG_SAY(("GetTempFileName failed with error code %d\n", GetLastError()));
|
|
} else {
|
|
RawFileClass temp_file(temp_file_name);
|
|
if (temp_file.Open(RawFileClass::WRITE)) {
|
|
|
|
//
|
|
// Save the data in the temp file.
|
|
//
|
|
void *bigbuf = new char [size + 1024];
|
|
src_file->Read(bigbuf, size);
|
|
temp_file.Write(bigbuf, size);
|
|
delete [] bigbuf;
|
|
temp_file.Close();
|
|
|
|
//
|
|
// Add the temp file to the mix file.
|
|
//
|
|
dest_mix->Add_File(temp_file_name, filename);
|
|
|
|
//
|
|
// Delete the temp file.
|
|
//
|
|
DeleteFile(temp_file_name);
|
|
}
|
|
}
|
|
}
|
|
|
|
src_mix->Return_File(src_file);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MixPatchMakerClass::Compare_File -- Compare two files in seperate mixfiles. *
|
|
* *
|
|
* *
|
|
* *
|
|
* INPUT: First mixfile *
|
|
* Second mixfile *
|
|
* Name of file to compare *
|
|
* *
|
|
* OUTPUT: true if files are the same *
|
|
* *
|
|
* WARNINGS: None *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/5/2002 9:54PM ST : Created *
|
|
*=============================================================================================*/
|
|
bool MixPatchMakerClass::Compare_File(MixFileFactoryClass *src_mix, MixFileFactoryClass *dest_mix, char *filename)
|
|
{
|
|
/*
|
|
** Get the file data from the source mix
|
|
*/
|
|
FileClass *src_file = src_mix->Get_File(filename);
|
|
int size = src_file->Size();
|
|
|
|
|
|
/*
|
|
** Get the file data from the dest mix
|
|
*/
|
|
FileClass *dest_file = dest_mix->Get_File(filename);
|
|
|
|
/*
|
|
** If the two files are a different size then we know they aren't the same.
|
|
*/
|
|
if (size != dest_file->Size()) {
|
|
src_mix->Return_File(src_file);
|
|
dest_mix->Return_File(dest_file);
|
|
return(false);
|
|
}
|
|
|
|
/*
|
|
** Read the source file into memory.
|
|
*/
|
|
src_file->Open();
|
|
unsigned char *src_data = new unsigned char [size + 32];
|
|
src_file->Read(src_data, size);
|
|
src_file->Close();
|
|
|
|
/*
|
|
** Read the dest file into memory.
|
|
*/
|
|
dest_file->Open();
|
|
unsigned char *dest_data = new unsigned char [size + 32];
|
|
dest_file->Read(dest_data, size);
|
|
dest_file->Close();
|
|
|
|
/*
|
|
** Compare them.
|
|
*/
|
|
bool same = (memcmp(src_data, dest_data, size) == 0) ? true : false;
|
|
|
|
/*
|
|
** Clean up.
|
|
*/
|
|
delete [] src_data;
|
|
delete [] dest_data;
|
|
src_mix->Return_File(src_file);
|
|
dest_mix->Return_File(dest_file);
|
|
|
|
return(same);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MixPatchMakerClass::Compare_Source_Art_File -- Compare source art .tga files *
|
|
* *
|
|
* *
|
|
* *
|
|
* INPUT: Filename *
|
|
* *
|
|
* OUTPUT: True if same *
|
|
* *
|
|
* WARNINGS: None *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/21/2002 4:31PM ST : Created *
|
|
*=============================================================================================*/
|
|
bool MixPatchMakerClass::Compare_Source_Art_File(char *filename)
|
|
{
|
|
/*
|
|
** Find the file in the old source art directory.
|
|
*/
|
|
char path_to_old_file[_MAX_PATH + 256];
|
|
bool got = Find_File(filename, OldArtDir, path_to_old_file);
|
|
|
|
if (got) {
|
|
|
|
/*
|
|
** Find the file in the old source art directory.
|
|
*/
|
|
char path_to_new_file[_MAX_PATH + 256];
|
|
got = Find_File(filename, NewArtDir, path_to_new_file);
|
|
|
|
if (got) {
|
|
|
|
/*
|
|
** Both files exist. Load them and compare.
|
|
*/
|
|
RawFileClass file1(path_to_old_file);
|
|
RawFileClass file2(path_to_new_file);
|
|
|
|
if (file1.Size() == file2.Size()) {
|
|
|
|
int size = file1.Size();
|
|
|
|
unsigned char * buf1 = new unsigned char [size + 1024];
|
|
unsigned char * buf2 = new unsigned char [size + 1024];
|
|
|
|
file1.Read(buf1, size);
|
|
file2.Read(buf2, size);
|
|
int diff = memcmp(buf1, buf2, size);
|
|
|
|
delete [] buf1;
|
|
delete [] buf2;
|
|
|
|
if (diff == 0) {
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return(false);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
/***********************************************************************************************
|
|
* MixPatchMakerClass::Find_File -- Find a file in a directory or sub directory *
|
|
* *
|
|
* *
|
|
* *
|
|
* INPUT: Name of file to search for *
|
|
* Path to start search *
|
|
* Ptr to string to receive path/filename of file if found *
|
|
* *
|
|
* OUTPUT: True if file was found *
|
|
* *
|
|
* WARNINGS: None *
|
|
* *
|
|
* HISTORY: *
|
|
* 2/21/2002 8:45PM ST : Created *
|
|
*=============================================================================================*/
|
|
bool MixPatchMakerClass::Find_File(char *file_name, char *path, char *found_path)
|
|
{
|
|
/*
|
|
** Look in the current directory.
|
|
*/
|
|
char path_to_file[_MAX_PATH + 256];
|
|
char *pointless_pointer = NULL;
|
|
int len = SearchPath(path, file_name, NULL, sizeof(path_to_file), path_to_file, &pointless_pointer);
|
|
|
|
if (len) {
|
|
strcpy(found_path, path_to_file);
|
|
return(true);
|
|
}
|
|
|
|
/*
|
|
** Look for a subdirectory to search.
|
|
*/
|
|
WIN32_FIND_DATA find_info = { 0 };
|
|
HANDLE find_handle;
|
|
char search_path[_MAX_PATH + 128];
|
|
strcpy(search_path, path);
|
|
strcat(search_path, "\\*.*");
|
|
find_handle = FindFirstFile(search_path, &find_info);
|
|
|
|
while (find_handle != INVALID_HANDLE_VALUE) {
|
|
if ((find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
|
|
if (strcmp(find_info.cFileName, ".") != 0) {
|
|
if (strcmp(find_info.cFileName, "..") != 0) {
|
|
char new_dir_name[_MAX_PATH + 128];
|
|
strcpy(new_dir_name, path);
|
|
strcat(new_dir_name, "\\");
|
|
strcat(new_dir_name, find_info.cFileName);
|
|
bool found = Find_File(file_name, new_dir_name, found_path);
|
|
if (found) {
|
|
FindClose(find_handle);
|
|
return(true);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
if (FindNextFile(find_handle, &find_info) == 0) {
|
|
FindClose(find_handle);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return(false);
|
|
} |