//===========================================================================//
// 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();
}
