/*
**	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 : LevelEdit                                                    *
 *                                                                                             *
 *                     $Archive:: /Commando/Code/Tools/LevelEdit/Export.cpp                   $*
 *                                                                                             *
 *                       Author:: Patrick Smith                                                *
 *                                                                                             *
 *                     $Modtime:: 6/26/02 2:13p                                               $*
 *                                                                                             *
 *                    $Revision:: 34                                                          $*
 *                                                                                             *
 *---------------------------------------------------------------------------------------------*
 * Functions:                                                                                  *
 * - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */


#include "stdafx.h"

#include "leveledit.h"
#include "leveleditdoc.h"
#include "export.h"
#include "editorini.h"
#include "utils.h"
#include "resource.h"
#include "nodemgr.h"
#include "vector3.h"
#include "matrix3d.h"
#include "ffactory.h"
#include "filemgr.h"
#include "_assetmgr.h"
#include "editorassetmgr.h"
#include "..\..\commando\lev_file.h"
#include "chunkio.h"
#include "sceneeditor.h"
#include "filelocations.h"
#include "inisections.h"
#include "hlod.h"
#include "w3derr.h"
#include "saveload.h"
#include "physstaticsavesystem.h"
#include "physdynamicsavesystem.h"
#include "combatsaveload.h"
#include "terrainnode.h"
#include "staticphys.h"
#include "savegame.h"
#include "regkeys.h"
#include "combat.h"
#include "assetdep.h"
#include "presetslibform.h"
#include "pathfind.h"
#include "gameobjmanager.h"
#include "stringsmgr.h"
#include "assetpackagemgr.h"
#include "presetmgr.h"


/////////////////////////////////////////////////////////
//	Local constants
/////////////////////////////////////////////////////////
const TCHAR * const STATIC_EXTENSION			= TEXT (".lsd");
const TCHAR * const DYNAMIC_EXTENSION			= TEXT (".ldd");
const TCHAR * const MIX_EXTENSION				= TEXT (".mix");

const TCHAR * const ALWAYS_ROOT_NAME			= TEXT ("always");
const TCHAR * const ALWAYS_DATA_FILENAME		= TEXT ("always.dat");
const TCHAR * const ALWAYS_DB_FILENAME			= TEXT ("always.dbs");


/////////////////////////////////////////////////////////
//
//	Export_Level
//
/////////////////////////////////////////////////////////
void
ExporterClass::Export_Level (LPCTSTR filename)
{	
	CWaitCursor wait_cursor;
	m_AssetList.Delete_All ();

	//
	//	Prepare the level for exporting...
	//
	DynamicVectorClass<NodeClass *> node_list;
	Pre_Level_Export (node_list);

	//
	//	Get the base destination path
	//
	Path = ::Strip_Filename_From_Path (filename);	

	//
	//	Strip the path and extension from the filename to give
	// us a base for use with other filenames
	//
	FilenameBase = ::Get_Filename_From_Path (filename);
	int extension_index = FilenameBase.ReverseFind ('.');
	if (extension_index != -1) {
		FilenameBase = FilenameBase.Left (extension_index);
	}

	//
	//	Setup the temporary directory
	//	
	Make_Temp_Directory ();

	//
	//	Export the level files
	//
	Export_Level_File ();
	Export_Level_Data ();
	Export_Dependency_File ();
	Export_Definition_Databases (false);

	//
	//	Now build the mix file...
	//
	MixFileCreator.Generate_Mix_File (filename);

	//
	//	Cleanup any temp files we needed
	//
	Delete_Temp_Directory ();

	//
	//	Resetore the level after exporting
	//
	Post_Level_Export (node_list);

	//
	//	Cache the directory path we just exported into
	//
	theApp.WriteProfileString (CONFIG_KEY, LAST_EXPORT_DIR_VALUE, Path);
	return ;	
}


