//===========================================================================//
// File: BatchExp.cpp 
// Project: MechWarrior 4
// Contents: Plugin to adjust height of max anmiations
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 04/28/99 JSE Initial coding, 
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive, Inc.
// All Rights reserved worldwide
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL
//===========================================================================//


#include "stdafx.h"

#ifdef STRICT // Already defined by stdafx,
#undef STRICT // so we avoid warning linking msg
#endif
#ifdef _MBCS // The same as above
#undef _MBCS
#endif

#include "BatchExport.h"
#include "ArmatureExport.h"
#include "GeoExport.h"
#include "ExportUtil.h"
#include <adept\collisionvolume.hpp>
#include "CVExport.h"
#include <GameOS\ToolOS.hpp>
#include <Stuff\StuffHeaders.hpp>
#include <DLLPlatform\DLLPlatform.hpp>
#include "MaxProxies\MaxProxyHeaders.hpp"
#include "Proxies\ProxyHeaders.hpp"
#include <proxies\optimizeflatshading.hpp>
#include <ElementRenderer\ShapeLODElement.hpp>
#include <ElementRenderer\MultiLODElement.hpp>
#include <ElementRenderer\TreeElement.hpp>
#include <MLR\MLRShape.hpp>

#include "editbatch.h"

#define MAXLODS 64
#define NEWBOUNDINGSPHERE
#define TREEELEMENT

//undef Verify

#ifndef _DEBUG //This is a hack To make it link in Release
#undef Spew
//void Spew() {}
#endif


extern CString g_strLogFile;
extern int g_bCleanupErrors;

/*******************************************************************************
/* function name: DoExport
/* description: 
/*******************************************************************************/
int BatchExp::DoExport(const TCHAR *name,ExpInterface *ei,Interface *i, BOOL suppressPrompts,DWORD options)
{ 
    int return_value;
    __try
    {
        return_value = DoExportWrap(name, ei, i, suppressPrompts?true:false);
        //return_value = true;
        GetSystemSetting(SYSSET_CLEAR_UNDO);
        Verify(!theHold.Holding());
    }
    __except(ProcessException(GetExceptionInformation()))
    {
    }
    
    return return_value;
}

/*******************************************************************************
/* function name: DoExportWrap
/* description: 
/*******************************************************************************/
int BatchExp::DoExportWrap(const TCHAR *name,ExpInterface *ei,Interface *i, bool bsuppressPrompts)
{
    m_current_export = 0;
    m_total_exports = 0;
    m_ip = i;
    m_exp.m_suppress = true;
    m_exp.m_isBRB = false;
    
    TurnOnExceptions();
    AFX_MANAGE_STATE(AfxGetStaticModuleState());
    
    // Load stuff from plugins.ini
    LoadPlugCfg(i);

	CString strBatch;
	CTime t = CTime::GetCurrentTime();
	strBatch.Format("\n-----%02i/%02i %02i:%02i    Start Batch Export %s", 
		t.GetMonth(), t.GetDay(), t.GetHour(), t.GetMinute(), name);

    ReportError(strBatch,true);

    //open this notefile on up...    
    Stuff::NotationFile note_file(name);   
    NotationFile::PageIterator *anim_page_list = note_file.MakePageIterator();
    Check_Object(anim_page_list);
    
    LPVOID arg = NULL;
    TurnOffExceptions();
    m_ip->ProgressStart(_T("Batch Exporting : "), TRUE, fn, arg);
    TurnOnExceptions();
    
	//use a notation file to keep track of errors
    Stuff::NotationFile log_file;
    Page *anim_page_entry;
    while ((anim_page_entry = anim_page_list->ReadAndNext()) != NULL)
    {
        const char *page_name = (const char *)anim_page_entry->GetName();
        Check_Pointer(page_name);
        
        ChainOf<Note*> *anim_name_list = anim_page_entry->MakeNoteChain("shape");
        Check_Object(anim_name_list);
        Page::NoteIterator shape_itr(anim_name_list);
        m_total_exports += shape_itr.GetSize();
        Check_Object(anim_name_list);
        delete anim_name_list;
        
        anim_name_list = anim_page_entry->MakeNoteChain("LODShape");
        Check_Object(anim_name_list);
        Page::NoteIterator lod_itr(anim_name_list);
        m_total_exports += lod_itr.GetSize();
        Check_Object(anim_name_list);
        delete anim_name_list;
        
        anim_name_list = anim_page_entry->MakeNoteChain("LODChar");
        Check_Object(anim_name_list);
        Page::NoteIterator lod_itr1(anim_name_list);
        m_total_exports += lod_itr1.GetSize();
        Check_Object(anim_name_list);
        delete anim_name_list;
        
        anim_name_list = anim_page_entry->MakeNoteChain("obb");
        Check_Object(anim_name_list);
        Page::NoteIterator obb_itr(anim_name_list);
        m_total_exports += obb_itr.GetSize();
        Check_Object(anim_name_list);
        delete anim_name_list;
        
        anim_name_list = anim_page_entry->MakeNoteChain("cage");
        Check_Object(anim_name_list);
        Page::NoteIterator cage_itr(anim_name_list);
        m_total_exports += cage_itr.GetSize();
        Check_Object(anim_name_list);
        delete anim_name_list;
        
        anim_name_list = anim_page_entry->MakeNoteChain("char");
        Check_Object(anim_name_list);
        Page::NoteIterator char_itr(anim_name_list);
        m_total_exports += char_itr.GetSize();
        Check_Object(anim_name_list);
        delete anim_name_list;
        
        anim_name_list = anim_page_entry->MakeNoteChain("anim");
        Check_Object(anim_name_list);
        Page::NoteIterator anim_itr(anim_name_list);
        Note *anim_name_entry;
        while ((anim_name_entry = anim_itr.ReadAndNext()) != NULL)
        {
            Check_Object(anim_name_entry);
            const char* source_directory_name;
            anim_name_entry->GetEntry(&source_directory_name);
            
            Stuff::MString filter_name(source_directory_name);
            filter_name += "*.max";
            
            char *file_name = NULL;
            
            file_name = gos_FindFiles(filter_name);
            
            while (file_name != NULL)
            {
                Check_Pointer(file_name);
                
                ++m_total_exports;
                
                file_name = gos_FindFilesNext();
            }
        }
        
        Check_Object(anim_name_list);
        delete anim_name_list;
    }

    int err = 0;
    anim_page_list->First();
    while ((anim_page_entry = anim_page_list->ReadAndNext()) != NULL)
    {
        const char *pagename = anim_page_entry->GetName();
        if (0==strnicmp(pagename,"Options",7))
        {
            const char *logfile;
            if (anim_page_entry->GetEntry("LogFile",&logfile))
            {
                g_strLogFile = logfile;
            }
            int value;
            if (anim_page_entry->GetEntry("CleanupErrors",&value))
            {
                g_bCleanupErrors = value;
            }
            int verbose;
            if (anim_page_entry->GetEntry("Verbose",&verbose))
            {
                m_exp.m_suppress = !verbose;
            }
            int brb;
            if (anim_page_entry->GetEntry("BRB",&brb))
            {

                // Shortcut hack. isBRB changed, reload stuff from plugins.ini
                m_exp.m_isBRB = brb?true:false;
				LoadPlugCfg(i);
            }
        }
        else
        {
            if (ElementProxies::MLRTexturePoolProxy::Instance)
            {
                CString oldpath = ElementProxies::MLRTexturePoolProxy::Instance->GetTexturePath();
                if (stricmp(oldpath,m_exp.m_texturePath))
                {
                    ElementProxies::MLRTexturePoolProxy::Instance->CloseLibrary(true);
                    ElementProxies::MLRTexturePoolProxy::Instance->SetTexturePath(m_exp.m_texturePath);
                }
            }
		
			Stuff::Page* log_page = log_file.SetPage(anim_page_entry->GetName());
			Check_Object(log_page);

			int pageErrors = ExportPage(anim_page_entry, log_page);
			log_page->SetEntry("Errors", pageErrors);
            err += pageErrors;
        }
    }
    delete anim_page_list;
    note_file.IgnoreChanges();
    
    
    // Recycle the texture library
    //
    if (ElementProxies::MLRTexturePoolProxy::Instance)
    {
        ElementProxies::MLRTexturePoolProxy::Instance->CloseLibrary(true);
    }
    
    
    TurnOffExceptions();
    m_ip->ProgressEnd();
    
	WriteSummary(&log_file);
	log_file.IgnoreChanges();

    if (err && m_exp.m_suppress)
        return false;
    else
        return true;
}

/*******************************************************************************
/* function name: LogStats
/* description: Log export statices 
/*******************************************************************************/
void BatchExp::LogStats(int ilod, Stuff::Scalar farDist, MidLevelRenderer::MLRShape *mlrShape, const CString& filename, Page* log_page) 
{
	Check_Object(mlrShape);
	Check_Object(log_page);

	// Record LOD distances
	CString entryName;
	entryName.Format("Dist_%d", ilod);
	log_page->SetEntry(entryName, farDist);
	
	// Record number of polys
	int polyCount = mlrShape->GetNumPrimitives();
	entryName.Format("Polygons_%d", ilod);
	int oldValue = 0;
	log_page->GetEntry(entryName, &oldValue);
	log_page->SetEntry(entryName, oldValue+polyCount);
	
	// Record number of vertices
	int vertCount = 0;
	for (int f=0; f<mlrShape->GetNum(); ++f)
	{
		vertCount += mlrShape->Find(f)->GetNumVertices();
	}
	entryName.Format("Vertices_%d", ilod);
	oldValue = 0;
	log_page->GetEntry(entryName, &oldValue);
	log_page->SetEntry(entryName, oldValue+vertCount);
	
	// Record number of states
	if (m_bLogStates) {
		NotationFile *pLogFile = log_page->GetNotationFile();
		Page *statesPage = pLogFile->FindPage(CString(log_page->GetName())+"_States");
		if (!statesPage)
			statesPage = pLogFile->AddPage(CString(log_page->GetName())+"_States");
		Check_Object(statesPage);
		
		CString states;
		DWORD* mlrStates = new DWORD[mlrShape->GetNum()*2];
		bool bReportDupe = true;
		for (int i=0; i<mlrShape->GetNum(); i++) {
			MidLevelRenderer::MLRState mlrState = mlrShape->Find(i)->GetReferenceState();
			mlrStates[2*i] =   mlrState.GetRenderStateFlags();
			mlrStates[2*i+1] = mlrState.GetProcessStateFlags();
			for (int j=0; j<i; j++) {
				if (mlrStates[2*j]   == mlrStates[2*i] &&
					mlrStates[2*j+1] == mlrStates[2*i+1])
					if (bReportDupe) {
						CString strBatch;
						strBatch.Format("Warning: LOD %d has duplicate states", ilod);
						ReportError(strBatch, true);
						bReportDupe = false;
					}
			}
			CString temp = states;
			states.Format("%s %08X %08X", temp, mlrStates[2*i], mlrStates[2*i+1]);
		}
		entryName.Format("%s_%d", filename, ilod);
		statesPage->SetEntry(entryName, states);
	}
}

