296 lines
7.3 KiB
C++
296 lines
7.3 KiB
C++
// xap2td.cpp : Defines the entry point for the console application.
|
|
//
|
|
|
|
#include "stdafx.h"
|
|
#include "xap2td.h"
|
|
#include <afxtempl.h>
|
|
#include <conio.h> //for _kbhit()
|
|
#include <fstream>
|
|
#ifdef _DEBUG
|
|
#define new DEBUG_NEW
|
|
#endif
|
|
|
|
|
|
// The one and only application object
|
|
|
|
CWinApp theApp;
|
|
|
|
using namespace std;
|
|
|
|
bool g_bStrict = false; // if true, do strict verification on project file. This includes enforcing a 1:1 mapping of sound to wave banks.
|
|
bool g_bDumpStats = false; // if true, dump xap data to the screen
|
|
bool g_bLog = false; // if true, will log to a file (xap2td.log)
|
|
|
|
ofstream g_logFile;
|
|
|
|
void CXap2tdCommandLineInfo::ParseParam(const char* pszParam, BOOL bFlag, BOOL bLast)
|
|
{
|
|
if (bFlag)
|
|
{
|
|
if (!stricmp(pszParam, "strict"))
|
|
g_bStrict = true;
|
|
else if (!stricmp(pszParam, "dumpstats"))
|
|
g_bDumpStats = true;
|
|
else if (!stricmp(pszParam, "log"))
|
|
g_bLog = true;
|
|
else
|
|
{
|
|
ShowUsage();
|
|
}
|
|
}
|
|
|
|
CCommandLineInfo::ParseParam(pszParam, bFlag, bLast);
|
|
}
|
|
|
|
int _tmain(int argc, TCHAR* argv[], TCHAR* envp[])
|
|
{
|
|
int nRetCode = 0;
|
|
|
|
// initialize MFC and print an error on failure
|
|
if (!AfxWinInit(::GetModuleHandle(NULL), NULL, ::GetCommandLine(), 0))
|
|
{
|
|
_tprintf(_T("Fatal Error: MFC initialization failed\n"));
|
|
nRetCode = 1;
|
|
}
|
|
else
|
|
{
|
|
CXap2tdCommandLineInfo cmdInfo;
|
|
|
|
theApp.ParseCommandLine(cmdInfo);
|
|
|
|
CStdioFile ProjectFile;
|
|
CString ProjectFileName;
|
|
CStdioFile HeaderFile;
|
|
CString HeaderFileName;
|
|
|
|
cout << "R*SD XACT 2 Project File (.xap) to TD File Converter v" << VERSION_STR << endl << endl;
|
|
|
|
|
|
if (argc < 2 || argc > 5 || cmdInfo.m_strFileName.IsEmpty())
|
|
{
|
|
ShowUsage();
|
|
}
|
|
|
|
ProjectFileName = cmdInfo.m_strFileName;
|
|
HeaderFileName = ProjectFileName;
|
|
HeaderFileName.Replace(".xap", ".h");
|
|
|
|
if (ProjectFileName.Right(4) != ".xap")
|
|
{
|
|
cerr << "Project file name must end in .xap!" << endl;
|
|
WaitForKey();
|
|
return 1;
|
|
}
|
|
|
|
//Make sure the .td files emerge in the same directory that the .xap and .h files are in.
|
|
CString Path(".");
|
|
|
|
int index = ProjectFileName.ReverseFind('\\');
|
|
|
|
if (index == -1)
|
|
index = ProjectFileName.ReverseFind('/');
|
|
|
|
if (index != -1)
|
|
{
|
|
Path = ProjectFileName.Left(index + 1);
|
|
SetCurrentDirectory((LPCSTR) Path);
|
|
}
|
|
|
|
CString logFilePath = Path + CString("\\xap2td.log");
|
|
|
|
if (g_bLog)
|
|
{
|
|
char fullPath[_MAX_PATH];
|
|
GetFullPathName((LPCSTR) logFilePath, _MAX_PATH, fullPath, NULL);
|
|
|
|
cout << "Logging to file: " << fullPath << "." << endl << endl;
|
|
|
|
time_t osBinaryTime;
|
|
time(&osBinaryTime);
|
|
CTime time(osBinaryTime);
|
|
|
|
g_logFile.open((LPCSTR)logFilePath, ofstream::out | ofstream::trunc);
|
|
g_logFile << "R*SD XACT 2 Project File (.xap) to TD File Converter v" << VERSION_STR << endl << endl;
|
|
g_logFile << time.Format( "%A, %B %d, %Y %I:%M:%S %p" ) << endl << endl;
|
|
}
|
|
|
|
if (!ProjectFile.Open(ProjectFileName, CFile::modeRead | CFile::shareDenyNone | CFile::typeBinary | CFile::osSequentialScan))
|
|
{
|
|
//Couldn't open project file
|
|
cerr << "Unable to open project file " << (LPCTSTR) ProjectFileName << "!" << endl;
|
|
WaitForKey();
|
|
return 1;
|
|
}
|
|
|
|
if (!HeaderFile.Open(HeaderFileName, CFile::modeRead | CFile::shareDenyNone))
|
|
{
|
|
//Couldn't open output file
|
|
cerr << "Unable to open header file " << (LPCTSTR) HeaderFileName << "!" << endl;
|
|
ProjectFile.Close();
|
|
WaitForKey();
|
|
return 1;
|
|
}
|
|
|
|
XAPFile* xap = NULL;
|
|
|
|
try
|
|
{
|
|
//Load up XAP data
|
|
cout << "Loading .xap file " << (LPCSTR) ProjectFile.GetFilePath() << "." << endl;
|
|
|
|
if (g_bLog)
|
|
g_logFile << "Loading .xap file " << (LPCSTR) ProjectFile.GetFilePath() << "." << endl << endl;
|
|
|
|
xap = new XAPFile(ProjectFile, g_bStrict);
|
|
|
|
//Optionally dump stats
|
|
if (g_bDumpStats)
|
|
xap->DumpStats(cout);
|
|
|
|
if (g_bLog)
|
|
xap->DumpStats(g_logFile);
|
|
|
|
//Make sure cue ordering is correct based on header file enums
|
|
cout << "Loading header file " << (LPCSTR) HeaderFile.GetFilePath() << "." << endl << endl;
|
|
|
|
if (g_bLog)
|
|
g_logFile << "Loading header file " << (LPCSTR) HeaderFile.GetFilePath() << "." << endl << endl;
|
|
|
|
xap->ReorderCues(HeaderFile);
|
|
|
|
//Write out the .td files
|
|
WriteTdFiles(xap);
|
|
|
|
cout << "Done!" << endl;
|
|
|
|
if (g_bLog)
|
|
g_logFile << "Done!" << endl << endl;
|
|
|
|
}
|
|
catch (XapException e)
|
|
{
|
|
cout << "Error: " << e.GetMessage() << endl << endl;
|
|
cout << "BUILD FAILED -- ERRORS ENCOUNTERED!" << endl;
|
|
|
|
if (g_bLog)
|
|
{
|
|
g_logFile << "Error: " << e.GetMessage() << endl << endl;
|
|
g_logFile << "BUILD FAILED -- ERRORS ENCOUNTERED!" << endl;
|
|
}
|
|
|
|
nRetCode = 1;
|
|
}
|
|
|
|
delete xap; //Safe even if NULL -- get over it.
|
|
|
|
ProjectFile.Close();
|
|
HeaderFile.Close();
|
|
|
|
if (g_bLog)
|
|
g_logFile.close();
|
|
}
|
|
|
|
WaitForKey();
|
|
return nRetCode;
|
|
}
|
|
|
|
void WriteTdFiles(const XAPFile* xapFile)
|
|
{
|
|
CStdioFile TDFile;
|
|
|
|
for (int i = 0; i < xapFile->GetNumSoundBanks(); i++)
|
|
{
|
|
CString TDFileName = xapFile->GetSoundBank(i).GetName() + CString(".td");
|
|
|
|
if (!TDFile.Open(TDFileName, CFile::modeWrite | CFile::modeCreate | CFile::shareDenyWrite))
|
|
{
|
|
//Couldn't open output file
|
|
CString error;
|
|
error.Format("Unable to open/create .td file %s.", (LPCTSTR) TDFileName);
|
|
throw XapException((LPCSTR)error);
|
|
}
|
|
|
|
CString strOutLine;
|
|
int NumCues = (int) xapFile->GetSoundBank(i).GetNumCues();
|
|
|
|
cout << "Writing .td file " << (LPCSTR) TDFile.GetFilePath() << "." << endl;
|
|
|
|
if (g_bLog)
|
|
g_logFile << "Writing .td file " << (LPCSTR) TDFile.GetFilePath() << "." << endl;
|
|
|
|
strOutLine = "TD_FILE "TD_VERSION_STR"\n";
|
|
TDFile.WriteString(strOutLine);
|
|
|
|
strOutLine.Format("STREAMING %d\n", xapFile->GetSoundBank(i).IsStreaming() ? 1 : 0);
|
|
TDFile.WriteString(strOutLine);
|
|
|
|
strOutLine.Format("BUFFERSIZE %d\n", xapFile->GetSoundBank(i).CalcStreamingBufferSize());
|
|
TDFile.WriteString(strOutLine);
|
|
|
|
strOutLine.Format("WAVEBANKREFS %d\n", xapFile->GetSoundBank(i).GetNumWaveBankRefs());
|
|
TDFile.WriteString(strOutLine);
|
|
|
|
for (int j = 0; j < xapFile->GetSoundBank(i).GetNumWaveBankRefs(); j++)
|
|
{
|
|
strOutLine.Format("\t%s\n", xapFile->GetSoundBank(i).GetWaveBankRef(j).GetName());
|
|
TDFile.WriteString(strOutLine);
|
|
}
|
|
|
|
strOutLine.Format("NUMSOUNDS %d\n", NumCues);
|
|
TDFile.WriteString(strOutLine);
|
|
|
|
for (int j = 0; j < NumCues; j++)
|
|
{
|
|
XACTCue cue = xapFile->GetSoundBank(i).GetCue(j);
|
|
char temp[32];
|
|
|
|
strOutLine = "\t";
|
|
strOutLine += cue.GetName();
|
|
strOutLine += "\n";
|
|
TDFile.WriteString(strOutLine);
|
|
|
|
strOutLine = "\t\t";
|
|
strOutLine += _itoa(j, temp, 10); //cue index
|
|
strOutLine += "\n";
|
|
TDFile.WriteString(strOutLine);
|
|
|
|
strOutLine = "\t\t";
|
|
strOutLine += cue.IsLooping() ? "1" : "0"; //Loop flag.
|
|
strOutLine += "\n";
|
|
TDFile.WriteString(strOutLine);
|
|
|
|
strOutLine = "\t\t";
|
|
strOutLine += _itoa(cue.GetLenghtInMs(), temp, 10); //length in milliseconds. Note that this only reflects
|
|
//the length of the last wave of the last sound in this
|
|
//cue, so really only useful when there's a 1:1:1 cue:sound:wave
|
|
//mapping
|
|
strOutLine += "\n";
|
|
TDFile.WriteString(strOutLine);
|
|
}
|
|
|
|
TDFile.Close();
|
|
}
|
|
}
|
|
|
|
void ShowUsage(void)
|
|
{
|
|
cerr << "Usage: xap2td <filename>.xap [-strict] [-dumpstats] [-log]" << endl;
|
|
cerr << " or just drag the .xap file onto executable." << endl;
|
|
|
|
WaitForKey();
|
|
exit(1);
|
|
}
|
|
|
|
void WaitForKey(int timeOutMS /* = 2000 */)
|
|
{
|
|
int ms = 0;
|
|
|
|
cerr << endl << "Press any key..." << endl;
|
|
|
|
do
|
|
{
|
|
Sleep(100);
|
|
ms += 100;
|
|
} while (!_kbhit() && ms < timeOutMS);
|
|
}
|