/////////////////////////////////////////////////////////
//
//	Make_Temp_Directory
//
/////////////////////////////////////////////////////////
void
ExporterClass::Make_Temp_Directory (void)
{
	//
	//	Get the path of the temp directory
	//
	char temp_dir[MAX_PATH] = { 0 };
	::GetTempPath (sizeof (temp_dir), temp_dir);

	CString temp_path = ::Make_Path (temp_dir, FilenameBase);
	
	//
	//	Try to find a unique temp directory to store our data
	//
	int index = 0;	
	do {		
		TempDirectory.Format ("%s%.2d.DIR", (const char *)temp_path, index++);
	} while (GetFileAttributes (TempDirectory) != 0xFFFFFFFF);

	//
	//	Create the directory
	//
	::CreateDirectory (TempDirectory, NULL);	
	::Set_Current_Directory (TempDirectory);
	::SetCurrentDirectory (TempDirectory);
	return ;
}


/////////////////////////////////////////////////////////
//
//	Delete_Temp_Directory
//
/////////////////////////////////////////////////////////
void
ExporterClass::Delete_Temp_Directory (void)
{
	//
	//	Change the current directory so we can remove the temporary one
	//
	::Set_Current_Directory (Path);
	::SetCurrentDirectory (Path);
	
	//
	//	Remove the temporary directory
	//
	Clean_Directory (TempDirectory);
	::RemoveDirectory (TempDirectory);
	return ;
}


/////////////////////////////////////////////////////////
//
//	Export_Dependency_File
//
/////////////////////////////////////////////////////////
void
ExporterClass::Export_Dependency_File (void)
{
	//
	//	Make a file that contains a list of all the W3D files that
	// were part of this export
	//
	StringClass dep_filename	= FilenameBase + ".dep";
	StringClass dep_path			= ::Make_Path (TempDirectory, dep_filename);
	AssetDependencyManager::Save_Level_Dependencies (dep_path, m_AssetList);

	//
	//	Add this file to the mix
	//
	MixFileCreator.Add_File (dep_path, ::Get_Filename_From_Path (dep_path));
	return ;
}


/////////////////////////////////////////////////////////
//
//	Export_Level_File
//
/////////////////////////////////////////////////////////
bool
ExporterClass::Export_Level_File (void)
{
	//
	//	Create filenames for both the static and dynamic data
	//
	CString ldd_filename = FilenameBase + DYNAMIC_EXTENSION;
	CString lsd_filename = FilenameBase + STATIC_EXTENSION;

	//
	//	Save the LSD and LDD files to a temporary directory
	//
	SaveGameManager::Set_Map_Filename (lsd_filename);
	SaveGameManager::Save_Level ();
	SaveGameManager::Save_Game (ldd_filename, NULL);

	//
	//	Now add these files to the mix file
	//
	CString lsd_path = Make_Path (TempDirectory, lsd_filename);
	CString ldd_path = Make_Path (TempDirectory, ldd_filename);	
	MixFileCreator.Add_File (lsd_path, lsd_filename);
	MixFileCreator.Add_File (ldd_path, ldd_filename);
	return true;
}