extern bool g_bSaveTextureAsPng;
/*******************************************************************************
/* function name: WriteSummary
/* description: Save the export summary to the log file 
/*******************************************************************************/
void BatchExp::WriteSummary(Stuff::NotationFile *pLogFile)
{
	CTime t = CTime::GetCurrentTime();
	CString strBatch;
	strBatch.Format("-----%02i/%02i %02i:%02i Finished Batch Export\n", 
		t.GetMonth(), t.GetDay(), t.GetHour(), t.GetMinute());
	ReportError(strBatch,true);

	ReportError(CString("Export directory:     ") + m_export_directory, true);
	ReportError(CString("Texture directory:    ") + m_exp.m_texturePath, true);
	ReportError(CString("Texture format:       ") + (g_bSaveTextureAsPng?"PNG":"TGA"), true);
	ReportError(CString("Exporting data files: ") + (m_exp.m_exportDataFiles?"Yes":"No"), true);

    NotationFile::PageIterator *page_list = pLogFile->MakePageIterator();
    Check_Object(page_list);

	int err = 0;
	int total = 0;
	int errTotal = 0;
    Page *page;
	CString errorObjs;
    while ((page = page_list->ReadAndNext()) != NULL)
    {
		CString page_name = page->GetName();
		page_name.MakeLower();

		if (page_name.Find("_states")>=0)
			continue;
		if (page_name.Find("_solid")>=0)
			continue;
		if (page_name.Find("_hierarchical")>=0)
			continue;

		total ++;
		int exportErr = 0;
		page->GetEntry("Errors", &exportErr);
		if (exportErr) {
			err += exportErr;
			errTotal ++;
			errorObjs = errorObjs + page->GetName() + "\n";
		}

		ReportError(CString("\n****************")+page_name+"****************",true);					

		strBatch.Format("%15s%10s%10s%10s%10s%10s%10s", 
			"", "LOD0", "LOD1", "LOD2", "LOD3", "LOD4", "LOD5");
		ReportError(strBatch,true);					

		int nums[MAXLODLEVELS];
		::memset(nums, 0, sizeof(int)*MAXLODLEVELS);
		GetEntryList(page, "Dist_", nums);
		strBatch.Format("%15s%10i%10i%10i%10i%10i%10i", 
			"Distances", nums[0], nums[1], nums[2], nums[3], nums[4], nums[5]);
		ReportError(strBatch,true);

		::memset(nums, 0, sizeof(int)*MAXLODLEVELS);
		GetEntryList(page, "Polygons_", nums);
		strBatch.Format("%15s%10i%10i%10i%10i%10i%10i", 
			"Polygons", nums[0], nums[1], nums[2], nums[3], nums[4], nums[5]);
		ReportError(strBatch,true);

		::memset(nums, 0, sizeof(int)*MAXLODLEVELS);
		GetEntryList(page, "Vertices_", nums);
		strBatch.Format("%15s%10i%10i%10i%10i%10i%10i", 
			"Vertices", nums[0], nums[1], nums[2], nums[3], nums[4], nums[5]);
		ReportError(strBatch,true);

		page = pLogFile->FindPage(page_name+"_States");
		if (page) {
			ReportError("STATES:",true);
			Page::NoteIterator *notes = page->MakeNoteIterator();
			Note *note;
			while ((note = notes->ReadAndNext()) != NULL)
			{
				CString name = note->GetName();
				if (name.Find("_0")<0)
					continue;

				name = name.Left(name.GetLength()-1);
				CString strStates[MAXLODLEVELS];
				int lods = GetEntryList(page, name, strStates);
				DWORD states[MAXLODLEVELS][10][2];
				::memset(states, 0, sizeof(DWORD)*MAXLODLEVELS*20);
				::memset(nums, 0, sizeof(int)*MAXLODLEVELS);
				int maxStates = 0;
				for (int i=0; i<lods; i++) {
					nums[i] = sscanf(strStates[i],"%X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X %X",
						&states[i][0][0],&states[i][0][1],&states[i][1][0],&states[i][1][1],
						&states[i][2][0],&states[i][2][1],&states[i][3][0],&states[i][3][1],
						&states[i][4][0],&states[i][4][1],&states[i][5][0],&states[i][5][1],
						&states[i][6][0],&states[i][6][1],&states[i][7][0],&states[i][7][1],
						&states[i][8][0],&states[i][8][1],&states[i][9][0],&states[i][9][1]);
					nums[i] /= 2;
					if (nums[i]>maxStates)
						maxStates = nums[i];
				}

				strBatch.Format("%15s%10i%10i%10i%10i%10i%10i", 
					name.Left(name.GetLength()-5), nums[0], nums[1], nums[2], nums[3], nums[4], nums[5]);
				ReportError(strBatch,true);

				for (i=0; i<lods; i++) {
					for (int j=0; j<nums[i]; j++) {
						strBatch = "";
						DWORD state = states[i][j][0];
						while (state != 0) {
							strBatch = ((state%2)?"1":"0") + strBatch;
							state = state >> 1;
						}
						while (strBatch.GetLength() < 32)
							strBatch = "0"+strBatch;

						strBatch = " "+strBatch;
						state = states[i][j][1];
						while (state != 0) {
							strBatch = ((state%2)?"1":"0") + strBatch;
							state = state >> 1;
						}
						while (strBatch.GetLength() < 52)
							strBatch = "0"+strBatch;

						ReportError(strBatch,true);
					}						
				}
			}
			delete notes;
		}

		page = pLogFile->FindPage(page_name+"_Solid");
		if (page) {
			ReportError("\nSolid OBB Material Types:",true);
			Page::NoteIterator *notes = page->MakeNoteIterator();
			Note *note;
			while ((note = notes->ReadAndNext()) != NULL)
			{
				CString name = note->GetName();
				const char *value;
				note->GetEntry(&value);
				ReportError(name + ": " + value,true);
			}
			delete notes;
		}

		page = pLogFile->FindPage(page_name+"_Hierarchical");
		if (page) {
			ReportError("Hierarchical OBB Material Types:",true);
			Page::NoteIterator *notes = page->MakeNoteIterator();
			Note *note;
			while ((note = notes->ReadAndNext()) != NULL)
			{
				CString name = note->GetName();
				const char *value;
				note->GetEntry(&value);
				ReportError(name + ": " + value,true);
			}
			delete notes;
		}

	}
	
	strBatch.Format("\nTotal Exported Objects:   %d", total);
	ReportError(strBatch,true);
	strBatch.Format(  "Objects with Errors (%d): %s", err, errorObjs);
	ReportError(strBatch,true);
}

/*******************************************************************************
/* function name: EditPage
/* description: Edit a page in a batch file
/*******************************************************************************/
int BatchExp::EditPage(Page *page)
{
    Check_Object(page);
	
    CEditBatch dlg(page);
	if (IDOK != dlg.DoModal())
	{
		// User cancelled the export
		return 1;
	}
	
	page->GetNotationFile()->Save();

    // Class is a required field
    Note *classnote = page->FindNote("class");

	if (NULL == classnote)
	{
		CString str = CString(page->GetName())+CString(": Error setting class. Export requires class entry to create game data files.");
		ReportError(str,m_exp.m_suppress);
		return 1;
	}
	
	return 0;
}

/*******************************************************************************
/* function name: ExportPage
/* description: Export a page in a batch file
/*******************************************************************************/
int BatchExp::ExportPage(Page *page, Page *log_page)
{
    Check_Object(page);
    int err = 0;

    // Class is a required field
    Note *classnote = page->FindNote("class");

	bool bDoExport = true;
	page->GetEntry("DoExport", &bDoExport);
    
    if ((NULL == classnote) || !bDoExport)
    {
        if (!m_exp.m_isBRB)
        {
            CString str = CString(page->GetName())+CString(": Missing class. Export requires class entry to create game data files.");
            ReportError(str,m_exp.m_suppress);
            return 1;
        }
        
		err += EditPage(page);
		if (err)
			return err;
    }

	if (!bDoExport)
		return 0;

    ReportError(CString(page->GetName()) + ": Start Exporting",true);

    // Better have class field now
    classnote = page->FindNote("class");
    Check_Object(classnote);
    
    const char *export_directory1;
    page->GetEntry("path", &export_directory1, true);
    Check_Pointer(export_directory1);
    m_export_directory = export_directory1;
    
    if (!Stuff::CreateDirectories(m_export_directory))
    {
        CString str = CString("Unable to create directory : ")+m_export_directory;
        ReportError(str,m_exp.m_suppress);
        return 1;
    }
    
	// If we aren't really a character export, we are exporting building or lights
    m_bIsChar = false; 

    const char *classentry;
    classnote->GetEntry(&classentry);
	CString strClass(classentry);

	// Turn flat shading optimization on by default for all
	// objects except for mechs
	m_bFlatShading = stricmp(classentry,"MechWarrior4::Mech") != 0;

	//
	// For performance we're turning off unique geometry for damage shading for all objects other
	// than mechs.
	//
	m_bSetUniqueYes = stricmp(classentry, "MechWarrior4::Mech") == 0;

	// Batch file can override it as always
	page->GetEntry("FlatShading", &m_bFlatShading);

	page->GetEntry("SetUniqueYes", &m_bSetUniqueYes);

    err += ExportShape(page, log_page);
    err += ExportObb(page, log_page);
    err += ExportLODShape(page, log_page);
    err += ExportLODSkel(page, log_page);
    err += ExportChar(page, log_page);
    err += ExportAnim(page, log_page);
    err += ExportMLRShape(page, log_page);
    
    // Export the rest of the file optionally
    {
        if (stricmp(classentry,"MechWarrior4::Building")==0 ||
			stricmp(classentry,"MechWarrior4::Bridge")==0)
        {
            if (m_exp.m_exportDataFiles || m_exp.m_isBRB) {
				// Export Data
				err += ExportBuildingData(page);
				// Export Damage
				err += ExportBuildingDamage(page);
				// Export Subsystems
				err += ExportBuildingSubsystems(page);
				// Export Instance
				err += ExportBuildingInstance(page);
			}
        }
        
        else if (stricmp(classentry,"MechWarrior4::Mech")==0)
        {
            if (m_exp.m_exportDataFiles || m_exp.m_isBRB) {
				// Export Data
				err += ExportMechData(page);
				// Export Damage
				err += ExportMechDamage(page);
				// Export Subsystems
				err += ExportMechSubsystems(page);
				// Export Instance
				err += ExportMechInstance(page);
			}
        }

        else
        {
            if (m_exp.m_exportDataFiles || m_exp.m_isBRB) {
				// Export Data
				err += ExportVehicleData(page);
				// Export Damage
				err += ExportVehicleDamage(page);
				// Export Subsystems
				err += ExportVehicleSubsystems(page);
				// Export Instance
				err += ExportVehicleInstance(page);
			}
		}
    }
    
	if (err == 0) {
        CString str;
        str.Format("%s: Successful",page->GetName());
		ReportError(str, true);
	}

    else if (err && g_bCleanupErrors)
    {
        CString str;
        str.Format("%s: Failed. Remove partial export in %s",page->GetName(),m_export_directory);
		ReportError(str, m_exp.m_suppress);

        // Remove m_export_directory
        ClearDirectory(m_export_directory);
    }

    return err;
}

