This repository has been archived on 2025-02-27. You can view files and clone it, but cannot push or open issues or pull requests.
CnC_Renegade/Code/ww3d2/w3d_dep.cpp

564 lines
27 KiB
C++

/*
** 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 <http://www.gnu.org/licenses/>.
*/
/***********************************************************************************************
*** 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 <assert.h>
#include <chunkio.h>
#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;
}