/////////////////////////////////////////////////////////
//
//	Export_Always_Files
//
/////////////////////////////////////////////////////////
void
ExporterClass::Export_Always_Files (LPCTSTR path)
{
	CWaitCursor wait_cursor;
	m_AssetList.Delete_All ();

	//
	// Does the path exist?
	//
	if (::GetFileAttributes (path) != 0xFFFFFFFF) {

		//
		//	Get the base destination path
		//
		Path = path;
		FilenameBase = ALWAYS_ROOT_NAME;
		
		//
		//	Setup the temporary directory
		//	
		Make_Temp_Directory ();

		//
		// Copy all the files used by the framework to the directory
		//
		STRING_LIST &global_list = ::Get_File_Mgr ()->Get_Global_Include_File_List ();		
		Process_Include_Files (global_list);
				
		//
		//	Get the always dependency search list
		//
		STRING_LIST search_list;
		CString always_dep_search_ini = ::Get_File_Mgr ()->Make_Full_Path (ALWAYS_DEP_INI_PATH);
		::Get_File_Mgr ()->Build_Include_List (always_dep_search_ini, search_list);
		
		//
		//	Find all the files in the search list and add them to a list
		//
		STRING_LIST file_list;
		Build_File_List (search_list, file_list);

		//
		//	Now add all the W3D files from the list to another list. *sigh*
		//
		DynamicVectorClass<StringClass> dep_list;
		for (int index = 0; index < file_list.Count (); index ++) {
			CString &filename = file_list[index];
			if (::Is_W3D_Filename (filename)) {
				dep_list.Add ((LPCTSTR)::Get_Filename_From_Path (filename));
			}
		}

		//
		//	Make a file that contains a list of all these W3D files
		//
		AssetDependencyManager::Save_Always_Dependencies (TempDirectory, dep_list);
		CString dep_file_path = Make_Path (TempDirectory, "always.dep");
		MixFileCreator.Add_File (dep_file_path, "always.dep");

		//
		//	Now build the mix file...
		//
		CString mix_filename = Make_Path (Path, ALWAYS_DATA_FILENAME);
		MixFileCreator.Generate_Mix_File (mix_filename);

		//
		//	Cleanup any temp files we needed
		//
		Delete_Temp_Directory ();

	} else {

		//
		// Invalid file, let the user know
		//
		::Message_Box (::AfxGetMainWnd ()->m_hWnd, IDS_CANT_EXPORT_MSG, IDS_CANT_EXPORT_TITLE, MB_ICONERROR | MB_OK);
	}

	return ;	
}


/////////////////////////////////////////////////////////
//
//	Export_Level_Data
//
/////////////////////////////////////////////////////////
bool
ExporterClass::Export_Level_Data (void)
{
	bool retval = true;

	//
	// Loop through all the files used by this level and copy them
	// to the same directory that the level definition was saved in.
	//
	int count = ::Get_File_Mgr ()->Get_File_Count ();
	for (int file_index = 0; file_index < count; file_index ++) {

		//
		// Build the complete paths of both the original file location and the new file location
		//
		CString file_path  = ::Get_File_Mgr ()->Get_File_Name (file_index);
		CString entry_name;

		//
		//	Is this file's subdirectory important?
		//
		bool path_important = ::Get_File_Mgr ()->Is_File_Path_Important (file_index);
		if (path_important) {
			
			//
			//	Pre-pend the subdirectory name to form the entry name
			//
			CString sub_dir = ::Get_Subdir_From_Full_Path (file_path);
			entry_name = Make_Path (sub_dir, ::Get_Filename_From_Path (file_path));

		} else {
			entry_name = ::Get_Filename_From_Path (file_path);
		}

		//
		// Add this file to the mix file with the given name
		//
		MixFileCreator.Add_File (file_path, entry_name);

		//
		//	Add this file to the level-asset list if its a W3D file
		//
		if (::Is_W3D_Filename (entry_name)) {
			m_AssetList.Add ((LPCTSTR)::Get_Filename_From_Path (entry_name));
		}
	}

	//
	//	Add the temp definition database
	//
	CString temp_db_filename  = ::Get_File_Mgr ()->Make_Full_Path (TEMP_DB_PATH);
	CString temp_db_name		  = FilenameBase + ".ddb";
	MixFileCreator.Add_File (temp_db_filename, temp_db_name);

	//
	// Copy all the files used by the framework to the directory
	//
	STRING_LIST &level_list = ::Get_File_Mgr ()->Get_Include_File_List ();
	Process_Include_Files (level_list);
	return retval;
}