/*******************************************************************************
/* function name: ExportShape
/* description: Export all "shape" entries to separate erf files from a batch file page
/*******************************************************************************/
int BatchExp::ExportShape(Page *page, Page *log_page)
{
	const char *hint=NULL;
	CString strHintFile;
	if (page->GetEntry("Hint",&hint))
	{
		strHintFile = hint;
	}

    ChainOf<Note*> *list = page->MakeNoteChain("shape");
    Check_Object(list);
    Page::NoteIterator shape_itr(list);
    Note *note;
    
    while ((note = shape_itr.ReadAndNext()) != NULL)
    {
        Check_Object(note);
        const char *file_name;
        note->GetEntry(&file_name);
        
        Check_Pointer(file_name);
        
        int total_done = (int)(((float)m_current_export/(float)m_total_exports)*100.0f);
        TurnOffExceptions();
        m_ip->ProgressUpdate(total_done, TRUE, const_cast<char*>(file_name));
        TurnOnExceptions();
        
        Stuff::MString export_name;
        
        export_name = file_name;
        export_name.StripExtension().StripDirectory();
        export_name = m_export_directory + export_name;
        export_name += ".erf";
        
        SPEW(("jerryeds","LOAD SHAPE: %s", file_name));
        SPEW(("jerryeds","SAVE SHAPE: %s", (const char *)export_name));
        
        
        int err = 0;
        TurnOffExceptions();
        if(m_ip->LoadFromFile(file_name,TRUE))
        {
            MAXProxies::MAXScene *max_scene = MakeMaxScene(m_ip,strHintFile);
			err = m_exp.ExportMaxScene(max_scene, m_ip, export_name);
        }
        TurnOnExceptions();
        
        if (err)
        {
            ReportError(CString(page->GetName())+": Error exporting shape",m_exp.m_suppress);
            delete list;
            ++m_current_export;
            return err;
        }
    }
    
    Check_Object(list);
    delete list;
    
    return 0;
}

/*******************************************************************************
/* function name: ExportObb
/* description: Export OBB entry in a batch file page
/*******************************************************************************/
int BatchExp::ExportObb(Page *page, Page *log_page)
{
    ChainOf<Note*> *list = page->MakeNoteChain("obb");
    Check_Object(list);
    Page::NoteIterator obb_itr(list);
    Note *note;
    while ((note = obb_itr.ReadAndNext()) != NULL)
    {
        Check_Object(note);
        const char *file_name;
        note->GetEntry(&file_name);
        Check_Pointer(file_name);
        
        int total_done = (int)(((float)m_current_export/(float)m_total_exports)*100.0f);
        TurnOffExceptions();
        m_ip->ProgressUpdate(total_done, TRUE, const_cast<char*>(file_name));
        TurnOnExceptions();
        
        Stuff::MString export_name;
        
        export_name = file_name;
        export_name.StripExtension().StripDirectory();
        export_name = m_export_directory + export_name;
        export_name += ".obb";
        
        SPEW(("jerryeds","LOAD CHAR: %s", file_name));
        SPEW(("jerryeds","SAVE CHAR: %s", (const char *)export_name));
        
        TurnOffExceptions();
        int err = 1;
        if(m_ip->LoadFromFile(file_name,TRUE)) {
            err = m_ip->ExportToFile(export_name,TRUE);
			if (log_page) {
				MaxCollisionVolume *pSolidCv = NULL;
				MaxCollisionVolume *pHierarchicalCv = NULL;
				ConstructCVs(m_ip, &pSolidCv, &pHierarchicalCv, true, false);
				CMapStringToPtr matCounts;

				// Count material for pHierarchicalCv
				CountMaterials(pHierarchicalCv, log_page, matCounts);
				
				NotationFile *pLogFile = log_page->GetNotationFile();
				Page *cvPage = pLogFile->FindPage(CString(log_page->GetName())+"_Hierarchical");
				if (!cvPage)
					cvPage = pLogFile->AddPage(CString(log_page->GetName())+"_Hierarchical");
				Check_Object(cvPage);
				
				POSITION pos = matCounts.GetStartPosition();
				while (pos)
				{
					CString key;
					LPVOID lpVoid;
					matCounts.GetNextAssoc(pos,key,lpVoid);
					cvPage->SetEntry(key, *(int*)lpVoid);
					if (lpVoid)
						delete lpVoid;
				}		
				matCounts.RemoveAll();
				if (pHierarchicalCv)
					delete pHierarchicalCv;
	
				// Count material for pSolidCv
				CountMaterials(pSolidCv, log_page, matCounts);
				
				cvPage = pLogFile->FindPage(CString(log_page->GetName())+"_Solid");
				if (!cvPage)
					cvPage = pLogFile->AddPage(CString(log_page->GetName())+"_Solid");
				Check_Object(cvPage);
				
				pos = matCounts.GetStartPosition();
				while (pos)
				{
					CString key;
					LPVOID lpVoid;
					matCounts.GetNextAssoc(pos,key,lpVoid);
					cvPage->SetEntry(key, *(int*)lpVoid);
					if (lpVoid)
						delete lpVoid;
				}
				matCounts.RemoveAll();
				if (pSolidCv)
					delete pSolidCv;
			}			
		}

        TurnOnExceptions();
        
        ++m_current_export;
        if (0 == err)
        {
            ReportError(CString(page->GetName())+": Error exporting obb",m_exp.m_suppress);
            delete list;
            return true;
        } 
    }
    Check_Object(list);
    delete list;
    return false;
}

/*******************************************************************************
/* function name: ExportLODShape
/* description: Export all LODShape entries in a batch file page into one
/*	combined erf file
/*******************************************************************************/
int BatchExp::ExportLODShape(Page *page, Page *log_page)
{
	int err = 0;
    ChainOf<Note*> *list = page->MakeNoteChain("LODshape");
    Check_Object(list);
    Page::NoteIterator lod_itr(list);
    Note *note = lod_itr.GetCurrent();
    if(note !=NULL)
    {
       
        const char *hint=NULL;
        CString strHintFile;
        if (page->GetEntry("Hint",&hint))
        {
            strHintFile = hint;
        }

		const char *megatextures=NULL;
		CString strMegaTextures;
		if (page->GetEntry("MegaTextures",&megatextures))
		{
			strMegaTextures = megatextures;
		}
        
		// Load billboard stuff
		LoadBillBoardInfo(page);
   
        CString LodLevels[MAXLODLEVELS];
        CString LodNames[MAXLODLEVELS];        
        int DistTotal = GetEntryList(page, "LODDist", LodLevels);
        int LodTotal = GetEntryList(page, "LODShape", LodNames);

        if(LodTotal!=DistTotal)
        {
            CString str;
			CString BaseName(page->GetName());
			str.Format("%s: missing geometry entry in batch list", (const char *)BaseName);
            ReportError(str,m_exp.m_suppress);
            return true;
        }

        CMapWordToPtr mapLOD;
        CMapWordToPtr lightLOD;
        
        // We have at least one LODShape
		for (int iLod=0; iLod<LodTotal; iLod++)
        {
			if (!MakeLightLODAndRemoveFromScene(LodNames[iLod], strHintFile, &lightLOD, iLod))
				return 1;
         
            ElementProxies::ElementSceneProxy *element_scene = InitElementScene(m_exp.m_texturePath);
            MAXProxies::MAXScene *max_scene = MakeMaxScene(m_ip,strHintFile);

            CopySceneMaxToElement(element_scene,max_scene,m_bFlatShading, m_ip,m_exp.m_progress,m_exp.m_suppress);
            FuseElementScene(element_scene,m_ip,m_exp.m_progress,m_exp.m_suppress);
            OptimizeElementScene(element_scene);
            
            mapLOD[iLod] = element_scene;
        }
        
		CString lightFileName = "";
        if (lightLOD.GetCount() != 0)
        {
            lightFileName = CString(page->GetName())+"_light.erf";
            err += SaveMapLOD(&lightLOD,LodLevels,lightFileName);
        }

		RootGeo rootGeos[MAX_PATH];
		int numOfRootGeos = 0;
        if (mapLOD.GetCount() != 0)
        {
            CString filename = CString(page->GetName())+".erf";
            err += SaveMapLOD(&mapLOD,LodLevels,filename,log_page);
			rootGeos[0].name=page->GetName();
			rootGeos[0].type="ShapeComponent";
			numOfRootGeos++;
        }
		
		CString megaHint = GetMegaTextureHint(strMegaTextures);
		err += MakeMapLOD(megaHint,LodTotal,LodNames,LodLevels,strHintFile,NULL,".erf",log_page,"keep_");

		GeoExporter exp(NULL);
        TurnOffExceptions();
		if(!m_ip->LoadFromFile(LodNames[0],TRUE))
		{
			CString str = CString("Unable to load LOD file : ")+LodNames[0];
			ReportError(str,m_exp.m_suppress);
			return false;
		}
        TurnOffExceptions();

		INode *pNode = m_ip->GetRootNode();
		exp.GetRootGeos(m_export_directory, rootGeos, &numOfRootGeos, pNode);

        exp.WriteOutGeometry_Video_Data_Files(
			m_export_directory, page->GetName(), "",
			lightFileName, rootGeos, numOfRootGeos, m_bSetUniqueYes);		
    }
	
	Check_Object(list);
	delete list;
	return err;
}

