duplicatecombiner.cpp 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592
  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. /***********************************************************************************************
  19. *** 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 ***
  20. ***********************************************************************************************
  21. * *
  22. * Project Name : WWAudio *
  23. * *
  24. * $Archive:: /Commando/Code/Tools/MixViewer/duplicatecombiner.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 2/21/02 5:25p $*
  29. * *
  30. * $Revision:: 3 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "stdafx.h"
  36. #include "duplicatecombiner.h"
  37. #include "ffactory.h"
  38. #include "mixfile.h"
  39. #include "rawfile.h"
  40. #include "bittype.h"
  41. #include "mixcombiningdialog.h"
  42. //////////////////////////////////////////////////////////////
  43. //
  44. // DuplicateRemoverClass
  45. //
  46. //////////////////////////////////////////////////////////////
  47. DuplicateRemoverClass::DuplicateRemoverClass (void) :
  48. TempFilenameStart (0),
  49. Dialog (NULL)
  50. {
  51. return ;
  52. }
  53. //////////////////////////////////////////////////////////////
  54. //
  55. // ~DuplicateRemoverClass
  56. //
  57. //////////////////////////////////////////////////////////////
  58. DuplicateRemoverClass::~DuplicateRemoverClass (void)
  59. {
  60. return ;
  61. }
  62. //////////////////////////////////////////////////////////////
  63. //
  64. // Process
  65. //
  66. //////////////////////////////////////////////////////////////
  67. void
  68. DuplicateRemoverClass::Process (void)
  69. {
  70. //
  71. // Kick off the worker thread...
  72. //
  73. ::AfxBeginThread (fnThreadProc, (LPVOID)this);
  74. //
  75. // Show the UI
  76. //
  77. MixCombiningDialogClass dialog;
  78. Dialog = &dialog;
  79. dialog.DoModal ();
  80. return ;
  81. }
  82. ////////////////////////////////////////////////////////////////////////////
  83. //
  84. // fnThreadProc
  85. //
  86. ////////////////////////////////////////////////////////////////////////////
  87. UINT
  88. DuplicateRemoverClass::fnThreadProc (LPVOID pParam)
  89. {
  90. //
  91. // Simply ask the combiner to start processing
  92. //
  93. DuplicateRemoverClass *remover = (DuplicateRemoverClass *)pParam;
  94. if (remover != NULL) {
  95. remover->Internal_Process ();
  96. }
  97. return 1;
  98. }
  99. //////////////////////////////////////////////////////////////
  100. //
  101. // Internal_Process
  102. //
  103. //////////////////////////////////////////////////////////////
  104. void
  105. DuplicateRemoverClass::Internal_Process (void)
  106. {
  107. Make_Temp_Directory ();
  108. //
  109. // Open the source mix files
  110. //
  111. DynamicVectorClass<MixFileFactoryClass *> mix_file_list;
  112. Open_Mix_Files (mix_file_list);
  113. //
  114. // Open the destination mix factory
  115. //
  116. MixFileFactoryClass dest_factory (DestinationMixFilename, _TheFileFactory);
  117. if (dest_factory.Is_Valid () && dest_factory.Build_Internal_Filename_List ()) {
  118. Dialog->Set_Status_Text ("Building temporary files...");
  119. Dialog->Set_Progress_Percent (0);
  120. //
  121. // Loop over each mix file...
  122. //
  123. for (int mix_index = 0; mix_index < mix_file_list.Count (); mix_index ++) {
  124. //
  125. // Get the list of filenames inside this mix file
  126. //
  127. DynamicVectorClass<StringClass> filename_list;
  128. mix_file_list[mix_index]->Get_Filename_List (filename_list);
  129. for (int file_index = 0; file_index < filename_list.Count (); file_index ++) {
  130. StringClass filename = ::strlwr (filename_list[file_index].Peek_Buffer ());
  131. DynamicVectorClass<int> dup_indices;
  132. dup_indices.Add (mix_index);
  133. //
  134. // Try to find this file in any of the other mix files
  135. //
  136. for (int test_mix_index = 0; test_mix_index < mix_file_list.Count (); test_mix_index ++) {
  137. if (test_mix_index != mix_index && Is_File_In_Factory (filename, mix_file_list[test_mix_index])) {
  138. dup_indices.Add (test_mix_index);
  139. }
  140. }
  141. //
  142. // Check to see if this file is already in the "destination" file
  143. //
  144. bool is_in_dest = Is_File_In_Factory (filename, &dest_factory);
  145. //
  146. // Were there any duplicates?
  147. //
  148. if (dup_indices.Count () > 1 || is_in_dest) {
  149. //
  150. // Copy the file from the source to the destination factory
  151. //
  152. if (is_in_dest == false) {
  153. Copy_File (mix_file_list[mix_index], &dest_factory, filename);
  154. }
  155. //
  156. // Remove all the duplicates from the mix files
  157. //
  158. for (int dup_index = 0; dup_index < dup_indices.Count (); dup_index ++) {
  159. mix_file_list[dup_indices[dup_index]]->Delete_File (filename);
  160. }
  161. }
  162. }
  163. //
  164. // Update the UI
  165. //
  166. Dialog->Set_Progress_Percent ((float)mix_index / float(mix_file_list.Count () + 1));
  167. }
  168. //
  169. // Save the changes
  170. //
  171. Dialog->Set_Status_Text ("Reconstructing mix files...");
  172. Dialog->Set_Progress_Percent (0);
  173. dest_factory.Flush_Changes ();
  174. Dialog->Set_Progress_Percent (1.0F / float(mix_file_list.Count () + 1));
  175. for (int index = 0; index < mix_file_list.Count (); index ++) {
  176. mix_file_list[index]->Flush_Changes ();
  177. Dialog->Set_Progress_Percent ((float)index / float(mix_file_list.Count () + 1));
  178. }
  179. Dialog->Set_Progress_Percent (1.0F);
  180. }
  181. //
  182. // Free each of the mix file factories
  183. //
  184. Close_Mix_Files (mix_file_list);
  185. //
  186. // Remove the temporary directory we created
  187. //
  188. Delete_Temp_Directory ();
  189. //
  190. // Close the dialog
  191. //
  192. Dialog->PostMessage (WM_COMMAND, MAKELPARAM (IDOK, BN_CLICKED));
  193. return ;
  194. }
  195. //////////////////////////////////////////////////////////////
  196. //
  197. // Copy_File
  198. //
  199. //////////////////////////////////////////////////////////////
  200. void
  201. DuplicateRemoverClass::Copy_File
  202. (
  203. MixFileFactoryClass * src_mix,
  204. MixFileFactoryClass * dest_mix,
  205. const char * filename
  206. )
  207. {
  208. //
  209. // Get the file data from the source mix
  210. //
  211. FileClass *src_file = src_mix->Get_File (filename);
  212. src_file->Open ();
  213. //
  214. // Create a destination file for the data
  215. //
  216. StringClass full_path;
  217. Get_Temp_Filename (full_path);
  218. RawFileClass dest_file;
  219. dest_file.Set_Name (full_path);
  220. if (dest_file.Open (RawFileClass::WRITE)) {
  221. //
  222. // Copy the data from the source mix file to the destination file
  223. //
  224. int file_size = src_file->Size ();
  225. uint8 buffer[4096];
  226. while (file_size > 0) {
  227. //
  228. // Read the data from the source file
  229. //
  230. int bytes = min (file_size, (int)sizeof (buffer));
  231. int copied_size = src_file->Read (buffer, bytes);
  232. file_size -= copied_size;
  233. if (copied_size <= 0) {
  234. break;
  235. }
  236. //
  237. // Copy the data to the dest file
  238. //
  239. dest_file.Write (buffer, copied_size);
  240. }
  241. //
  242. // Add the file to the destination mix file
  243. //
  244. dest_mix->Add_File (full_path, filename);
  245. //
  246. // Close the temporary data file
  247. //
  248. dest_file.Close ();
  249. }
  250. //
  251. // Return the file
  252. //
  253. src_mix->Return_File (src_file);
  254. return ;
  255. }
  256. //////////////////////////////////////////////////////////////
  257. //
  258. // Open_Mix_Files
  259. //
  260. //////////////////////////////////////////////////////////////
  261. void
  262. DuplicateRemoverClass::Open_Mix_Files (DynamicVectorClass<MixFileFactoryClass *> &list)
  263. {
  264. //
  265. // Loop over all the mix filenames in our list
  266. //
  267. for (int index = 0; index < MixFileList.Count (); index ++) {
  268. //
  269. // Create this mix file factory and add it to our list
  270. //
  271. MixFileFactoryClass *mix_factory = new MixFileFactoryClass (MixFileList[index], _TheFileFactory);
  272. if (mix_factory->Is_Valid () && mix_factory->Build_Internal_Filename_List ()) {
  273. list.Add (mix_factory);
  274. } else {
  275. delete mix_factory;
  276. mix_factory = NULL;
  277. }
  278. //
  279. // Update the UI
  280. //
  281. Dialog->Set_Progress_Percent ((float)index / float(MixFileList.Count () + 1));
  282. }
  283. return ;
  284. }
  285. //////////////////////////////////////////////////////////////
  286. //
  287. // Close_Mix_Files
  288. //
  289. //////////////////////////////////////////////////////////////
  290. void
  291. DuplicateRemoverClass::Close_Mix_Files (DynamicVectorClass<MixFileFactoryClass *> &list)
  292. {
  293. //
  294. // Simply free each entry in the list
  295. //
  296. for (int index = 0; index < list.Count (); index ++) {
  297. delete list[index];
  298. }
  299. list.Delete_All ();
  300. return ;
  301. }
  302. /////////////////////////////////////////////////////////
  303. //
  304. // Make_Temp_Directory
  305. //
  306. /////////////////////////////////////////////////////////
  307. void
  308. DuplicateRemoverClass::Make_Temp_Directory (void)
  309. {
  310. //
  311. // Get the path of the temp directory
  312. //
  313. char temp_dir[MAX_PATH] = { 0 };
  314. ::GetTempPath (sizeof (temp_dir), temp_dir);
  315. CString temp_path = Make_Path (temp_dir, "mixcombiner");
  316. //
  317. // Try to find a unique temp directory to store our data
  318. //
  319. int index = 0;
  320. do {
  321. TempDirectory.Format ("%s%.2d.DIR", (const char *)temp_path, index++);
  322. } while (GetFileAttributes (TempDirectory) != 0xFFFFFFFF);
  323. //
  324. // Create the directory
  325. //
  326. ::CreateDirectory (TempDirectory, NULL);
  327. ::SetCurrentDirectory (TempDirectory);
  328. return ;
  329. }
  330. /////////////////////////////////////////////////////////
  331. //
  332. // Delete_Temp_Directory
  333. //
  334. /////////////////////////////////////////////////////////
  335. void
  336. DuplicateRemoverClass::Delete_Temp_Directory (void)
  337. {
  338. //
  339. // Change the current directory so we can remove the temporary one
  340. //
  341. ::SetCurrentDirectory ("c:\\");
  342. //
  343. // Remove the temporary directory
  344. //
  345. Clean_Directory (TempDirectory);
  346. ::RemoveDirectory (TempDirectory);
  347. return ;
  348. }
  349. //////////////////////////////////////////////////////////////////////////////////
  350. //
  351. // Clean_Directory
  352. //
  353. //////////////////////////////////////////////////////////////////////////////////
  354. bool
  355. DuplicateRemoverClass::Clean_Directory (LPCTSTR local_dir)
  356. {
  357. bool retval = true;
  358. //
  359. // Build a search mask from the directory
  360. //
  361. StringClass search_mask = StringClass (local_dir) + "\\*.*";
  362. //
  363. // Loop through all the files in this directory and add them
  364. // to our list
  365. //
  366. DynamicVectorClass<StringClass> file_list;
  367. BOOL keep_going = TRUE;
  368. WIN32_FIND_DATA find_info = { 0 };
  369. for (HANDLE hfind = ::FindFirstFile (search_mask, &find_info);
  370. (hfind != INVALID_HANDLE_VALUE) && keep_going;
  371. keep_going = ::FindNextFile (hfind, &find_info))
  372. {
  373. //
  374. // If this file isn't a directory, add it to the list
  375. //
  376. if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  377. StringClass filename = find_info.cFileName;
  378. file_list.Add (filename);
  379. } else if (find_info.cFileName[0] != '.') {
  380. //
  381. // Recurse into this subdirectory
  382. //
  383. StringClass full_path = Make_Path (local_dir, find_info.cFileName);
  384. Clean_Directory (full_path);
  385. //
  386. // Add this directory to the list so it will get
  387. // deleted with the files...
  388. //
  389. StringClass filename = find_info.cFileName;
  390. file_list.Add (filename);
  391. }
  392. }
  393. //
  394. // Close the search handle
  395. //
  396. if (hfind != NULL) {
  397. ::FindClose (hfind);
  398. }
  399. //
  400. // Now loop through all the files and delete them
  401. //
  402. for (int index = 0; index < file_list.Count (); index ++) {
  403. StringClass &filename = file_list[index];
  404. StringClass full_path = Make_Path (local_dir, filename);
  405. Delete_File (full_path);
  406. }
  407. return retval;
  408. }
  409. //////////////////////////////////////////////////////////////////////////////////
  410. //
  411. // Make_Path
  412. //
  413. //////////////////////////////////////////////////////////////////////////////////
  414. StringClass
  415. DuplicateRemoverClass::Make_Path (LPCTSTR path, LPCTSTR filename)
  416. {
  417. StringClass full_path = path;
  418. //
  419. // Delimit the path if necessary
  420. //
  421. if (full_path[full_path.Get_Length () - 1] != '\\') {
  422. full_path += "\\";
  423. }
  424. //
  425. // Concatenate the filename onto the path
  426. //
  427. full_path += filename;
  428. return full_path;
  429. }
  430. /////////////////////////////////////////////////////////
  431. //
  432. // Get_Temp_Filename
  433. //
  434. /////////////////////////////////////////////////////////
  435. void
  436. DuplicateRemoverClass::Get_Temp_Filename (StringClass &full_path)
  437. {
  438. CString temp_path = Make_Path (TempDirectory, "tempfile");
  439. //
  440. // Try to find a unique temp filename
  441. //
  442. do {
  443. full_path.Format ("%s%.5d.dat", (const char *)temp_path, TempFilenameStart++);
  444. } while (GetFileAttributes (full_path) != 0xFFFFFFFF);
  445. return ;
  446. }
  447. ////////////////////////////////////////////////////////////////////////////
  448. //
  449. // Delete_File
  450. //
  451. ////////////////////////////////////////////////////////////////////////////
  452. bool
  453. DuplicateRemoverClass::Delete_File (LPCTSTR filename)
  454. {
  455. bool retval = false;
  456. ASSERT (filename != NULL);
  457. if (filename != NULL) {
  458. //
  459. // Strip the readonly bit off if necessary
  460. //
  461. DWORD attributes = ::GetFileAttributes (filename);
  462. if ((attributes != 0xFFFFFFFF) &&
  463. ((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY))
  464. {
  465. ::SetFileAttributes (filename, attributes & (~FILE_ATTRIBUTE_READONLY));
  466. }
  467. //
  468. // Perform the delete operation!
  469. //
  470. if ((attributes != 0xFFFFFFFF) &&
  471. ((attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY))
  472. {
  473. retval = (::RemoveDirectory (filename) == TRUE);
  474. } else {
  475. retval = (::DeleteFile (filename) == TRUE);
  476. }
  477. }
  478. return retval;
  479. }
  480. ////////////////////////////////////////////////////////////////////////////
  481. //
  482. // Is_File_In_Factory
  483. //
  484. ////////////////////////////////////////////////////////////////////////////
  485. bool
  486. DuplicateRemoverClass::Is_File_In_Factory (const StringClass &filename, MixFileFactoryClass *factory)
  487. {
  488. bool retval = false;
  489. //
  490. // Get the list of filenames inside this mix file
  491. //
  492. DynamicVectorClass<StringClass> *test_filename_list = NULL;
  493. factory->Get_Filename_List (&test_filename_list);
  494. //
  495. // Try to find the file inside this mix file
  496. //
  497. for (int index = 0; index < test_filename_list->Count (); index ++) {
  498. if (filename.Compare_No_Case ((*test_filename_list)[index]) == 0) {
  499. retval = true;
  500. break;
  501. }
  502. }
  503. return retval;
  504. }