/*==LICENSE==*
CyanWorlds.com Engine - MMOG client, server and tools
Copyright (C) 2011 Cyan Worlds, 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 .
You can contact Cyan Worlds, Inc. by email legal@cyan.com
or by snail mail at:
Cyan Worlds, Inc.
14617 N Newport Hwy
Mead, WA 99021
*==LICENSE==*/
#include "HeadSpin.h"
#include "hsTypes.h"
#include "plExportDlg.h"
#include "MaxMain/resource.h"
#include "max.h"
#include "MaxMain/plMaxCFGFile.h"
#include
#include
using std::vector;
using std::string;
extern HINSTANCE hInstance;
class plExportDlgImp : public plExportDlg
{
protected:
HWND fDlg; // Handle to the setup dialog
bool fPreshade;
bool fPhysicalsOnly;
bool fLightMap;
char fExportPage[256];
char fExportSourceDir[MAX_PATH];
bool fExporting;
bool fAutoExporting;
bool fExportFile;
int fXPos, fYPos;
DWORD fLastExportTime;
static BOOL CALLBACK ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
BOOL DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam);
void IDestroy();
void IExportCurrentFile(const char* exportPath);
void IDoExport();
void IInitDlg(HWND hDlg);
void IGetRadio(HWND hDlg);
public:
plExportDlgImp();
~plExportDlgImp();
virtual void Show();
virtual bool IsExporting() { return fExporting; }
virtual bool IsAutoExporting() { return fAutoExporting; }
virtual bool GetDoPreshade() { return fPreshade; }
virtual bool GetPhysicalsOnly() { return fPhysicalsOnly; }
virtual bool GetDoLightMap() { return fLightMap; }
virtual const char* GetExportPage();
virtual void StartAutoExport();
};
plExportDlgImp::plExportDlgImp() : fDlg(NULL), fPreshade(true), fPhysicalsOnly(false), fLightMap(true), fLastExportTime(0), fExporting(false), fAutoExporting(false)
{
const char* path = plMaxConfig::GetPluginIni();
fXPos = GetPrivateProfileInt("Export", "X", 0, path);
fYPos = GetPrivateProfileInt("Export", "Y", 30, path);
GetPrivateProfileString("Export", "Dir", "", fExportSourceDir, sizeof(fExportSourceDir), path);
memset(fExportPage, 0, sizeof(fExportPage));
}
BOOL WritePrivateProfileInt(LPCSTR lpAppName, LPCSTR lpKeyName, int val, LPCSTR lpFileName)
{
char buf[30];
itoa(val, buf, 10);
return WritePrivateProfileString(lpAppName, lpKeyName, buf, lpFileName);
}
plExportDlgImp::~plExportDlgImp()
{
const char* path = plMaxConfig::GetPluginIni();
WritePrivateProfileInt("Export", "X", fXPos, path);
WritePrivateProfileInt("Export", "Y", fYPos, path);
WritePrivateProfileString("Export", "Dir", fExportSourceDir, path);
}
plExportDlg& plExportDlg::Instance()
{
static plExportDlgImp theInstance;
return theInstance;
}
BOOL plExportDlgImp::ForwardDlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
return ((plExportDlgImp&)Instance()).DlgProc(hDlg, msg, wParam, lParam);
}
const char* plExportDlgImp::GetExportPage()
{
if (fExportPage[0] == '\0')
return nil;
else
return fExportPage;
}
#include "MaxComponent/plComponentBase.h"
#include "MaxComponent/plMiscComponents.h"
#include "MaxMain/plMaxNode.h"
#include "hsStlSortUtils.h"
#include
typedef std::set CompSet;
static void GetPagesRecur(plMaxNode* node, CompSet& comps)
{
if (!node)
return;
plComponentBase* comp = node->ConvertToComponent();
if (comp && (comp->ClassID() == ROOM_CID || comp->ClassID() == PAGEINFO_CID))
{
comps.insert(comp);
}
for (int i = 0; i < node->NumberOfChildren(); i++)
GetPagesRecur((plMaxNode*)node->GetChildNode(i), comps);
}
static const char* kAllPages = "(All Pages)";
void plExportDlgImp::IGetRadio(HWND hDlg)
{
fExportFile = (IsDlgButtonChecked(hDlg, IDC_RADIO_FILE) == BST_CHECKED);
EnableWindow(GetDlgItem(hDlg, IDC_PAGE_COMBO), fExportFile);
// EnableWindow(GetDlgItem(hDlg, IDC_EXPORT_PATH), !fExportFile);
EnableWindow(GetDlgItem(hDlg, IDC_BROWSE_EXPORT), !fExportFile);
}
void plExportDlgImp::IInitDlg(HWND hDlg)
{
// Set the client path
const char* path = plMaxConfig::GetClientPath(false, true);
SetDlgItemText(hDlg, IDC_CLIENT_PATH, path);
// Set the preshade button
CheckDlgButton(hDlg, IDC_PRESHADE_CHECK, fPreshade ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hDlg, IDC_PHYSICAL_CHECK, fPhysicalsOnly ? BST_CHECKED : BST_UNCHECKED);
CheckDlgButton(hDlg, IDC_LIGHTMAP_CHECK, fLightMap ? BST_CHECKED : BST_UNCHECKED);
char buf[256];
sprintf(buf, "Last export took %d:%02d", fLastExportTime/60, fLastExportTime%60);
SetDlgItemText(hDlg, IDC_LAST_EXPORT, buf);
SetWindowPos(hDlg, NULL, fXPos, fYPos, 0, 0, SWP_NOSIZE | SWP_NOZORDER);
//
// Get the names of all the pages in this scene and put them in the combo
//
HWND hPages = GetDlgItem(hDlg, IDC_PAGE_COMBO);
ComboBox_AddString(hPages, kAllPages);
bool foundPage = false;
CompSet comps;
GetPagesRecur((plMaxNode*)GetCOREInterface()->GetRootNode(), comps);
for (CompSet::iterator it = comps.begin(); it != comps.end(); it++)
{
const char* page = LocCompGetPage(*it);
if (page)
{
int idx = ComboBox_AddString(hPages, page);
if (!strcmp(page, fExportPage))
{
foundPage = true;
ComboBox_SetCurSel(hPages, idx);
}
}
}
if (!foundPage)
{
fExportPage[0] = '\0';
ComboBox_SetCurSel(hPages, 0);
}
CheckRadioButton(hDlg, IDC_RADIO_FILE, IDC_RADIO_DIR, IDC_RADIO_FILE);
IGetRadio(hDlg);
SetDlgItemText(hDlg, IDC_EXPORT_PATH, fExportSourceDir);
}
#include "plFile/plBrowseFolder.h"
BOOL plExportDlgImp::DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam)
{
switch (msg)
{
case WM_INITDIALOG:
IInitDlg(hDlg);
return TRUE;
case WM_COMMAND:
{
int cmd = HIWORD(wParam);
int resID = LOWORD(wParam);
if (cmd == BN_CLICKED)
{
if (resID == IDCANCEL)
{
IDestroy();
return TRUE;
}
else if (resID == IDC_EXPORT)
{
IDoExport();
return TRUE;
}
else if (resID == IDC_PRESHADE_CHECK)
{
fPreshade = (IsDlgButtonChecked(hDlg, IDC_PRESHADE_CHECK) == BST_CHECKED);
return TRUE;
}
else if (resID == IDC_PHYSICAL_CHECK)
{
fPhysicalsOnly = (IsDlgButtonChecked(hDlg, IDC_PHYSICAL_CHECK) == BST_CHECKED);
return TRUE;
}
else if (resID == IDC_LIGHTMAP_CHECK)
{
fLightMap = (IsDlgButtonChecked(hDlg, IDC_LIGHTMAP_CHECK) == BST_CHECKED);
return TRUE;
}
else if (resID == IDC_DIR)
{
// Get a new client path
const char* path = plMaxConfig::GetClientPath(true);
if (path)
SetDlgItemText(hDlg, IDC_CLIENT_PATH, path);
return TRUE;
}
else if (resID == IDC_RADIO_FILE || resID == IDC_RADIO_DIR)
{
IGetRadio(hDlg);
return TRUE;
}
else if (resID == IDC_BROWSE_EXPORT)
{
plBrowseFolder::GetFolder(fExportSourceDir,
fExportSourceDir,
"Choose the source directory",
hDlg);
SetDlgItemText(hDlg, IDC_EXPORT_PATH, fExportSourceDir);
return TRUE;
}
}
else if (cmd == CBN_SELCHANGE && resID == IDC_PAGE_COMBO)
{
int sel = ComboBox_GetCurSel((HWND)lParam);
// If the user selected a page, save it
if (sel != 0 && sel != CB_ERR)
ComboBox_GetText((HWND)lParam, fExportPage, sizeof(fExportPage));
// Else, clear it (export all pages)
else
fExportPage[0] = '\0';
return TRUE;
}
}
break;
}
return FALSE;
}
void plExportDlgImp::IExportCurrentFile(const char* exportPath)
{
// Delete the old prd so we don't get the stupid overwrite warning
DeleteFile(exportPath);
GetCOREInterface()->ExportToFile(exportPath);
}
#include "plFile/hsFiles.h"
void plExportDlgImp::IDoExport()
{
fExporting = true;
// Hide the window, since we don't get control back until the export is done
ShowWindow(fDlg, SW_HIDE);
// Do the export
char exportPath[MAX_PATH];
GetDlgItemText(fDlg, IDC_CLIENT_PATH, exportPath, sizeof(exportPath));
strcat(exportPath, "Export.prd");
// For export time stats
DWORD exportTime = timeGetTime();
if (fExportFile)
IExportCurrentFile(exportPath);
else
{
hsFolderIterator sourceDir(fExportSourceDir);
while (sourceDir.NextFileSuffix(".max"))
{
char exportFile[MAX_PATH];
sourceDir.GetPathAndName(exportFile);
if (GetCOREInterface()->LoadFromFile(exportFile))
IExportCurrentFile(exportPath);
}
}
fLastExportTime = (timeGetTime() - exportTime) / 1000;
IDestroy();
fExporting = false;
}
void plExportDlgImp::IDestroy()
{
if (fDlg)
{
// Save the window pos
RECT rect;
GetWindowRect(fDlg, &rect);
fXPos = rect.left;
fYPos = rect.top;
DestroyWindow(fDlg);
fDlg = NULL;
}
}
void plExportDlgImp::Show()
{
if (!fDlg)
fDlg = CreateDialog(hInstance, MAKEINTRESOURCE(IDD_EXPORT), GetCOREInterface()->GetMAXHWnd(), ForwardDlgProc);
}
static bool IsExcluded(const char* fileName, vector& excludeFiles)
{
for (int i = 0; i < excludeFiles.size(); i++)
{
if (!strcmp(fileName, excludeFiles[i].c_str()))
return true;
}
return false;
}
static bool AutoExportDir(const char* inputDir, const char* outputDir, const char* groupFiles, vector& excludeFiles)
{
bool exportedFile = false;
char outputFileName[MAX_PATH];
sprintf(outputFileName, "%s\\Export.prd", outputDir);
char outputLog[MAX_PATH];
sprintf(outputLog, "%s\\AutoExport.log", outputDir);
char doneDir[MAX_PATH];
sprintf(doneDir, "%s\\Done\\", inputDir);
CreateDirectory(doneDir, NULL);
// Don't give missing bitmap warnings
TheManager->SetSilentMode(TRUE);
hsFolderIterator sourceDir(inputDir);
while (sourceDir.NextFileSuffix(".max"))
{
char exportFile[MAX_PATH];
sourceDir.GetPathAndName(exportFile);
if (IsExcluded(sourceDir.GetFileName(), excludeFiles))
continue;
// If we're doing grouped files, and this isn't one, keep looking
if (groupFiles && strncmp(sourceDir.GetFileName(), groupFiles, strlen(groupFiles)) != 0)
continue;
hsUNIXStream log;
if (log.Open(outputLog, "ab"))
{
log.WriteFmt("%s\r\n", sourceDir.GetFileName());
log.Close();
}
if (GetCOREInterface()->LoadFromFile(exportFile))
{
sprintf(doneDir, "%s\\Done\\%s", inputDir, sourceDir.GetFileName());
MoveFileEx(exportFile, doneDir, MOVEFILE_REPLACE_EXISTING);
GetCOREInterface()->ExportToFile(outputFileName, TRUE);
exportedFile = true;
// If we're not doing grouped files, this is it, we exported our one file
if (!groupFiles)
break;
}
}
return exportedFile;
}
// I'm sure there's a better way to do this but I can't find it in the docs
static void ShutdownMax()
{
// If we're auto-exporting, write out a file to let the build scripts know
// we're done writing to disk, and if we don't exit soon we probably crashed
if (plExportDlg::Instance().IsAutoExporting())
{
hsUNIXStream s;
s.Open("log\\AutoExportDone.txt", "wb");
s.Close();
}
GetCOREInterface()->FlushUndoBuffer();
SetSaveRequiredFlag(FALSE);
PostMessage(GetCOREInterface()->GetMAXHWnd(), WM_CLOSE, 0, 0);
}
static void GetStringSection(const char* configFile, const char* keyName, vector& strings)
{
char source[256];
GetPrivateProfileString("Settings", keyName, "", source, sizeof(source), configFile);
char* seps = ",";
char* token = strtok(source, seps);
while (token != NULL)
{
strings.push_back(token);
token = strtok(NULL, seps);
}
}
void plExportDlgImp::StartAutoExport()
{
char configFile[MAX_PATH];
strcpy(configFile, GetCOREInterface()->GetDir(APP_PLUGCFG_DIR));
strcat(configFile, "\\AutoExport.ini");
char inputDir[MAX_PATH];
GetPrivateProfileString("Settings", "MaxInputDir", "", inputDir, sizeof(inputDir), configFile);
char outputDir[MAX_PATH];
GetPrivateProfileString("Settings", "MaxOutputDir", "", outputDir, sizeof(outputDir), configFile);
if (inputDir[0] == '\0' || outputDir == '\0')
return;
fAutoExporting = true;
// If we're doing an autoexport, suppress prompts now
hsMessageBox_SuppressPrompts = true;
// Files to ignore
vector excludeFiles;
GetStringSection(configFile, "ExcludeFiles", excludeFiles);
//
// Get the file substrings to export in one session
//
vector groupedFiles;
GetStringSection(configFile, "GroupedFiles", groupedFiles);
for (int i = 0; i < groupedFiles.size(); i++)
{
if (AutoExportDir(inputDir, outputDir, groupedFiles[i].c_str(), excludeFiles))
{
ShutdownMax();
fAutoExporting = false;
return;
}
}
if (AutoExportDir(inputDir, outputDir, NULL, excludeFiles))
{
ShutdownMax();
fAutoExporting = false;
return;
}
DeleteFile(configFile);
fAutoExporting = false;
ShutdownMax();
}