/*******************************************************************************
/* function name: SaveMapLOD
/* description: Save list of LOD shapes into one combined erf file
/*******************************************************************************/
int BatchExp::SaveMapLOD(CMapWordToPtr *pMap,CString *LodLevels, const CString& filename,Page *log_page)
{
    ElementRenderer::Element *pElement = NULL;
    ElementRenderer::ShapeLODElement *pLOD = NULL;
    ElementRenderer::MultiLODElement *pMultiLOD = NULL;
    ElementRenderer::TreeElement *pTreeLOD = NULL;
    
    for (WORD ilod = 0;ilod<pMap->GetCount();ilod++)
    {
        //WORD ilod;
        LPVOID value;
        //mapLOD.GetNextAssoc(pos,ilod,value);
        value = (*pMap)[ilod];
        
        ElementProxies::ElementSceneProxy *element_scene = (ElementProxies::ElementSceneProxy *)value;
        //
        // It is possible for LOD's to exist out of turn, which means that we wouldn't have an entry for all
        // the values in the middle necessarily. Even though we have 4 total, they may be in elements 0, 2, 3, and 5
        //
        if (value)
        {
            Proxies::ChildProxy *first_child = element_scene->UseFirstChildProxy();
            ElementProxies::ElementPolygonMeshProxy *first_mesh =
                Cast_Object(ElementProxies::ElementPolygonMeshProxy*, first_child);
            ElementRenderer::Element *mesh_element = first_mesh->GetProxiedShape();
            
			Stuff::Scalar fadeIn=0.0f,nearDist=0.0f,farDist=0.0f,fadeOut=0.0f;
            int scancount = sscanf(LodLevels[ilod],"%f %f %f %f",
				&nearDist,&fadeIn,&fadeOut,&farDist);
            
			// Cross fade is set
            if (scancount>1)
            {
                // We haven't run through this code yet. This needs to do the same thing as ShapeLOD
                // Do lights too - do we crossfade lights?
                // Do bounds calculations too
				if (0==ilod)
				{
#ifndef TREEELEMENT
					gos_PushCurrentHeap(ElementRenderer::MultiLODElement::s_Heap);
					pMultiLOD = new ElementRenderer::MultiLODElement;
					pMultiLOD->SetSize(pMap->GetCount());
					gos_PopCurrentHeap();
#else
					gos_PushCurrentHeap(ElementRenderer::TreeElement::s_Heap);
					pTreeLOD = new ElementRenderer::TreeElement;
					pTreeLOD->SetSize(pMap->GetCount());

					// Billboarding stuff
					if (m_nBillBoardStartLOD<0 || m_nBillBoardStartLOD>pMap->GetCount())
						m_nBillBoardStartLOD = pMap->GetCount();
					pTreeLOD->m_startAligningAt = m_nBillBoardStartLOD;
					pTreeLOD->m_alignType = m_billBoardAlign<<ElementRenderer::Element::AlignXBit;

					gos_PopCurrentHeap();
#endif
				}
#ifndef TREEELEMENT
                ElementRenderer::MultiLODElement::Entry entry;
				entry.m_fadeInSquared =	fadeIn*fadeIn;
				entry.m_nearSquared = nearDist*nearDist;
                entry.m_farSquared = farDist*farDist;
				entry.m_fadeOutSquared = fadeOut*fadeOut;

				// Billboarding stuff
				if (ilod>=m_nBillBoardStartLOD) {
					if (m_billBoardAlign&AlignX)
						mesh_element->SetAlignX();
					if (m_billBoardAlign&AlignY)
						mesh_element->SetAlignY();
				}

                pMultiLOD->AttachLODFirstAvailableSlot(mesh_element,entry);
				pElement = pMultiLOD;
#else
                ElementRenderer::TreeElement::Entry entry;
				entry.m_fadeInSquared =	fadeIn*fadeIn;
				entry.m_nearSquared = nearDist*nearDist;
                entry.m_farSquared = farDist*farDist;
				entry.m_fadeOutSquared = fadeOut*fadeOut;

                ElementRenderer::ShapeElement *shapeelement = Cast_Object(ElementRenderer::ShapeElement *,mesh_element);
				MidLevelRenderer::MLRShape *mlrShape = shapeelement->GetMLRShape();
				pTreeLOD->AttachLODFirstAvailableSlot(mlrShape, entry);
				pElement = pTreeLOD;
				if (log_page) {
					Check_Object(log_page);
					LogStats(ilod, farDist, mlrShape, filename, log_page);
				}
#endif
            }

			// No cross fade
            else
            {
                if (0==ilod)
                {
                    gos_PushCurrentHeap(ElementRenderer::ShapeLODElement::s_Heap);
                    pLOD = new ElementRenderer::ShapeLODElement;					
                    pLOD->SetSize(pMap->GetCount());
                    gos_PopCurrentHeap();

#ifndef NEWBOUNDINGSPHERE                    
                    Stuff::Sphere sphere;
                    first_mesh->PolygonMeshProxy::GetBoundingSphere(&sphere);
                    
                    pLOD->m_localOBB.localToParent.BuildTranslation(sphere.center);
                    pLOD->m_localOBB.sphereRadius = sphere.radius;
                    pLOD->SetSphereMode();
                    static int o1 = 0;
                    if (o1)
                        pLOD->SetVolumeCullMode();
                    static int o2 = 0;
                    if (o2)
                        pLOD->Sync();
#endif
                }

				nearDist = 0.0f;
				if (ilod)
				{
					sscanf(LodLevels[ilod-1],"%f",&nearDist);
				}
				sscanf(LodLevels[ilod],"%f",&farDist);

				ElementRenderer::LODElement::Entry entry;
                entry.m_nearSquared = nearDist*nearDist;
                entry.m_farSquared = farDist*farDist;

                ElementRenderer::ShapeElement *shapeelement = Cast_Object(ElementRenderer::ShapeElement *,mesh_element);
				MidLevelRenderer::MLRShape *mlrShape = shapeelement->GetMLRShape();
                pLOD->AttachLODFirstAvailableSlot(mlrShape,entry);
                pElement = pLOD;

				if (log_page) {
					Check_Object(log_page);
					LogStats(ilod, farDist, mlrShape, filename, log_page);
				}
            }
            first_mesh->DetachReference();
            //DestroyElementScene(element_scene);
        }
    }

	// Calculating bounding volumes
#ifdef NEWBOUNDINGSPHERE                    
	Stuff::DynamicArrayOf<Stuff::Point3D> points;
	pElement->GetCoordData(&points);

	// If there is billboarding, we have to take into account that the align axis might
	// not be the center of the billboard geometry, so here we rotate the geometry 3 times, 
	// each time by 90 degrees, and gather all the vertices, from which the bounding volume 
	// is computed.  That way, when the billboard geometry is rotated to align with the 
	// camera, the bounding volume is always encompassing all vertices.
	if (m_nBillBoardStartLOD<pMap->GetCount() && m_nBillBoardStartLOD>=0) {
		Stuff::YawPitchRoll	ypr_angle = Stuff::YawPitchRoll::Identity;

		int len = points.GetLength();
		points.SetLength(len*4);
		for (int i=1; i<4; i++) {
			ypr_angle.yaw = (PI/2.0f)*i;
			Stuff::LinearMatrix4D mat(ypr_angle);
			for (int j=0; j<len; j++) {
				Stuff::Point3D temp = points[j];
				points[j+len*i].Multiply(temp, mat);
			}
		}
	}

	if (points.GetLength() > 0) {

		// Compute the bounding sphere
	    Stuff::Sphere sphere;
		sphere.ComputeBounds(points);

		// Compute the bounding OBB
		Stuff::OBB obb;
		obb.ComputeBounds(points);
		
		// If the sphere is not "way" bigger than the OBB
		// then use the sphere, since culling against a sphere is
		// significant faster than culling against a OBB volume.
		if (sphere.GetVolume() <= obb.GetVolume()*10.0) {
			pElement->m_localOBB.localToParent.BuildTranslation(sphere.center);
			pElement->m_localOBB.sphereRadius = sphere.radius * 1.01f;
			pElement->SetSphereMode();
		}
		
		else {
			pElement->m_localOBB = obb;
			pElement->SetOBBMode();
		}

	}
#endif

    SaveElementShape(pElement,m_export_directory+filename);
    if (pElement)
        delete pElement;

    for (ilod = 0;ilod<pMap->GetCount();ilod++)
    {
        LPVOID value;
        value = (*pMap)[ilod];
        
        ElementProxies::ElementSceneProxy *element_scene = (ElementProxies::ElementSceneProxy *)value;
        DestroyElementScene(element_scene);
	}

    return 0;
}

/*******************************************************************************
/* function name: ReversePages
/* description: Reverse the order of the pages in a notation file 
/*******************************************************************************/
static int ReversePages(CString filename)
{
    NotationFile outfile;
    
    {
        NotationFile infile(filename);
        NotationFile::PageIterator *pages = infile.MakePageIterator();
        Page *inpage;
        pages->Last();
        while (inpage = pages->ReadAndPrevious())
        {
            Page *outpage = outfile.AddPage(inpage->GetName());
            
            Page::NoteIterator *notes = inpage->MakeNoteIterator();
            Note *innote;
            while (innote = notes->ReadAndNext())
            {
                CString name = innote->GetName();
                const char *value;
                innote->GetEntry(&value);
                outpage->AppendEntry(name,value);
            }
            delete notes;
        }
        delete pages;
    }
    
    outfile.SaveAs(filename);
    return 0;
}

/*******************************************************************************
/* function name: ExportLODSkel
/* description: Export all LODChar entries into erf files, each joint in the 
/*	skeleton files will generate a separate erf file
/*******************************************************************************/
int BatchExp::ExportLODSkel(Page *page, Page *log_page)
{
    Note *SkelNote = page->FindNote("LODSkel");
    if (SkelNote)
    {
        const char *hint=NULL;
        CString strHintFile;
        if (page->GetEntry("Hint",&hint))
        {
            strHintFile = hint;
        }
        
		const char *megatextures=NULL;
		CString strMegaTextures;
		if (page->GetEntry("MegaTextures",&megatextures))
		{
			strMegaTextures = megatextures;
		}

		// Load billboard stuff
		LoadBillBoardInfo(page);

        // we have at least one LODSkel
        Check_Object(SkelNote);
        CString BaseName(page->GetName());
        const char *SkelName;
        SkelNote->GetEntry(&SkelName);
        
        CString LODDist[MAXLODS];
        CString LodName[MAXLODS];
		CString DamName[MAXLODS];
        int DistTotal = GetEntryList(page, "LODDist", LODDist);
		int LodTotal = GetEntryList(page, "LODChar", LodName);
		int DamTotal = GetEntryList(page, "LODDam", DamName);

        m_bIsChar = (LodTotal>0);
        if(m_bIsChar &&(LodTotal!=DistTotal))
        {
            CString str;
			str.Format("%s: missing geometry entry in batch list", (const char *)BaseName);
            ReportError(str,m_exp.m_suppress);
            return 1;
        }

        const char *cagename = NULL;
        CString strCage;
        if (page->GetEntry("Cage",&cagename))
        {
            Stuff::MString export_name;
            
            export_name = cagename;
            export_name.StripExtension().StripDirectory();
            export_name += ".erf";
            
            strCage = export_name;
            
            export_name = m_export_directory + export_name;
            
            int err = 1;
            TurnOffExceptions();
            if (m_ip->LoadFromFile(cagename,TRUE)) {
	            MAXProxies::MAXScene *max_scene = MakeMaxScene(m_ip,strHintFile);
				err = m_exp.ExportMaxScene(max_scene, m_ip, export_name);
			}
            TurnOnExceptions();
			if (err)
			{
				ReportError(CString(page->GetName())+": Error exporting cage",m_exp.m_suppress);
				return err;
			}
        }

        //Do Exporting here
        
        if (m_bIsChar)
        {
			TurnOffExceptions();
			if(!m_ip->LoadFromFile((LPCSTR)SkelName,TRUE))
			{
				ReportError(CString(BaseName)+": Unable to load Skeleton file "+SkelName);
				return 1;
			}
			TurnOnExceptions();
			
			MAXProxies::MAXScene *max_scene = MakeMaxScene(m_ip,strHintFile);
			
			CMapStringToPtr TransInfo;        
			GetMeshMatrices(max_scene,&TransInfo);
			CString strExtension = ".erf";
			CString hint = GetMegaTextureHint(strMegaTextures);
			int err = MakeMapLOD(hint,LodTotal,LodName,LODDist,strHintFile,&TransInfo,strExtension,log_page);
			if (0 == err)
			{
				strExtension = "_dam.erf";
				hint = GetDamagedMegaTextureHint(strMegaTextures);
				err = MakeMapLOD(hint,DamTotal,DamName,LODDist,strHintFile,&TransInfo,strExtension,log_page);
			}
			
			POSITION pos = TransInfo.GetStartPosition();
			while (pos)
			{
				CString key;
				LPVOID lpVoid;
				TransInfo.GetNextAssoc(pos,key,lpVoid);
				if (lpVoid)
					delete lpVoid;
			}
			
			if (err)
				return err;
			
			TurnOffExceptions();
			if(!m_ip->LoadFromFile((LPCSTR)SkelName,TRUE))
			{
				ReportError(CString(BaseName)+": Unable to load Skeleton file "+SkelName);
				return 1;
			}
			TurnOnExceptions();
			
			max_scene = MakeMaxScene(m_ip,strHintFile);
			INode *pNode = max_scene->GetProxiedScene();
			
			ArmatureExporter armatureExp(strHintFile);
			armatureExp.WriteOutArmatureFile(m_export_directory, BaseName, pNode);
			armatureExp.WriteOutContentFile(m_export_directory, BaseName, pNode);
			armatureExp.WriteOutGeometry_Video_Data_Files(m_export_directory, pNode, strCage, m_bSetUniqueYes);
		}
	}
	return 0;
}

