Files
firestorm/Gameleap/code/mw4/Libraries/Adept/Tool.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

1507 lines
44 KiB
C++

//===========================================================================//
// File: Tool.cpp //
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 08/25/97 JMA Infrastructure changes //
// 08/25/97 ECH Infrastructure changes //
//---------------------------------------------------------------------------//
// Copyright (C) 1994-97, Virtual World Entertainment, Inc. //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include "AdeptHeaders.hpp"
#include "Tool.hpp"
#include "RendererManager.hpp"
#include "Application.hpp"
#include "AudioRenderer.hpp"
#include <gosFX\EffectLibrary.hpp>
#include "ResourceImagePool.hpp"
#include "ResourceEffectLibrary.hpp"
#include "AudioSample.hpp"
#include <mbstring.h>
#define __mbsrchr(s,c) (char*)_mbsrchr((const unsigned char*)(s),(c))
Tool *Tool::Instance = NULL;
//#############################################################################
//######################## Tool #########################################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Tool::Tool()
{
//
//--------------------------------
// hook up the global tool pointer
//--------------------------------
//
Verify(Tool::Instance == NULL);
Tool::Instance = this;
Environment.HookGetFile = GetFileForGOS;
Environment.HookDoesFileExist = FindFileForGOS;
//
//-----------------------------------------
// Set up the path stack within the content
//-----------------------------------------
//
stackSize = 128;
filePathStack = new char*[stackSize];
Check_Pointer(filePathStack);
stackIndex = 1;
filePathStack[0] = "";
filePathStack[1] = "Content\\";
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Tool::~Tool()
{
Verify(stackIndex == 1);
Check_Pointer(filePathStack);
delete[] filePathStack;
Verify(Tool::Instance == this);
Tool::Instance = NULL;
Environment.HookDoesFileExist = NULL;
Environment.HookGetFile = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Tool::TestInstance()
{
Verify(stackIndex >= 0 && stackIndex < stackSize);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Tool::PushFilePath(Stuff::NotationFile *file)
{
Check_Object(this);
Check_Object(file);
char *dir = strdup(file->GetFileName());
Check_Pointer(dir);
char *p = __mbsrchr(dir, '\\');
if (p)
*++p = '\0';
else
*dir = '\0';
//
//---------------------------------
// Add the directory onto the stack
//---------------------------------
//
Check_Pointer(filePathStack);
if (++stackIndex == stackSize)
{
STOP(("Out of stack not handled"));
}
filePathStack[stackIndex] = dir;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Tool::PushFilePath(Stuff::FileStream *file)
{
Check_Object(this);
Check_Object(file);
char *dir = strdup(file->GetFileName());
Check_Pointer(dir);
char *p = __mbsrchr(dir, '\\');
if (p)
*++p = '\0';
else
*dir = '\0';
//
//---------------------------------
// Add the directory onto the stack
//---------------------------------
//
Check_Pointer(filePathStack);
if (++stackIndex == stackSize)
{
STOP(("Out of stack not handled"));
}
filePathStack[stackIndex] = dir;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Tool::PopFilePath()
{
Check_Object(this);
Check_Pointer(filePathStack);
Verify(stackIndex > 0);
char *dir = filePathStack[stackIndex--];
Check_Pointer(dir);
free(dir);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void __stdcall
Tool::GetFileForGOS(
const char* file_name,
BYTE** memory,
DWORD* size
)
{
Check_Pointer(file_name);
Tool *tool = Tool::Instance;
Check_Object(tool);
#ifdef LAB_ONLY
strcpy(MWGameInfo::g_LastFileOpenRequest, "Tool::GetFileForGOS: ");
strcpy(MWGameInfo::g_LastFileOpenRequest + 22, file_name);
#endif
//
//-----------------------------------------------------------------------
// If this is a rom file, find it in the resources and return the pointer
//-----------------------------------------------------------------------
//
if (!strncmp(file_name, "ROM\\", 4))
{
Resource texture(file_name+4);
texture.LoadData();
*memory = static_cast<BYTE*>(texture.GetPointer());
*size = texture.GetSize();
texture.AbandonData();
return;
}
//
//--------------------------------------------------
// Only do the search if we don't start with content
//--------------------------------------------------
//
SPEW(("sagem", "file=%s", file_name));
if (_strnicmp(file_name, "content\\", 8))
{
Check_Pointer(tool->filePathStack);
Verify((unsigned)tool->stackIndex < (unsigned)tool->stackSize);
Check_Pointer(tool->filePathStack[tool->stackIndex]);
Str_Copy(
FileStream::RedirectedName,
tool->filePathStack[tool->stackIndex],
sizeof(FileStream::RedirectedName)
);
Verify(
*FileStream::RedirectedName == '\0'
|| FileStream::RedirectedName[strlen(FileStream::RedirectedName)-1] == '\\'
);
#ifdef LAB_ONLY
strcat(MWGameInfo::g_LastFileOpenRequest, " --Redirected-> ");
strcat(MWGameInfo::g_LastFileOpenRequest + 15, FileStream::RedirectedName);
#endif
//
//-----------------------------------------------------
// for each directory in the path, try to find the file
//-----------------------------------------------------
//
while (true)
{
//
//-------------------------------------------------------------------
// Find the separator character, and append the file name to the path
//-------------------------------------------------------------------
//
char *p = __mbsrchr((char*)FileStream::RedirectedName, '\\');
if (p)
Str_Copy(
p+1,
file_name,
sizeof(FileStream::RedirectedName) - (p - FileStream::RedirectedName) - 1
);
else
Str_Copy(
FileStream::RedirectedName,
file_name,
sizeof(FileStream::RedirectedName)
);
//
//-----------------------------------------
// If the file exists, go ahead and open it
//-----------------------------------------
//
if (gos_DoesFileExist(FileStream::RedirectedName))
{
FileStream::IsRedirected=true;
gos_PushCurrentHeap(FileStream::s_Heap);
gos_GetFile(FileStream::RedirectedName, memory, size);
gos_PopCurrentHeap();
return;
}
//
//-------------------------------------------------------------------
// Since it doesn't exist, we need to clip off the last directory and
// try again. If there was no separator, no file will be found
//-------------------------------------------------------------------
//
if (p)
*p = '\0';
else
break;
}
}
FileStream::IsRedirected = false;
gos_PushCurrentHeap(FileStream::s_Heap);
gos_GetFile(file_name, memory, size);
gos_PopCurrentHeap();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool __stdcall
Tool::FindFileForGOS(const char* file_name)
{
Check_Pointer(file_name);
Tool *tool = Tool::Instance;
Check_Object(tool);
//
//--------------------------------------------------
// Only do the search if we don't start with content
//--------------------------------------------------
//
if (_strnicmp(file_name, "content\\", 8))
{
Check_Pointer(tool->filePathStack);
Verify((unsigned)tool->stackIndex < (unsigned)tool->stackSize);
Check_Pointer(tool->filePathStack[tool->stackIndex]);
Str_Copy(
FileStream::RedirectedName,
tool->filePathStack[tool->stackIndex],
sizeof(FileStream::RedirectedName)
);
//
//-----------------------------------------------------
// for each directory in the path, try to find the file
//-----------------------------------------------------
//
while (true)
{
//
//-------------------------------------------------------------------
// Find the separator character, and append the file name to the path
//-------------------------------------------------------------------
//
char *p = __mbsrchr((char*)FileStream::RedirectedName, '\\');
if (p)
Str_Copy(
p+1,
file_name,
sizeof(FileStream::RedirectedName) - (p - FileStream::RedirectedName) - 1
);
else
Str_Copy(
FileStream::RedirectedName,
file_name,
sizeof(FileStream::RedirectedName)
);
//
//-----------------------------------------
// If the file exists, go ahead and open it
//-----------------------------------------
//
if (gos_DoesFileExist(FileStream::RedirectedName))
{
FileStream::IsRedirected = true;
return true;
}
//
//-------------------------------------------------------------------
// Since it doesn't exist, we need to clip off the last directory and
// try again. If there was no separator, no file will be found
//-------------------------------------------------------------------
//
if (p)
*p = '\0';
else
break;
}
}
FileStream::IsRedirected = false;
return gos_DoesFileExist(file_name);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
const char*
Tool::GetToolFileName(const char* file_name)
{
Check_Pointer(file_name);
const char* result = strstr(file_name, "Content\\");
if (!result)
{
result = strstr(file_name, "content\\");
if (!result)
return file_name;
}
Check_Pointer(result);
return result+8;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::ClassData*
Tool::ConstructGameModel(
MemoryStream *model_stream,
const char *model_class,
NotationFile *model_file
)
{
Check_Object(this);
Check_Object(model_stream);
Check_Pointer(model_class);
Check_Object(model_file);
#if defined(_ARMOR)
int stack_index = stackIndex;
#endif
//
//-----------------------------------
// Find the class data for this model
//-----------------------------------
//
RegisteredClass::ClassData *class_data;
Entity::ClassData *entity_class_data;
class_data = Entity::FindClassData(model_class);
Check_Object(class_data);
entity_class_data = Cast_Pointer(Entity::ClassData*, class_data);
Check_Object(entity_class_data);
//
//--------------------------------------------------------------
// Create the game model parameters and call the creation method
//--------------------------------------------------------------
//
Entity::GameModel::Script
script(
model_stream,
model_file,
entity_class_data
);
Entity::GameModel::Factory game_model_factory =
entity_class_data->modelFactory;
Check_Pointer(game_model_factory);
(*game_model_factory)(&script);
Verify(stack_index == stackIndex);
return entity_class_data;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ResourceID
Tool::MakeNewGameModel(
const char *model_name,
const char *model_class
)
{
Check_Object(this);
Check_Pointer(model_name);
Check_Pointer(model_class);
//
//---------------------------------------------------------------------
// Create the New Model Resource. If it is already there return the ID
//---------------------------------------------------------------------
//
MString game_model_name = model_name;
game_model_name += "{GameModel}";
Resource model_resource(game_model_name);
if (model_resource.DoesResourceExist() && model_resource.IsResourceUpToDate())
{
Check_Object(&model_resource);
//
//----------------------------------------------------------------
// In order to deal with partial builds we have to give our parent
// our file dependencies
//----------------------------------------------------------------
//
if (Resource::ParentFileDependencies)
model_resource.AddFileDependenciesTo(Resource::ParentFileDependencies);
return model_resource.GetResourceID();
}
//
//-----------------------------------
// Find the class data for this model
//-----------------------------------
//
RegisteredClass::ClassData *class_data;
Entity::ClassData *entity_class_data;
class_data = Entity::FindClassData(model_class);
Check_Object(class_data);
entity_class_data = Cast_Pointer(Entity::ClassData*, class_data);
Check_Object(entity_class_data);
//
//--------------------------------------------------------------
// Create the game model parameters and call the creation method
//--------------------------------------------------------------
//
DynamicMemoryStream model_stream;
Entity::GameModel::Script
script(
&model_stream,
NULL,
entity_class_data
);
Entity::GameModel::Factory game_model_factory =
entity_class_data->modelFactory;
Check_Pointer(game_model_factory);
(*game_model_factory)(&script);
//
//----------------------------------
//Save the GameModel Resource Stream
//----------------------------------
//
Check_Object(entity_class_data);
model_resource.Save(&model_stream, NULL);
STOP(("Not updated"));
Check_Object(&model_resource);
return model_resource.GetResourceID();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::ClassData*
Tool::ConstructDataList(
Resource *resource,
const char* model_name
)
{
Check_Object(this);
Check_Object(resource);
Check_Pointer(model_name);
#if defined(_ARMOR)
int stack_index = stackIndex;
#endif
//
//---------------------------------------------------------------------------------------
// First open this up as a filestream to get a clue on the name - no need to expand it if
// it already exists
//---------------------------------------------------------------------------------------
//
if (!gos_DoesFileExist(model_name))
STOP(("%s can't be found!"));
//
//---------------------------------------------------
// See if the data file can be found in the resources
//---------------------------------------------------
//
if (FileStream::IsRedirected)
{
model_name = FileStream::RedirectedName;
FileStream::IsRedirected = false;
}
Check_Pointer(model_name);
MString model_file_name = model_name;
resource->FindName(model_file_name);
if (resource->DoesResourceExist() && resource->IsResourceUpToDate())
{
Check_Object(resource);
//
//----------------------------------------------------------------
// In order to deal with partial builds we have to give our parent
// our file dependencies
//----------------------------------------------------------------
//
if (Resource::ParentFileDependencies && !resource->IsRegistered())
resource->AddFileDependenciesTo(Resource::ParentFileDependencies);
//
//-----------------------------------------------
// Figure out the class data object and return it
//-----------------------------------------------
//
RegisteredClass::ClassID class_id =
Entity::GetClassIDFromDataListID(resource->GetResourceID());
Entity::ClassData *class_data =
Cast_Pointer(
Entity::ClassData*,
RegisteredClass::FindClassData(class_id)
);
Check_Object(class_data);
Verify(stack_index == stackIndex);
return class_data;
}
//
//--------------------------------------------------------------------
// We are building this model, so set the file path to the model file,
// then setup our initial dependencies to match the model file
//--------------------------------------------------------------------
//
NotationFile model_file(model_file_name);
PushFilePath(&model_file);
FileDependencies *parent_dependencies = Resource::ParentFileDependencies;
FileDependencies data_dependencies(*model_file.GetFileDependencies());
//
//--------------------
// Find the class name
//--------------------
//
const char *model_class;
Page *page = model_file.GetPage("GameData");
Check_Object(page);
page->GetEntry("Class", &model_class, true);
//
//--------------------------------------------
// Create the memory stream for the game model
//--------------------------------------------
//
FileDependencies model_dependencies(*model_file.GetFileDependencies());
Resource::ParentFileDependencies = &model_dependencies;
MString game_model_name = model_file_name;
game_model_name += "{GameModel}";
Resource model_resource(game_model_name);
DynamicMemoryStream model_stream;
Entity::ClassData *entity_class_data =
ConstructGameModel(
&model_stream,
model_class,
&model_file
);
Check_Object(entity_class_data);
model_resource.Save(&model_stream, &model_dependencies);
Check_Object(&model_resource);
if (!model_resource.IsRegistered())
data_dependencies.AddDependencies(&model_dependencies);
//
//------------------------------------------------------
// Create a memory stream to hold data list resource IDs
//------------------------------------------------------
//
Check_Object(RendererManager::Instance);
int model_list_size = RendererManager::Instance->GetModelListSize();
Verify(model_list_size >= 1);
DynamicArrayOf<ResourceID> resource_ids(ResourceID::Null, model_list_size);
resource_ids[RendererManager::ReservedForGameModelRendererType] =
model_resource.GetResourceID();
int i;
for(i=(RendererManager::ReservedForGameModelRendererType + 1); i<model_list_size; i++)
resource_ids[i] = ResourceID::Null;
//
//---------------------------------------------------------
// Call the platform tool, and let it add the renderer data
//---------------------------------------------------------
//
Page *renderer_page = model_file.FindPage("Renderers");
if (renderer_page)
{
Page::NoteIterator *renderers = renderer_page->MakeNoteIterator();
Check_Object(renderers);
Note *renderer;
while ((renderer = renderers->ReadAndNext()) != NULL)
{
Check_Object(renderer);
const char *renderer_name = renderer->GetName();
Check_Pointer(renderer_name);
Check_Object(RendererManager::Instance);
int renderer_type =
RendererManager::Instance->FindRendererType(renderer_name);
Verify(renderer_type > RendererManager::ReservedForGameModelRendererType);
Verify(renderer_type < model_list_size);
resource_ids[renderer_type] = ResourceID(resource->GetResourceID().m_fileID, 0);
NotationFile renderer_file;
renderer->GetEntry(&renderer_file);
FileDependencies renderer_dependencies(*renderer_file.GetFileDependencies());
Resource::ParentFileDependencies = &renderer_dependencies;
CreateRendererData(
&resource_ids[renderer_type],
entity_class_data,
&renderer_file,
renderer_type
);
data_dependencies.AddDependencies(&renderer_dependencies);
}
Check_Object(renderers);
delete renderers;
}
//
//-----------------------
// Save out the data list
//-----------------------
//
MemoryStream res_id_stream(resource_ids.GetData(), resource_ids.GetSize());
resource->Save(&res_id_stream, &data_dependencies);
Resource::ParentFileDependencies = parent_dependencies;
if (parent_dependencies && !resource->IsRegistered())
parent_dependencies->AddDependencies(&data_dependencies);
//
//---------
// Clean up
//---------
//
PopFilePath();
Verify(stack_index == stackIndex);
return entity_class_data;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::CreateMessage*
Tool::ConstructCreateMessage(
Stuff::MemoryStream *message_stream,
Page *page,
ReplicatorID *base_id
)
{
Check_Object(this);
Check_Object(message_stream);
Check_Object(page);
Check_Object(base_id);
#if defined(_ARMOR)
int stack_index = stackIndex;
#endif
//
//----------------------------------------------------------------------
// Remember where the id started so we can compute the span of id's used
// in this message
//----------------------------------------------------------------------
//
ReplicatorID original_id = *base_id;
//
//---------------------------------------------------------------------
// Locate the model file entry and make sure it is already in resources
//---------------------------------------------------------------------
//
const char* model_name;
page->GetEntry("Model", &model_name, true);
Entity::CreateMessage::Script script;
script.classData = ConstructDataList(&script.modelResource, model_name);
Check_Object(script.classData);
//
//--------------------------------------------------------------
// Now that we have the data object for the model type, call the
// appropriate make message factory
//--------------------------------------------------------------
//
script.messageStream = message_stream;
script.instancePage = page;
script.baseID = base_id;
Entity::CreateMessage::Factory factory =
(Entity::CreateMessage::Factory)script.classData->messageFactory;
Check_Pointer(factory);
(*factory)(&script);
//
//-------------------------------------------------------------------
// Get the message, and set the span on the replicant ID to match the
// number of ids used by this and its children
//-------------------------------------------------------------------
//
Entity::CreateMessage *message =
Cast_Pointer(
Entity::CreateMessage*,
message_stream->GetPointer()
);
Check_Object(message);
message->replicatorID.localID =
static_cast<WORD>(script.baseID->localID - original_id.localID);
Verify(stack_index == stackIndex);
return message;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Tool::ConstructCreateMessage(
Resource *resource,
NotationFile *instance_file,
ReplicatorID *base_id
)
{
Check_Object(this);
Check_Object(resource);
Check_Object(instance_file);
Check_Object(base_id);
#if defined(_ARMOR)
int stack_index = stackIndex;
#endif
//
//---------------------------------------------------------
// Open up the instance file and push the path on the stack
//---------------------------------------------------------
//
const char* instance_file_name = instance_file->GetFileName();
PushFilePath(instance_file);
//
//----------------------------------------
// See if the resource needs to be rebuilt
//----------------------------------------
//
resource->FindName(instance_file_name);
if (resource->DoesResourceExist() && resource->IsResourceUpToDate())
{
//
//--------------------------------------------------------------------
// We need to bump the replicator ID, so cast the resource as a create
// message and apply the bump number to the base id
//--------------------------------------------------------------------
//
resource->LoadData();
Entity::CreateMessage *message =
Cast_Pointer(
Entity::CreateMessage*,
resource->GetPointer()
);
Check_Object(message);
*base_id += message->replicatorID.localID;
PopFilePath();
Verify(stack_index == stackIndex);
if (Resource::ParentFileDependencies && !resource->IsRegistered())
resource->AddFileDependenciesTo(Resource::ParentFileDependencies);
return;
}
//
//-----------------------------------------------------------------------
// For instances, assume the first page is our instance, so take whatever
// name it has
//-----------------------------------------------------------------------
//
FileDependencies instance_dep(*instance_file->GetFileDependencies());
FileDependencies *parent = Resource::ParentFileDependencies;
Resource::ParentFileDependencies = &instance_dep;
NotationFile::PageIterator *instance_pages = instance_file->MakePageIterator();
Check_Object(instance_pages);
Page *instance = instance_pages->GetCurrent();
if (!instance)
STOP(("Error: %s has no instance pages!", instance_file->GetFileName()));
Check_Object(instance);
//
//------------------------------
// Put the message into a stream
//------------------------------
//
DynamicMemoryStream instance_stream;
ConstructCreateMessage(
&instance_stream,
instance,
base_id
);
resource->Save(&instance_stream, &instance_dep);
if (parent && !resource->IsRegistered())
parent->AddDependencies(&instance_dep);
Resource::ParentFileDependencies = parent;
//
//-------------
// Now clean up
//-------------
//
PopFilePath();
Check_Object(instance_pages);
delete instance_pages;
Verify(stack_index == stackIndex);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
Entity::CreateMessage*
Tool::ConstructCreateMessage(
Stuff::MemoryStream *message_stream,
NotationFile *instance_file,
ReplicatorID *base_id
)
{
Check_Object(this);
Check_Object(message_stream);
Check_Pointer(instance_file);
Check_Object(base_id);
#if defined(_ARMOR)
int stack_index = stackIndex;
#endif
//
//---------------------------------------------------------
// Open up the instance file and push the path on the stack
//---------------------------------------------------------
//
PushFilePath(instance_file);
//
//-----------------------------------------------------------------------
// For instances, assume the first page is our instance, so take whatever
// name it has
//-----------------------------------------------------------------------
//
NotationFile::PageIterator *instance_pages = instance_file->MakePageIterator();
Check_Object(instance_pages);
Page *instance = instance_pages->GetCurrent();
Check_Object(instance);
//
//------------------------------
// Put the message into a stream
//------------------------------
//
Entity::CreateMessage *message =
ConstructCreateMessage(
message_stream,
instance,
base_id
);
//
//-------------
// Now clean up
//-------------
//
PopFilePath();
Check_Object(instance_pages);
delete instance_pages;
Verify(stack_index == stackIndex);
return message;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Tool::BuildResources(
Stuff::NotationFile *build_file,
DWORD content_version,
Stuff::FileDependencies *dependencies,
short level,
__int64 parent_build_age
)
{
Check_Object(this);
Check_Object(build_file);
Verify(Application::BuildOK);
#if defined(_ARMOR)
int stack_index = stackIndex;
#endif
//
//-------------------------------------------------------------------
// Create the resource directories (if they don't exist) and bump the
// file ID
//-------------------------------------------------------------------
//
if (level++ == -1)
{
if (!gos_DoesFileExist("Resource"))
gos_CreateDirectory("Resource");
FileStream::IsRedirected = false;
if (!gos_DoesFileExist("Resource\\Maps"))
gos_CreateDirectory("Resource\\Maps");
FileStream::IsRedirected = false;
if (!gos_DoesFileExist("Resource\\Missions"))
gos_CreateDirectory("Resource\\Missions");
FileStream::IsRedirected = false;
if (!gos_DoesFileExist("Resource\\Variants"))
gos_CreateDirectory("Resource\\Variants");
FileStream::IsRedirected = false;
PushFilePath(build_file);
}
//
//-----------------------------------------
// Spin through all the entries in the file
//-----------------------------------------
//
Page *null_page = build_file->FindPage("");
if (null_page)
{
Check_Object(null_page);
Page::NoteIterator *file_entries = null_page->MakeNoteIterator();
Check_Object(file_entries);
Note *file_entry;
while ((file_entry = file_entries->ReadAndNext()) != NULL)
{
Check_Object(file_entry);
//
//-------------------------------
// Open up the next file to parse
//-------------------------------
//
const char* parse_filename = file_entry->GetName();
Check_Pointer(parse_filename);
NotationFile parse_file(parse_filename, NotationFile::NonEmpty);
parse_filename = parse_file.GetFileName();
//
//-------------------------------------------------------
// Figure out how old it is and push it on the path stack
//-------------------------------------------------------
//
__int64 build_age = gos_FileTimeStamp(parse_filename);
if (build_age < parent_build_age)
build_age = parent_build_age;
PushFilePath(&parse_file);
//
//------------------------------------------------------------
// Each page name will be the name of a resource file to build
//------------------------------------------------------------
//
NotationFile::PageIterator *resource_file_pages =
parse_file.MakePageIterator();
Check_Object(resource_file_pages);
Page *resource_file_entry = resource_file_pages->ReadAndNext();
Verify(!resource_file_pages->ReadAndNext());
Check_Object(resource_file_entry);
const char* resource_file_name = resource_file_entry->GetName();
//
//--------------------------------------------------------------------
// Delete the file if it is older than the build file, then open it up
//--------------------------------------------------------------------
//
__int64 res_file_age = 0;
if (gos_DoesFileExist(resource_file_name))
res_file_age = gos_FileTimeStamp(resource_file_name);
bool dirty = res_file_age < build_age;
FileStream::IsRedirected = false;
MString dep_filename = resource_file_name;
char *p = strrchr(dep_filename, '.');
if (p)
strcpy(p+1, "dep");
if (!gos_DoesFileExist(dep_filename))
dirty = true;
FileStream::IsRedirected = false;
ResourceFile *res_file =
ResourceManager::Instance->OpenResourceFile(
resource_file_name,
dep_filename,
level,
content_version,
true,
dirty
);
//
//-------------------------------------------------------
// If we have dependencies, write them into resource zero
//-------------------------------------------------------
//
{
Resource build_dependencies(resource_file_name);
if (!build_dependencies.DoesResourceExist())
{
unsigned length = dependencies->m_fileNameStream.GetBytesUsed();
if (length>0)
{
MemoryStream chain(
static_cast<BYTE*>(dependencies->m_fileNameStream.GetPointer()) - length,
length,
length
);
build_dependencies.Save(&chain, dependencies);
}
else
{
MemoryStream chain("", 1, 1);
build_dependencies.Save(&chain, NULL);
}
}
Verify(build_dependencies.GetResourceID().GetRecordID() == ResourceID::BuildDependenciesRecordID);
}
//
//-----------------------------------------------------------------
// Remember the state of the effect library prior to our build file
// so we can restore to it after our children are created
//-----------------------------------------------------------------
//
Check_Object(gosFX::EffectLibrary::Instance);
unsigned old_effect_count = gosFX::EffectLibrary::Instance->GetLength();
FileDependencies old_deps = gosFX::EffectLibrary::Instance->m_fileDependencies;
//
//-------------------------------------
// Parse the build file and save it out
//-------------------------------------
//
bool core = ParseBuildFile(res_file, resource_file_entry);
Check_Object(resource_file_pages);
delete resource_file_pages;
res_file->Save();
res_file->UnloadRecordData();
//
//-----------------------------------------
// If this is core, ignore the dependencies
//-----------------------------------------
//
if (core)
{
NotationFile children;
file_entry->GetEntry(&children);
BuildResources(
&children,
content_version,
dependencies,
level,
build_age
);
}
//
//--------------------------
// Otherwise, write them out
//--------------------------
//
else
{
Check_Object(dependencies);
FileDependencies deps(*dependencies);
deps.AddDependency(resource_file_name);
NotationFile children;
file_entry->GetEntry(&children);
BuildResources(
&children,
content_version,
&deps,
level,
build_age
);
}
delete res_file;
Check_Object(gosFX::EffectLibrary::Instance);
gosFX::EffectLibrary::Instance->SetLength(old_effect_count);
gosFX::EffectLibrary::Instance->m_fileDependencies = old_deps;
PopFilePath();
}
Check_Object(file_entries);
delete file_entries;
}
if (!level)
{
Check_Object(FileStreamManager::Instance);
FileStreamManager::Instance->PurgeFileCompareCache();
PopFilePath();
}
Verify(stack_index == stackIndex);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
Tool::ParseBuildFile(
ResourceFile *res_file,
Stuff::Page *resource_file_entry
)
{
Check_Object(this);
Check_Object(res_file);
#if defined(_ARMOR)
int stack_index = stackIndex;
#endif
//
//-------------------------------------------------------
// Look for the effects entry. If one is there, build it
//-------------------------------------------------------
//
ResourceID last_id(res_file->GetFileID(),-1);
ResourceID res_id(res_file);
ChainOf<Note*>* effects = resource_file_entry->MakeNoteChain("effects");
if (!effects->IsEmpty())
{
Resource resource("{effects}");
if (!resource.DoesResourceExist())
resource.Save(NULL, NULL);
last_id = resource.GetResourceID();
}
//
//-----------------------------------------------------------------
// Make a list of all the entries within the page, and spin through
// them, constructing a single empty resource for each one
//-----------------------------------------------------------------
//
Page::NoteIterator *file_entries = resource_file_entry->MakeNoteIterator();
Check_Object(file_entries);
Note *file_entry;
while ((file_entry = file_entries->ReadAndNext()) != NULL)
{
Check_Object(file_entry);
//
//---------------------------------------------------
// Look at the type of entry to see what we should do
//---------------------------------------------------
//
const char* entry_type = file_entry->GetName();
Check_Pointer(entry_type);
const char* entry_filename = NULL;
file_entry->GetEntry(&entry_filename);
if (!entry_filename)
STOP((
"%s: [%s]%s is a bad entry!",
resource_file_entry->GetNotationFile()->GetFileName(),
resource_file_entry->GetName(),
entry_type
));
Check_Pointer(entry_filename);
//
//----------------------------------------------------------------
// If we are an instance, go ahead and build the make message into
// the resources
//----------------------------------------------------------------
//
if (IsRegisterable(entry_type))
{
Resource data_resource(entry_filename);
if (!data_resource.DoesResourceExist())
data_resource.Save(NULL, NULL);
if (last_id.GetRecordID() < 65535)
{
if (data_resource.GetResourceID().GetFileID() != last_id.GetFileID())
{
PAUSE((
"Error: Registered resource %s already appears in resource file %d",
data_resource.GetName(),
data_resource.GetResourceID().GetFileID()
));
}
else
Verify(data_resource.GetResourceID().GetRecordID() == last_id.GetRecordID()+1);
}
last_id = data_resource.GetResourceID();
}
}
res_file->SetLastRegisteredResourceID(last_id);
//
//-----------------------------------------------------------------------
// Store each individual effect library in the resource file, concatenate
// them into the main library, and keep track of all effect libraries
// created
//-----------------------------------------------------------------------
//
Check_Object(gosFX::EffectLibrary::Instance);
DynamicArrayOf<ResourceID> effect_IDs(effects->GetSize());
unsigned i=0;
Check_Object(gosFX::EffectLibrary::Instance);
ChainIteratorOf<Note*> effect_iterator(effects);
while ((file_entry = effect_iterator.ReadAndNext()) != NULL)
{
Check_Object(file_entry);
const char* effects_filename;
file_entry->GetEntry(&effects_filename);
//
//---------------------------------------------------
// Open up the effect file and gets its real filename
//---------------------------------------------------
//
FileStream stream(effects_filename);
effects_filename = stream.GetFileName();
Resource resource(effects_filename);
//
//--------------------------------------------------
// If the resource needs to be refreshed, refresh it
//--------------------------------------------------
//
if (!resource.DoesResourceExist() || !resource.IsResourceUpToDate())
{
FileDependencies effects_dependencies;
effects_dependencies.AddDependency(&stream);
resource.Save(&stream, &effects_dependencies);
}
//
//---------------------------------------------
// Load the effects library and remember its ID
//---------------------------------------------
//
Verify(resource.DoesResourceExist());
gosFX::EffectLibrary::Instance->m_fileDependencies.AddDependency(effects_filename);
resource.LoadData();
gosFX::EffectLibrary::Instance->Load(&resource);
effect_IDs[i++] = resource.GetResourceID();
}
delete effects;
//
//-----------------------------------------------------------------------
// If we actually had effect libraries, save the list out to the resource
// file now
//-----------------------------------------------------------------------
//
if (i>0)
{
Resource resource("{effects}");
if (!resource.DoesResourceExist())
{
MemoryStream stream(
&effect_IDs[0],
effect_IDs.GetSize(),
effect_IDs.GetSize()
);
resource.Save(&stream, &gosFX::EffectLibrary::Instance->m_fileDependencies);
}
}
//
//---------------------------------------------------------------------
// Look for the texture pool entry. We build the textures here so that
// the texture name lookups happen as quickly as possible
//---------------------------------------------------------------------
//
NotationFile audio_hint;
if (resource_file_entry->GetEntry("soundpool", &audio_hint))
AudioSample::BuildSoundPool(&audio_hint);
//
//--------------------------------
// Build each resource in the file
//--------------------------------
//
file_entries->First();
while ((file_entry = file_entries->ReadAndNext()) != NULL)
{
Check_Object(file_entry);
const char* entry_type = file_entry->GetName();
Check_Pointer(entry_type);
const char* entry_filename = NULL;
file_entry->GetEntry(&entry_filename);
Check_Pointer(entry_filename);
BuildResource(entry_type, entry_filename);
}
//
//---------------------------------------------------------------------
// Look for the texture pool entry. We build the textures here so that
// the texture name lookups happen as quickly as possible
//---------------------------------------------------------------------
//
NotationFile hint_file;
if (resource_file_entry->GetEntry("texturepool", &hint_file))
ResourceImagePool::BuildTexturePool(&hint_file);
//
//---------
// Clean up
//---------
//
bool core=false;
resource_file_entry->GetEntry("Core", &core);
Check_Object(file_entries);
delete file_entries;
res_file->Save();
Verify(stack_index == stackIndex);
return core;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
Tool::IsRegisterable(const char* entry_type)
{
Check_Object(this);
Check_Pointer(entry_type);
return !_stricmp(entry_type, "instance") ||
!_stricmp(entry_type, "data") ||
!_stricmp(entry_type, "file") ||
!_stricmp(entry_type, "notation");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Tool::BuildResource(
const char* entry_type,
const char* entry_filename
)
{
Check_Object(this);
Check_Pointer(entry_type);
Check_Pointer(entry_filename);
#if defined(_ARMOR)
int stack_index = stackIndex;
#endif
//
//----------------------------------------------------------------
// If we are an instance, go ahead and build the make message into
// the resources
//----------------------------------------------------------------
//
if (!_stricmp(entry_type, "instance"))
{
NotationFile instance_file(entry_filename, NotationFile::NonEmpty);
Resource instance_resource;
ReplicatorID base_id = ReplicatorID::Null;
ConstructCreateMessage(
&instance_resource,
&instance_file,
&base_id
);
}
//
//--------------------------------------
// If we are a data list, construct that
//--------------------------------------
//
else if (!_stricmp(entry_type, "data"))
{
Resource data_resource;
ConstructDataList(&data_resource, entry_filename);
}
//
//-------------------------------------
// Put a notation file in the resources
//-------------------------------------
//
else if (!_stricmp(entry_type, "notation"))
{
NotationFile notation_file(entry_filename, NotationFile::NonEmpty);
const char* file_name = notation_file.GetFileName();
Check_Pointer(file_name);
Resource resource(file_name);
if (!resource.DoesResourceExist() || !resource.IsResourceUpToDate())
{
DynamicMemoryStream stream;
notation_file.Write(&stream);
resource.Save(&stream, notation_file.GetFileDependencies());
}
}
//
//-------------------------------------
// Put a general file in the resources
//-------------------------------------
//
else if (!_stricmp(entry_type, "file"))
{
FileStream file(entry_filename);
const char* file_name = file.GetFileName();
Check_Pointer(file_name);
Resource resource(file_name);
if (!resource.DoesResourceExist() || !resource.IsResourceUpToDate())
{
FileDependencies file_dep;
file_dep.AddDependency(&file);
resource.Save(&file, &file_dep);
}
}
//
//--------------------------------------------------------
// Put a whole directory of general files in the resources
//--------------------------------------------------------
//
else if (!_stricmp(entry_type, "directory"))
{
MString path = entry_filename;
path += '\\';
Directory directory(path + "*.*");
const char *file_name;
while ((file_name = directory.GetCurrentFileName()) != NULL)
{
FileStream file(path+file_name);
file_name = file.GetFileName();
Check_Pointer(file_name);
Resource resource(file_name);
if (!resource.DoesResourceExist() || !resource.IsResourceUpToDate())
{
FileDependencies file_dep;
file_dep.AddDependency(&file);
resource.Save(&file, &file_dep);
}
directory.AdvanceCurrentFile();
}
}
Verify(stack_index == stackIndex);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
Tool::CreateRendererData(
ResourceID *stream_resource,
Entity::ClassData *class_data,
NotationFile *renderer_file,
int renderer_type
)
{
Check_Object(stream_resource);
Check_Pointer(class_data);
Check_Object(renderer_file);
PushFilePath(renderer_file);
switch (renderer_type)
{
case RendererManager::VideoRendererType:
VideoRenderer::CreateRendererData(
stream_resource,
class_data,
renderer_file
);
break;
case RendererManager::AudioRendererType:
AudioRenderer::CreateRendererData(
stream_resource,
class_data,
renderer_file
);
break;
default:
STOP(("Unknown renderer type!"));
}
PopFilePath();
}