/////////////////////////////////////////////////////////
//
//	Export_Database_Mix
//
/////////////////////////////////////////////////////////
void
ExporterClass::Export_Database_Mix (LPCTSTR path)
{
	CWaitCursor wait_cursor;
	m_AssetList.Delete_All ();

	Path				= path;
	FilenameBase	= "always.dbs";

	//
	//	Setup the temporary directory
	//		
	Make_Temp_Directory ();

	//
	//	Export the definition databases
	//
	Export_Definition_Databases (true);

	//
	//	Cleanup any temp files we needed
	//
	Delete_Temp_Directory ();
	return ;
}


/////////////////////////////////////////////////////////
//
//	Export_Definition_Databases
//
/////////////////////////////////////////////////////////
bool
ExporterClass::Export_Definition_Databases (bool use_temp_strings_library)
{
	EditorMixFileCreator	always_db_mix_creator;

	//
	//	Save the preset library to the temp directory
	//
	PresetsFormClass *presets_form = ::Get_Presets_Form ();
	if (presets_form != NULL) {
		CString path = ::Make_Path (TempDirectory, "objects.ddb");
		presets_form->Save_Presets (path, 0, false, false);		
		always_db_mix_creator.Add_File (path, ::Get_Filename_From_Path (path));
	}
	
	//
	//	Add the strings table
	//
	if (use_temp_strings_library == false) {
		CString filename = ::Get_File_Mgr ()->Make_Full_Path (STRINGS_DB_PATH);
		always_db_mix_creator.Add_File (filename, ::Get_Filename_From_Path (filename));
	} else {		
		//
		//	Save the strings table to the temporary directory, then
		// add it to the mix file
		//
		CString path = ::Make_Path (TempDirectory, STRINGS_DB_NAME);
		StringsMgrClass::Save_Translation_Database (path);
		always_db_mix_creator.Add_File (path, STRINGS_DB_NAME);
	}

	//
	//	Add the global conversation database
	//
	CString filename = ::Get_File_Mgr ()->Make_Full_Path (CONV_DB_PATH);
	always_db_mix_creator.Add_File (filename, ::Get_Filename_From_Path (filename));

	//
	//	Now generate the definition databases mix file
	//
	CString mix_filename = ::Make_Path (Path, ALWAYS_DB_FILENAME);
	always_db_mix_creator.Generate_Mix_File (mix_filename);	
	return true;
}


/////////////////////////////////////////////////////////
//
//	Build_File_List
//
/////////////////////////////////////////////////////////
bool
ExporterClass::Build_File_List (const STRING_LIST &search_list, STRING_LIST &file_list)
{
	bool retval = true;

	//
	// Loop through all the files in our include list
	//
	for (int index = 0; index < search_list.Count (); index ++) {
		CString wildcard = search_list[index];
		CString search = ::Get_File_Mgr ()->Make_Full_Path (wildcard);
		
		//
		// Make sure we have all the assets for this always group
		//
		if (DontGetVersionsFromVSS == false) {
			::Get_File_Mgr ()->Get_Subproject (::Strip_Filename_From_Path (wildcard));
		}
		
		//
		//	Add the all the files that match the search mask to this list
		//
		Find_Files (search, file_list);		
	}

	return retval;
}