/*******************************************************************************
/* function name: ExportChar
/* description: Export all Char entries in a batch file page into separate erf files
/*******************************************************************************/
int BatchExp::ExportChar(Page *page, Page *log_page)
{
    ChainOf<Note*> *list = page->MakeNoteChain("char");
    Check_Object(list);
    Page::NoteIterator char_itr(list);
    Note *note;
    while ((note= char_itr.ReadAndNext()) != NULL)
    {
        const char *file_name;
        note->GetEntry(&file_name);
        Check_Pointer(file_name);
        
        int total_done = (int)(((float)m_current_export/(float)m_total_exports)*100.0f);
        TurnOffExceptions();
        m_ip->ProgressUpdate(total_done, TRUE, const_cast<char*>(file_name));
        TurnOnExceptions();
        
        Stuff::MString export_name;
        
        export_name = file_name;
        export_name.StripExtension().StripDirectory();
        export_name = m_export_directory + export_name;
        export_name += ".armature";
        
        SPEW(("jerryeds","LOAD CHAR: %s", file_name));
        SPEW(("jerryeds","SAVE CHAR: %s", (const char *)export_name));
        
        int err = 1;
        TurnOffExceptions();
        if(m_ip->LoadFromFile(file_name,TRUE))
            err = m_ip->ExportToFile(export_name,TRUE);
        TurnOnExceptions();
        if (0 == err)
        {
            ReportError(CString(page->GetName())+": Error exporting armature",m_exp.m_suppress);
            delete list;
            return 1;
        }
        ++m_current_export;
    }
    delete list;
    return 0;
}

/*******************************************************************************
/* function name: ExportAnim
/* description: Export the Anim entry in a batch file page
/*******************************************************************************/
int BatchExp::ExportAnim(Page *page, Page *log_page)
{
    ChainOf<Note*> *list = page->MakeNoteChain("anim");
    Check_Object(list);
    Page::NoteIterator anim_itr(list);
    Note *note;
    if ((note = anim_itr.ReadAndNext()) != NULL)
    {
        Stuff::MString new_directory;
        Stuff::MString anim_directory;
        anim_directory = "animation\\";
        new_directory = m_export_directory + anim_directory;
        
        if (!Stuff::CreateDirectories(new_directory))
        {
            //STOP(("Unable to create directory : %s", (const char *)new_directory));
            CString str = CString("Unable to create directory : ")+(const char *)new_directory;
            ReportError(str,m_exp.m_suppress);
            delete list;
            return 1;
        }
    }
    
    anim_itr.First();
    
    while ((note = anim_itr.ReadAndNext()) != NULL)
    {
        Stuff::MString source_directory_name;
        const char *temp;
        note->GetEntry(&temp);
        source_directory_name = temp;
        
        Stuff::MString filter_name;
        filter_name = source_directory_name + "*.max";
        
        
        char *file_name = NULL;
        
        file_name = gos_FindFiles(filter_name);
        
        while (file_name != NULL)
        {
            int total_done = (int)(((float)m_current_export/(float)m_total_exports)*100.0f);
            TurnOffExceptions();
            m_ip->ProgressUpdate(total_done, TRUE, file_name);
            TurnOnExceptions();
            
            Check_Pointer(file_name);
            
            Stuff::MString full_pathed_name;
            full_pathed_name = source_directory_name + file_name;
            
            Stuff::MString export_name;
            export_name = file_name;
            export_name.StripExtension().StripDirectory();
            Stuff::MString new_directory;
            Stuff::MString anim_directory;
            anim_directory = "animation\\";
            new_directory = m_export_directory + anim_directory;
            export_name = new_directory + export_name;
            export_name += ".mw4anim";
            
            SPEW(("jerryeds","LOAD ANIM: %s", (const char *)full_pathed_name));
            SPEW(("jerryeds","SAVE ANIM: %s", (const char *)export_name));
            
            TurnOffExceptions();
            int err = 1;
            if(m_ip->LoadFromFile(full_pathed_name,TRUE))
                err = m_ip->ExportToFile(export_name,TRUE);
            TurnOnExceptions();
            if (0 == err)
            {
                
                gos_FindFilesClose();
                ReportError(CString(page->GetName())+": Error exporting animation",m_exp.m_suppress);
                delete list;
                return 1;
            }
            file_name = gos_FindFilesNext();
            ++m_current_export;
        }
        gos_FindFilesClose();
    }
    delete list;
    return 0;
}

LODNODE::LODNODE()
{
    pNext = NULL;
    pChild = NULL;
}

LODNODE::~LODNODE()
{
    if (pChild)
        delete pChild;
    if (pNext)
        delete pNext;
}

/*******************************************************************************
/* function name: ExportMLRShape
/* description: Export all MLRShapge entries into separate mlr files
/*******************************************************************************/
int BatchExp::ExportMLRShape(Page *page, Page *log_page)
{
    ChainOf<Note*> *list = page->MakeNoteChain("mlrshape");
    Check_Object(list);
    Page::NoteIterator shape_itr(list);
    Note *note;
    
    while ((note = shape_itr.ReadAndNext()) != NULL)
    {
        Check_Object(note);
        const char *file_name;
        note->GetEntry(&file_name);
        
        Check_Pointer(file_name);
        
        int total_done = (int)(((float)m_current_export/(float)m_total_exports)*100.0f);
        TurnOffExceptions();
        m_ip->ProgressUpdate(total_done, TRUE, const_cast<char*>(file_name));
        TurnOnExceptions();
        
        Stuff::MString export_name;
        
        export_name = file_name;
        export_name.StripExtension().StripDirectory();
        export_name = m_export_directory + export_name;
        export_name += ".mlr";
        
        
        SPEW(("jkyle","LOAD SHAPE: %s", file_name));
        SPEW(("jkyle","SAVE SHAPE: %s", (const char *)export_name));
        
        
        int err = 1;
        TurnOffExceptions();
        if(m_ip->LoadFromFile(file_name,TRUE))
        {
            err = m_ip->ExportToFile(export_name,TRUE);
        }
        TurnOnExceptions();
        
        if (0 == err)
        {
            ReportError(CString(page->GetName())+": Error exporting mlr shape",m_exp.m_suppress);
            delete list;
            ++m_current_export;
            return 1;
        }
    }
    
    Check_Object(list);
    delete list;
    
    return 0;
}

int BatchExp::ExportMechInstance(Page *page)
{
    CString mechname = page->GetName();
    CString instancefile = m_export_directory+mechname+".instance";
    if (::gos_DoesFileExist(instancefile) && !m_exp.m_isBRB)
        return 0;
    
	return WriteDataFile(IDR_MECH_INSTANCE, instancefile, mechname);
}

int BatchExp::ExportBuildingData(Page *page)
{
    CString name = page->GetName();
    CString filename = m_export_directory+name+".data";
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
	int err = WriteDataFile(IDR_BUILDING_DATA, filename, name);
	if (err)
		return err;

    NotationFile notefile(filename);
    
    Page *dataPage = notefile.FindPage("GameData");
	Verify(dataPage);
    
    const char *classentry;
    if (page->GetEntry("class", &classentry))
	{
		dataPage->SetEntry("class", classentry);
	}

    // Write out obb info
    WriteOBBInfo(dataPage, name);

	CString DestroyedEffectResourceFile = "Effects\\BuildingExplosions\\"+name+"\\"+name+".data";
    if (::gos_DoesFileExist(::GetGamePath(m_ip)+"content\\"+DestroyedEffectResourceFile))
        dataPage->SetEntry("DestroyedEffectResource",DestroyedEffectResourceFile);
	else 
		dataPage->SetEntry("DestroyedEffectResource","Effects\\BuildingExplosions\\Generic_Building\\Generic_Building.data");

    CString DeathEntityResourceFile = "Buildings\\"+name+"_Destroyed\\"+name+"_Destroyed.data";
    if (::gos_DoesFileExist(::GetGamePath(m_ip)+"content\\"+DeathEntityResourceFile))
        dataPage->SetEntry("DeathEntityResource",DeathEntityResourceFile);

    Note *note = page->FindNote("Anim");
    if (note || m_bIsChar)
    {
		if (::gos_DoesFileExist(::GetGamePath(m_ip)+"Content\\Buildings\\"+name+"\\"+name+".animscript"))
			dataPage->SetEntry("AnimationScript", "Content\\Buildings\\"+name+"\\"+name+".animscript");
    }
    else 
    {
        dataPage = notefile.AddPage("Renderers");
        dataPage->SetEntry("VideoRenderer",name+".video");
    }
    notefile.Save();
    return 0;
}

int BatchExp::ExportBuildingDamage(Page *page)
{
    CString name = page->GetName();
    CString filename = m_export_directory+name+".damage";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
	return WriteDataFile(IDR_BUILDING_DAMAGE, filename, name);
}

