w3d_dep.cpp 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  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 : G *
  23. * *
  24. * $Archive:: /Commando/Code/ww3d2/w3d_dep.cpp $*
  25. * *
  26. * $Author:: Byon_g $*
  27. * *
  28. * $Modtime:: 7/23/01 6:17p $*
  29. * *
  30. * $Revision:: 4 $*
  31. * *
  32. *---------------------------------------------------------------------------------------------*
  33. * Functions: *
  34. * Get_W3D_Dependencies -- Scans a W3D file to determine which other files it depends on. *
  35. * Scan_Chunk -- Chooses the correct chunk loader for this chunk. *
  36. * Scan_Mesh -- Scans a mesh for references to other files. *
  37. * Scan_Mesh_Header -- Scans a mesh's header for file references. *
  38. * Scan_Mesh_Textures -- Scans a mesh's textures. *
  39. * Scan_HTree -- Scans an HTree for references to other files. *
  40. * Scan_Anim -- Scans an animation for references to other files. *
  41. * Scan_Compressed_Anim -- Scans an animation for references to other files. *
  42. * Scan_HModel -- Scans an HModel for references to other files. *
  43. * Scan_Emitter -- Scans an emitter for references to other files. *
  44. * Scan_Aggregate -- Scans an aggregate for references to other files. *
  45. * Scan_HLOD -- Scans an HLOD for references to other files. *
  46. * Get_W3D_Name -- Gets a W3D object name from a W3D filename. *
  47. * Make_W3D_Filename -- Converts a W3D object name into a W3D filename. *
  48. * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
  49. #include "w3d_dep.h"
  50. #include "w3d_file.h"
  51. #include <assert.h>
  52. #include <chunkio.h>
  53. #include "ffactory.h"
  54. /*
  55. ** Forward declarations.
  56. */
  57. static void Scan_Chunk (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  58. static void Scan_Mesh (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  59. static void Scan_Mesh_Header (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  60. static void Scan_Mesh_Textures (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  61. static void Scan_Anim (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  62. static void Scan_Compressed_Anim (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  63. static void Scan_HModel (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  64. static void Scan_Emitter (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  65. static void Scan_Aggregate (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  66. static void Scan_HLOD (ChunkLoadClass &cload, StringList &files, const char *w3d_name);
  67. static void Get_W3D_Name (const char *filename, char *w3d_name);
  68. static const char * Make_W3D_Filename (const char *w3d_name);
  69. /***********************************************************************************************
  70. * Get_W3D_Dependencies -- Scans a W3D file to determine which other files it depends on. *
  71. * *
  72. * INPUT: *
  73. * *
  74. * OUTPUT: *
  75. * *
  76. * WARNINGS: *
  77. * *
  78. * HISTORY: *
  79. * 4/3/00 AJA : Created. *
  80. *=============================================================================================*/
  81. bool Get_W3D_Dependencies (const char *w3d_filename, StringList &files)
  82. {
  83. assert(w3d_filename);
  84. // Open the W3D file.
  85. FileClass *file=_TheFileFactory->Get_File(w3d_filename);
  86. if ( file ) {
  87. file->Open();
  88. if ( ! file->Is_Open()) {
  89. _TheFileFactory->Return_File(file);
  90. file=NULL;
  91. return false;
  92. }
  93. } else {
  94. return false;
  95. }
  96. // Get the W3D name from the filename.
  97. char w3d_name[W3D_NAME_LEN];
  98. Get_W3D_Name(w3d_filename, w3d_name);
  99. // Create a chunk loader for this file, and scan the file.
  100. ChunkLoadClass cload(file);
  101. while (cload.Open_Chunk())
  102. {
  103. Scan_Chunk(cload, files, w3d_name);
  104. cload.Close_Chunk();
  105. }
  106. // Close the file.
  107. file->Close();
  108. _TheFileFactory->Return_File(file);
  109. file=NULL;
  110. // Sort the set of filenames, and remove any duplicates.
  111. files.sort();
  112. files.unique();
  113. return true;
  114. }
  115. /***********************************************************************************************
  116. * Scan_Chunk -- Chooses the correct chunk loader for this chunk. *
  117. * *
  118. * INPUT: *
  119. * *
  120. * OUTPUT: *
  121. * *
  122. * WARNINGS: *
  123. * *
  124. * HISTORY: *
  125. * 4/3/00 AJA : Created. *
  126. *=============================================================================================*/
  127. static void Scan_Chunk (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  128. {
  129. assert(w3d_name);
  130. switch (cload.Cur_Chunk_ID())
  131. {
  132. case W3D_CHUNK_MESH:
  133. Scan_Mesh(cload, files, w3d_name);
  134. break;
  135. case W3D_CHUNK_ANIMATION:
  136. Scan_Anim(cload, files, w3d_name);
  137. break;
  138. case W3D_CHUNK_COMPRESSED_ANIMATION:
  139. Scan_Compressed_Anim(cload, files, w3d_name);
  140. break;
  141. case W3D_CHUNK_HMODEL:
  142. Scan_HModel(cload, files, w3d_name);
  143. break;
  144. case W3D_CHUNK_EMITTER:
  145. Scan_Emitter(cload, files, w3d_name);
  146. break;
  147. case W3D_CHUNK_AGGREGATE:
  148. Scan_Aggregate(cload, files, w3d_name);
  149. break;
  150. case W3D_CHUNK_HLOD:
  151. Scan_HLOD(cload, files, w3d_name);
  152. break;
  153. }
  154. }
  155. /***********************************************************************************************
  156. * Scan_Mesh -- Scans a mesh for references to other files. *
  157. * *
  158. * INPUT: *
  159. * *
  160. * OUTPUT: *
  161. * *
  162. * WARNINGS: *
  163. * *
  164. * HISTORY: *
  165. *=============================================================================================*/
  166. static void Scan_Mesh (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  167. {
  168. // Scan the mesh's sub-chunks.
  169. while (cload.Open_Chunk())
  170. {
  171. switch (cload.Cur_Chunk_ID())
  172. {
  173. case W3D_CHUNK_MESH_HEADER3:
  174. // Ahh, the mesh header.
  175. Scan_Mesh_Header(cload, files, w3d_name);
  176. break;
  177. case W3D_CHUNK_TEXTURES:
  178. // We sure want textures...
  179. Scan_Mesh_Textures(cload, files, w3d_name);
  180. break;
  181. }
  182. cload.Close_Chunk();
  183. }
  184. }
  185. /***********************************************************************************************
  186. * Scan_Mesh_Header -- Scans a mesh's header for file references. *
  187. * *
  188. * INPUT: *
  189. * *
  190. * OUTPUT: *
  191. * *
  192. * WARNINGS: *
  193. * *
  194. * HISTORY: *
  195. * 4/3/00 AJA : Created. *
  196. *=============================================================================================*/
  197. static void Scan_Mesh_Header (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  198. {
  199. // In the mesh header, we want the 'ContainerName' string.
  200. W3dMeshHeader3Struct hdr;
  201. cload.Read(&hdr, sizeof(hdr));
  202. if (strcmp(hdr.ContainerName, w3d_name) != 0)
  203. {
  204. // The container is not this file... Create a W3D filename
  205. // for the container object and add it to the list of
  206. // files we refer to.
  207. const char *filename = Make_W3D_Filename(hdr.ContainerName);
  208. if (*filename) // don't push empty filenames
  209. files.push_back(filename);
  210. }
  211. }
  212. /***********************************************************************************************
  213. * Scan_Mesh_Textures -- Scans a mesh's textures. *
  214. * *
  215. * INPUT: *
  216. * *
  217. * OUTPUT: *
  218. * *
  219. * WARNINGS: *
  220. * *
  221. * HISTORY: *
  222. * 4/3/00 AJA : Created. *
  223. *=============================================================================================*/
  224. static void Scan_Mesh_Textures (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  225. {
  226. // Let's see which textures are used by this mesh...
  227. while (cload.Open_Chunk())
  228. {
  229. // We're interested in the TEXTURE sub-chunk.
  230. if (cload.Cur_Chunk_ID() == W3D_CHUNK_TEXTURE)
  231. {
  232. while (cload.Open_Chunk())
  233. {
  234. // We're interested in the TEXTURE_NAME sub-chunk.
  235. if (cload.Cur_Chunk_ID() == W3D_CHUNK_TEXTURE_NAME)
  236. {
  237. // This chunk's data is a NULL-terminated string
  238. // which is the texture filename. Read it and
  239. // add it to the list of files referred to.
  240. char texture[_MAX_PATH];
  241. cload.Read(texture, cload.Cur_Chunk_Length());
  242. if (*texture) // don't push empty filenames
  243. files.push_back(texture);
  244. }
  245. cload.Close_Chunk();
  246. }
  247. }
  248. cload.Close_Chunk();
  249. }
  250. }
  251. /***********************************************************************************************
  252. * Scan_Anim -- Scans an animation for references to other files. *
  253. * *
  254. * INPUT: *
  255. * *
  256. * OUTPUT: *
  257. * *
  258. * WARNINGS: *
  259. * *
  260. * HISTORY: *
  261. *=============================================================================================*/
  262. static void Scan_Anim (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  263. {
  264. while (cload.Open_Chunk())
  265. {
  266. // We only want the animation header, because it can refer to an HTree.
  267. if (cload.Cur_Chunk_ID() == W3D_CHUNK_ANIMATION_HEADER)
  268. {
  269. // Read in the header.
  270. W3dAnimHeaderStruct hdr;
  271. cload.Read(&hdr, sizeof(hdr));
  272. if (strcmp(hdr.HierarchyName, w3d_name) != 0)
  273. {
  274. const char *hierarchy = Make_W3D_Filename(hdr.HierarchyName);
  275. if (*hierarchy) // don't push an empty filename
  276. files.push_back(hierarchy);
  277. }
  278. }
  279. cload.Close_Chunk();
  280. }
  281. }
  282. /***********************************************************************************************
  283. * Scan_Compressed_Anim -- Scans a compressed animation mesh for references to other files. *
  284. * *
  285. * INPUT: *
  286. * *
  287. * OUTPUT: *
  288. * *
  289. * WARNINGS: *
  290. * *
  291. * HISTORY: *
  292. *=============================================================================================*/
  293. static void Scan_Compressed_Anim (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  294. {
  295. while (cload.Open_Chunk())
  296. {
  297. // We only want the animation header, because it can refer to an HTree.
  298. if (cload.Cur_Chunk_ID() == W3D_CHUNK_ANIMATION_HEADER)
  299. {
  300. // Read in the header.
  301. W3dCompressedAnimHeaderStruct hdr;
  302. cload.Read(&hdr, sizeof(hdr));
  303. if (strcmp(hdr.HierarchyName, w3d_name) != 0)
  304. {
  305. const char *hierarchy = Make_W3D_Filename(hdr.HierarchyName);
  306. if (*hierarchy) // don't push an empty filename
  307. files.push_back(hierarchy);
  308. }
  309. }
  310. cload.Close_Chunk();
  311. }
  312. }
  313. /***********************************************************************************************
  314. * Scan_HModel -- Scans an HModel for references to other files. *
  315. * *
  316. * INPUT: *
  317. * *
  318. * OUTPUT: *
  319. * *
  320. * WARNINGS: *
  321. * *
  322. * HISTORY: *
  323. *=============================================================================================*/
  324. static void Scan_HModel (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  325. {
  326. while (cload.Open_Chunk())
  327. {
  328. // We only want the header because it can refer to an HTree.
  329. if (cload.Cur_Chunk_ID() == W3D_CHUNK_HMODEL_HEADER)
  330. {
  331. // Read in the header.
  332. W3dHModelHeaderStruct hdr;
  333. cload.Read(&hdr, sizeof(hdr));
  334. if (strcmp(hdr.HierarchyName, w3d_name) != 0)
  335. {
  336. const char *hierarchy = Make_W3D_Filename(hdr.HierarchyName);
  337. if (*hierarchy) // don't push an empty filename
  338. files.push_back(hierarchy);
  339. }
  340. }
  341. cload.Close_Chunk();
  342. }
  343. }
  344. /***********************************************************************************************
  345. * Scan_Emitter -- Scans an emitter for references to other files. *
  346. * *
  347. * INPUT: *
  348. * *
  349. * OUTPUT: *
  350. * *
  351. * WARNINGS: *
  352. * *
  353. * HISTORY: *
  354. *=============================================================================================*/
  355. static void Scan_Emitter (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  356. {
  357. while (cload.Open_Chunk())
  358. {
  359. // We only want the emitter info chunk, it has a texture name in it.
  360. if (cload.Cur_Chunk_ID() == W3D_CHUNK_EMITTER_INFO)
  361. {
  362. // Read in the header.
  363. W3dEmitterInfoStruct hdr;
  364. cload.Read(&hdr, sizeof(hdr));
  365. if (hdr.TextureFilename[0]) // don't push an empty texture name
  366. files.push_back(hdr.TextureFilename);
  367. }
  368. cload.Close_Chunk();
  369. }
  370. }
  371. /***********************************************************************************************
  372. * Scan_Aggregate -- Scans an aggregate for references to other files. *
  373. * *
  374. * INPUT: *
  375. * *
  376. * OUTPUT: *
  377. * *
  378. * WARNINGS: *
  379. * *
  380. * HISTORY: *
  381. *=============================================================================================*/
  382. static void Scan_Aggregate (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  383. {
  384. while (cload.Open_Chunk())
  385. {
  386. // We want the aggregate info chunk.
  387. if (cload.Cur_Chunk_ID() == W3D_CHUNK_AGGREGATE_INFO)
  388. {
  389. W3dAggregateInfoStruct chunk;
  390. cload.Read(&chunk, sizeof(chunk));
  391. if (strcmp(chunk.BaseModelName, w3d_name) != 0)
  392. {
  393. // Check the name of the base model against the name of this file.
  394. const char *base_model = Make_W3D_Filename(chunk.BaseModelName);
  395. if (*base_model)
  396. files.push_back(base_model);
  397. }
  398. // Iterate through the sub-objects.
  399. unsigned int i;
  400. for (i = 0; i < chunk.SubobjectCount; ++i)
  401. {
  402. W3dAggregateSubobjectStruct subchunk;
  403. cload.Read(&subchunk, sizeof(subchunk));
  404. if (strcmp(subchunk.SubobjectName, w3d_name) != 0)
  405. {
  406. // Check the name of the subobject against the name of this file.
  407. const char *subobject = Make_W3D_Filename(subchunk.SubobjectName);
  408. if (*subobject)
  409. files.push_back(subobject);
  410. }
  411. }
  412. }
  413. cload.Close_Chunk();
  414. }
  415. }
  416. /***********************************************************************************************
  417. * Scan_HLOD -- Scans an HLOD for references to other files. *
  418. * *
  419. * INPUT: *
  420. * *
  421. * OUTPUT: *
  422. * *
  423. * WARNINGS: *
  424. * *
  425. * HISTORY: *
  426. *=============================================================================================*/
  427. static void Scan_HLOD (ChunkLoadClass &cload, StringList &files, const char *w3d_name)
  428. {
  429. while (cload.Open_Chunk())
  430. {
  431. // We only want the header.
  432. if (cload.Cur_Chunk_ID() == W3D_CHUNK_HLOD_HEADER)
  433. {
  434. W3dHLodHeaderStruct hdr;
  435. cload.Read(&hdr, sizeof(hdr));
  436. if (strcmp(hdr.HierarchyName, w3d_name) != 0)
  437. {
  438. const char *hierarchy = Make_W3D_Filename(hdr.HierarchyName);
  439. if (*hierarchy)
  440. files.push_back(hierarchy);
  441. }
  442. }
  443. cload.Close_Chunk();
  444. }
  445. }
  446. /***********************************************************************************************
  447. * Get_W3D_Name -- Gets a W3D object name from a W3D filename. *
  448. * *
  449. * INPUT: *
  450. * *
  451. * OUTPUT: *
  452. * *
  453. * WARNINGS: *
  454. * *
  455. * HISTORY: *
  456. * 4/3/00 AJA : Created. *
  457. *=============================================================================================*/
  458. static void Get_W3D_Name (const char *filename, char *w3d_name)
  459. {
  460. assert(filename);
  461. assert(w3d_name);
  462. // Figure out the first character of the name of the file
  463. // (bypass the path if it was given).
  464. const char *start = strrchr(filename, '\\');
  465. if (start)
  466. ++start; // point to first character after the last backslash
  467. else
  468. start = filename; // point to the start of the filename
  469. // We don't want to copy the .w3d extension. Find where
  470. // it occurs.
  471. const char *end = strrchr(start, '.');
  472. if (!end)
  473. end = start + strlen(start); // point to the null character
  474. // Copy all characters from start to end (excluding 'end')
  475. // into the w3d_name buffer. Then capitalize the string.
  476. memset(w3d_name, 0, W3D_NAME_LEN); // blank out the buffer
  477. int num_chars = end - start;
  478. strncpy(w3d_name, start, num_chars < W3D_NAME_LEN ? num_chars : W3D_NAME_LEN-1);
  479. strupr(w3d_name);
  480. }
  481. /***********************************************************************************************
  482. * Make_W3D_Filename -- Converts a W3D object name into a W3D filename. *
  483. * *
  484. * INPUT: *
  485. * *
  486. * OUTPUT: *
  487. * *
  488. * WARNINGS: *
  489. * *
  490. * HISTORY: *
  491. * 4/3/00 AJA : Created. *
  492. *=============================================================================================*/
  493. static const char * Make_W3D_Filename (const char *w3d_name)
  494. {
  495. assert(w3d_name);
  496. assert(strlen(w3d_name) < W3D_NAME_LEN);
  497. // Copy the w3d name into a static buffer, turn it into lowercase
  498. // letters, and append a ".w3d" file extension. That's the filename.
  499. static char buffer[64];
  500. if (*w3d_name == 0)
  501. {
  502. // Empty W3D name case.
  503. buffer[0] = 0;
  504. return buffer;
  505. }
  506. strcpy(buffer, w3d_name);
  507. char *dot = strchr(buffer, '.');
  508. if (dot)
  509. *dot = 0;
  510. strlwr(buffer);
  511. strcat(buffer, ".w3d");
  512. return buffer;
  513. }