/////////////////////////////////////////////////////////
//
//	Find_Files
//
/////////////////////////////////////////////////////////
void
ExporterClass::Find_Files (const char *search_mask, STRING_LIST &file_list)
{
	//
	//	Get the directory where we'll be copying the files from
	//
	CString directory = ::Strip_Filename_From_Path (search_mask);
	CString mask		= ::Get_Filename_From_Path (search_mask);

	//
	// Find all files that match this wildcard
	//
	WIN32_FIND_DATA find_info = { 0 };
	BOOL keep_going = TRUE;
	for (HANDLE hfile_find = ::FindFirstFile (search_mask, &find_info);
		  (hfile_find != INVALID_HANDLE_VALUE) && keep_going;
		  keep_going = ::FindNextFile (hfile_find, &find_info))
	{			
		//
		//	Don't do this if its a directory
		//
		if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {

			//
			//	Add this file to the list
			//
			CString full_path = Make_Path (directory, ::Get_Filename_From_Path (find_info.cFileName));
			file_list.Add (full_path);

		}
	}

	if (hfile_find != INVALID_HANDLE_VALUE) {
		::FindClose (hfile_find);
	}

	CString full_search_mask = Make_Path (directory, "*.*");

	//
	// Now recurse into any sub-directories
	//
	keep_going = TRUE;
	for (hfile_find = ::FindFirstFile (full_search_mask, &find_info);
		  (hfile_find != INVALID_HANDLE_VALUE) && keep_going;
		  keep_going = ::FindNextFile (hfile_find, &find_info))
	{			
		//
		//	Don't do this if its a directory
		//
		if ((find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) && (find_info.cFileName[0] != '.')) {

			//
			//	Build a search mask for this sub-directory
			//
			CString full_path	= Make_Path (directory, ::Get_Filename_From_Path (find_info.cFileName));
			full_path			= Make_Path (full_path, mask);

			//
			//	Recurse into this sub-directory
			//
			Find_Files (full_path, file_list);
		}
	}

	if (hfile_find != INVALID_HANDLE_VALUE) {
		::FindClose (hfile_find);
	}


	return ;
}


/////////////////////////////////////////////////////////
//
//	Process_Include_Files
//
/////////////////////////////////////////////////////////
void
ExporterClass::Process_Include_Files (const STRING_LIST &search_list)
{
	STRING_LIST file_list;
	Build_File_List (search_list, file_list);
	Export_Include_Files (file_list);
	return ;
}


/////////////////////////////////////////////////////////
//
//	Export_Include_Files
//
/////////////////////////////////////////////////////////
bool
ExporterClass::Export_Include_Files (const STRING_LIST &file_list)
{	
	//
	//	Add all the files from the list
	//
	for (int index = 0; index < file_list.Count (); index ++) {
		const CString &full_path = file_list[index];

		//
		// Add this file to the mix file
		//
		MixFileCreator.Add_File (full_path, ::Get_Filename_From_Path (full_path));

		//
		//	Add this file to the level-asset list if its a W3D file
		//
		if (::Is_W3D_Filename (full_path)) {
			m_AssetList.Add ((LPCTSTR)::Get_Filename_From_Path (full_path));
		}
	}

	return true;
}