int BatchExp::ExportBuildingTorso(Page *page)
{
    CString name = page->GetName();
	if (!::gos_DoesFileExist(m_export_directory+name+".contents"))
		return 0;

	NotationFile noteFile(m_export_directory+name+".contents");
	Page *pitchPage = noteFile.FindPage("Joint_Pitch");
	Page *twistPage = noteFile.FindPage("Joint_Twist");
	Page *pivotPage = noteFile.FindPage("Joint_Pivot");
	Page *turretPage = noteFile.FindPage("Joint_Turret");
	if (!pitchPage && !twistPage && !pivotPage && !turretPage)
		return 0;

    CString filename = m_export_directory+name+".torso";

    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
    int err = WriteDataFile(IDR_VEHICLE_TORSO, filename, name);
	if (err)
		return err;

	NotationFile torsoFile(filename);
	Page *dataPage = torsoFile.FindPage("GameData");
	Verify(dataPage);
	if (pivotPage) {
		dataPage->SetEntry("TwistJointName", "Joint_Pivot");
		dataPage->SetEntry("PitchJointName", "Joint_Pivot");
	}
	if (turretPage) {
		dataPage->SetEntry("TwistJointName", "Joint_Turret");
		dataPage->SetEntry("PitchJointName", "Joint_Turret");
	}
    if (pitchPage) {
		dataPage->SetEntry("TwistJointName", "Joint_Twist");
	}
    if (twistPage) {
		dataPage->SetEntry("PitchJointName", "Joint_Pitch");
	}

	if (dataPage->FindNote("TwistJointName") && dataPage->FindNote("PitchJointName")) {
		dataPage->SetEntry("TotalCritLocations", 2);
	}
	else {
		dataPage->SetEntry("TotalCritLocations", 1);
	}

	torsoFile.Save();

	return err;
}

int BatchExp::ExportBuildingSubsystems(Page *page)
{
	int err = ExportBuildingTorso(page);
	if (err)
		return err;

    CString name = page->GetName();
	if (!::gos_DoesFileExist(m_export_directory+name+".torso"))
		return 0;

    CString filename = m_export_directory+name+".subsystems";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
	return WriteDataFile(IDR_BUILDING_SUBSYSTEMS, filename, name);
}

int BatchExp::ExportBuildingInstance(Page *page)
{
    CString name = page->GetName();
    CString filename = m_export_directory+name+".instance";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
	int err = WriteDataFile(IDR_BUILDING_INSTANCE, filename, name);
	if (err)
		return err;

	NotationFile noteFile(filename);
	Page *dataPage = noteFile.FindPage(name);
	Verify(dataPage);

    if (::gos_DoesFileExist(m_export_directory+name+".contents")) {
		dataPage->SetEntry("Armature", name+".contents");
	}
    
	if (::gos_DoesFileExist(m_export_directory+name+".subsystems")) {
		dataPage->SetEntry("Subsystems", name+".subsystems");
	}
    
	if (::gos_DoesFileExist(m_export_directory+name+".damage")) {
		dataPage->SetEntry("DamageObjects", name+".damage");
	}

	noteFile.Save();

	return err;
}

int BatchExp::ExportMechData(Page *page)
{
    CString name = page->GetName();
    CString filename = m_export_directory+name+".data";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;

	return WriteDataFile(IDR_MECH_DATA, filename, name);
}

int BatchExp::ExportMechSubsystems(Page *page)
{
    CString name = page->GetName();
    CString filename = m_export_directory+name+".subsystems";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
	return WriteDataFile(IDR_MECH_SUBSYSTEMS, filename, name);
}

int BatchExp::ExportMechDamage(Page *page)
{
    CString name = page->GetName();
    CString filename = m_export_directory+name+".damage";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
    return WriteDataFile(IDR_MECH_DAMAGE, filename, name);
}

int BatchExp::ExportVehicleData(Page *page)
{
    CString name = page->GetName();
    CString filename = m_export_directory+name+".data";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
    int err = WriteDataFile(IDR_VEHICLE_DATA, filename, name);
	if (err)
		return err;

	NotationFile noteFile(filename);
    
    Page *dataPage = noteFile.FindPage("GameData");
	Verify(dataPage);
    
    const char *classentry;
    if (page->GetEntry("class", &classentry))
	{
		dataPage->SetEntry("class", classentry);
	}

    // Write out obb info
    WriteOBBInfo(dataPage, name);

	CString DestroyedEffectResourceFile = "Effects\\Vehicle_Destruction\\"+name+"\\"+name+".data";
    if (::gos_DoesFileExist(::GetGamePath(m_ip)+"content\\"+DestroyedEffectResourceFile))
        dataPage->SetEntry("DestroyedEffectResource",DestroyedEffectResourceFile);
	else 
		dataPage->SetEntry("DestroyedEffectResource","Effects\\Vehicle_Destruction\\Vehicle_Destruction.data");

    CString DeathEntityResourceFile = "Vehicles\\"+name+"_Destroyed\\"+name+"_Destroyed.data";
    if (::gos_DoesFileExist(::GetGamePath(m_ip)+"content\\"+DeathEntityResourceFile))
        dataPage->SetEntry("DeathEntityResource",DeathEntityResourceFile);

    if (!m_bIsChar)
    {
        Page* dataPage = noteFile.AddPage("Renderers");
        dataPage->SetEntry("VideoRenderer",name+".video");
    }
    noteFile.Save();

	return err;
}

int BatchExp::ExportVehicleDamage(Page *page)
{
    CString name = page->GetName();
    CString filename = m_export_directory+name+".damage";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
	if (::gos_DoesFileExist(m_export_directory+name+".contents")) {
	
		NotationFile noteFile(m_export_directory+name+".contents");
		Page *jointPage = noteFile.FindPage("Joint_Turret");
		if (jointPage)
			return WriteDataFile(IDR_TURRET_DAMAGE, filename, name);
	}
	
	return WriteDataFile(IDR_VEHICLE_DAMAGE, filename, name);
}

int BatchExp::ExportVehicleTorso(Page *page)
{
    CString name = page->GetName();
	if (!::gos_DoesFileExist(m_export_directory+name+".contents"))
		return 0;

	NotationFile noteFile(m_export_directory+name+".contents");
	Page *jointPage = noteFile.FindPage("Joint_Turret");
	if (!jointPage)
		return 0;

    CString filename = m_export_directory+name+".torso";

    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
    return WriteDataFile(IDR_VEHICLE_TORSO, filename, name);
}

int BatchExp::ExportVehicleSubsystems(Page *page)
{
	int err = ExportVehicleTorso(page);
	if (err)
		return err;

    CString name = page->GetName();
	if (!::gos_DoesFileExist(m_export_directory+name+".torso"))
		return 0;

    CString filename = m_export_directory+name+".subsystems";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
	return WriteDataFile(IDR_VEHICLE_SUBSYSTEMS, filename, name);
}

int BatchExp::ExportVehicleInstance(Page *page)
{
    CString name = page->GetName();
    CString filename = m_export_directory+name+".instance";
    
    if (::gos_DoesFileExist(filename) && !m_exp.m_isBRB)
        return 0;
    
	int err = WriteDataFile(IDR_VEHICLE_INSTANCE, filename, name);
	if (err)
		return err;

	NotationFile noteFile(filename);
	Page *dataPage = noteFile.FindPage(name);
	Verify(dataPage);

    if (::gos_DoesFileExist(m_export_directory+name+".subsystems")) {
		dataPage->SetEntry("Subsystems", name+".subsystems");
	}

    if (::gos_DoesFileExist(m_export_directory+name+".contents")) {
		dataPage->SetEntry("Armature", name+".contents");
	}

    if (::gos_DoesFileExist(m_export_directory+name+".damage")) {
		dataPage->SetEntry("DamageObjects", name+".damage");
	}

	noteFile.Save();
	return err;
}

/*******************************************************************************
/* function name: TranslateVertices
/* description: 
/*******************************************************************************/
static int TranslateVertices(Stuff::LinearMatrix4D NewMeshToWorld,ElementProxies::ElementSceneProxy *element_scene,Stuff::LinearMatrix4D *pObjectToWorld)
{
    Check_Object(element_scene);
    Check_Object(pObjectToWorld);
    
    Proxies::ChildProxy *first_child = element_scene->UseFirstChildProxy();
    ElementProxies::ElementPolygonMeshProxy *element_mesh =
        Cast_Object(ElementProxies::ElementPolygonMeshProxy*, first_child);
    
    Stuff::LinearMatrix4D WorldToOldMesh;
    WorldToOldMesh.Invert( *pObjectToWorld);
    Stuff::LinearMatrix4D NewMeshToOldMesh;
    NewMeshToOldMesh.Multiply(NewMeshToWorld, WorldToOldMesh);
    
    
    DynamicArrayOf< Proxies::VertexProxy*> vertices;
    unsigned vertex_count = element_mesh->UseVertexArray(&vertices);
    for (unsigned vi=0; vi<vertex_count; ++vi)
    {
        Proxies::VertexProxy *vertex = vertices[vi];
        Check_Object(vertex);
        Stuff::Point3D old_position;
        vertex->GetPosition(&old_position);
        
        Stuff::Point3D new_position;
        new_position.Multiply(old_position,NewMeshToOldMesh);
        vertex->SetPosition(new_position);
        Stuff::Normal3D old_normal;
        if(vertex->GetNormal(&old_normal))
        {
            Stuff::Normal3D new_normal;
            new_normal.Multiply(old_normal, NewMeshToOldMesh);
            vertex->SetNormal(new_normal);
        }
    }
    element_mesh->DetachArrayReferences(&vertices);
    element_mesh->DetachReference();
    return 0;
}

