MixPatchMaker.cpp 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451
  1. /*
  2. ** Command & Conquer Renegade(tm)
  3. ** Copyright 2025 Electronic Arts Inc.
  4. **
  5. ** This program is free software: you can redistribute it and/or modify
  6. ** it under the terms of the GNU General Public License as published by
  7. ** the Free Software Foundation, either version 3 of the License, or
  8. ** (at your option) any later version.
  9. **
  10. ** This program is distributed in the hope that it will be useful,
  11. ** but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  13. ** GNU General Public License for more details.
  14. **
  15. ** You should have received a copy of the GNU General Public License
  16. ** along with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. #include "stdafx.h"
  19. #include "mixpatchmaker.h"
  20. #include "ffactory.h"
  21. #include "mixfile.h"
  22. #include "rawfile.h"
  23. #include "bittype.h"
  24. #include "mixcombiningdialog.h"
  25. MixPatchMakerClass::MixPatchMakerClass(void) :
  26. Dialog(NULL)
  27. {
  28. }
  29. void MixPatchMakerClass::Make_Patch(char *old_input_file, char *new_input_file, char *output_file, char *old_art_dir, char *new_art_dir)
  30. {
  31. strcpy(OldInputFile, old_input_file);
  32. strcpy(NewInputFile, new_input_file);
  33. strcpy(OutputFile, output_file);
  34. strcpy(OldArtDir, old_art_dir);
  35. strcpy(NewArtDir, new_art_dir);
  36. //
  37. // Kick off the worker thread...
  38. //
  39. ::AfxBeginThread(Do_Stuff, (LPVOID)this);
  40. //
  41. // Show the UI
  42. //
  43. MixCombiningDialogClass dialog;
  44. Dialog = &dialog;
  45. dialog.DoModal();
  46. }
  47. unsigned int MixPatchMakerClass::Do_Stuff(void *param)
  48. {
  49. ((MixPatchMakerClass*)param)->Thread_Make();
  50. return(1);
  51. }
  52. void MixPatchMakerClass::Thread_Make(void)
  53. {
  54. char name[_MAX_PATH];
  55. char name_compare[_MAX_PATH];
  56. while (Dialog == NULL) {
  57. Sleep(0);
  58. }
  59. /*
  60. ** Get just the file name.
  61. */
  62. char justname[_MAX_PATH];
  63. char justname_new[_MAX_PATH];
  64. _splitpath(OldInputFile, NULL, NULL, justname, NULL);
  65. _splitpath(NewInputFile, NULL, NULL, justname_new, NULL);
  66. char text[_MAX_PATH + 128];
  67. sprintf(text, "Looking for differences between %s and %s...", justname, justname_new);
  68. Dialog->Set_Title("Building patch mixfile...");
  69. Dialog->Set_Status_Text(text);
  70. Dialog->Set_Progress_Percent (0);
  71. MixFileFactoryClass old_mix(OldInputFile, _TheFileFactory);
  72. MixFileFactoryClass new_mix(NewInputFile, _TheFileFactory);
  73. if (new_mix.Is_Valid() && new_mix.Build_Internal_Filename_List()) {
  74. if (old_mix.Is_Valid() && old_mix.Build_Internal_Filename_List()) {
  75. /*
  76. ** Get the list of files in the new source mix file.
  77. */
  78. DynamicVectorClass<StringClass> new_filename_list;
  79. new_mix.Get_Filename_List(new_filename_list);
  80. /*
  81. ** Get the list of files in the old source mix file.
  82. */
  83. DynamicVectorClass<StringClass> old_filename_list;
  84. old_mix.Get_Filename_List(old_filename_list);
  85. /*
  86. ** Create the output mix file.
  87. */
  88. MixFileCreator mix_out(OutputFile);
  89. /*
  90. ** 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
  91. ** different, then copy it into the output mix file.
  92. */
  93. for (int i=0 ; i<new_filename_list.Count() ; i++) {
  94. strcpy(name, new_filename_list[i].Peek_Buffer());
  95. strupr(name);
  96. bool copy = false;
  97. if (stricmp(name, "STRINGS.TDB") == 0) {
  98. continue;
  99. }
  100. /*
  101. ** Search for the file in the old mixfile.
  102. */
  103. bool found = false;
  104. for (int j=0 ; j<old_filename_list.Count() ; j++) {
  105. strcpy(name_compare, old_filename_list[j].Peek_Buffer());
  106. if (stricmp(name, name_compare) == 0) {
  107. found = true;
  108. /*
  109. ** 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
  110. ** then we need to use the newer file in the patch mix.
  111. */
  112. bool same = Compare_File(&new_mix, &old_mix, new_filename_list[i].Peek_Buffer());
  113. if (!same) {
  114. /*
  115. ** If it's a .dds (texture) file, compare the source art for changes. The .dds can change even when the
  116. ** source art doesn't.
  117. */
  118. if (strstr(name, ".DDS")) {
  119. char targa_name[_MAX_PATH];
  120. strcpy(targa_name, name);
  121. char *ext = strstr(targa_name, ".DDS");
  122. if (ext) {
  123. strcpy(ext, ".TGA");
  124. /*
  125. ** If the two source art files are the same then break out.
  126. */
  127. if (Compare_Source_Art_File(targa_name)) {
  128. break;
  129. }
  130. }
  131. }
  132. copy = true;
  133. }
  134. break;
  135. }
  136. }
  137. /*
  138. ** If the file doesn't exist at all in the older mixfile then it needs to be in the patch mix.
  139. */
  140. if (!found) {
  141. copy = true;
  142. }
  143. if (copy) {
  144. Copy_File(&new_mix, &mix_out, new_filename_list[i].Peek_Buffer());
  145. }
  146. Dialog->Set_Progress_Percent((float)i / float(new_filename_list.Count() + 1));
  147. }
  148. }
  149. }
  150. Dialog->PostMessage(WM_COMMAND, MAKELPARAM(IDOK, BN_CLICKED));
  151. }
  152. void MixPatchMakerClass::Copy_File(MixFileFactoryClass *src_mix, MixFileCreator *dest_mix, char *filename)
  153. {
  154. //
  155. // Get the file data from the source mix
  156. //
  157. FileClass *src_file = src_mix->Get_File (filename);
  158. src_file->Open ();
  159. int size = src_file->Size();
  160. //
  161. // Create a temporary destination file for the data
  162. //
  163. char temp_file_name[_MAX_PATH];
  164. char temp_path[_MAX_PATH];
  165. int chars = GetTempPath(_MAX_PATH, temp_path);
  166. if (chars) {
  167. int res = GetTempFileName(temp_path, "MIX", 0, temp_file_name);
  168. if (res == 0) {
  169. WWDEBUG_SAY(("GetTempFileName failed with error code %d\n", GetLastError()));
  170. } else {
  171. RawFileClass temp_file(temp_file_name);
  172. if (temp_file.Open(RawFileClass::WRITE)) {
  173. //
  174. // Save the data in the temp file.
  175. //
  176. void *bigbuf = new char [size + 1024];
  177. src_file->Read(bigbuf, size);
  178. temp_file.Write(bigbuf, size);
  179. delete [] bigbuf;
  180. temp_file.Close();
  181. //
  182. // Add the temp file to the mix file.
  183. //
  184. dest_mix->Add_File(temp_file_name, filename);
  185. //
  186. // Delete the temp file.
  187. //
  188. DeleteFile(temp_file_name);
  189. }
  190. }
  191. }
  192. src_mix->Return_File(src_file);
  193. }
  194. /***********************************************************************************************
  195. * MixPatchMakerClass::Compare_File -- Compare two files in seperate mixfiles. *
  196. * *
  197. * *
  198. * *
  199. * INPUT: First mixfile *
  200. * Second mixfile *
  201. * Name of file to compare *
  202. * *
  203. * OUTPUT: true if files are the same *
  204. * *
  205. * WARNINGS: None *
  206. * *
  207. * HISTORY: *
  208. * 2/5/2002 9:54PM ST : Created *
  209. *=============================================================================================*/
  210. bool MixPatchMakerClass::Compare_File(MixFileFactoryClass *src_mix, MixFileFactoryClass *dest_mix, char *filename)
  211. {
  212. /*
  213. ** Get the file data from the source mix
  214. */
  215. FileClass *src_file = src_mix->Get_File(filename);
  216. int size = src_file->Size();
  217. /*
  218. ** Get the file data from the dest mix
  219. */
  220. FileClass *dest_file = dest_mix->Get_File(filename);
  221. /*
  222. ** If the two files are a different size then we know they aren't the same.
  223. */
  224. if (size != dest_file->Size()) {
  225. src_mix->Return_File(src_file);
  226. dest_mix->Return_File(dest_file);
  227. return(false);
  228. }
  229. /*
  230. ** Read the source file into memory.
  231. */
  232. src_file->Open();
  233. unsigned char *src_data = new unsigned char [size + 32];
  234. src_file->Read(src_data, size);
  235. src_file->Close();
  236. /*
  237. ** Read the dest file into memory.
  238. */
  239. dest_file->Open();
  240. unsigned char *dest_data = new unsigned char [size + 32];
  241. dest_file->Read(dest_data, size);
  242. dest_file->Close();
  243. /*
  244. ** Compare them.
  245. */
  246. bool same = (memcmp(src_data, dest_data, size) == 0) ? true : false;
  247. /*
  248. ** Clean up.
  249. */
  250. delete [] src_data;
  251. delete [] dest_data;
  252. src_mix->Return_File(src_file);
  253. dest_mix->Return_File(dest_file);
  254. return(same);
  255. }
  256. /***********************************************************************************************
  257. * MixPatchMakerClass::Compare_Source_Art_File -- Compare source art .tga files *
  258. * *
  259. * *
  260. * *
  261. * INPUT: Filename *
  262. * *
  263. * OUTPUT: True if same *
  264. * *
  265. * WARNINGS: None *
  266. * *
  267. * HISTORY: *
  268. * 2/21/2002 4:31PM ST : Created *
  269. *=============================================================================================*/
  270. bool MixPatchMakerClass::Compare_Source_Art_File(char *filename)
  271. {
  272. /*
  273. ** Find the file in the old source art directory.
  274. */
  275. char path_to_old_file[_MAX_PATH + 256];
  276. bool got = Find_File(filename, OldArtDir, path_to_old_file);
  277. if (got) {
  278. /*
  279. ** Find the file in the old source art directory.
  280. */
  281. char path_to_new_file[_MAX_PATH + 256];
  282. got = Find_File(filename, NewArtDir, path_to_new_file);
  283. if (got) {
  284. /*
  285. ** Both files exist. Load them and compare.
  286. */
  287. RawFileClass file1(path_to_old_file);
  288. RawFileClass file2(path_to_new_file);
  289. if (file1.Size() == file2.Size()) {
  290. int size = file1.Size();
  291. unsigned char * buf1 = new unsigned char [size + 1024];
  292. unsigned char * buf2 = new unsigned char [size + 1024];
  293. file1.Read(buf1, size);
  294. file2.Read(buf2, size);
  295. int diff = memcmp(buf1, buf2, size);
  296. delete [] buf1;
  297. delete [] buf2;
  298. if (diff == 0) {
  299. return(true);
  300. }
  301. }
  302. }
  303. }
  304. return(false);
  305. }
  306. /***********************************************************************************************
  307. * MixPatchMakerClass::Find_File -- Find a file in a directory or sub directory *
  308. * *
  309. * *
  310. * *
  311. * INPUT: Name of file to search for *
  312. * Path to start search *
  313. * Ptr to string to receive path/filename of file if found *
  314. * *
  315. * OUTPUT: True if file was found *
  316. * *
  317. * WARNINGS: None *
  318. * *
  319. * HISTORY: *
  320. * 2/21/2002 8:45PM ST : Created *
  321. *=============================================================================================*/
  322. bool MixPatchMakerClass::Find_File(char *file_name, char *path, char *found_path)
  323. {
  324. /*
  325. ** Look in the current directory.
  326. */
  327. char path_to_file[_MAX_PATH + 256];
  328. char *pointless_pointer = NULL;
  329. int len = SearchPath(path, file_name, NULL, sizeof(path_to_file), path_to_file, &pointless_pointer);
  330. if (len) {
  331. strcpy(found_path, path_to_file);
  332. return(true);
  333. }
  334. /*
  335. ** Look for a subdirectory to search.
  336. */
  337. WIN32_FIND_DATA find_info = { 0 };
  338. HANDLE find_handle;
  339. char search_path[_MAX_PATH + 128];
  340. strcpy(search_path, path);
  341. strcat(search_path, "\\*.*");
  342. find_handle = FindFirstFile(search_path, &find_info);
  343. while (find_handle != INVALID_HANDLE_VALUE) {
  344. if ((find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0) {
  345. if (strcmp(find_info.cFileName, ".") != 0) {
  346. if (strcmp(find_info.cFileName, "..") != 0) {
  347. char new_dir_name[_MAX_PATH + 128];
  348. strcpy(new_dir_name, path);
  349. strcat(new_dir_name, "\\");
  350. strcat(new_dir_name, find_info.cFileName);
  351. bool found = Find_File(file_name, new_dir_name, found_path);
  352. if (found) {
  353. FindClose(find_handle);
  354. return(true);
  355. }
  356. }
  357. }
  358. }
  359. if (FindNextFile(find_handle, &find_info) == 0) {
  360. FindClose(find_handle);
  361. break;
  362. }
  363. }
  364. return(false);
  365. }