////////////////////////////////////////////////////////////////////////////
//
//  Delete_File
//
////////////////////////////////////////////////////////////////////////////
bool
ExporterClass::Delete_File (LPCTSTR filename)
{
	bool retval = false;

	ASSERT (filename != NULL);
	if (filename != NULL) {

		//
		// Strip the readonly bit off if necessary
		//
		DWORD attributes = ::GetFileAttributes (filename);
		if ((attributes != 0xFFFFFFFF) &&
			 ((attributes & FILE_ATTRIBUTE_READONLY) == FILE_ATTRIBUTE_READONLY))
		{
			::SetFileAttributes (filename, attributes & (~FILE_ATTRIBUTE_READONLY));
		}

		//
		// Perform the delete operation!
		//
		if ((attributes != 0xFFFFFFFF) &&
			 ((attributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY))
		{
			retval = (::RemoveDirectory (filename) == TRUE);
		} else {
			retval = (::DeleteFile (filename) == TRUE);
		}
	}

	return retval;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Clean_Directory
//
//////////////////////////////////////////////////////////////////////////////////
bool
ExporterClass::Clean_Directory (LPCTSTR local_dir)
{
	bool retval = true;

	//
	// Build a search mask from the directory
	//
	CString search_mask = CString (local_dir) + "\\*.*";	
	
	//
	// Loop through all the files in this directory and add them	
	// to our list
	//
	DynamicVectorClass<CString> file_list;
	BOOL keep_going = TRUE;
	WIN32_FIND_DATA find_info = { 0 };
	for (HANDLE hfind = ::FindFirstFile (search_mask, &find_info);
		  (hfind != INVALID_HANDLE_VALUE) && keep_going;
		  keep_going = ::FindNextFile (hfind, &find_info))
	{
		
		//
		// If this file isn't a directory, add it to the list
		//
		if (!(find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY)) {
			CString filename = find_info.cFileName;
			file_list.Add (filename);
		} else if (find_info.cFileName[0] != '.') {
			
			//
			//	Recurse into this subdirectory
			//
			CString full_path = local_dir;
			::Delimit_Path (full_path);
			full_path += find_info.cFileName;
			Clean_Directory (full_path);
			
			//
			//	Add this directory to the list so it will get
			// deleted with the files...
			//
			CString filename = find_info.cFileName;
			file_list.Add (filename);
		}
	}

	//
	// Close the search handle
	//
	if (hfind != NULL) {
		::FindClose (hfind);
	}
	
	//
	//	Now loop through all the files and delete them
	//
	for (int index = 0; index < file_list.Count (); index ++) {
		CString &filename = file_list[index];
		CString full_path = ::Make_Path (local_dir, filename);
		Delete_File (full_path);
	}

	return retval;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Pre_Level_Export
//
//////////////////////////////////////////////////////////////////////////////////
void
ExporterClass::Pre_Level_Export (DynamicVectorClass<NodeClass *> &node_list)
{
	//
	//	Force static anim phys to be displayed
	//
	::Get_Scene_Editor ()->Display_Static_Anim_Phys (true);
	::Get_Scene_Editor ()->Display_Editor_Objects (true);

	//
	// Make sure the scenes are repartitioned
	// (gth) can't re-partition the object culling systems or
	// we'll lose hierarchical vis
	//
	::Get_Scene_Editor ()->Re_Partition_Static_Lights ();
	::Get_Scene_Editor ()->Re_Partition_Audio_System ();
	::Get_Scene_Editor ()->Set_Selection (NULL);
	::Get_Scene_Editor ()->Display_Vis_Points (false);	
	
	//
	//	Make sure the buildings are in a good state
	//
	GameObjManager::Update_Building_Collection_Spheres ();
	GameObjManager::Init_Buildings ();	

	//
	//	Let the nodes know we are about to start exporting them.
	// This way they can remove any 'editor-only' artifacts from
	// the system.
	//	
	NodeMgrClass::Build_Full_Node_List (node_list);
	for (int index = 0; index < node_list.Count (); index ++) {
		node_list[index]->Pre_Export ();
	}	

	//
	//	Force the pathfind system to use any available waypath data
	// in its evaluation
	//
	PathfindClass::Get_Instance ()->Generate_Waypath_Sectors_And_Portals ();
	return ;
}


//////////////////////////////////////////////////////////////////////////////////
//
//	Post_Level_Export
//
//////////////////////////////////////////////////////////////////////////////////
void
ExporterClass::Post_Level_Export (DynamicVectorClass<NodeClass *> &node_list)
{
	//
	//	Let the nodes know we're done exporting
	//
	for (int index = 0; index < node_list.Count (); index ++) {
		NodeClass *node = node_list[index];
		node->Post_Export ();
	}

	return ;
}


/////////////////////////////////////////////////////////
//
//	Export_Package
//
/////////////////////////////////////////////////////////
void
ExporterClass::Export_Package (LPCTSTR full_path)
{	
	CWaitCursor wait_cursor;

	//
	//	Save the preset changes to disk as necessary
	//
	if (PresetMgrClass::Are_Presets_Dirty ()) {
		PresetMgrClass::Check_In_Presets ();
	}

	//
	//	Get the base destination path
	//
	Path = ::Strip_Filename_From_Path (full_path);	

	//
	//	Strip the path and extension from the filename to give
	// us a base for use with other filenames
	//
	FilenameBase = ::Get_Filename_From_Path (full_path);
	int extension_index = FilenameBase.ReverseFind ('.');
	if (extension_index != -1) {
		FilenameBase = FilenameBase.Left (extension_index);
	}

	//
	//	Add all the files from our asset tree to this package
	//
	Add_Files_To_Mod_Package (AssetPackageMgrClass::Get_Current_Package_Path ());

	//
	//	Create the mix file...
	//
	MixFileCreator.Generate_Mix_File (full_path);

	//
	//	Cache the directory path we just exported into
	//
	theApp.WriteProfileString (CONFIG_KEY, LAST_EXPORT_DIR_VALUE, Path);
	return ;	
}


/////////////////////////////////////////////////////////
//
//	Add_Files_To_Mod_Package
//
/////////////////////////////////////////////////////////
void
ExporterClass::Add_Files_To_Mod_Package (LPCTSTR full_path)
{
	//
	//	Build a search path for all the files in this directory
	//
	CString search_path = ::Make_Path (full_path, "*.*");

	//
	//	Search for all the files in this directory
	//
	WIN32_FIND_DATA find_info	= { 0 };
	BOOL keep_going				= TRUE;
	HANDLE file_find				= NULL;
	for (file_find = ::FindFirstFile (search_path, &find_info);
		 (file_find != INVALID_HANDLE_VALUE) && keep_going;
		  keep_going = ::FindNextFile (file_find, &find_info))
	{
		if (find_info.cFileName[0] != '.') {
			
			if ((find_info.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) == FILE_ATTRIBUTE_DIRECTORY) {
				
				//
				//	Recurse into this sub-directory
				//
				CString subdir_path = ::Make_Path (full_path, find_info.cFileName);
				Add_Files_To_Mod_Package (subdir_path);

			} else {
				
				//
				//	Skip all lvl files
				//
				if (::strstr (find_info.cFileName, ".lvl") == NULL) {
					
					//
					//	Simply add this file to the mix
					//
					CString file_path = ::Make_Path (full_path, find_info.cFileName);
					MixFileCreator.Add_File (file_path, find_info.cFileName);
				}
			}
		}
	}

	//
	//	Close the search handle
	//
	if (file_find != INVALID_HANDLE_VALUE) {			  
		::FindClose (file_find); 
	}
	
	return ;	
}


/////////////////////////////////////////////////////////
//
//	Export_Level_Only
//
/////////////////////////////////////////////////////////
void
ExporterClass::Export_Level_Only (LPCTSTR path)
{	
	CWaitCursor wait_cursor;
	m_AssetList.Delete_All ();

	//
	//	Prepare the level for exporting...
	//
	DynamicVectorClass<NodeClass *> node_list;
	Pre_Level_Export (node_list);

	//
	//	Get the base destination path
	//
	Path				= ::Strip_Filename_From_Path (path);	
	TempDirectory	= Path;
	::Set_Current_Directory (TempDirectory);
	::SetCurrentDirectory (TempDirectory);

	//
	//	Strip the path and extension from the filename to give
	// us a base for use with other filenames
	//
	FilenameBase = ::Get_Filename_From_Path (path);
	int extension_index = FilenameBase.ReverseFind ('.');
	if (extension_index != -1) {
		FilenameBase = FilenameBase.Left (extension_index);
	}	

	//
	//	Export the level in game format
	//
	Export_Level_File ();

	//
	//	Add the temp definition database
	//
	CString temp_db_source  = ::Get_File_Mgr ()->Make_Full_Path (TEMP_DB_PATH);
	CString temp_db_dest		= ::Make_Path(Path,FilenameBase + ".ddb");
	::CopyFile(temp_db_source,temp_db_dest,false);

	//
	//	Resetore the level after exporting
	//
	Post_Level_Export (node_list);

	//
	//	Cache the directory path we just exported into
	//
	theApp.WriteProfileString (CONFIG_KEY, LAST_EXPORT_DIR_VALUE, Path);
	return ;
}
