Export.cpp 26 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015
  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 : LevelEdit *
  23. * *
  24. * $Archive:: /Commando/Code/Tools/LevelEdit/Export.cpp $*
  25. * *
  26. * Author:: Patrick Smith *
  27. * *
  28. * $Modtime:: 6/26/02 2:13p $*
  29. * *
  30. * $Revision:: 34 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  35. #include "stdafx.h"
  36. #include "leveledit.h"
  37. #include "leveleditdoc.h"
  38. #include "export.h"
  39. #include "editorini.h"
  40. #include "utils.h"
  41. #include "resource.h"
  42. #include "nodemgr.h"
  43. #include "vector3.h"
  44. #include "matrix3d.h"
  45. #include "ffactory.h"
  46. #include "filemgr.h"
  47. #include "_assetmgr.h"
  48. #include "editorassetmgr.h"
  49. #include "..\..\commando\lev_file.h"
  50. #include "chunkio.h"
  51. #include "sceneeditor.h"
  52. #include "filelocations.h"
  53. #include "inisections.h"
  54. #include "hlod.h"
  55. #include "w3derr.h"
  56. #include "saveload.h"
  57. #include "physstaticsavesystem.h"
  58. #include "physdynamicsavesystem.h"
  59. #include "combatsaveload.h"
  60. #include "terrainnode.h"
  61. #include "staticphys.h"
  62. #include "savegame.h"
  63. #include "regkeys.h"
  64. #include "combat.h"
  65. #include "assetdep.h"
  66. #include "presetslibform.h"
  67. #include "pathfind.h"
  68. #include "gameobjmanager.h"
  69. #include "stringsmgr.h"
  70. #include "assetpackagemgr.h"
  71. #include "presetmgr.h"
  72. /////////////////////////////////////////////////////////
  73. // Local constants
  74. /////////////////////////////////////////////////////////
  75. const TCHAR * const STATIC_EXTENSION = TEXT (".lsd");
  76. const TCHAR * const DYNAMIC_EXTENSION = TEXT (".ldd");
  77. const TCHAR * const MIX_EXTENSION = TEXT (".mix");
  78. const TCHAR * const ALWAYS_ROOT_NAME = TEXT ("always");
  79. const TCHAR * const ALWAYS_DATA_FILENAME = TEXT ("always.dat");
  80. const TCHAR * const ALWAYS_DB_FILENAME = TEXT ("always.dbs");
  81. /////////////////////////////////////////////////////////
  82. //
  83. // Export_Level
  84. //
  85. /////////////////////////////////////////////////////////
  86. void
  87. ExporterClass::Export_Level (LPCTSTR filename)
  88. {
  89. CWaitCursor wait_cursor;
  90. m_AssetList.Delete_All ();
  91. //
  92. // Prepare the level for exporting...
  93. //
  94. DynamicVectorClass<NodeClass *> node_list;
  95. Pre_Level_Export (node_list);
  96. //
  97. // Get the base destination path
  98. //
  99. Path = ::Strip_Filename_From_Path (filename);
  100. //
  101. // Strip the path and extension from the filename to give
  102. // us a base for use with other filenames
  103. //
  104. FilenameBase = ::Get_Filename_From_Path (filename);
  105. int extension_index = FilenameBase.ReverseFind ('.');
  106. if (extension_index != -1) {
  107. FilenameBase = FilenameBase.Left (extension_index);
  108. }
  109. //
  110. // Setup the temporary directory
  111. //
  112. Make_Temp_Directory ();
  113. //
  114. // Export the level files
  115. //
  116. Export_Level_File ();
  117. Export_Level_Data ();
  118. Export_Dependency_File ();
  119. Export_Definition_Databases (false);
  120. //
  121. // Now build the mix file...
  122. //
  123. MixFileCreator.Generate_Mix_File (filename);
  124. //
  125. // Cleanup any temp files we needed
  126. //
  127. Delete_Temp_Directory ();
  128. //
  129. // Resetore the level after exporting
  130. //
  131. Post_Level_Export (node_list);
  132. //
  133. // Cache the directory path we just exported into
  134. //
  135. theApp.WriteProfileString (CONFIG_KEY, LAST_EXPORT_DIR_VALUE, Path);
  136. return ;
  137. }
  138. /////////////////////////////////////////////////////////
  139. //
  140. // Make_Temp_Directory
  141. //
  142. /////////////////////////////////////////////////////////
  143. void
  144. ExporterClass::Make_Temp_Directory (void)
  145. {
  146. //
  147. // Get the path of the temp directory
  148. //
  149. char temp_dir[MAX_PATH] = { 0 };
  150. ::GetTempPath (sizeof (temp_dir), temp_dir);
  151. CString temp_path = ::Make_Path (temp_dir, FilenameBase);
  152. //
  153. // Try to find a unique temp directory to store our data
  154. //
  155. int index = 0;
  156. do {
  157. TempDirectory.Format ("%s%.2d.DIR", (const char *)temp_path, index++);
  158. } while (GetFileAttributes (TempDirectory) != 0xFFFFFFFF);
  159. //
  160. // Create the directory
  161. //
  162. ::CreateDirectory (TempDirectory, NULL);
  163. ::Set_Current_Directory (TempDirectory);
  164. ::SetCurrentDirectory (TempDirectory);
  165. return ;
  166. }
  167. /////////////////////////////////////////////////////////
  168. //
  169. // Delete_Temp_Directory
  170. //
  171. /////////////////////////////////////////////////////////
  172. void
  173. ExporterClass::Delete_Temp_Directory (void)
  174. {
  175. //
  176. // Change the current directory so we can remove the temporary one
  177. //
  178. ::Set_Current_Directory (Path);
  179. ::SetCurrentDirectory (Path);
  180. //
  181. // Remove the temporary directory
  182. //
  183. Clean_Directory (TempDirectory);
  184. ::RemoveDirectory (TempDirectory);
  185. return ;
  186. }
  187. /////////////////////////////////////////////////////////
  188. //
  189. // Export_Dependency_File
  190. //
  191. /////////////////////////////////////////////////////////
  192. void
  193. ExporterClass::Export_Dependency_File (void)
  194. {
  195. //
  196. // Make a file that contains a list of all the W3D files that
  197. // were part of this export
  198. //
  199. StringClass dep_filename = FilenameBase + ".dep";
  200. StringClass dep_path = ::Make_Path (TempDirectory, dep_filename);
  201. AssetDependencyManager::Save_Level_Dependencies (dep_path, m_AssetList);
  202. //
  203. // Add this file to the mix
  204. //
  205. MixFileCreator.Add_File (dep_path, ::Get_Filename_From_Path (dep_path));
  206. return ;
  207. }
  208. /////////////////////////////////////////////////////////
  209. //
  210. // Export_Level_File
  211. //
  212. /////////////////////////////////////////////////////////
  213. bool
  214. ExporterClass::Export_Level_File (void)
  215. {
  216. //
  217. // Create filenames for both the static and dynamic data
  218. //
  219. CString ldd_filename = FilenameBase + DYNAMIC_EXTENSION;
  220. CString lsd_filename = FilenameBase + STATIC_EXTENSION;
  221. //
  222. // Save the LSD and LDD files to a temporary directory
  223. //
  224. SaveGameManager::Set_Map_Filename (lsd_filename);
  225. SaveGameManager::Save_Level ();
  226. SaveGameManager::Save_Game (ldd_filename, NULL);
  227. //
  228. // Now add these files to the mix file
  229. //
  230. CString lsd_path = Make_Path (TempDirectory, lsd_filename);
  231. CString ldd_path = Make_Path (TempDirectory, ldd_filename);
  232. MixFileCreator.Add_File (lsd_path, lsd_filename);
  233. MixFileCreator.Add_File (ldd_path, ldd_filename);
  234. return true;
  235. }
  236. /////////////////////////////////////////////////////////
  237. //
  238. // Export_Always_Files
  239. //
  240. /////////////////////////////////////////////////////////
  241. void
  242. ExporterClass::Export_Always_Files (LPCTSTR path)
  243. {
  244. CWaitCursor wait_cursor;
  245. m_AssetList.Delete_All ();
  246. //
  247. // Does the path exist?
  248. //
  249. if (::GetFileAttributes (path) != 0xFFFFFFFF) {
  250. //
  251. // Get the base destination path
  252. //
  253. Path = path;
  254. FilenameBase = ALWAYS_ROOT_NAME;
  255. //
  256. // Setup the temporary directory
  257. //
  258. Make_Temp_Directory ();
  259. //
  260. // Copy all the files used by the framework to the directory
  261. //
  262. STRING_LIST &global_list = ::Get_File_Mgr ()->Get_Global_Include_File_List ();
  263. Process_Include_Files (global_list);
  264. //
  265. // Get the always dependency search list
  266. //
  267. STRING_LIST search_list;
  268. CString always_dep_search_ini = ::Get_File_Mgr ()->Make_Full_Path (ALWAYS_DEP_INI_PATH);
  269. ::Get_File_Mgr ()->Build_Include_List (always_dep_search_ini, search_list);
  270. //
  271. // Find all the files in the search list and add them to a list
  272. //
  273. STRING_LIST file_list;
  274. Build_File_List (search_list, file_list);
  275. //
  276. // Now add all the W3D files from the list to another list. *sigh*
  277. //
  278. DynamicVectorClass<StringClass> dep_list;
  279. for (int index = 0; index < file_list.Count (); index ++) {
  280. CString &filename = file_list[index];
  281. if (::Is_W3D_Filename (filename)) {
  282. dep_list.Add ((LPCTSTR)::Get_Filename_From_Path (filename));
  283. }
  284. }
  285. //
  286. // Make a file that contains a list of all these W3D files
  287. //
  288. AssetDependencyManager::Save_Always_Dependencies (TempDirectory, dep_list);
  289. CString dep_file_path = Make_Path (TempDirectory, "always.dep");
  290. MixFileCreator.Add_File (dep_file_path, "always.dep");
  291. //
  292. // Now build the mix file...
  293. //
  294. CString mix_filename = Make_Path (Path, ALWAYS_DATA_FILENAME);
  295. MixFileCreator.Generate_Mix_File (mix_filename);
  296. //
  297. // Cleanup any temp files we needed
  298. //
  299. Delete_Temp_Directory ();
  300. } else {
  301. //
  302. // Invalid file, let the user know
  303. //
  304. ::Message_Box (::AfxGetMainWnd ()->m_hWnd, IDS_CANT_EXPORT_MSG, IDS_CANT_EXPORT_TITLE, MB_ICONERROR | MB_OK);
  305. }
  306. return ;
  307. }
  308. /////////////////////////////////////////////////////////
  309. //
  310. // Export_Level_Data
  311. //
  312. /////////////////////////////////////////////////////////
  313. bool
  314. ExporterClass::Export_Level_Data (void)
  315. {
  316. bool retval = true;
  317. //
  318. // Loop through all the files used by this level and copy them
  319. // to the same directory that the level definition was saved in.
  320. //
  321. int count = ::Get_File_Mgr ()->Get_File_Count ();
  322. for (int file_index = 0; file_index < count; file_index ++) {
  323. //
  324. // Build the complete paths of both the original file location and the new file location
  325. //
  326. CString file_path = ::Get_File_Mgr ()->Get_File_Name (file_index);
  327. CString entry_name;
  328. //
  329. // Is this file's subdirectory important?
  330. //
  331. bool path_important = ::Get_File_Mgr ()->Is_File_Path_Important (file_index);
  332. if (path_important) {
  333. //
  334. // Pre-pend the subdirectory name to form the entry name
  335. //
  336. CString sub_dir = ::Get_Subdir_From_Full_Path (file_path);
  337. entry_name = Make_Path (sub_dir, ::Get_Filename_From_Path (file_path));
  338. } else {
  339. entry_name = ::Get_Filename_From_Path (file_path);
  340. }
  341. //
  342. // Add this file to the mix file with the given name
  343. //
  344. MixFileCreator.Add_File (file_path, entry_name);
  345. //
  346. // Add this file to the level-asset list if its a W3D file
  347. //
  348. if (::Is_W3D_Filename (entry_name)) {
  349. m_AssetList.Add ((LPCTSTR)::Get_Filename_From_Path (entry_name));
  350. }
  351. }
  352. //
  353. // Add the temp definition database
  354. //
  355. CString temp_db_filename = ::Get_File_Mgr ()->Make_Full_Path (TEMP_DB_PATH);
  356. CString temp_db_name = FilenameBase + ".ddb";
  357. MixFileCreator.Add_File (temp_db_filename, temp_db_name);
  358. //
  359. // Copy all the files used by the framework to the directory
  360. //
  361. STRING_LIST &level_list = ::Get_File_Mgr ()->Get_Include_File_List ();
  362. Process_Include_Files (level_list);
  363. return retval;
  364. }
  365. /////////////////////////////////////////////////////////
  366. //
  367. // Export_Database_Mix
  368. //
  369. /////////////////////////////////////////////////////////
  370. void
  371. ExporterClass::Export_Database_Mix (LPCTSTR path)
  372. {
  373. CWaitCursor wait_cursor;
  374. m_AssetList.Delete_All ();
  375. Path = path;
  376. FilenameBase = "always.dbs";
  377. //
  378. // Setup the temporary directory
  379. //
  380. Make_Temp_Directory ();
  381. //
  382. // Export the definition databases
  383. //
  384. Export_Definition_Databases (true);
  385. //
  386. // Cleanup any temp files we needed
  387. //
  388. Delete_Temp_Directory ();
  389. return ;
  390. }
  391. /////////////////////////////////////////////////////////
  392. //
  393. // Export_Definition_Databases
  394. //
  395. /////////////////////////////////////////////////////////
  396. bool
  397. ExporterClass::Export_Definition_Databases (bool use_temp_strings_library)
  398. {
  399. EditorMixFileCreator always_db_mix_creator;
  400. //
  401. // Save the preset library to the temp directory
  402. //
  403. PresetsFormClass *presets_form = ::Get_Presets_Form ();
  404. if (presets_form != NULL) {
  405. CString path = ::Make_Path (TempDirectory, "objects.ddb");
  406. presets_form->Save_Presets (path, 0, false, false);
  407. always_db_mix_creator.Add_File (path, ::Get_Filename_From_Path (path));
  408. }
  409. //
  410. // Add the strings table
  411. //
  412. if (use_temp_strings_library == false) {
  413. CString filename = ::Get_File_Mgr ()->Make_Full_Path (STRINGS_DB_PATH);
  414. always_db_mix_creator.Add_File (filename, ::Get_Filename_From_Path (filename));
  415. } else {
  416. //
  417. // Save the strings table to the temporary directory, then
  418. // add it to the mix file
  419. //
  420. CString path = ::Make_Path (TempDirectory, STRINGS_DB_NAME);
  421. StringsMgrClass::Save_Translation_Database (path);
  422. always_db_mix_creator.Add_File (path, STRINGS_DB_NAME);
  423. }
  424. //
  425. // Add the global conversation database
  426. //
  427. CString filename = ::Get_File_Mgr ()->Make_Full_Path (CONV_DB_PATH);
  428. always_db_mix_creator.Add_File (filename, ::Get_Filename_From_Path (filename));
  429. //
  430. // Now generate the definition databases mix file
  431. //
  432. CString mix_filename = ::Make_Path (Path, ALWAYS_DB_FILENAME);
  433. always_db_mix_creator.Generate_Mix_File (mix_filename);
  434. return true;
  435. }
  436. /////////////////////////////////////////////////////////
  437. //
  438. // Build_File_List
  439. //
  440. /////////////////////////////////////////////////////////
  441. bool
  442. ExporterClass::Build_File_List (const STRING_LIST &search_list, STRING_LIST &file_list)
  443. {
  444. bool retval = true;
  445. //
  446. // Loop through all the files in our include list
  447. //
  448. for (int index = 0; index < search_list.Count (); index ++) {
  449. CString wildcard = search_list[index];
  450. CString search = ::Get_File_Mgr ()->Make_Full_Path (wildcard);
  451. //
  452. // Make sure we have all the assets for this always group
  453. //
  454. if (DontGetVersionsFromVSS == false) {
  455. ::Get_File_Mgr ()->Get_Subproject (::Strip_Filename_From_Path (wildcard));
  456. }
  457. //
  458. // Add the all the files that match the search mask to this list
  459. //
  460. Find_Files (search, file_list);
  461. }
  462. return retval;
  463. }
  464. /////////////////////////////////////////////////////////
  465. //
  466. // Find_Files
  467. //
  468. /////////////////////////////////////////////////////////
  469. void
  470. ExporterClass::Find_Files (const char *search_mask, STRING_LIST &file_list)
  471. {
  472. //
  473. // Get the directory where we'll be copying the files from
  474. //
  475. CString directory = ::Strip_Filename_From_Path (search_mask);
  476. CString mask = ::Get_Filename_From_Path (search_mask);
  477. //
  478. // Find all files that match this wildcard
  479. //
  480. WIN32_FIND_DATA find_info = { 0 };
  481. BOOL keep_going = TRUE;
  482. for (HANDLE hfile_find = ::FindFirstFile (search_mask, &find_info);
  483. (hfile_find != INVALID_HANDLE_VALUE) && keep_going;
  484. keep_going = ::FindNextFile (hfile_find, &find_info))
  485. {
  486. //
  487. // Don't do this if its a directory
  488. //
  489. if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  490. //
  491. // Add this file to the list
  492. //
  493. CString full_path = Make_Path (directory, ::Get_Filename_From_Path (find_info.cFileName));
  494. file_list.Add (full_path);
  495. }
  496. }
  497. if (hfile_find != INVALID_HANDLE_VALUE) {
  498. ::FindClose (hfile_find);
  499. }
  500. CString full_search_mask = Make_Path (directory, "*.*");
  501. //
  502. // Now recurse into any sub-directories
  503. //
  504. keep_going = TRUE;
  505. for (hfile_find = ::FindFirstFile (full_search_mask, &find_info);
  506. (hfile_find != INVALID_HANDLE_VALUE) && keep_going;
  507. keep_going = ::FindNextFile (hfile_find, &find_info))
  508. {
  509. //
  510. // Don't do this if its a directory
  511. //
  512. if ((find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (find_info.cFileName[0] != '.')) {
  513. //
  514. // Build a search mask for this sub-directory
  515. //
  516. CString full_path = Make_Path (directory, ::Get_Filename_From_Path (find_info.cFileName));
  517. full_path = Make_Path (full_path, mask);
  518. //
  519. // Recurse into this sub-directory
  520. //
  521. Find_Files (full_path, file_list);
  522. }
  523. }
  524. if (hfile_find != INVALID_HANDLE_VALUE) {
  525. ::FindClose (hfile_find);
  526. }
  527. return ;
  528. }
  529. /////////////////////////////////////////////////////////
  530. //
  531. // Process_Include_Files
  532. //
  533. /////////////////////////////////////////////////////////
  534. void
  535. ExporterClass::Process_Include_Files (const STRING_LIST &search_list)
  536. {
  537. STRING_LIST file_list;
  538. Build_File_List (search_list, file_list);
  539. Export_Include_Files (file_list);
  540. return ;
  541. }
  542. /////////////////////////////////////////////////////////
  543. //
  544. // Export_Include_Files
  545. //
  546. /////////////////////////////////////////////////////////
  547. bool
  548. ExporterClass::Export_Include_Files (const STRING_LIST &file_list)
  549. {
  550. //
  551. // Add all the files from the list
  552. //
  553. for (int index = 0; index < file_list.Count (); index ++) {
  554. const CString &full_path = file_list[index];
  555. //
  556. // Add this file to the mix file
  557. //
  558. MixFileCreator.Add_File (full_path, ::Get_Filename_From_Path (full_path));
  559. //
  560. // Add this file to the level-asset list if its a W3D file
  561. //
  562. if (::Is_W3D_Filename (full_path)) {
  563. m_AssetList.Add ((LPCTSTR)::Get_Filename_From_Path (full_path));
  564. }
  565. }
  566. return true;
  567. }
  568. ////////////////////////////////////////////////////////////////////////////
  569. //
  570. // Delete_File
  571. //
  572. ////////////////////////////////////////////////////////////////////////////
  573. bool
  574. ExporterClass::Delete_File (LPCTSTR filename)
  575. {
  576. bool retval = false;
  577. ASSERT (filename != NULL);
  578. if (filename != NULL) {
  579. //
  580. // Strip the readonly bit off if necessary
  581. //
  582. DWORD attributes = ::GetFileAttributes (filename);
  583. if ((attributes != 0xFFFFFFFF) &&
  584. ((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY))
  585. {
  586. ::SetFileAttributes (filename, attributes & (~FILE_ATTRIBUTE_READONLY));
  587. }
  588. //
  589. // Perform the delete operation!
  590. //
  591. if ((attributes != 0xFFFFFFFF) &&
  592. ((attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY))
  593. {
  594. retval = (::RemoveDirectory (filename) == TRUE);
  595. } else {
  596. retval = (::DeleteFile (filename) == TRUE);
  597. }
  598. }
  599. return retval;
  600. }
  601. //////////////////////////////////////////////////////////////////////////////////
  602. //
  603. // Clean_Directory
  604. //
  605. //////////////////////////////////////////////////////////////////////////////////
  606. bool
  607. ExporterClass::Clean_Directory (LPCTSTR local_dir)
  608. {
  609. bool retval = true;
  610. //
  611. // Build a search mask from the directory
  612. //
  613. CString search_mask = CString (local_dir) + "\\*.*";
  614. //
  615. // Loop through all the files in this directory and add them
  616. // to our list
  617. //
  618. DynamicVectorClass<CString> file_list;
  619. BOOL keep_going = TRUE;
  620. WIN32_FIND_DATA find_info = { 0 };
  621. for (HANDLE hfind = ::FindFirstFile (search_mask, &find_info);
  622. (hfind != INVALID_HANDLE_VALUE) && keep_going;
  623. keep_going = ::FindNextFile (hfind, &find_info))
  624. {
  625. //
  626. // If this file isn't a directory, add it to the list
  627. //
  628. if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
  629. CString filename = find_info.cFileName;
  630. file_list.Add (filename);
  631. } else if (find_info.cFileName[0] != '.') {
  632. //
  633. // Recurse into this subdirectory
  634. //
  635. CString full_path = local_dir;
  636. ::Delimit_Path (full_path);
  637. full_path += find_info.cFileName;
  638. Clean_Directory (full_path);
  639. //
  640. // Add this directory to the list so it will get
  641. // deleted with the files...
  642. //
  643. CString filename = find_info.cFileName;
  644. file_list.Add (filename);
  645. }
  646. }
  647. //
  648. // Close the search handle
  649. //
  650. if (hfind != NULL) {
  651. ::FindClose (hfind);
  652. }
  653. //
  654. // Now loop through all the files and delete them
  655. //
  656. for (int index = 0; index < file_list.Count (); index ++) {
  657. CString &filename = file_list[index];
  658. CString full_path = ::Make_Path (local_dir, filename);
  659. Delete_File (full_path);
  660. }
  661. return retval;
  662. }
  663. //////////////////////////////////////////////////////////////////////////////////
  664. //
  665. // Pre_Level_Export
  666. //
  667. //////////////////////////////////////////////////////////////////////////////////
  668. void
  669. ExporterClass::Pre_Level_Export (DynamicVectorClass<NodeClass *> &node_list)
  670. {
  671. //
  672. // Force static anim phys to be displayed
  673. //
  674. ::Get_Scene_Editor ()->Display_Static_Anim_Phys (true);
  675. ::Get_Scene_Editor ()->Display_Editor_Objects (true);
  676. //
  677. // Make sure the scenes are repartitioned
  678. // (gth) can't re-partition the object culling systems or
  679. // we'll lose hierarchical vis
  680. //
  681. ::Get_Scene_Editor ()->Re_Partition_Static_Lights ();
  682. ::Get_Scene_Editor ()->Re_Partition_Audio_System ();
  683. ::Get_Scene_Editor ()->Set_Selection (NULL);
  684. ::Get_Scene_Editor ()->Display_Vis_Points (false);
  685. //
  686. // Make sure the buildings are in a good state
  687. //
  688. GameObjManager::Update_Building_Collection_Spheres ();
  689. GameObjManager::Init_Buildings ();
  690. //
  691. // Let the nodes know we are about to start exporting them.
  692. // This way they can remove any 'editor-only' artifacts from
  693. // the system.
  694. //
  695. NodeMgrClass::Build_Full_Node_List (node_list);
  696. for (int index = 0; index < node_list.Count (); index ++) {
  697. node_list[index]->Pre_Export ();
  698. }
  699. //
  700. // Force the pathfind system to use any available waypath data
  701. // in its evaluation
  702. //
  703. PathfindClass::Get_Instance ()->Generate_Waypath_Sectors_And_Portals ();
  704. return ;
  705. }
  706. //////////////////////////////////////////////////////////////////////////////////
  707. //
  708. // Post_Level_Export
  709. //
  710. //////////////////////////////////////////////////////////////////////////////////
  711. void
  712. ExporterClass::Post_Level_Export (DynamicVectorClass<NodeClass *> &node_list)
  713. {
  714. //
  715. // Let the nodes know we're done exporting
  716. //
  717. for (int index = 0; index < node_list.Count (); index ++) {
  718. NodeClass *node = node_list[index];
  719. node->Post_Export ();
  720. }
  721. return ;
  722. }
  723. /////////////////////////////////////////////////////////
  724. //
  725. // Export_Package
  726. //
  727. /////////////////////////////////////////////////////////
  728. void
  729. ExporterClass::Export_Package (LPCTSTR full_path)
  730. {
  731. CWaitCursor wait_cursor;
  732. //
  733. // Save the preset changes to disk as necessary
  734. //
  735. if (PresetMgrClass::Are_Presets_Dirty ()) {
  736. PresetMgrClass::Check_In_Presets ();
  737. }
  738. //
  739. // Get the base destination path
  740. //
  741. Path = ::Strip_Filename_From_Path (full_path);
  742. //
  743. // Strip the path and extension from the filename to give
  744. // us a base for use with other filenames
  745. //
  746. FilenameBase = ::Get_Filename_From_Path (full_path);
  747. int extension_index = FilenameBase.ReverseFind ('.');
  748. if (extension_index != -1) {
  749. FilenameBase = FilenameBase.Left (extension_index);
  750. }
  751. //
  752. // Add all the files from our asset tree to this package
  753. //
  754. Add_Files_To_Mod_Package (AssetPackageMgrClass::Get_Current_Package_Path ());
  755. //
  756. // Create the mix file...
  757. //
  758. MixFileCreator.Generate_Mix_File (full_path);
  759. //
  760. // Cache the directory path we just exported into
  761. //
  762. theApp.WriteProfileString (CONFIG_KEY, LAST_EXPORT_DIR_VALUE, Path);
  763. return ;
  764. }
  765. /////////////////////////////////////////////////////////
  766. //
  767. // Add_Files_To_Mod_Package
  768. //
  769. /////////////////////////////////////////////////////////
  770. void
  771. ExporterClass::Add_Files_To_Mod_Package (LPCTSTR full_path)
  772. {
  773. //
  774. // Build a search path for all the files in this directory
  775. //
  776. CString search_path = ::Make_Path (full_path, "*.*");
  777. //
  778. // Search for all the files in this directory
  779. //
  780. WIN32_FIND_DATA find_info = { 0 };
  781. BOOL keep_going = TRUE;
  782. HANDLE file_find = NULL;
  783. for (file_find = ::FindFirstFile (search_path, &find_info);
  784. (file_find != INVALID_HANDLE_VALUE) && keep_going;
  785. keep_going = ::FindNextFile (file_find, &find_info))
  786. {
  787. if (find_info.cFileName[0] != '.') {
  788. if ((find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
  789. //
  790. // Recurse into this sub-directory
  791. //
  792. CString subdir_path = ::Make_Path (full_path, find_info.cFileName);
  793. Add_Files_To_Mod_Package (subdir_path);
  794. } else {
  795. //
  796. // Skip all lvl files
  797. //
  798. if (::strstr (find_info.cFileName, ".lvl") == NULL) {
  799. //
  800. // Simply add this file to the mix
  801. //
  802. CString file_path = ::Make_Path (full_path, find_info.cFileName);
  803. MixFileCreator.Add_File (file_path, find_info.cFileName);
  804. }
  805. }
  806. }
  807. }
  808. //
  809. // Close the search handle
  810. //
  811. if (file_find != INVALID_HANDLE_VALUE) {
  812. ::FindClose (file_find);
  813. }
  814. return ;
  815. }
  816. /////////////////////////////////////////////////////////
  817. //
  818. // Export_Level_Only
  819. //
  820. /////////////////////////////////////////////////////////
  821. void
  822. ExporterClass::Export_Level_Only (LPCTSTR path)
  823. {
  824. CWaitCursor wait_cursor;
  825. m_AssetList.Delete_All ();
  826. //
  827. // Prepare the level for exporting...
  828. //
  829. DynamicVectorClass<NodeClass *> node_list;
  830. Pre_Level_Export (node_list);
  831. //
  832. // Get the base destination path
  833. //
  834. Path = ::Strip_Filename_From_Path (path);
  835. TempDirectory = Path;
  836. ::Set_Current_Directory (TempDirectory);
  837. ::SetCurrentDirectory (TempDirectory);
  838. //
  839. // Strip the path and extension from the filename to give
  840. // us a base for use with other filenames
  841. //
  842. FilenameBase = ::Get_Filename_From_Path (path);
  843. int extension_index = FilenameBase.ReverseFind ('.');
  844. if (extension_index != -1) {
  845. FilenameBase = FilenameBase.Left (extension_index);
  846. }
  847. //
  848. // Export the level in game format
  849. //
  850. Export_Level_File ();
  851. //
  852. // Add the temp definition database
  853. //
  854. CString temp_db_source = ::Get_File_Mgr ()->Make_Full_Path (TEMP_DB_PATH);
  855. CString temp_db_dest = ::Make_Path(Path,FilenameBase + ".ddb");
  856. ::CopyFile(temp_db_source,temp_db_dest,false);
  857. //
  858. // Resetore the level after exporting
  859. //
  860. Post_Level_Export (node_list);
  861. //
  862. // Cache the directory path we just exported into
  863. //
  864. theApp.WriteProfileString (CONFIG_KEY, LAST_EXPORT_DIR_VALUE, Path);
  865. return ;
  866. }