/*******************************************************************************
/* function name: WriteOutLODMesh
/* description: Write out a max polygon mesh into a element proxy that's ready
/*	to be saved out to an erf file
/*******************************************************************************/
int BatchExp::WriteOutLODMesh(CString hint,Proxies::GenericProxy *proxy, Interface *ip,CMapStringToOb *pMapNames,int iLod,CMapStringToPtr *pTransInfo)
{
    Stuff::MString mstr;
    proxy->GetName(&mstr);
    mstr.RemoveSpaces();
    mstr.ToLower();
    
    CString name(mstr);
	Stuff::LinearMatrix4D *pObjectToWorld = NULL;

    if (pTransInfo) {
		pObjectToWorld = (Stuff::LinearMatrix4D *)(*pTransInfo)[name];
		if (NULL == pObjectToWorld)
		{
			CString str;
			str.Format("%s: found in %s and not in skeleton",name,ip->GetCurFileName());
			ReportError(str, m_exp.m_suppress);
			return 1;
		}
		Check_Object(pObjectToWorld);
	}
    
    ElementProxies::ElementSceneProxy *element_scene = InitElementScene(m_exp.m_texturePath);    
    MAXProxies::MAXScene *max_scene;
    
	Stuff::LinearMatrix4D NewMeshToWorld;
    if(proxy->IsDerivedFrom(Proxies::PolygonMeshProxy::DefaultData)) {
	    MAXProxies::MAXPolygonMesh *max_mesh = Cast_Object(MAXProxies::MAXPolygonMesh *,proxy);    
		max_scene = max_mesh->GetSceneProxy();
		CopyPolygonMeshProxyToElement(element_scene,max_mesh,m_bFlatShading,ip,m_exp.m_progress,m_exp.m_suppress);
		max_mesh->GetLocalToWorld(&NewMeshToWorld);
	}

    else if (proxy->IsDerivedFrom(Proxies::GroupProxy::DefaultData)) {
	    MAXProxies::MAXGroup *max_group = Cast_Object(MAXProxies::MAXGroup *,proxy);    
		max_scene = max_group->GetSceneProxy();
		CopyGroupProxyToElement(element_scene,max_group,m_bFlatShading,ip,m_exp.m_progress,m_exp.m_suppress);
		max_group->GetLocalToWorld(&NewMeshToWorld);
	}


	if (hint.GetLength())
		CoalesceMegaTexture(max_scene,element_scene,hint);

    FuseElementScene(element_scene,ip,m_exp.m_progress,m_exp.m_suppress);
	if (pObjectToWorld) {
		TranslateVertices(NewMeshToWorld,element_scene,pObjectToWorld);
	}

    OptimizeElementScene(element_scene);

	// Set some special modes for lights
	if (name.Find("light") >= 0)
	{
		SetExplicitAlphaMode(element_scene, MidLevelRenderer::MLRState::AlphaOneMode);
		SetExplicitLightingMode(element_scene, MidLevelRenderer::MLRState::LightingOffMode);
		SetExplicitFogMode(element_scene, StateProxy::LightFogMode);
	}
    
    CMapWordToPtr *pMapLod = NULL;
    if (0==iLod)
    {
        pMapLod = new CMapWordToPtr;
        (*pMapNames)[name] = pMapLod;
    }
    else
    {
        CString key;
        CObject *Value;
        if (pMapNames->Lookup(name,Value))
        {
            pMapLod = (CMapWordToPtr *)Value;
        }
        else
        {
			CString str;
			str.Format("%s: found in %s and not in skeleton",name,ip->GetCurFileName());
			ReportError(str, m_exp.m_suppress);
			return 1;
        }
    }
    int iTemp = (*pMapLod).GetCount(); // we always want to add at the end
    if (iTemp <= iLod)
    {
        (*pMapLod)[iTemp] = element_scene;
    }
#ifdef LAB_ONLY
    else
    {
        MString element_name;
        element_name = "";
        element_scene->GetName(&element_name);
        char szTemp[256];
        sprintf(szTemp, "'%s':'%s': (iTemp <= iLod): Most likely caused by two objects with the same name in a LOD model. Second one being ignored.", (LPCSTR) hint, (LPCSTR)element_name);
        ReportError((szTemp),m_exp.m_suppress);
        
    }
#endif
    return 0;
}

/*******************************************************************************
/* function name: MakeAllLODMeshs
/* description: Break a max file into separate polygon meshes
/*******************************************************************************/
int BatchExp::MakeAllLODMeshs(CString hint,Proxies::GenericProxy *proxy,Interface *ip,CMapStringToOb *pMapNames,int iLod,CMapStringToPtr *pTransInfo,CString strContains)
{
    if(proxy->IsDerivedFrom(Proxies::SceneProxy::DefaultData))
    {
        Proxies::SceneProxy *scene = Cast_Object(Proxies::SceneProxy *,proxy);
        Proxies::ChildProxy *child = scene->UseFirstChildProxy();
        while (child)
        {
            Check_Object(child);
            Proxies::ChildProxy *next = child->UseNextSiblingProxy();
            int err = MakeAllLODMeshs(hint,child,ip,pMapNames,iLod,pTransInfo,strContains);
            child->DetachReference();
            if (err)
                return err;
            child = next;
        }
        
    }
    else
        if(proxy->IsDerivedFrom(Proxies::GroupProxy::DefaultData))
        {
			Stuff::MString mstr;
			proxy->GetName(&mstr);
			mstr.RemoveSpaces();
			mstr.ToLower();
			strContains.MakeLower();

			if (CString(mstr).Find("cv_")>=0 || CString(mstr).Find("joint_")>=0) {
				Proxies::GroupProxy *group = Cast_Object(Proxies::GroupProxy *,proxy);
				Proxies::ChildProxy *child = group->UseFirstChildProxy();
				while (child)
				{
					Check_Object(child);
					Proxies::ChildProxy *next = child->UseNextSiblingProxy();
					int err = MakeAllLODMeshs(hint,child,ip,pMapNames,iLod,pTransInfo,strContains);
					child->DetachReference();
					if (err)
						return err;
					child = next;
				}
				return 0;
			}

			else if (strContains =="" || CString(mstr).Find(strContains)>=0) {
				int err = WriteOutLODMesh(hint,proxy,ip,pMapNames,iLod,pTransInfo);
				if (err)
					return err;
			}

            Proxies::GroupProxy *group = Cast_Object(Proxies::GroupProxy *,proxy);
            Proxies::ChildProxy *child = group->UseFirstChildProxy();
            while (child)
            {
                Check_Object(child);
                Proxies::ChildProxy *next = child->UseNextSiblingProxy();

				Stuff::MString mstr;
				child->GetName(&mstr);
				CString name(mstr);
				name.MakeLower();

				// Save lights and keep node separately
				if ((name.Find("light")>=0 || name.Find("keep_")>=0) && 
					child->IsDerivedFrom(Proxies::PolygonMeshProxy::DefaultData)) {
					
					int err = WriteOutLODMesh(hint,child,ip,pMapNames,iLod,pTransInfo);
					if (err)
						return err;
				}
				child->DetachReference();
				child = next;
            }
        }
        else
            if(proxy->IsDerivedFrom(Proxies::PolygonMeshProxy::DefaultData))
            {
				Stuff::MString mstr;
				proxy->GetName(&mstr);
				mstr.RemoveSpaces();
				mstr.ToLower();
				strContains.MakeLower();
				if (strContains =="" || CString(mstr).Find(strContains)>=0) {
					int err = WriteOutLODMesh(hint,proxy,ip,pMapNames,iLod,pTransInfo);
					if (err)
						return err;
				}
            }
            return 0;
}

/*******************************************************************************
/* function name: MakeMapLOD
/* description: Make a list of LOD max files into a table of polygon meshes and save
/*	them out to separate erf files
/*******************************************************************************/
int BatchExp::MakeMapLOD(CString hint,int totalLODs,CString LodNames[MAXLODLEVELS],CString levels[MAXLODLEVELS],CString strHintFile,CMapStringToPtr *pTransInfo,CString strExtension,Page* log_page,CString strContains)
{
	// return if nothing to export
	if (totalLODs == 0)
		return 0;

    CMapStringToOb mapNames; // This maps the name of mesh to a mapLOD CMapWordToPtr object
    
	// break down the LOD max files into a table of polygons meshes
    int err = 0;
    for (int CurLod=0;CurLod<totalLODs;CurLod++)
    {
		
        TurnOffExceptions();
        if(!m_ip->LoadFromFile((LPCSTR)LodNames[CurLod],TRUE))
        {
            CString str = CString("Unable to load LOD file : ")+LodNames[CurLod];
            ReportError(str,m_exp.m_suppress);
            err = 1;
        }
        TurnOnExceptions();

        if (0==err)
        {
            MAXProxies::MAXScene *max_scene = MakeMaxScene(m_ip,strHintFile);
            err = MakeAllLODMeshs(hint,max_scene,m_ip,&mapNames,CurLod,pTransInfo,strContains);
        }
    }
	
	// save the meshes out, each joint in the skeleton file gets saved out to
	// a separate erf file, which contains a list of meshes from all the LODs
    POSITION pos = mapNames.GetStartPosition();
    while (pos)
    {
        CObject *pObj;
        CString name;
        mapNames.GetNextAssoc(pos,name,pObj);
        name.MakeLower();
        
        CMapWordToPtr *pMapLOD = (CMapWordToPtr *)pObj;
        
        CString filename = name+strExtension;
        SaveMapLOD(pMapLOD,levels,filename,log_page);
        delete pMapLOD;
    }

	if (hint.GetLength()) {
		// Recycle the texture library
		//
		if (ElementProxies::MLRTexturePoolProxy::Instance)
		{
			ElementProxies::MLRTexturePoolProxy::Instance->CloseLibrary(true);
            ElementProxies::MLRTexturePoolProxy::Instance->SetTexturePath(m_exp.m_texturePath);
		}
		MakeMegaTexture(strHintFile, hint);
	}  			

    return err;
}

/*******************************************************************************
/* function name: MakeLightLODAndRemoveFromScene
/* description: Copy lights into a polygon mesh list, and delete from the scene
/*******************************************************************************/
bool BatchExp::MakeLightLODAndRemoveFromScene(CString file_name, CString strHintFile, CMapWordToPtr* lightLOD, int iLod)
{
	
	// First rip the lights out to export the lights
	TurnOffExceptions();

	if(!m_ip->LoadFromFile(file_name,TRUE))
	{
		CString str = CString("Unable to load LOD file : ")+file_name;
		ReportError(str,m_exp.m_suppress);
		return false;
	}

	bool bHasLights = false;
	INode *pNode = m_ip->GetRootNode();
	for (int x=pNode->NumberOfChildren()-1;x>=0;x--)
	{
		INode *pChild = pNode->GetChildNode(x);
		CString name = pChild->GetName();
		name.MakeLower();
		if (IsNodeGeometry(pChild) && name.Find("light")>=0)
		{
			// this is the bldg lights node
			int x=1;
			bHasLights = true;
		}
		else
		{
			m_ip->DeleteNode(pChild);
		}
	}
	TurnOnExceptions();
	
	if (bHasLights)
	{
		ElementProxies::ElementSceneProxy *element_scene = InitElementScene(m_exp.m_texturePath);
		MAXProxies::MAXScene *max_scene = MakeMaxScene(m_ip,strHintFile);
		CopySceneMaxToElement(element_scene,max_scene,true,m_ip,m_exp.m_progress,m_exp.m_suppress);
		FuseElementScene(element_scene,m_ip,m_exp.m_progress,m_exp.m_suppress);
		OptimizeElementScene(element_scene);

		// Set some special modes for lights, 
		// so that they don't pick up fog, and are self-illuminated	
		SetExplicitAlphaMode(element_scene, MidLevelRenderer::MLRState::AlphaOneMode);
		SetExplicitLightingMode(element_scene, MidLevelRenderer::MLRState::LightingOffMode);
		SetExplicitFogMode(element_scene, StateProxy::LightFogMode);

		
		(*lightLOD)[iLod] = element_scene;
	}

	TurnOffExceptions();	
	if(!m_ip->LoadFromFile(file_name,TRUE))
	{
		CString str = CString("Unable to load LOD file : ")+file_name;
		ReportError(str,m_exp.m_suppress);
		return false;
	}

	// Now delete the lights and KEEP_ nodes from the model
	// because they will have to be exported into separate erfs
	pNode = m_ip->GetRootNode();
	for (x=pNode->NumberOfChildren()-1;x>=0;x--)
	{
		INode *pChild = pNode->GetChildNode(x);
		CString name = pChild->GetName();
		name.MakeLower();
		if (name.Find("light") >= 0 ||
			name.Find("keep_") >= 0)
		{
			m_ip->DeleteNode(pChild);
		}
	}
	
	TurnOnExceptions();	
	return true;
}

