/* ** 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 . */ /*********************************************************************************************** *** 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 *** *********************************************************************************************** * * * Project Name : G * * * * $Archive:: /Commando/Code/ww3d2/w3d_dep.cpp $* * * * $Author:: Byon_g $* * * * $Modtime:: 7/23/01 6:17p $* * * * $Revision:: 4 $* * * *---------------------------------------------------------------------------------------------* * Functions: * * Get_W3D_Dependencies -- Scans a W3D file to determine which other files it depends on. * * Scan_Chunk -- Chooses the correct chunk loader for this chunk. * * Scan_Mesh -- Scans a mesh for references to other files. * * Scan_Mesh_Header -- Scans a mesh's header for file references. * * Scan_Mesh_Textures -- Scans a mesh's textures. * * Scan_HTree -- Scans an HTree for references to other files. * * Scan_Anim -- Scans an animation for references to other files. * * Scan_Compressed_Anim -- Scans an animation for references to other files. * * Scan_HModel -- Scans an HModel for references to other files. * * Scan_Emitter -- Scans an emitter for references to other files. * * Scan_Aggregate -- Scans an aggregate for references to other files. * * Scan_HLOD -- Scans an HLOD for references to other files. * * Get_W3D_Name -- Gets a W3D object name from a W3D filename. * * Make_W3D_Filename -- Converts a W3D object name into a W3D filename. * * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */ #include "w3d_dep.h" #include "w3d_file.h" #include #include #include "ffactory.h" /* ** Forward declarations. */ static void Scan_Chunk (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Scan_Mesh (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Scan_Mesh_Header (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Scan_Mesh_Textures (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Scan_Anim (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Scan_Compressed_Anim (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Scan_HModel (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Scan_Emitter (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Scan_Aggregate (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Scan_HLOD (ChunkLoadClass &cload, StringList &files, const char *w3d_name); static void Get_W3D_Name (const char *filename, char *w3d_name); static const char * Make_W3D_Filename (const char *w3d_name); /*********************************************************************************************** * Get_W3D_Dependencies -- Scans a W3D file to determine which other files it depends on. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 4/3/00 AJA : Created. * *=============================================================================================*/ bool Get_W3D_Dependencies (const char *w3d_filename, StringList &files) { assert(w3d_filename); // Open the W3D file. FileClass *file=_TheFileFactory->Get_File(w3d_filename); if ( file ) { file->Open(); if ( ! file->Is_Open()) { _TheFileFactory->Return_File(file); file=NULL; return false; } } else { return false; } // Get the W3D name from the filename. char w3d_name[W3D_NAME_LEN]; Get_W3D_Name(w3d_filename, w3d_name); // Create a chunk loader for this file, and scan the file. ChunkLoadClass cload(file); while (cload.Open_Chunk()) { Scan_Chunk(cload, files, w3d_name); cload.Close_Chunk(); } // Close the file. file->Close(); _TheFileFactory->Return_File(file); file=NULL; // Sort the set of filenames, and remove any duplicates. files.sort(); files.unique(); return true; } /*********************************************************************************************** * Scan_Chunk -- Chooses the correct chunk loader for this chunk. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 4/3/00 AJA : Created. * *=============================================================================================*/ static void Scan_Chunk (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { assert(w3d_name); switch (cload.Cur_Chunk_ID()) { case W3D_CHUNK_MESH: Scan_Mesh(cload, files, w3d_name); break; case W3D_CHUNK_ANIMATION: Scan_Anim(cload, files, w3d_name); break; case W3D_CHUNK_COMPRESSED_ANIMATION: Scan_Compressed_Anim(cload, files, w3d_name); break; case W3D_CHUNK_HMODEL: Scan_HModel(cload, files, w3d_name); break; case W3D_CHUNK_EMITTER: Scan_Emitter(cload, files, w3d_name); break; case W3D_CHUNK_AGGREGATE: Scan_Aggregate(cload, files, w3d_name); break; case W3D_CHUNK_HLOD: Scan_HLOD(cload, files, w3d_name); break; } } /*********************************************************************************************** * Scan_Mesh -- Scans a mesh for references to other files. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ static void Scan_Mesh (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { // Scan the mesh's sub-chunks. while (cload.Open_Chunk()) { switch (cload.Cur_Chunk_ID()) { case W3D_CHUNK_MESH_HEADER3: // Ahh, the mesh header. Scan_Mesh_Header(cload, files, w3d_name); break; case W3D_CHUNK_TEXTURES: // We sure want textures... Scan_Mesh_Textures(cload, files, w3d_name); break; } cload.Close_Chunk(); } } /*********************************************************************************************** * Scan_Mesh_Header -- Scans a mesh's header for file references. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 4/3/00 AJA : Created. * *=============================================================================================*/ static void Scan_Mesh_Header (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { // In the mesh header, we want the 'ContainerName' string. W3dMeshHeader3Struct hdr; cload.Read(&hdr, sizeof(hdr)); if (strcmp(hdr.ContainerName, w3d_name) != 0) { // The container is not this file... Create a W3D filename // for the container object and add it to the list of // files we refer to. const char *filename = Make_W3D_Filename(hdr.ContainerName); if (*filename) // don't push empty filenames files.push_back(filename); } } /*********************************************************************************************** * Scan_Mesh_Textures -- Scans a mesh's textures. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 4/3/00 AJA : Created. * *=============================================================================================*/ static void Scan_Mesh_Textures (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { // Let's see which textures are used by this mesh... while (cload.Open_Chunk()) { // We're interested in the TEXTURE sub-chunk. if (cload.Cur_Chunk_ID() == W3D_CHUNK_TEXTURE) { while (cload.Open_Chunk()) { // We're interested in the TEXTURE_NAME sub-chunk. if (cload.Cur_Chunk_ID() == W3D_CHUNK_TEXTURE_NAME) { // This chunk's data is a NULL-terminated string // which is the texture filename. Read it and // add it to the list of files referred to. char texture[_MAX_PATH]; cload.Read(texture, cload.Cur_Chunk_Length()); if (*texture) // don't push empty filenames files.push_back(texture); } cload.Close_Chunk(); } } cload.Close_Chunk(); } } /*********************************************************************************************** * Scan_Anim -- Scans an animation for references to other files. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ static void Scan_Anim (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { while (cload.Open_Chunk()) { // We only want the animation header, because it can refer to an HTree. if (cload.Cur_Chunk_ID() == W3D_CHUNK_ANIMATION_HEADER) { // Read in the header. W3dAnimHeaderStruct hdr; cload.Read(&hdr, sizeof(hdr)); if (strcmp(hdr.HierarchyName, w3d_name) != 0) { const char *hierarchy = Make_W3D_Filename(hdr.HierarchyName); if (*hierarchy) // don't push an empty filename files.push_back(hierarchy); } } cload.Close_Chunk(); } } /*********************************************************************************************** * Scan_Compressed_Anim -- Scans a compressed animation mesh for references to other files. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ static void Scan_Compressed_Anim (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { while (cload.Open_Chunk()) { // We only want the animation header, because it can refer to an HTree. if (cload.Cur_Chunk_ID() == W3D_CHUNK_ANIMATION_HEADER) { // Read in the header. W3dCompressedAnimHeaderStruct hdr; cload.Read(&hdr, sizeof(hdr)); if (strcmp(hdr.HierarchyName, w3d_name) != 0) { const char *hierarchy = Make_W3D_Filename(hdr.HierarchyName); if (*hierarchy) // don't push an empty filename files.push_back(hierarchy); } } cload.Close_Chunk(); } } /*********************************************************************************************** * Scan_HModel -- Scans an HModel for references to other files. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ static void Scan_HModel (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { while (cload.Open_Chunk()) { // We only want the header because it can refer to an HTree. if (cload.Cur_Chunk_ID() == W3D_CHUNK_HMODEL_HEADER) { // Read in the header. W3dHModelHeaderStruct hdr; cload.Read(&hdr, sizeof(hdr)); if (strcmp(hdr.HierarchyName, w3d_name) != 0) { const char *hierarchy = Make_W3D_Filename(hdr.HierarchyName); if (*hierarchy) // don't push an empty filename files.push_back(hierarchy); } } cload.Close_Chunk(); } } /*********************************************************************************************** * Scan_Emitter -- Scans an emitter for references to other files. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ static void Scan_Emitter (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { while (cload.Open_Chunk()) { // We only want the emitter info chunk, it has a texture name in it. if (cload.Cur_Chunk_ID() == W3D_CHUNK_EMITTER_INFO) { // Read in the header. W3dEmitterInfoStruct hdr; cload.Read(&hdr, sizeof(hdr)); if (hdr.TextureFilename[0]) // don't push an empty texture name files.push_back(hdr.TextureFilename); } cload.Close_Chunk(); } } /*********************************************************************************************** * Scan_Aggregate -- Scans an aggregate for references to other files. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ static void Scan_Aggregate (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { while (cload.Open_Chunk()) { // We want the aggregate info chunk. if (cload.Cur_Chunk_ID() == W3D_CHUNK_AGGREGATE_INFO) { W3dAggregateInfoStruct chunk; cload.Read(&chunk, sizeof(chunk)); if (strcmp(chunk.BaseModelName, w3d_name) != 0) { // Check the name of the base model against the name of this file. const char *base_model = Make_W3D_Filename(chunk.BaseModelName); if (*base_model) files.push_back(base_model); } // Iterate through the sub-objects. unsigned int i; for (i = 0; i < chunk.SubobjectCount; ++i) { W3dAggregateSubobjectStruct subchunk; cload.Read(&subchunk, sizeof(subchunk)); if (strcmp(subchunk.SubobjectName, w3d_name) != 0) { // Check the name of the subobject against the name of this file. const char *subobject = Make_W3D_Filename(subchunk.SubobjectName); if (*subobject) files.push_back(subobject); } } } cload.Close_Chunk(); } } /*********************************************************************************************** * Scan_HLOD -- Scans an HLOD for references to other files. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * *=============================================================================================*/ static void Scan_HLOD (ChunkLoadClass &cload, StringList &files, const char *w3d_name) { while (cload.Open_Chunk()) { // We only want the header. if (cload.Cur_Chunk_ID() == W3D_CHUNK_HLOD_HEADER) { W3dHLodHeaderStruct hdr; cload.Read(&hdr, sizeof(hdr)); if (strcmp(hdr.HierarchyName, w3d_name) != 0) { const char *hierarchy = Make_W3D_Filename(hdr.HierarchyName); if (*hierarchy) files.push_back(hierarchy); } } cload.Close_Chunk(); } } /*********************************************************************************************** * Get_W3D_Name -- Gets a W3D object name from a W3D filename. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 4/3/00 AJA : Created. * *=============================================================================================*/ static void Get_W3D_Name (const char *filename, char *w3d_name) { assert(filename); assert(w3d_name); // Figure out the first character of the name of the file // (bypass the path if it was given). const char *start = strrchr(filename, '\\'); if (start) ++start; // point to first character after the last backslash else start = filename; // point to the start of the filename // We don't want to copy the .w3d extension. Find where // it occurs. const char *end = strrchr(start, '.'); if (!end) end = start + strlen(start); // point to the null character // Copy all characters from start to end (excluding 'end') // into the w3d_name buffer. Then capitalize the string. memset(w3d_name, 0, W3D_NAME_LEN); // blank out the buffer int num_chars = end - start; strncpy(w3d_name, start, num_chars < W3D_NAME_LEN ? num_chars : W3D_NAME_LEN-1); strupr(w3d_name); } /*********************************************************************************************** * Make_W3D_Filename -- Converts a W3D object name into a W3D filename. * * * * INPUT: * * * * OUTPUT: * * * * WARNINGS: * * * * HISTORY: * * 4/3/00 AJA : Created. * *=============================================================================================*/ static const char * Make_W3D_Filename (const char *w3d_name) { assert(w3d_name); assert(strlen(w3d_name) < W3D_NAME_LEN); // Copy the w3d name into a static buffer, turn it into lowercase // letters, and append a ".w3d" file extension. That's the filename. static char buffer[64]; if (*w3d_name == 0) { // Empty W3D name case. buffer[0] = 0; return buffer; } strcpy(buffer, w3d_name); char *dot = strchr(buffer, '.'); if (dot) *dot = 0; strlwr(buffer); strcat(buffer, ".w3d"); return buffer; }