/*******************************************************************************
/* function name: WriteOBBInfo
/* description: Write out OBB related entries in the .data file
/*******************************************************************************/
void BatchExp::WriteOBBInfo(Page* newpage, CString name)
{
    CString solidOBBfileName = m_export_directory+name+"_SOLID.obb";
	CString	StationaryOBBfileName = m_export_directory+name+"_STATIONARY.obb";
    CString obbfileName = m_export_directory+name+".obb";

	// Is there an object_SOLID.obb file?
    if (::gos_DoesFileExist(solidOBBfileName))
    {
        newpage->SetEntry("SolidOBB",name+"_SOLID.OBB");
		newpage->SetEntry("Collider","true");
		newpage->SetEntry("CanBeShot", "true");
		newpage->SetEntry("CanBeWalkedOn", "true");
    }

	// Is there an object_STATIONARY.obb file?
	else if (::gos_DoesFileExist(StationaryOBBfileName)) {
		newpage->SetEntry("SolidOBB",name+"_STATIONARY.obb");
		newpage->SetEntry("Collider","false");
		newpage->SetEntry("CanBeShot", "true");
		newpage->SetEntry("CanBeWalkedOn", "true");
    }

	else {
		newpage->SetEntry("Collider","false");
		newpage->SetEntry("CanBeShot", "false");
		newpage->SetEntry("CanBeWalkedOn", "false");
    }

	// Is there an object.obb file?
    if (::gos_DoesFileExist(obbfileName))
		newpage->SetEntry("HierarchicalOBB",name+".obb");
}

/*******************************************************************************
/* function name: CoalesceMegaTexture
/* description: coalesce a mega texture (512x512) from a list of smaller (256x256) textures
/*******************************************************************************/
int BatchExp::CoalesceMegaTexture(MAXProxies::MAXScene *max_scene, ElementProxies::ElementSceneProxy *element_scene, CString hint)
{
    Check_Object(element_scene);
    Check_Object(max_scene);
	Verify(hint.GetLength()>0);
        
    Stuff::NotationFile hintfile(max_scene->strHintFile);
    //
    //-----------------------------
    // Now run the coalesce process
    //-----------------------------
    //
	if (max_scene->strHintFile.GetLength())
	{
		Proxies::CoalesceTexturesProcess coalesce(&hintfile, hint, m_exp.m_suppress, (LPVOID)&ReportError);
		element_scene->CoalesceTextures(&coalesce);
	}
    return 0;
}

/*******************************************************************************
/* function name: MakeMegaTexture
/* description: Make a mega texture (512x512) from a list of smaller (256x256) textures
/*******************************************************************************/
int BatchExp::MakeMegaTexture(CString strHintFile, CString hint)
{
	Verify(hint.GetLength()>0);

	ElementProxies::ElementSceneProxy *element_scene = InitElementScene(m_exp.m_texturePath);
	ElementProxies::MLRStatePoolProxy *state_library = Cast_Object(ElementProxies::MLRStatePoolProxy *,element_scene->GetStateLibrary());
	Check_Object(state_library);

	Proxies::TextureLibrary *texture_library = state_library->GetTextureLibrary();
	Check_Object(texture_library);

	// do the arranging
    Stuff::NotationFile hintfile(strHintFile);
	Stuff::Page *mega_page = hintfile.FindPage(hint);
	Stuff::NotationFile mega_file;
	Stuff::Page *new_page = mega_file.AddPage(hint);
	Page::NoteIterator *noteIt = mega_page->MakeNoteIterator();
	Note *note;
	
	// ArrangeMegatexturesProcess wants the entries in a slightly different format
	// so rearrange the entries here
	while (note = noteIt->ReadAndNext())
	{
		const char *entry;
		note->GetEntry(&entry);
		const char *name = note->GetName();
		int x, y;
		int param_count = sscanf(entry, "%d %d", &x, &y);
		if (param_count==2 && stricmp(name, "PageSize")) {
			CString entryString;
			entryString.Format("%s %s", name,entry);
			new_page->AppendEntry("Texture",entryString);
		}
		else
			new_page->AppendEntry(name,entry);
	}
	delete noteIt;
	
	Proxies::ArrangeMegatexturesProcess arrange_process(&mega_file);
	texture_library->ArrangeMegatextures(&arrange_process);
	
	Proxies::BuildMegatexturesProcess build_process(&mega_file);
	texture_library->BuildMegatextures(&build_process);
	
	// Now delete the intermediate files
	ChainOf<Note*> *textures = new_page->MakeNoteChain("Texture");
	Register_Object(textures);
	Page::NoteIterator texture_itr(textures);
	int texture_count = -1;
	Note *entry;
	while ((entry = texture_itr.ReadAndNext()) != NULL)
	{
		Check_Object(entry);
		++texture_count;
		Verify(texture_count >= 0);
		
		//
		//--------------------------------------------
		// Extract the texture data from the tron file
		//--------------------------------------------
		//
		const char* data;
		entry->GetEntry(&data);
		Check_Pointer(data);
		char texture_name[80];
		int param_count = sscanf(data, "%s", texture_name);
		if (param_count == 1 && strlen(texture_name))
		{
			CString path = m_exp.m_texturePath+texture_name;					
			CString fullname = path+".png";
			if (gos_DoesFileExist(fullname))
			{
				gos_DeleteFile(fullname);
			}
			else
			{
				fullname = path+".tga";
				if (gos_DoesFileExist(fullname))
				{
					gos_DeleteFile(fullname);
				}
			}
		}					
	}
	mega_file.IgnoreChanges();
	return 0;
}

const CString BatchExp::GetMegaTextureHint(const CString& pagename)
{
	int pos = pagename.FindOneOf(",");
	if (pos >= 0)
		return pagename.Left(pos);
	else 
		return pagename;
}

const CString BatchExp::GetDamagedMegaTextureHint(const CString& pagename)
{
	int pos = pagename.FindOneOf(",");
	if (pos >= 0)
		return pagename.Right(pagename.GetLength()-pos-1);
	else 
		return GetMegaTextureHint(pagename);
}

int BatchExp::WriteDataFile(int resourceID, CString filename, CString strReplace)
{
 
    HRSRC hResInfo = FindResource(hInstance, MAKEINTRESOURCE(resourceID), "DATAFILE");
    if (!hResInfo)
    {
        // Did not find the resource
        int x=1;
    }
    HGLOBAL hResource = LoadResource(hInstance, hResInfo);
    if (!hResource)
    {
        // Did not load the resource
        int x=1;
    }
    
    LPVOID lpData = LockResource(hResource);
    DWORD dwSize;
    if (!lpData || !(dwSize = SizeofResource(hInstance, hResInfo)))
    {
        // Did not lock resource
        int x=1;
    }
    else
    {
        // Copy the resource
        DWORD datasize = dwSize+1;
        char *data = new char[datasize];
        memcpy(data,lpData,datasize);
        data[datasize] = '\0';
        CString str = data;
        str.Replace("%1%",strReplace);
        //CNodeDisk *pDisk = pDoc->GetRootNode()->DiskCopyFile(lpData,dwSize,output);
        HGOSFILE hfile;
        ::gos_OpenFile(&hfile,filename,READWRITE);
        ::gos_WriteFile(hfile,str.GetBuffer(str.GetLength()),str.GetLength());
        ::gos_CloseFile(hfile);
        
        UnlockResource(hResource);
    }
    FreeResource(hResource);
    return 0;
}

void BatchExp::LoadBillBoardInfo(Page* page)
{
	// Billboarding stuff
	m_nBillBoardStartLOD = 999;
	m_billBoardAlign = AlignY;
	const char *billBoardFrom = NULL;
	if (page->GetEntry("BillboardFrom",&billBoardFrom))
	{
		// Get LOD number to start billboarding
		m_nBillBoardStartLOD = billBoardFrom[::strlen(billBoardFrom)-1]-'0';
		
		// Get Align axis for billboarding
		const char *billBoardAlign = NULL;
		if (page->GetEntry("BillboardAlign",&billBoardAlign)) {

			CString strAlign = billBoardAlign;
			strAlign.MakeLower();
			if (strAlign.Find("x")>=0 && strAlign.Find("y")>=0)
				m_billBoardAlign = (ALIGNFLAG)(AlignX|AlignY);
			else if (strAlign.Find("x")>=0)
				m_billBoardAlign = AlignX;
			else if (strAlign.Find("y")>=0)
				m_billBoardAlign = AlignY;
		}
	}	
}

int BatchExp::GetEntryList(Page* page, const CString& entryName, CString entries[])
{
	ChainOf<Note*> *list = page->MakeNoteChain(entryName);
	Check_Object(list);
	Page::NoteIterator itr(list);
	Note *note;
	
	int total = 0;
	while ((note = itr.ReadAndNext()) != NULL)
	{
		int level;
		CString LodEntry(note->GetName());
		level=atoi((LPCSTR)LodEntry.Right(LodEntry.GetLength()-strlen(entryName)));
		Verify(level<MAXLODS);
		const char *entry;
		note->GetEntry(&entry);
		entries[level] = entry;
		total++;
	}
	Check_Object(list);
	delete list;

	return total;
}

int BatchExp::GetEntryList(Page* page, const CString& entryName, int entries[])
{
	ChainOf<Note*> *list = page->MakeNoteChain(entryName);
	Check_Object(list);
	Page::NoteIterator itr(list);
	Note *note;
	
	int total = 0;
	while ((note = itr.ReadAndNext()) != NULL)
	{
		int level;
		CString LodEntry(note->GetName());
		level=atoi((LPCSTR)LodEntry.Right(LodEntry.GetLength()-strlen(entryName)));
		Verify(level<MAXLODS);
		int entry;
		note->GetEntry(&entry);
		entries[level] = entry;
		total++;
	}
	Check_Object(list);
	delete list;

	return total;
}

bool BatchExp::LoadPlugCfg(Interface *i)
{
	if (!m_exp.LoadPlugCfg(i))
		return false;

	m_bLogStates = false;
	CString ini_file = m_ip->GetDir(APP_PLUGCFG_DIR);
	ini_file += "\\Plugins.ini";
	NotationFile inifile(ini_file);
	
	Page* page = inifile.FindPage("ExportData");
	if (page)
	{
		page->GetEntry("LogStates", &m_bLogStates);
	}

	return true;
}
