jedioutcast/utils/Assimilate/AssimilateDoc.cpp

3368 lines
88 KiB
C++
Raw Permalink Normal View History

2013-04-04 18:02:27 +00:00
// AssimilateDoc.cpp : implementation of the CAssimilateDoc class
//
#include "stdafx.h"
#include "Includes.h"
#include "BuildAll.h"
#include "sourcesafe.h"
#include "gla.h" // just for string stuff
#include <set>
using namespace std;
#define sSAVEINFOSTRINGCHECK "(SaveInfo):" // so comment reader can stop these damn things accumulating
#ifdef _DEBUG
#define new DEBUG_NEW
#undef THIS_FILE
static char THIS_FILE[] = __FILE__;
#endif
int giLODLevelOverride = 0; // MUST default to 0
void SS_DisposingOfCurrent(LPCSTR psFileName, bool bDirty);
static bool FileUsesGLAReference(LPCSTR psFilename, LPCSTR psGLAReference);
keywordArray_t CAssimilateDoc::s_Symbols[] =
{
"\\", TK_BACKSLASH,
"/", TK_SLASH,
".", TK_DOT,
"_", TK_UNDERSCORE,
"-", TK_DASH,
"$", TK_DOLLAR,
NULL, TK_EOF,
};
keywordArray_t CAssimilateDoc::s_Keywords[] =
{
"aseanimgrabinit", TK_AS_GRABINIT,
"scale", TK_AS_SCALE,
"keepmotion", TK_AS_KEEPMOTION,
"pcj", TK_AS_PCJ,
"aseanimgrab", TK_AS_GRAB,
"aseanimgrab_gla", TK_AS_GRAB_GLA,
"aseanimgrabfinalize", TK_AS_GRABFINALIZE,
"aseanimconvert", TK_AS_CONVERT,
"aseanimconvertmdx", TK_AS_CONVERTMDX,
"aseanimconvertmdx_noask", TK_AS_CONVERTMDX_NOASK,
NULL, TK_EOF,
};
keywordArray_t CAssimilateDoc::s_grabKeywords[] =
{
"frames", TK_AS_FRAMES,
"fill", TK_AS_FILL,
"sound", TK_AS_SOUND,
"action", TK_AS_ACTION,
"enum", TK_AS_ENUM,
"loop", TK_AS_LOOP,
"qdskipstart", TK_AS_QDSKIPSTART, // useful so qdata can quickly skip extra stuff without having to know the syntax of what to skip
"qdskipstop", TK_AS_QDSKIPSTOP,
"additional", TK_AS_ADDITIONAL,
"prequat", TK_AS_PREQUAT,
"framespeed", TK_AS_FRAMESPEED, // retro hack because original format only supported frame speeds on additional sequences, not masters
"genloopframe", TK_AS_GENLOOPFRAME,
NULL, TK_EOF,
};
keywordArray_t CAssimilateDoc::s_convertKeywords[] =
{
"playerparms", TK_AS_PLAYERPARMS,
"origin", TK_AS_ORIGIN,
"smooth", TK_AS_SMOOTH,
"losedupverts", TK_AS_LOSEDUPVERTS,
"makeskin", TK_AS_MAKESKIN,
"ignorebasedeviations", TK_AS_IGNOREBASEDEVIATIONS, // temporary!
"skew90", TK_AS_SKEW90,
"noskew90", TK_AS_NOSKEW90,
"skel", TK_AS_SKEL,
"makeskel", TK_AS_MAKESKEL,
NULL, TK_EOF,
};
LPCTSTR CAssimilateDoc::GetKeyword(int token, int table)
{
if ((table == TABLE_ANY) || (table == TABLE_QDT))
{
int i = 0;
while(s_Keywords[i].m_tokenvalue != TK_EOF)
{
if (s_Keywords[i].m_tokenvalue == token)
{
return s_Keywords[i].m_keyword;
}
i++;
}
}
if ((table == TABLE_ANY) || (table == TABLE_GRAB))
{
int i = 0;
while(s_grabKeywords[i].m_tokenvalue != TK_EOF)
{
if (s_grabKeywords[i].m_tokenvalue == token)
{
return s_grabKeywords[i].m_keyword;
}
i++;
}
}
if ((table == TABLE_ANY) || (table == TABLE_CONVERT))
{
int i = 0;
while(s_convertKeywords[i].m_tokenvalue != TK_EOF)
{
if (s_convertKeywords[i].m_tokenvalue == token)
{
return s_convertKeywords[i].m_keyword;
}
i++;
}
}
return NULL;
}
/////////////////////////////////////////////////////////////////////////////
// CAssimilateDoc
IMPLEMENT_DYNCREATE(CAssimilateDoc, CDocument)
BEGIN_MESSAGE_MAP(CAssimilateDoc, CDocument)
//{{AFX_MSG_MAP(CAssimilateDoc)
ON_COMMAND(IDM_ADDFILES, OnAddfiles)
ON_COMMAND(IDM_EXTERNAL, OnExternal)
ON_COMMAND(IDM_RESEQUENCE, OnResequence)
ON_COMMAND(IDM_BUILD, OnBuild)
ON_COMMAND(IDM_BUILD_MULTILOD, OnBuildMultiLOD)
ON_COMMAND(IDM_VALIDATE, OnValidate)
ON_COMMAND(IDM_CARWASH, OnCarWash)
ON_COMMAND(IDM_VALIDATE_MULTILOD, OnValidateMultiLOD)
ON_COMMAND(ID_VIEW_ANIMENUMS, OnViewAnimEnums)
ON_UPDATE_COMMAND_UI(ID_VIEW_ANIMENUMS, OnUpdateViewAnimEnums)
ON_COMMAND(ID_VIEW_FRAMEDETAILS, OnViewFrameDetails)
ON_UPDATE_COMMAND_UI(ID_VIEW_FRAMEDETAILS, OnUpdateViewFrameDetails)
ON_UPDATE_COMMAND_UI(IDM_RESEQUENCE, OnUpdateResequence)
ON_UPDATE_COMMAND_UI(ID_FILE_SAVE, OnUpdateFileSave)
ON_UPDATE_COMMAND_UI(ID_FILE_SAVE_AS, OnUpdateFileSaveAs)
ON_UPDATE_COMMAND_UI(IDM_EXTERNAL, OnUpdateExternal)
ON_UPDATE_COMMAND_UI(IDM_VALIDATE, OnUpdateValidate)
ON_UPDATE_COMMAND_UI(IDM_BUILD, OnUpdateBuild)
ON_COMMAND(ID_EDIT_BUILDALL, OnEditBuildall)
ON_COMMAND(IDM_EDIT_BUILDDEPENDANT, OnEditBuildDependant)
ON_COMMAND(ID_VIEW_FRAMEDETAILSONADDITIONALSEQUENCES, OnViewFramedetailsonadditionalsequences)
ON_UPDATE_COMMAND_UI(ID_VIEW_FRAMEDETAILSONADDITIONALSEQUENCES, OnUpdateViewFramedetailsonadditionalsequences)
ON_UPDATE_COMMAND_UI(IDM_EDIT_BUILDDEPENDANT, OnUpdateEditBuilddependant)
ON_COMMAND(ID_EDIT_LAUNCHMODVIEWONCURRENT, OnEditLaunchmodviewoncurrent)
ON_UPDATE_COMMAND_UI(ID_EDIT_LAUNCHMODVIEWONCURRENT, OnUpdateEditLaunchmodviewoncurrent)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CAssimilateDoc construction/destruction
CAssimilateDoc::CAssimilateDoc()
{
// TODO: add one-time construction code here
Init();
}
CAssimilateDoc::~CAssimilateDoc()
{
}
BOOL CAssimilateDoc::OnNewDocument()
{
SS_DisposingOfCurrent(m_strPathName, !!IsModified());
if (!CDocument::OnNewDocument())
return FALSE;
// TODO: add reinitialization code here
// (SDI documents will reuse this document)
SetTitle("Untitled"); // for some reason MFC doesn't do this from time to time
return TRUE;
}
void CAssimilateDoc::Init()
{
m_comments = NULL;
m_modelList = NULL;
m_curModel = NULL;
m_lastModel = NULL;
}
/////////////////////////////////////////////////////////////////////////////
// CAssimilateDoc serialization
CModel* CAssimilateDoc::AddModel()
{
CModel* thisModel = CModel::Create(m_comments);
m_comments = NULL;
if (m_modelList == NULL)
{
m_modelList = thisModel;
}
else
{
CModel* curModel = m_modelList;
while (curModel->GetNext() != NULL)
{
curModel = curModel->GetNext();
}
curModel->SetNext(thisModel);
}
m_curModel = thisModel;
return m_curModel;
}
// remember to account for Mike's m_lastModel and move it down if necessary (until I can throw it away)...
//
void CAssimilateDoc::DeleteModel(CModel *deleteModel)
{
// linklist is only 1-way, so we need to find the stage previous to this (if any)...
//
CModel* prevModel = NULL;
CModel* scanModel = GetFirstModel();
while (scanModel && scanModel != deleteModel)
{
prevModel = scanModel;
scanModel = scanModel->GetNext();
}
if (scanModel == deleteModel)
{
// we found it, so was this the first model in the list?
//
if (prevModel)
{
prevModel->SetNext(scanModel->GetNext()); // ...no
}
else
{
m_modelList = scanModel->GetNext(); // ...yes
}
scanModel->Delete();
}
// fixme: ditch this whenever possible
// keep Mike's var up to date...
//
scanModel = GetFirstModel();
while(scanModel && scanModel->GetNext())
{
scanModel = scanModel->GetNext();
}
m_lastModel = scanModel;
}
void CAssimilateDoc::EndModel()
{
m_lastModel = m_curModel;
m_curModel = NULL;
}
// XSI or GLA anim grab...
//
void CAssimilateDoc::ParseGrab(CTokenizer* tokenizer, int iGrabType)
{
if (m_curModel == NULL)
{
tokenizer->Error("Grab without an active model");
tokenizer->GetToEndOfLine()->Delete();
return;
}
CToken* curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
CString path = curToken->GetStringValue();
curToken->Delete();
while(curToken != NULL)
{
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
switch(curToken->GetType())
{
case TK_SLASH:
case TK_BACKSLASH:
path += "/";
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
// hack for "8472" as in models/players/8472/blah.car. Arrggh!!!!!!!!!!!!!!!!!!
//
if (curToken->GetType() == TK_INT)
{
path += curToken->GetStringValue();
curToken->Delete();
break;
}
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
path += curToken->GetStringValue();
curToken->Delete();
break;
case TK_UNDERSCORE:
case TK_DASH:
path += curToken->GetStringValue();
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
path += curToken->GetStringValue();
curToken->Delete();
break;
case TK_DOT:
path += curToken->GetStringValue();
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
path += curToken->GetStringValue();
curToken->Delete();
curToken = NULL;
break;
case TK_SPACE:
curToken->Delete();
curToken = NULL;
break;
case TK_EOL:
tokenizer->PutBackToken(curToken);
curToken = NULL;
break;
default:
tokenizer->PutBackToken(curToken);
curToken = NULL;
break;
}
}
CString enumname;
int fill = -1;
int loop = 0;
CString sound;
CString action;
int startFrame = 0;
int targetFrame = 0;
int framecount = -1;
int framespeed = iDEFAULTSEQFRAMESPEED;
bool bFrameSpeedFound = false;
int iFrameSpeedFromHeader;
//
int iStartFrames[MAX_ADDITIONAL_SEQUENCES]={0};
int iFrameCounts[MAX_ADDITIONAL_SEQUENCES]={0};
int iLoopFrames [MAX_ADDITIONAL_SEQUENCES]={0};
int iFrameSpeeds[MAX_ADDITIONAL_SEQUENCES]={0};
CString csEnums [MAX_ADDITIONAL_SEQUENCES];
int iAdditionalSeqNum = 0;
bool bGenLoopFrame = false;
bool bSomeParamsFound = false;
curToken = tokenizer->GetToken(TKF_USES_EOL);
switch (iGrabType)
{
case TK_AS_GRAB:
{
while (curToken->GetType() == TK_DASH)
{
bSomeParamsFound = true;
curToken->Delete();
curToken = tokenizer->GetToken(s_grabKeywords, TKF_USES_EOL, 0);
switch (curToken->GetType())
{
case TK_AS_FRAMES:
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
startFrame = curToken->GetIntValue();
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
targetFrame = curToken->GetIntValue();
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
framecount = curToken->GetIntValue();
break;
case TK_AS_FILL:
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
fill = curToken->GetIntValue();
break;
case TK_AS_ENUM:
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
enumname = curToken->GetStringValue();
break;
case TK_AS_SOUND:
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
sound = curToken->GetStringValue();
break;
case TK_AS_ACTION:
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
action = curToken->GetStringValue();
break;
case TK_AS_LOOP:
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
loop = curToken->GetIntValue();
break;
case TK_AS_QDSKIPSTART:
case TK_AS_QDSKIPSTOP:
//curToken->Delete(); // don't do this, the whole thing relies on case statements leaving one current token for the outside loop to delete
break;
case TK_AS_GENLOOPFRAME:
bGenLoopFrame = true;
break;
case TK_AS_FRAMESPEED: // this is still read in for compatibility, but gets overwritten lower down
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
framespeed = curToken->GetIntValue();
bFrameSpeedFound = true;
break;
case TK_AS_PREQUAT:
m_curModel->SetPreQuat(true);
break;
case TK_AS_ADDITIONAL:
curToken->Delete();
if (iAdditionalSeqNum == MAX_ADDITIONAL_SEQUENCES)
{
tokenizer->Error(TKERR_USERERROR, va("Trying to define > %d additional sequences for this master",MAX_ADDITIONAL_SEQUENCES));
tokenizer->GetToEndOfLine()->Delete();
return;
}
// startframe...
//
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
iStartFrames[iAdditionalSeqNum] = curToken->GetIntValue();
// framecount...
//
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
iFrameCounts[iAdditionalSeqNum] = curToken->GetIntValue();
// loopframe...
//
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
iLoopFrames[iAdditionalSeqNum] = curToken->GetIntValue();
// framespeed...
//
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
iFrameSpeeds[iAdditionalSeqNum] = curToken->GetIntValue();
// enum...
//
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_USES_EOL, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
csEnums[iAdditionalSeqNum] = curToken->GetStringValue();
iAdditionalSeqNum++;
break;
default:
tokenizer->Error(TKERR_UNEXPECTED_TOKEN, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
curToken->Delete();
curToken = tokenizer->GetToken(s_grabKeywords, TKF_USES_EOL, 0);
}
}
break;
case TK_AS_GRAB_GLA:
{
// no additional params permitted for this type currently...
//
}
break;
}
path.MakeLower();
//
// if no extension, assume ".xsi"... (or ".gla" now)
//
if (!(path.GetAt(path.GetLength()-4) == '.'))
{
path += (iGrabType == TK_AS_GRAB)?".xsi":".gla";
}
if (curToken->GetType() != TK_EOL)
{
tokenizer->Error(TKERR_UNEXPECTED_TOKEN, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
// ignore any user params about speed and frame counts, and just re-grab them from the XSI file...
//
// if (bSomeParamsFound)
// {
// }
// else
{
// at this point, it must be one of the paramless entries in a .CAR file, so we need to
// provide the values for: startFrame, targetFrame, framecount
//
// read in values from the actual file, in case we need to use them...
//
CString nameASE = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir();
nameASE+= path;
int iStartFrame, iFrameCount;
ReadASEHeader( nameASE, iStartFrame, iFrameCount, iFrameSpeedFromHeader, (iGrabType == TK_AS_GRAB_GLA) );
// if (strstr(nameASE,"death16"))
// {
// int z=1;
// }
startFrame = 0; // always
targetFrame= 0; // any old shite value
framecount = iFrameCount;
if (iGrabType != TK_AS_GRAB_GLA)
{
if (!bFrameSpeedFound)
{
framespeed = iFrameSpeedFromHeader;
}
}
}
curToken->Delete();
CSequence* sequence = CSequence::_Create(bGenLoopFrame,(iGrabType == TK_AS_GRAB_GLA), path, startFrame, targetFrame, framecount, framespeed, iFrameSpeedFromHeader);
m_curModel->AddSequence(sequence);
sequence->AddComment(m_curModel->ExtractComments());
sequence->DeriveName();
if (enumname.IsEmpty())
{
sequence->SetEnum(sequence->GetName());
}
else
{
sequence->SetEnum(enumname);
}
sequence->SetFill(fill);
sequence->SetSound(sound);
sequence->SetAction(action);
sequence->SetValidEnum(((CAssimilateApp*)AfxGetApp())->ValidEnum(sequence->GetEnum()));
sequence->SetLoopFrame(loop);
for (int i=0; i<MAX_ADDITIONAL_SEQUENCES; i++)
{
sequence->AdditionalSeqs[i]->SetStartFrame(iStartFrames[i]);
sequence->AdditionalSeqs[i]->SetFrameCount(iFrameCounts[i]);
sequence->AdditionalSeqs[i]->SetFrameSpeed(iFrameSpeeds[i]);
sequence->AdditionalSeqs[i]->SetLoopFrame (iLoopFrames [i]);
sequence->AdditionalSeqs[i]->SetEnum (csEnums [i]);
sequence->AdditionalSeqs[i]->SetValidEnum (((CAssimilateApp*)AfxGetApp())->ValidEnum(sequence->AdditionalSeqs[i]->GetEnum()));
}
}
// return = success. if false ret, return from caller because of error
//
bool Tokenizer_ReadPath(CString& path, CTokenizer* &tokenizer, CToken* &curToken )
{
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return false;
}
path += curToken->GetStringValue();
curToken->Delete();
while(curToken != NULL)
{
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | (path.IsEmpty()?0:TKF_SPACETOKENS), 0);
switch(curToken->GetType())
{
case TK_SLASH:
case TK_BACKSLASH:
path += "/";
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
// hack for "8472" as in models/players/8472/blah.car. Arrggh!!!!!!!!!!!!!!!!!!
//
if (curToken->GetType() == TK_INT)
{
path += curToken->GetStringValue();
curToken->Delete();
break;
}
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return false;
}
path += curToken->GetStringValue();
curToken->Delete();
break;
case TK_UNDERSCORE:
case TK_DASH:
path += curToken->GetStringValue();
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return false;
}
path += curToken->GetStringValue();
curToken->Delete();
break;
case TK_DOT:
path += curToken->GetStringValue();
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return false;
}
path += curToken->GetStringValue();
curToken->Delete();
curToken = NULL;
break;
case TK_SPACE:
case TK_EOL:
curToken->Delete();
curToken = NULL;
break;
default:
tokenizer->PutBackToken(curToken);
curToken = NULL;
break;
}
}
return true;
}
void CAssimilateDoc::ParseConvert(CTokenizer* tokenizer, int iTokenType)
{
if (m_lastModel == NULL)
{
tokenizer->Error("Convert without an internal model");
tokenizer->GetToEndOfLine()->Delete();
return;
}
CToken* curToken = NULL;
CString path;
if (!Tokenizer_ReadPath(path, tokenizer, curToken ))
return;
/*
while(curToken != NULL)
{
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
switch(curToken->GetType())
{
case TK_SLASH:
case TK_BACKSLASH:
path += "/";
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
// hack for "8472" as in models/players/8472/blah.car. Arrggh!!!!!!!!!!!!!!!!!!
//
if (curToken->GetType() == TK_INT)
{
path += curToken->GetStringValue();
curToken->Delete();
break;
}
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
path += curToken->GetStringValue();
curToken->Delete();
break;
case TK_UNDERSCORE:
case TK_DASH:
path += curToken->GetStringValue();
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
path += curToken->GetStringValue();
curToken->Delete();
break;
case TK_DOT:
path += curToken->GetStringValue();
curToken->Delete();
curToken = tokenizer->GetToken(NULL, TKF_NUMERICIDENTIFIERSTART | TKF_USES_EOL | TKF_SPACETOKENS, 0);
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_IDENTIFIER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
path += curToken->GetStringValue();
curToken->Delete();
curToken = NULL;
break;
case TK_SPACE:
case TK_EOL:
curToken->Delete();
curToken = NULL;
break;
default:
tokenizer->PutBackToken(curToken);
curToken = NULL;
break;
}
}
*/
int originx = 0; // important to default to 0!
int originy = 0; //
int originz = 0; //
int parm1 = 0;
int parm2 = 0;
int parm3 = 0;
int parm4 = 0;
curToken = tokenizer->GetToken();
while(curToken->GetType() == TK_DASH)
{
curToken->Delete();
curToken = tokenizer->GetToken(s_convertKeywords, 0, 0);
switch(curToken->GetType())
{
case TK_AS_ORIGIN:
curToken->Delete();
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
originx = curToken->GetIntValue();
curToken->Delete();
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
originy = curToken->GetIntValue();
curToken->Delete();
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
originz = curToken->GetIntValue();
curToken->Delete();
curToken = tokenizer->GetToken();
break;
case TK_AS_PLAYERPARMS:
curToken->Delete();
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
parm1 = curToken->GetIntValue();
curToken->Delete();
/* this param no longer exists...
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
parm2 = curToken->GetIntValue();
curToken->Delete();
*/
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
parm3 = curToken->GetIntValue();
curToken->Delete();
/* this param no longer exists...
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_INTEGER)
{
tokenizer->Error(TKERR_EXPECTED_INTEGER, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
parm4 = curToken->GetIntValue();
curToken->Delete();
*/ parm4 = 1;
curToken = tokenizer->GetToken();
break;
case TK_AS_SMOOTH:
curToken->Delete();
m_lastModel->SetSmooth(true);
curToken = tokenizer->GetToken();
break;
case TK_AS_LOSEDUPVERTS:
curToken->Delete();
m_lastModel->SetLoseDupVerts(true);
curToken = tokenizer->GetToken();
break;
case TK_AS_MAKESKIN:
curToken->Delete();
m_lastModel->SetMakeSkin(true);
curToken = tokenizer->GetToken();
break;
case TK_AS_IGNOREBASEDEVIATIONS:
curToken->Delete();
m_lastModel->SetIgnoreBaseDeviations(true);
curToken = tokenizer->GetToken();
break;
case TK_AS_SKEW90:
curToken->Delete();
m_lastModel->SetSkew90(true);
curToken = tokenizer->GetToken();
break;
case TK_AS_NOSKEW90:
curToken->Delete();
m_lastModel->SetNoSkew90(true);
curToken = tokenizer->GetToken();
break;
/*
case TK_AS_SKEL:
{
CString strSkelPath;
curToken->Delete();
if (!Tokenizer_ReadPath(strSkelPath, tokenizer, curToken ))
{
return;
}
m_lastModel->SetSkelPath(strSkelPath);
m_lastModel->SetMakeSkelPath("");
curToken = tokenizer->GetToken();
}
break;
*/
case TK_AS_MAKESKEL:
{
CString strMakeSkelPath;
curToken->Delete();
if (!Tokenizer_ReadPath(strMakeSkelPath, tokenizer, curToken))
{
return;
}
m_lastModel->SetMakeSkelPath(strMakeSkelPath);
// m_lastModel->SetSkelPath("");
curToken = tokenizer->GetToken();
}
break;
default:
tokenizer->Error(TKERR_UNEXPECTED_TOKEN, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
}
tokenizer->PutBackToken(curToken);
path.MakeLower();
m_lastModel->DeriveName(path);
m_lastModel->SetParms(parm1, parm2, parm3, parm4);
m_lastModel->SetOrigin(originx, originy, originz);
m_lastModel->SetConvertType(iTokenType);
}
void CAssimilateDoc::AddComment(LPCTSTR comment)
{
// some code to stop those damn timestamps accumulating...
//
if (!strnicmp(comment,sSAVEINFOSTRINGCHECK,strlen(sSAVEINFOSTRINGCHECK)))
{
return;
}
CComment* thisComment = CComment::Create(comment);
if (m_curModel != NULL)
{
m_curModel->AddComment(thisComment);
return;
}
if (m_comments == NULL)
{
m_comments = thisComment;
}
else
{
CComment* curComment = m_comments;
while (curComment->GetNext() != NULL)
{
curComment = curComment->GetNext();
}
curComment->SetNext(thisComment);
}
}
///////////////////////////////////////////////
#define MAX_FOUND_FILES 0x1000
#define MAX_OSPATH MAX_PATH
#include <stdio.h>
#include <io.h>
char **Sys_ListFiles( const char *directory, const char *extension, int *numfiles ) {
char search[MAX_OSPATH];
int nfiles;
char **listCopy;
char *list[MAX_FOUND_FILES];
struct _finddata_t findinfo;
int findhandle;
int flag;
int i;
if ( !extension) {
extension = "";
}
if ( extension[0] == '/' && extension[1] == 0 ) {
extension = "";
flag = 0;
} else {
flag = _A_SUBDIR;
}
sprintf( search, "%s\\*%s", directory, extension );
// search
nfiles = 0;
findhandle = _findfirst (search, &findinfo);
if (findhandle == -1) {
*numfiles = 0;
return NULL;
}
do {
if ( flag ^ ( findinfo.attrib & _A_SUBDIR ) ) {
if ( nfiles == MAX_FOUND_FILES - 1 ) {
break;
}
list[ nfiles ] = strdup( strlwr(findinfo.name) );
nfiles++;
}
} while ( _findnext (findhandle, &findinfo) != -1 );
list[ nfiles ] = 0;
_findclose (findhandle);
// return a copy of the list
*numfiles = nfiles;
if ( !nfiles ) {
return NULL;
}
listCopy = (char **) malloc( ( nfiles + 1 ) * sizeof( *listCopy ) );
for ( i = 0 ; i < nfiles ; i++ ) {
listCopy[i] = list[i];
}
listCopy[i] = NULL;
return listCopy;
}
void Sys_FreeFileList( char **_list ) {
int i;
if ( !_list ) {
return;
}
for ( i = 0 ; _list[i] ; i++ ) {
free( _list[i] );
}
free( _list );
}
//////////////////////////////////////////
CString strSkippedFiles;
CString strSkippedDirs;
CString strCARsFound;
int iCARsFound = 0;
void AlphaSortCARs(void)
{
typedef set <string> SortedStrings_t;
SortedStrings_t SortedStrings;
for (int i=0; i<iCARsFound; i++)
{
CString strThisFile = strCARsFound;
int iLoc = strThisFile.Find("\n");
if (iLoc>=0)
{
SortedStrings.insert(SortedStrings.end(), (LPCSTR)(strThisFile.Left(iLoc)));
strCARsFound= strCARsFound.Mid(iLoc+1);
}
}
// clear files-found string out, and re-enter from sorted set...
//
strCARsFound = "";
for (SortedStrings_t::iterator it = SortedStrings.begin(); it != SortedStrings.end(); ++it)
{
strCARsFound += (*it).c_str();
strCARsFound += "\n";
}
}
void R_CheckCARs( LPCSTR psDir, int iScanDepth, LPCSTR psGLAReferenceItShouldInclude )
{
((CMainFrame*)AfxGetMainWnd())->StatusMessage(va("(%d .CAR files found so far) Scanning Dir: %s",iCARsFound,psDir));
// ignore any dir with "test" in it...
//
if (//strstr(psDir,"\\test")
//||
strstr(psDir,"\\backup")
||
strstr(psDir,"\\ignore_")
)
{
strSkippedDirs += psDir;
strSkippedDirs += "\n";
return;
}
char **sysFiles, **dirFiles;//, *args[5];
int numSysFiles, i, /*len,*/ numdirs;
char altname[MAX_OSPATH];
// char command1[MAX_OSPATH];
// char command2[MAX_OSPATH];
dirFiles = Sys_ListFiles(psDir, "/", &numdirs);
if (numdirs > 2)
{
// if (!iScanDepth) // recursion limiter, to avoid scanning backup subdirs within model subdirs
{
for (i=2;i<numdirs;i++)
{
sprintf(altname, "%s\\%s", psDir, dirFiles[i]);
//if (stricmp(altname,"q:\\send\\quake\\baseq3\\models\\players")) // dont recurse this dir
{
R_CheckCARs(altname, iScanDepth+1, psGLAReferenceItShouldInclude );
}
}
}
}
// sprintf(command1, "-targa");
// sprintf(command2, "-outfile");
sysFiles = Sys_ListFiles( psDir, ".car", &numSysFiles );
for ( i=0; i<numSysFiles; i++ )
{
CString strThisFile = va("%s\\%s", psDir, sysFiles[i]);
if (strstr((LPCSTR) strThisFile,"copy of "))
{
strSkippedFiles += strThisFile;
strSkippedFiles += "\n";
continue;
}
if (psGLAReferenceItShouldInclude && psGLAReferenceItShouldInclude [0]
&&
!FileUsesGLAReference(strThisFile, psGLAReferenceItShouldInclude)
)
{
strSkippedFiles += strThisFile;
strSkippedFiles += "\n";
continue;
}
strCARsFound += strThisFile + "\n";
iCARsFound++;
/* char tgain[MAX_OSPATH];
sprintf(tgain,"%s\\%s", psDir, sysFiles[i]);
strcpy( altname, tgain );
len = strlen( altname );
altname[len-3] = 'j';
altname[len-2] = 'p';
altname[len-1] = 'g';
args[0] = "cjpeg";
args[1] = command2;
args[2] = altname;
args[3] = command1;
args[4] = tgain;
//printf("%s", tgain);
*/
/* len = qmain(5, args);
if (!len)
{
iNumberOf_FilesConverted++;
iSizeOf_JPGsWritten += scGetFileLen(altname);
iSizeOf_TGAsDeleted += scGetFileLen(tgain);
printf(" nuked!(NOT)");
// remove(tgain);
}
printf("\n");
*/
/*
byte * pPixels = NULL;
int iWidth;
int iHeight;
bool bRedundant = ScanTGA(tgain, &pPixels, &iWidth, &iHeight);
if (pPixels)
{
free(pPixels);
}
if (bRedundant)
{
strTGAsWithRedundantAlpha += va("%s\n",tgain);
iRedundantFilesFound++;
}
*/
}
Sys_FreeFileList( sysFiles );
Sys_FreeFileList( dirFiles );
}
void CAssimilateDoc::Parse(CFile* file)
{
Parse(file->GetFilePath());
}
void CAssimilateDoc::Parse(LPCSTR psFilename)
{
gbParseError = false;
CAlertErrHandler errhandler;
CTokenizer* tokenizer = CTokenizer::Create(TKF_NOCASEKEYWORDS | TKF_COMMENTTOKENS);
tokenizer->SetErrHandler(&errhandler);
tokenizer->SetSymbols(s_Symbols);
tokenizer->SetKeywords(s_Keywords);
tokenizer->AddParseFile(psFilename);
extern bool gbSkipXSIRead_QuestionAsked;
extern bool gbSkipXSIRead;
gbSkipXSIRead_QuestionAsked = false; // opening a new file so reset our question
gbSkipXSIRead = false;
int tokType = TK_UNDEFINED;
while(tokType != TK_EOF)
{
CToken* curToken = tokenizer->GetToken();
tokType = curToken->GetType();
switch(tokType)
{
case TK_EOF:
curToken->Delete();
break;
case TK_DOLLAR:
curToken->Delete();
curToken = tokenizer->GetToken();
switch(curToken->GetType())
{
case TK_AS_GRABINIT:
curToken->Delete();
AddModel();
break;
case TK_AS_SCALE:
curToken->Delete();
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_FLOAT && curToken->GetType() != TK_INT)
{
tokenizer->Error(TKERR_EXPECTED_FLOAT, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
m_curModel->SetScale((curToken->GetType() == TK_FLOAT)?curToken->GetFloatValue():curToken->GetIntValue());
curToken->Delete();
break;
case TK_AS_KEEPMOTION:
curToken->Delete();
m_curModel->SetKeepMotion(true);
break;
case TK_AS_PCJ:
curToken->Delete();
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_IDENTIFIER && curToken->GetType() != TK_DOLLAR) // eg: '$pcj pelvis' or '$pcj $flatten'
{
tokenizer->Error(TKERR_EXPECTED_STRING, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
if (curToken->GetType() == TK_DOLLAR) // read string after '$' char
{
curToken->Delete();
curToken = tokenizer->GetToken();
if (curToken->GetType() != TK_IDENTIFIER)
{
tokenizer->Error(TKERR_EXPECTED_STRING, curToken->GetStringValue());
curToken->Delete();
tokenizer->GetToEndOfLine()->Delete();
return;
}
m_curModel->PCJList_AddEntry(va("$%s",curToken->GetStringValue()));
}
else
{
m_curModel->PCJList_AddEntry(curToken->GetStringValue());
}
curToken->Delete();
break;
case TK_AS_GRAB:
curToken->Delete();
ParseGrab(tokenizer,TK_AS_GRAB);
break;
case TK_AS_GRAB_GLA:
curToken->Delete();
ParseGrab(tokenizer,TK_AS_GRAB_GLA);
break;
case TK_AS_GRABFINALIZE:
curToken->Delete();
EndModel();
break;
case TK_AS_CONVERT:
curToken->Delete();
ParseConvert(tokenizer,TK_AS_CONVERT);
break;
case TK_AS_CONVERTMDX:
curToken->Delete();
ParseConvert(tokenizer,TK_AS_CONVERTMDX);
break;
case TK_AS_CONVERTMDX_NOASK:
curToken->Delete();
ParseConvert(tokenizer,TK_AS_CONVERTMDX_NOASK);
break;
case TK_COMMENT:
AddComment(curToken->GetStringValue());
curToken->Delete();
break;
default:
tokenizer->Error(TKERR_UNEXPECTED_TOKEN);
curToken->Delete();
break;
}
break;
case TK_COMMENT:
AddComment(curToken->GetStringValue());
curToken->Delete();
break;
default:
tokenizer->Error(TKERR_UNEXPECTED_TOKEN);
curToken->Delete();
break;
}
}
tokenizer->Delete();
UpdateAllViews(NULL, AS_NEWFILE, NULL);
Resequence();
}
void CAssimilateDoc::Write(CFile* file)
{
CTxtFile* outfile = CTxtFile::Create(file);
/* // write out time/date stamp...
//
CString commentLine;
CTime time = CTime::GetCurrentTime();
commentLine.Format("// %s %s updated %s", sSAVEINFOSTRINGCHECK, file->GetFileName(), time.Format("%H:%M %A, %B %d, %Y"));
outfile->Writeln(commentLine);
*/
CModel* curModel = m_modelList;
while(curModel != NULL)
{
curModel->Write(outfile);
curModel = curModel->GetNext();
}
CComment* curComment = m_comments;
while(curComment != NULL)
{
curComment->Write(outfile);
curComment = curComment->GetNext();
}
outfile->Delete();
}
void CAssimilateDoc::Serialize(CArchive& ar)
{
if (ar.IsStoring())
{
// TODO: add storing code here
Write(ar.GetFile());
}
else
{
// TODO: add loading code here
Parse(ar.GetFile());
}
}
/////////////////////////////////////////////////////////////////////////////
// CAssimilateDoc diagnostics
#ifdef _DEBUG
void CAssimilateDoc::AssertValid() const
{
CDocument::AssertValid();
}
void CAssimilateDoc::Dump(CDumpContext& dc) const
{
CDocument::Dump(dc);
}
#endif //_DEBUG
/////////////////////////////////////////////////////////////////////////////
// CAssimilateDoc commands
void CAssimilateDoc::DeleteContents()
{
// TODO: Add your specialized code here and/or call the base class
UpdateAllViews(NULL, AS_DELETECONTENTS, NULL);
while(m_comments != NULL)
{
CComment* curComment = m_comments;
m_comments = curComment->GetNext();
curComment->Delete();
}
while(m_modelList != NULL)
{
CModel* curModel = m_modelList;
m_modelList = curModel->GetNext();
curModel->Delete();
}
m_curModel = NULL;
m_lastModel = NULL;
gbReportMissingASEs = true;
giFixUpdatedASEFrameCounts = YES;
CDocument::DeleteContents();
}
CModel* CAssimilateDoc::GetFirstModel()
{
return m_modelList;
}
int CAssimilateDoc::GetNumModels()
{
int iCount = 0;
CModel *theModel = m_modelList;
while (theModel)
{
iCount++;
theModel = theModel->GetNext();
}
return iCount;
}
void CAssimilateDoc::OnAddfiles()
{
// TODO: Add your command handler code here
CFileDialog theDialog(true, ".xsi", NULL, OFN_EXPLORER | OFN_ALLOWMULTISELECT | OFN_FILEMUSTEXIST | OFN_PATHMUSTEXIST,
_T("Anim Files (*.ase)(*.xsi)(*.gla)|*.ase;*.xsi;*.gla|All Files|*.*||"), NULL);
////////////
// Model files (*.MDR)(*.MD3)|*.md?|
////////////
char filenamebuffer[16384];
filenamebuffer[0] = '\0';
theDialog.m_ofn.lpstrFile = filenamebuffer;
theDialog.m_ofn.nMaxFile = sizeof(filenamebuffer);
CString
strInitialDir = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir();
strInitialDir+= "models/players";
strInitialDir.Replace("/","\\");
theDialog.m_ofn.lpstrInitialDir = strInitialDir;
int result = theDialog.DoModal();
if (result != IDOK)
{
return;
}
CWaitCursor waitcursor;
POSITION pos = theDialog.GetStartPosition();
while(pos != NULL)
{
CString thisfile = theDialog.GetNextPathName(pos);
/* int loc = thisfile.Find(':');
if (loc > 0)
{
thisfile = thisfile.Right(thisfile.GetLength() - loc - 1);
}
*/
Filename_RemoveBASEQ(thisfile);
AddFile(thisfile);
}
SetModifiedFlag();
UpdateAllViews(NULL, AS_NEWFILE, NULL);
Resequence(); // must be AFTER UpdateAllViews
}
void CAssimilateDoc::AddFile(LPCTSTR name)
{
CString strTemp(name);
strTemp.MakeLower();
if (!strstr(strTemp,"root.xsi") || GetYesNo("You're trying to add \"root.xsi\", which is inherent, you should only do this if you're making a model that has no seperate anim files\n\nProceed?"))
{
// update, ignore any files with a "_1" (2,3, etc) just before the suffix (this skips LOD files)...
//
// int iNameLen = strlen(name);
// (also, only check files of at least namelen "_?.ase")
//
// if ( iNameLen>6 && name[iNameLen-6]=='_' && isdigit(name[iNameLen-5]) )
{
// this is a LOD filename, so ignore it...
}
// else
{
CModel *curModel = GetCurrentUserSelectedModel();
if (!curModel)
{
curModel = AddModel();
CString path = name;
path.MakeLower();
path.Replace('\\', '/');
int loc = path.ReverseFind('.');
if (loc > -1)
{
path = path.Left(loc);
}
loc = path.ReverseFind('/');
path = path.Left(loc);
path = path + "/root";
curModel->DeriveName(path);
}
// check that we don't already have this file...
//
if (!curModel->ContainsFile(name))
{
curModel->AddSequence(CSequence::CreateFromFile(name, curModel->ExtractComments()));
}
}
}
}
void CAssimilateDoc::OnExternal()
{
bool bCFGWritten = false;
if (WriteCFGFiles(true,bCFGWritten))
{
CString strReport;
if (bCFGWritten)
{
if (((CAssimilateApp*)AfxGetApp())->GetMultiPlayerMode())
{
strReport = "\n\n( CFG file written for MULTI-PLAYER format )";
}
else
{
strReport = "\n\n( CFG file written for SINGLE-PLAYER format )";
}
}
else
{
strReport = "\n\n( CFG not needed, not written )";
}
InfoBox(strReport);
}
}
// called both from the menu, and now from the Build() member...
//
bool CAssimilateDoc::WriteCFGFiles(bool bPromptForNames, bool &bCFGWritten)
{
bCFGWritten = false;
CModel* curModel = m_modelList;
while(curModel != NULL)
{
bool bThisCFGWritten = false;
if (!curModel->WriteExternal(bPromptForNames,bThisCFGWritten))
{
return false;
}
if (bThisCFGWritten)
bCFGWritten = true;
curModel = curModel->GetNext();
}
return true;
}
void CAssimilateDoc::Resequence()
{
CModel* curModel = m_modelList;
while(curModel != NULL)
{
curModel->Resequence(true);
curModel = curModel->GetNext();
}
SetModifiedFlag();
UpdateAllViews(NULL, AS_FILESUPDATED, NULL); // needed
}
void CAssimilateDoc::OnResequence()
{
extern bool gbSkipXSIRead_QuestionAsked;
extern bool gbSkipXSIRead;
gbSkipXSIRead_QuestionAsked = false; // make it happen again
gbSkipXSIRead = false;
Resequence();
}
void CAssimilateDoc::OnViewAnimEnums()
{
gbViewAnimEnums = !gbViewAnimEnums;
UpdateAllViews(NULL, AS_FILESUPDATED, NULL);
}
void CAssimilateDoc::OnUpdateViewAnimEnums(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(gbViewAnimEnums);
}
void CAssimilateDoc::OnViewFrameDetails()
{
gbViewFrameDetails = !gbViewFrameDetails;
UpdateAllViews(NULL, AS_FILESUPDATED, NULL);
}
void CAssimilateDoc::OnUpdateViewFrameDetails(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(gbViewFrameDetails);
}
// 1) save qdt, 2) run qdata on it, 3) if success, save .cfg file...
//
bool CAssimilateDoc::Build(bool bAllowedToShowSuccessBox, int iLODLevel, bool bSkipSave) // damn this stupid Serialize() crap
{
bool bSuccess = false;
if (Validate()) // notepad will have been launched with a textfile of errors at this point if faulty
{
// seems valid, so save the QDT...
//
giLODLevelOverride = iLODLevel;
if (!bSkipSave)
{
OnFileSave();
}
CString csQDataLocation = ((CAssimilateApp*)AfxGetApp())->GetQDataFilename();
// hack-city!!!!!!!!!!
//
CModel* curModel = ghAssimilateView->GetDocument()->GetCurrentUserSelectedModel();
if (curModel->GetPreQuat())
{
csQDataLocation.MakeLower();
csQDataLocation.Replace("carcass","carcass_prequat");
}
CString params = csQDataLocation;
if (!bSkipSave)
{
params += " -keypress";
}
params += " -silent";
params += " ";
params += m_strPathName; // += (eg) {"Q:\quake\baseq3\models\players\ste_assimilate_test\ste_testaa.qdt"}
Filename_AccountForLOD(params, giLODLevelOverride);
PROCESS_INFORMATION pi;
STARTUPINFO startupinfo;
startupinfo.cb = sizeof(startupinfo);
startupinfo.lpReserved = NULL;
startupinfo.lpDesktop = NULL;
startupinfo.lpTitle = NULL;
startupinfo.dwFlags = 0;
startupinfo.cbReserved2 = 0;
startupinfo.lpReserved2 = NULL;
params.Replace("/","\\");
csQDataLocation.Replace("/","\\");
LPTSTR paramsPass = params.GetBuffer(params.GetLength() + 1);
StartWait(); // ++++++++++++++++++++++++
if (CreateProcess(csQDataLocation, paramsPass, NULL, NULL, FALSE, CREATE_NEW_CONSOLE, NULL, NULL, &startupinfo, &pi))
{
WaitForSingleObject(pi.hProcess, INFINITE);
EndWait(); // ------------------------
DWORD result;
GetExitCodeProcess(pi.hProcess,&result);
if (result)
{
char error[64];
sprintf(error,"Process returned error: %d",result);
MessageBox(NULL,error,"Build Failed",MB_OK|MB_ICONERROR);
}
CloseHandle(pi.hProcess);
if (result==0)
{
// QData was run successfully at this point, so write the CFG file(s)...
//
if (iLODLevel) // only LOD 0 writes out the CFG file
{
bSuccess = true;
}
else
{
bool bCFGWritten = false;
if (WriteCFGFiles(false,bCFGWritten)) // false = no name prompt, derive automatically from model name
{
// success (on a plate)...
//
if (bAllowedToShowSuccessBox)
{
CString strReport("Everything seemed to go ok\n\nCAR");
if (bCFGWritten)
{
strReport += " and CFG files written";
if (((CAssimilateApp*)AfxGetApp())->GetMultiPlayerMode())
{
strReport += "\n\n\n\n( CFG file written for MULTI-PLAYER format )";
}
else
{
strReport += "\n\n\n\n( CFG file written for SINGLE-PLAYER format )";
}
}
else
{
strReport += " file written";
strReport += "\n\n\n\n( CFG file not written for GLA-referencing model )";
}
InfoBox(strReport);
}
bSuccess = true;
}
}
}
}
else
{
EndWait(); // ------------------------
MessageBox(NULL,"Could not spawn process.","Build Failed",MB_OK|MB_ICONERROR);
}
params.ReleaseBuffer();
}
giLODLevelOverride = 0;
return bSuccess;
}
void CAssimilateDoc::OnBuildMultiLOD()
{
int iErrors = 0;
// to save time, I'll run all the validates first, and only if they're all ok will I go on to the build
// (which incidentally does a harmless re-validate again)
//
for (int i=0; i< 1+EXTRA_LOD_LEVELS; i++)
{
iErrors += Validate(false, i)?0:1;
}
// go ahead if all clear...
//
// (I'll write them in reverse-LOD order so the last one written is the standard one. This should hopefully avoid
// any problems with the current document potentially becoming "(name)_3.qdt" from then on to MFC)
//
if (!iErrors)
{
for (int i=EXTRA_LOD_LEVELS; i>=0; i--)
{
Build( !i, // bool bAllowedToShowSuccessBox (ie only on last one)
i, // int iLODLevel
false // bool bSkipSave
);
}
}
}
// 1) save qdt, 2) run qdata on it, 3) if success, save .cfg file...
//
void CAssimilateDoc::OnBuild()
{
Build( true, // bool bAllowedToShowSuccessBox,
0, // int iLODLevel
false // bool bSkipSave
);
}
void CAssimilateDoc::ClearModelUserSelectionBools()
{
CModel* curModel = m_modelList;
while (curModel)
{
curModel->SetUserSelectionBool(false);
curModel = curModel->GetNext();
}
}
// if there's only one model loaded, return that, if none, return NULL, else if >1, return selected one, else NULL
//
CModel* CAssimilateDoc::GetCurrentUserSelectedModel()
{
CModel *curModel = m_modelList;
if (!curModel || !curModel->GetNext())
{
return curModel; // 0 or 1 models total loaded
}
// more than one loaded, so find the selected one and return that...
//
while (curModel)
{
if (curModel->GetUserSelectionBool())
{
return curModel;
}
curModel = curModel->GetNext();
}
return NULL; // multiple loaded, but none selected
}
static bool FileUsesGLAReference(LPCSTR psFilename, LPCSTR psGLAReference)
{
bool bReturn = false;
FILE *fHandle = fopen(psFilename,"rt");
if (fHandle)
{
int iLen = filesize(fHandle);
if (iLen>0)
{
char *psText = (char*) malloc(iLen+1);
if (psText)
{
fread(psText,1,iLen,fHandle);
psText[iLen]='\0';
strlwr(psText);
// this is a simple test that could be made more precise, but for now...
//
if ( (strstr(psText,"aseanimgrab_gla") || strstr(psText,"makeskel"))
&&
strstr(psText,psGLAReference)
)
{
bReturn = true;
}
free(psText);
}
}
fclose(fHandle);
}
return bReturn;
}
#define sASSUMEPATH "w:\\game\\base"
bool gbCarWash_YesToXSIScan;
bool gbCarWash_DoingScan = false; // MUST default to this
bool gbQueryGoAhead = true; // MUST defualt to this
LPCSTR gpsCARWashDirOverride = NULL;// MUST default to this
CString strCarWashErrors;
bool gbCarwashErrorsOccured;
CString strMustContainThisGLA; // MUST be blank, else name of GLA to be present to be considered
void CAssimilateDoc::OnCarWashActual()
{
gbCarwashErrorsOccured = false;
CString strStartDir = gpsCARWashDirOverride?gpsCARWashDirOverride:((CAssimilateApp*)AfxGetApp())->GetQuakeDir();
strStartDir.Replace("/","\\");
if (!strStartDir.GetLength())
{
ErrorBox("Quake path not known at this point. Prefs not setup?");
gbCarwashErrorsOccured = true;
return;
// if (!GetYesNo("Quake path not known at this point because you've not loaded anything yet\n\nShould I assume " "\"" sASSUMEPATH "\"" "?"))
// return;
//
// strStartDir = sASSUMEPATH;
}
// (this app was written so that GetQuakeDir() returns a path with a trailing slash, not nice normally, but here...)
//
// if (strStartDir.GetAt(strStartDir.GetLength()-1)=='\\')
// strStartDir = strStartDir.Left(strStartDir.GetLength()-1);
if (gpsCARWashDirOverride == NULL)
{
strStartDir += "models";//\\players";
}
if (gbQueryGoAhead)
{
if (!GetYesNo(va("About to scan: \"%s\\*.CAR /s\"\n\n"/*"This can take a LONG time, "*/"Proceed?",strStartDir)))
return;
}
gbCarWash_YesToXSIScan = GetYesNo("Full XSI scan? ( \"NO\" will skip XSI reads, but v3.0 files are quick to read anyway now )");
CWaitCursor wait;
strCARsFound.Empty();
iCARsFound = 0;
strSkippedDirs.Empty();
strSkippedFiles.Empty();
R_CheckCARs( strStartDir, 0, strMustContainThisGLA); //bool bBuildListOnly
AlphaSortCARs(); // not important to alpha-sort here (during car-wash), just looks nicer
((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready");
// ok, now ready to begin pass 2...
//
CString strReport;
if (!iCARsFound)
{
ASSERT(0);
strReport = "No suitable .CAR files found for processing!\n\n";
if (!strSkippedDirs.IsEmpty())
{
strReport+= "Skipped Dirs:\n\n";
strReport+= strSkippedDirs;
}
if (!strSkippedFiles.IsEmpty())
{
strReport+= "\n\nSkipped Files:\n\n";
strReport+= strSkippedFiles;
}
ErrorBox(strReport);
gbCarwashErrorsOccured = true;
}
else
{
//----------------
gbCarWash_DoingScan = true;
strCarWashErrors.Empty();
//----------------
CString strTotalErrors;
strReport = "Processed files:\n\n";
for (int i=0; i<iCARsFound; i++)
{
CString strThisFile = strCARsFound;
int iLoc = strThisFile.Find("\n");
if (iLoc>=0)
{
strThisFile = strThisFile.Left(iLoc);
strCARsFound= strCARsFound.Mid(iLoc+1);
strReport += strThisFile + "\n";
((CMainFrame*)AfxGetMainWnd())->StatusMessage(va("Scanning File %d/%d: %s",i+1,iCARsFound,(LPCSTR)strThisFile));
if (1)//strMustContainThisGLA.IsEmpty() || FileUsesGLAReference(strThisFile, strMustContainThisGLA))
{
OnNewDocument();
Parse(strThisFile);
if (gbParseError)
{
strTotalErrors += va("\nParse error in CAR file \"%s\"\n",(LPCSTR)strThisFile);
}
else
{
OnValidate();
if (!strCarWashErrors.IsEmpty())
{
// "something is wrong..." :-)
//
strTotalErrors += va("\nError in file \"%s\":\n\n%s\n",(LPCSTR)strThisFile,(LPCSTR)strCarWashErrors);
}
strCarWashErrors.Empty();
}
}
}
else
{
ASSERT(0);
strThisFile.Insert(0,"I fucked up, the following line didn't seem to have a CR: (tell me! -Ste)\n\n");
ErrorBox(strThisFile);
}
}
//----------------
gbCarWash_DoingScan = false;
//----------------
OnNewDocument(); // trash whatever was loaded last
// strReport = "Processed files:\n\n";
// strReport+= strCARsFound;
strReport+= "\n\nSkipped Dirs:\n\n";
strReport+= strSkippedDirs;
strReport+= "\n\nSkipped Files:\n\n";
strReport+= strSkippedFiles;
if (strTotalErrors.IsEmpty())
{
strReport.Insert(0,"(No additional errors found)\n\n");
}
else
{
strReport+= "\n\nAdditional errors will now be sent to Notepad!...";
gbCarwashErrorsOccured = true;
}
if (gbQueryGoAhead || gbCarwashErrorsOccured)
{
InfoBox(strReport);
}
if (!strTotalErrors.IsEmpty())
{
strTotalErrors.Insert(0, "The following errors occured during CARWash...\n\n");
SendToNotePad(strTotalErrors, "carwash_errors.txt");
}
}
//#define sASSUMEPATH "q:\\quake\\baseq3"
// strTGAsWithRedundantAlpha.Insert(0,"The following files are defined as 32-bit (ie with alpha), but the alpha channel is blank (ie all 255)...\n\n");
// SendToNotePad(strTGAsWithRedundantAlpha, "TGAs_With_Redundant_Alpha.txt");
((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready");
}
void CAssimilateDoc::OnCarWash()
{
strMustContainThisGLA.Empty();
OnCarWashActual();
}
// creates as temp file, then spawns notepad with it...
//
bool SendToNotePad(LPCSTR psWhatever, LPCSTR psLocalFileName)
{
bool bReturn = false;
LPCSTR psOutputFileName = va("%s\\%s",scGetTempPath(),psLocalFileName);
FILE *handle = fopen(psOutputFileName,"wt");
if (handle)
{
fprintf(handle,psWhatever);
fclose(handle);
char sExecString[MAX_PATH];
sprintf(sExecString,"notepad %s",psOutputFileName);
if (WinExec(sExecString, // LPCSTR lpCmdLine, // address of command line
SW_SHOWNORMAL // UINT uCmdShow // window style for new application
)
>31 // don't ask me, Windoze just uses >31 as OK in this call.
)
{
// ok...
//
bReturn = true;
}
else
{
ErrorBox("Unable to locate/run NOTEPAD on this machine!\n\n(let me know about this -Ste)");
}
}
else
{
ErrorBox(va("Unable to create file \"%s\" for notepad to use!",psOutputFileName));
}
return bReturn;
}
// AFX OnXxxx calls need to be void return, but the validate needs to ret a bool for elsewhere, so...
//
void CAssimilateDoc::OnValidate()
{
Validate(!gbCarWash_DoingScan);
}
void CAssimilateDoc::OnValidateMultiLOD()
{
int iErrors = 0;
for (int i=0; i< 1+EXTRA_LOD_LEVELS; i++)
{
iErrors += Validate(false, i)?0:1;
}
if (!iErrors)
{
InfoBox(va("Everything seems OK\n\n(Original + %d LOD levels checked)",EXTRA_LOD_LEVELS));
}
}
// checks for things that would stop the build process, such as missing ASE files, invalid loopframes, bad anim enums, etc,
// and writes all the faults to a text file that it displays via launching notepad
//
bool CAssimilateDoc::Validate(bool bInfoBoxAllowed, int iLODLevel)
{
OnResequence();
int iNumModels = ghAssimilateView->GetDocument()->GetNumModels();
CModel* curModel = ghAssimilateView->GetDocument()->GetCurrentUserSelectedModel();
bool bValidateAll = false;
int iFaults = 0;
StartWait();
if (iNumModels)
{
CString sOutputTextFile;
sOutputTextFile.Format("%s\\validation_faults%s.txt",scGetTempPath(),iLODLevel?va("_LOD%d",iLODLevel):"");
FILE *hFile = fopen(sOutputTextFile,"wt");
if (hFile)
{
if (iNumModels>1)
{
if (!curModel) // >1 models, but none selected, so validate all
{
bValidateAll = true;
}
else
{
// >1 models, 1 selected, ask if we should do all...
//
bValidateAll = GetYesNo(va("Validate ALL models?\n\n( NO = model \"%s\" only )",curModel->GetName()));
}
}
if (bValidateAll)
{
curModel = ghAssimilateView->GetDocument()->GetFirstModel();
}
if (iLODLevel)
{
fprintf(hFile,"(LOD Level %d)\n",iLODLevel);
}
while (curModel)
{
fprintf(hFile,"\nModel: \"%s\"\n\n",curModel->GetName());
int iThisModelFaults = iFaults;
if ( ( curModel->GetMakeSkelPath() && strlen(curModel->GetMakeSkelPath()) )
// ||
// ( curModel->GetSkelPath() && strlen(curModel->GetSkelPath()) )
)
{
// ... then all is fine
}
else
{
// this is an error, UNLESS you have a GLA sequence...
//
if (!curModel->HasGLA())
{
//fprintf(hFile, "Model must have either a 'skel' or 'makeskel' path\n ( Double-click on the model name in the treeview to edit )\n");
fprintf(hFile, "Model must have a 'makeskel' path\n ( Double-click on the top tree item's name (should be a folder), then click \"Makes it's own skeleton\" in the dialog )\n");
iFaults++;
}
}
//
// validate all sequences within this model...
//
int iGLACount = 0;
int iXSICount = 0;
CSequence* curSequence = curModel->GetFirstSequence();
while (curSequence)
{
// we'll need to check these counts after checking all sequences...
//
if (curSequence->IsGLA())
{
iGLACount++;
}
else
{
iXSICount++;
}
#define SEQREPORT CString temp;temp.Format("Sequence \"%s\":",curSequence->GetName());while (temp.GetLength()<35)temp+=" ";
// check 1, does the ASE file exist? (actually this is talking about XSIs/GLAs, but WTF...
//
CString nameASE = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir();
nameASE+= curSequence->GetPath();
Filename_AccountForLOD(nameASE, iLODLevel);
const int iNameSpacing=35;
if (!FileExists(nameASE))
{
if (!gbCarWash_DoingScan) // because this will only duplicate reports otherwise
{
SEQREPORT;
temp += va(" ASE/XSI/GLA file not found: \"%s\"\n",nameASE);
fprintf(hFile,temp);
iFaults++;
}
}
// new check, if this is an LOD ASE it must have the same framecount as the base (non-LOD) version...
//
if (iLODLevel)
{
// read this file's framecount...
//
int iStartFrame, iFrameCount, iFrameSpeed;
curSequence->ReadASEHeader( nameASE, iStartFrame, iFrameCount, iFrameSpeed);
// read basefile's framecount...
//
CString baseASEname = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir();
baseASEname+= curSequence->GetPath();
int iStartFrameBASE, iFrameCountBASE, iFrameSpeedBASE;
curSequence->ReadASEHeader( baseASEname, iStartFrameBASE, iFrameCountBASE, iFrameSpeedBASE);
// same?...
//
if (iFrameCount != iFrameCountBASE)
{
SEQREPORT;
temp += va(" (SERIOUS ERROR) base ASE has %d frames, but this LOD version has %d frames!\n",iFrameCountBASE,iFrameCount);
fprintf(hFile,temp);
iFaults++;
}
}
// check 2, is the loopframe higher than the framecount of that sequence?...
//
if (curSequence->GetLoopFrame() >= curSequence->GetFrameCount())
{
SEQREPORT;
temp += va(" loopframe %d is illegal, max = %d\n",curSequence->GetLoopFrame(),curSequence->GetFrameCount()-1);
fprintf(hFile,temp);
iFaults++;
}
if (!curModel->IsGhoul2())
{
// check 3, is the enum valid?...
//
if (curSequence->GetEnumType() == ET_INVALID)
{
SEQREPORT;
temp += va(" invalid animation enum \"%s\"\n",curSequence->GetEnum());
fprintf(hFile,temp);
iFaults++;
}
}
int iEnumUsageCount = curModel->AnimEnumInUse(curSequence->GetEnum());
if (iEnumUsageCount>1)
{
SEQREPORT;
temp += va(" animation enum \"%s\" is used %d times\n",curSequence->GetEnum(),iEnumUsageCount);
fprintf(hFile,temp);
iFaults++;
}
// a whole bunch of checks for the additional sequences...
//
if (!curSequence->IsGLA())
{
for (int i=0; i<MAX_ADDITIONAL_SEQUENCES; i++)
{
CSequence *additionalSeq = curSequence->AdditionalSeqs[i];
#define ADDITIONALSEQREPORT CString temp;temp.Format("Sequence \"%s\": (Additional: \"%s\"):",curSequence->GetName(),additionalSeq->GetEnum());while (temp.GetLength()<60)temp+=" ";
if (additionalSeq->AdditionalSequenceIsValid())
{
// check for duplicate enum names...
//
int iEnumUsageCount = curModel->AnimEnumInUse(additionalSeq->GetEnum());
if (iEnumUsageCount>1)
{
ADDITIONALSEQREPORT;
temp += va(" animation enum \"%s\" is used %d times\n",additionalSeq->GetEnum(),iEnumUsageCount);
fprintf(hFile,temp);
iFaults++;
}
// additional sequences must actually have some frames...
//
if (additionalSeq->GetFrameCount()<=0)
{
ADDITIONALSEQREPORT;
temp += va(" a frame count of %d is illegal (min = 1)\n",additionalSeq->GetFrameCount());
fprintf(hFile,temp);
iFaults++;
}
else
{
// the start/count range of this additional seq can't exceed it's master...
//
if (additionalSeq->GetStartFrame() + additionalSeq->GetFrameCount() > curSequence->GetFrameCount())
{
ADDITIONALSEQREPORT;
temp += va(" illegal start/count range of %d..%d exceeds master range of %d..%d\n",
additionalSeq->GetStartFrame(),
additionalSeq->GetStartFrame() + additionalSeq->GetFrameCount(),
curSequence->GetStartFrame(),
curSequence->GetStartFrame() + curSequence->GetFrameCount()
);
fprintf(hFile,temp);
iFaults++;
}
else
{
// loopframe of an additional seq must be within its own seq framecount...
//
if (additionalSeq->GetLoopFrame() >= additionalSeq->GetFrameCount())
{
ADDITIONALSEQREPORT;
temp += va(" loopframe %d is illegal, max is %d\n",additionalSeq->GetLoopFrame(),additionalSeq->GetFrameCount()-1);
fprintf(hFile,temp);
iFaults++;
}
}
}
}
else
{
// is this additional sequence invalid because of being just empty or being bad?...
//
if (strlen(additionalSeq->GetEnum()))
{
// it's a bad sequence (probably because of its enum being deleted from anims.h since it was saved)
//
ADDITIONALSEQREPORT;
temp += va(" this animation enum no longer exists in \"%s\"\n",sDEFAULT_ENUM_FILENAME);
fprintf(hFile,temp);
iFaults++;
}
}
}
}
curSequence = curSequence->GetNext();
}
// special GLA/XSI checks...
//
{
if (iGLACount>1)
{
fprintf(hFile, "Model has more than one GLA file specified\n");
iFaults++;
}
if (iGLACount && iXSICount)
{
fprintf(hFile, "Model has both GLA and XSI files specified. Pick one method or the other\n");
iFaults++;
}
if (iGLACount && (curModel->GetMakeSkelPath() && strlen(curModel->GetMakeSkelPath())) )
{
fprintf(hFile, "Model has both a GLA sequence and a '-makeskel' path, this is meaningless\n");
iFaults++;
}
/*
if (iGLACount && (curModel->GetSkelPath() && strlen(curModel->GetSkelPath())) )
{
fprintf(hFile, "Model has both a GLA sequence and a '-skel' path, you should probably blank out the 'skel' path\n");
iFaults++;
}
*/
}
if (iThisModelFaults == iFaults)
{
fprintf(hFile,"(ok)\n"); // just to be nice if reporting on >1 model...
}
else
{
fprintf(hFile,"\n(%d faults)\n",iFaults-iThisModelFaults);
}
curModel = curModel->GetNext();
if (!bValidateAll)
break;
}// while (curModel)
fclose(hFile);
if (iFaults)
{
// now run notepad.exe on the file we've just created...
//
CString sExecString;
sExecString.Format("notepad %s",sOutputTextFile);
if (WinExec(sExecString, // LPCSTR lpCmdLine, // address of command line
SW_SHOWNORMAL // UINT uCmdShow // window style for new application
)
>31 // don't ask me, Windoze just uses >31 as OK in this call.
)
{
// ok.
}
else
{
ErrorBox("Unable to locate/run NOTEPAD on this machine!\n\n(let me know about this -Ste)");
}
}
else
{
if (bInfoBoxAllowed)
{
InfoBox("Everything ok\n\n( All files exist, enums exist, and frames seem to be valid ranges )");
}
}
}// if (hFile)
else
{
ErrorBox(va("Arrgh! Unable to create file '%s'!\n\n(let me know about this -Ste)",sOutputTextFile));
}
}// if (iNumModels)
EndWait();
return !iFaults;
}
void CAssimilateDoc::OnUpdateResequence(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!!GetNumModels());
}
void CAssimilateDoc::OnUpdateFileSave(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!!GetNumModels());
}
void CAssimilateDoc::OnUpdateFileSaveAs(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!!GetNumModels());
}
void CAssimilateDoc::OnUpdateExternal(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!!GetNumModels());
}
void CAssimilateDoc::OnUpdateValidate(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!!GetNumModels());
}
void CAssimilateDoc::OnUpdateBuild(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!!GetNumModels());
}
BOOL CAssimilateDoc::DoFileSave()
{
// sourcesafe stuff
{
///////////////////////////////////////////
//
// some stuff so I can leave the code below untouched...
//
LPCSTR filename = (LPCSTR) m_strPathName;
#define Sys_Printf(blah) StatusText(blah)
//
///////////////////////////////////////////
//
// check it out first, if necessary...
//
if ( SS_FunctionsAvailable() )
{
if ( SS_IsUnderSourceControl( filename ) )
{
if ( SS_IsCheckedOut( filename ))
{
if ( !SS_IsCheckedOutByMe( filename ))
{
CString strCheckOuts;
int iCount;
if (SS_ListCheckOuts( filename, strCheckOuts, iCount ))
{
ErrorBox( va("File \"%s\" is checked out by:\n\n%s\n... so you can't save over it...\n\n... so you can't compile...\n\nTough luck matey!....(bwahahahaha!!!!!)",filename,(LPCSTR) strCheckOuts));
return false;
}
}
else
{
Sys_Printf ("(You own this file under SourceSafe)\n");
}
}
else
{
if ( GetYesNo( va("The file \"%s\"\n\n...needs to be checked out so I can save over it\n\nProceed? ('No' will abort the save)",filename) ))
{
if (SS_CheckOut( filename ))
{
Sys_Printf ("(File checked out ok)\n");
}
else
{
ASSERT(0); // I want to know if this ever happens
Sys_Printf ("(Error during file checkout, aborting save\n");
return false;
}
}
else
{
Sys_Printf ("(Checkout cancelled, aborting save\n");
return false;
}
}
}
else
{
Sys_Printf ("(This file is not under SourceSafe control)\n");
}
}
// now do seperate check for files that are still write-protected...
//
DWORD dw = GetFileAttributes( filename );
if (dw != 0xFFFFFFFF && ( dw & FILE_ATTRIBUTE_READONLY ))
{
// hmmm, still write protected...
//
if (SS_SetupOk())
{
if (GetYesNo( va("The file \"%s\" is write-protected, but probably not because of SourceSafe, just as a safety thing.\n\n(Tell me if you believe this is wrong -Ste)\n\nDo you want me to un-writeprotect it so you can save over it? ('No' will abort the save)",filename )))
{
if ( !SetFileAttributes( filename, dw&~FILE_ATTRIBUTE_READONLY) )
{
ErrorBox("Failed to remove write protect, aborting...");
return false;
}
}
else
{
Sys_Printf ("(File was not write-enabled, aborting save)");
return false;
}
}
else
{
ErrorBox( va("The file \"%s\" is write-protected, but you don't appear to have SourceSafe set up properly on this machine, so I can't tell if the file is protected or just not checked out to you.\n\nIf you really want to edit this you'll have to write-enable it yourself (which I'm deliberately not offering to do for you here <g>)",filename));
}
}
}
BOOL b = CDocument::DoFileSave();
if (b == TRUE)
{
// sourcesafe
LPCSTR filename = (LPCSTR) m_strPathName;
#define Sys_Printf(blah) StatusText(blah)
if ( SS_FunctionsAvailable() )
{
if ( SS_IsUnderSourceControl( filename ))
{
if ( SS_IsCheckedOutByMe( filename ))
{
// if ( SS_CheckIn( filename ))
// {
// Sys_Printf("(Checked in ok)\n");
// }
// else
// {
// Sys_Printf("Error during CheckIn\n");
// }
}
else
{
ErrorBox( va("You do not have file \"%s\" checked out",filename));
}
}
else
{
// new bit, if it wasn't under SourceSafe, then ask if they want to add it...
//
if (GetYesNo(va("File \"%s\" is not under SourceSafe control, add to database?",filename)))
{
if ( SS_Add( filename ))
{
Sys_Printf("(File was added to SourceSafe Ok)\n");
// check it out as well...
//
if (SS_CheckOut( filename ))
{
Sys_Printf ("(File checked out ok)\n");
}
else
{
ASSERT(0); // I want to know if this ever happens
Sys_Printf ("( Error during file checkout! )\n");
}
}
else
{
ErrorBox( va("Error adding file \"%s\" to SourceSafe",filename));
}
}
}
}
}
StatusText(NULL);
return b;
}
BOOL CAssimilateDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
CString strFileName = lpszPathName;
Filename_AccountForLOD(strFileName, giLODLevelOverride); // this is actually junk now, should lose all this LODoverride stuff
return CDocument::OnSaveDocument(strFileName);
}
// remember these two from session to session, maybe write to registry sometime?...
//
bool gbPreValidate = true;
CString strInitialBuildPath = "models/players";
void CAssimilateDoc::OnEditBuildDependant()
{
CModel* curModel = m_modelList;
if (curModel)
{
CString strStartDir = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir();
if (!strStartDir.GetLength())
{
// should never happen...
//
ErrorBox("Base path not known at this point. Prefs not setup?");
return;
}
LPCSTR psCurrentGLAName = curModel->GetMakeSkelPath();
if (psCurrentGLAName)
{
char sCurrentGLAName[1024];
strcpy(sCurrentGLAName,psCurrentGLAName);
strMustContainThisGLA = sCurrentGLAName;
CBuildAll dlgBuildAll(strInitialBuildPath, gbPreValidate);
if (dlgBuildAll.DoModal() == IDOK)
{
dlgBuildAll.GetData(strInitialBuildPath, gbPreValidate);
strStartDir += strInitialBuildPath;
strStartDir.Replace("/","\\");
while (strStartDir.Replace("\\\\","\\")){}
if (gbPreValidate)
{
gbQueryGoAhead = false;
gpsCARWashDirOverride = (LPCSTR) strStartDir;
OnCarWashActual();
gbQueryGoAhead = true;
gpsCARWashDirOverride = NULL;
if (gbCarwashErrorsOccured)
{
InfoBox("Build-All aborted because of errors");
return;
}
}
((CMainFrame*)AfxGetMainWnd())->StatusMessage("Validated Ok, building...");
//////////////////////// largely block-pasted from CarWash.... :-)
CWaitCursor wait;
strCARsFound.Empty();
iCARsFound = 0;
strSkippedDirs.Empty();
strSkippedFiles.Empty();
// build up a list...
//
R_CheckCARs( strStartDir, 0, strMustContainThisGLA); //bool bBuildListOnly
AlphaSortCARs(); // important to do them in alpha-order during build, because of "_humanoid" - type dirs.
((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready");
// ok, now ready to begin pass 2...
//
CString strReport;
if (!iCARsFound)
{
ASSERT(0);
strReport = "No suitable .CAR files found for processing!\n\n";
if (!strSkippedDirs.IsEmpty())
{
strReport+= "Skipped Dirs:\n\n";
strReport+= strSkippedDirs;
}
if (!strSkippedFiles.IsEmpty())
{
strReport+= "\n\nSkipped Files:\n\n";
strReport+= strSkippedFiles;
}
ErrorBox(strReport);
}
else
{
//----------------
gbCarWash_DoingScan = true;
strCarWashErrors.Empty();
//----------------
CString strTotalErrors;
strReport = "Processed files:\n\n";
for (int i=0; i<iCARsFound; i++)
{
CString strThisFile = strCARsFound;
int iLoc = strThisFile.Find("\n");
if (iLoc>=0)
{
strThisFile = strThisFile.Left(iLoc);
strCARsFound= strCARsFound.Mid(iLoc+1);
strReport += strThisFile + "\n";
((CMainFrame*)AfxGetMainWnd())->StatusMessage(va("Scanning File %d/%d: %s",i+1,iCARsFound,(LPCSTR)strThisFile));
if (1)//FileUsesGLAReference(strThisFile, sCurrentGLAName))
{
OnNewDocument();
if (OnOpenDocument_Actual(strThisFile, false))
{
if (gbParseError)
{
strTotalErrors += va("\nParse error in file \"%s\"\n",(LPCSTR)strThisFile);
break;
}
else
{
m_strPathName = strThisFile; // fucking stupid MFC doesn't set this!!!!!!!!!!!!!
bool bSuccess = Build( false, // bool bAllowedToShowSuccessBox,
0, // int iLODLevel
true // bool bSkipSave
);
if (!strCarWashErrors.IsEmpty())
{
// "something is wrong..." :-)
//
strTotalErrors += va("\nError in file \"%s\":\n\n%s\n",strThisFile,strCarWashErrors);
}
strCarWashErrors.Empty();
if (!bSuccess)
break;
}
}
else
{
strTotalErrors += va("\nUnable to open file \"%s\"\n",(LPCSTR)strThisFile);
break;
}
}
else
{
// this CAR file doesn't use the current GLA name, so ignore it
}
}
else
{
ASSERT(0);
strThisFile.Insert(0,"I fucked up, the following line didn't seem to have a CR: (tell me! -Ste)\n\n");
ErrorBox(strThisFile);
}
}
//----------------
gbCarWash_DoingScan = false;
//----------------
OnNewDocument(); // trash whatever was loaded last
// strReport = "Processed files:\n\n";
// strReport+= strCARsFound;
strReport+= "\n\nSkipped Dirs:\n\n";
strReport+= strSkippedDirs;
strReport+= "\n\nSkipped Files:\n\n";
strReport+= strSkippedFiles;
if (strTotalErrors.IsEmpty())
{
strReport.Insert(0,"(No additional errors found)\n\n");
}
else
{
strReport+= "\n\nAdditional errors will now be sent to Notepad!...";
}
InfoBox(strReport);
if (!strTotalErrors.IsEmpty())
{
strTotalErrors.Insert(0, "The following errors occured during build...\n\n");
SendToNotePad(strTotalErrors, "build_errors.txt");
}
}
}
}
else
{
ErrorBox("Currently loaded model does not make a skeleton, so there are no dependants\n\n\nDUH!!");
}
}
else
{
ErrorBox("No model loaded to build dependants of!\n\n\nDUH!!");
}
((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready");
}
void CAssimilateDoc::OnEditBuildall()
{
// if (!GetYesNo("Safety Feature: Do you really want to rebuild every CAR file in the whole game?"))
// return;
// validity-check...
//
CString strStartDir = ((CAssimilateApp*)AfxGetApp())->GetQuakeDir();
if (!strStartDir.GetLength())
{
ErrorBox("Base path not known at this point. Prefs not setup?");
return;
}
CBuildAll dlgBuildAll(strInitialBuildPath, gbPreValidate);
if (dlgBuildAll.DoModal() == IDOK)
{
dlgBuildAll.GetData(strInitialBuildPath, gbPreValidate);
strStartDir += strInitialBuildPath;
strStartDir.Replace("/","\\");
while (strStartDir.Replace("\\\\","\\")){}
if (gbPreValidate)
{
gbQueryGoAhead = false;
gpsCARWashDirOverride = (LPCSTR) strStartDir;
OnCarWash();
gbQueryGoAhead = true;
gpsCARWashDirOverride = NULL;
if (gbCarwashErrorsOccured)
{
InfoBox("Build-All aborted because of errors");
return;
}
}
((CMainFrame*)AfxGetMainWnd())->StatusMessage("Validated Ok, building...");
//////////////////////// largely block-pasted from CarWash.... :-)
CWaitCursor wait;
strCARsFound.Empty();
iCARsFound = 0;
strSkippedDirs.Empty();
strSkippedFiles.Empty();
// build up a list...
//
R_CheckCARs( strStartDir, 0, "" ); //bool bBuildListOnly
AlphaSortCARs(); // important to do them in alpha-order during build, because of "_humanoid" - type dirs.
((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready");
// ok, now ready to begin pass 2...
//
CString strReport;
if (!iCARsFound)
{
ASSERT(0);
strReport = "No suitable .CAR files found for processing!\n\n";
if (!strSkippedDirs.IsEmpty())
{
strReport+= "Skipped Dirs:\n\n";
strReport+= strSkippedDirs;
}
if (!strSkippedFiles.IsEmpty())
{
strReport+= "\n\nSkipped Files:\n\n";
strReport+= strSkippedFiles;
}
ErrorBox(strReport);
}
else
{
//----------------
gbCarWash_DoingScan = true;
strCarWashErrors.Empty();
//----------------
CString strTotalErrors;
strReport = "Processed files:\n\n";
for (int i=0; i<iCARsFound; i++)
{
CString strThisFile = strCARsFound;
int iLoc = strThisFile.Find("\n");
if (iLoc>=0)
{
strThisFile = strThisFile.Left(iLoc);
strCARsFound= strCARsFound.Mid(iLoc+1);
strReport += strThisFile + "\n";
((CMainFrame*)AfxGetMainWnd())->StatusMessage(va("Scanning File %d/%d: %s",i+1,iCARsFound,(LPCSTR)strThisFile));
OnNewDocument();
if (OnOpenDocument_Actual(strThisFile, false))
{
if (gbParseError)
{
strTotalErrors += va("\nParse error in file \"%s\"\n",(LPCSTR)strThisFile);
break;
}
else
{
m_strPathName = strThisFile; // fucking stupid MFC doesn't set this!!!!!!!!!!!!!
bool bSuccess = Build( false, // bool bAllowedToShowSuccessBox,
0, // int iLODLevel
true // bool bSkipSave
);
if (!strCarWashErrors.IsEmpty())
{
// "something is wrong..." :-)
//
strTotalErrors += va("\nError in file \"%s\":\n\n%s\n",strThisFile,strCarWashErrors);
}
strCarWashErrors.Empty();
if (!bSuccess)
break;
}
}
else
{
strTotalErrors += va("\nUnable to open file \"%s\"\n",(LPCSTR)strThisFile);
break;
}
}
else
{
ASSERT(0);
strThisFile.Insert(0,"I fucked up, the following line didn't seem to have a CR: (tell me! -Ste)\n\n");
ErrorBox(strThisFile);
}
}
//----------------
gbCarWash_DoingScan = false;
//----------------
OnNewDocument(); // trash whatever was loaded last
// strReport = "Processed files:\n\n";
// strReport+= strCARsFound;
strReport+= "\n\nSkipped Dirs:\n\n";
strReport+= strSkippedDirs;
strReport+= "\n\nSkipped Files:\n\n";
strReport+= strSkippedFiles;
if (strTotalErrors.IsEmpty())
{
strReport.Insert(0,"(No additional errors found)\n\n");
}
else
{
strReport+= "\n\nAdditional errors will now be sent to Notepad!...";
}
InfoBox(strReport);
if (!strTotalErrors.IsEmpty())
{
strTotalErrors.Insert(0, "The following errors occured during build...\n\n");
SendToNotePad(strTotalErrors, "build_errors.txt");
}
}
}
((CMainFrame*)AfxGetMainWnd())->StatusMessage("Ready");
}
void SS_DisposingOfCurrent(LPCSTR psFileName, bool bDirty)
{
if (psFileName[0])
{
LPCSTR filename = psFileName; // compile laziness
#undef Sys_Printf
#define Sys_Printf(blah)
if ( SS_FunctionsAvailable() )
{
if ( SS_IsUnderSourceControl( filename ) )
{
if ( SS_IsCheckedOutByMe( filename ))
{
if (bDirty)
{
// if 'need_save' then the user has clicked ok-to-lose-changes, so...
//
if ( GetYesNo( va("Since you've decided to lose changes on the file:\n\n\"%s\"\n\n...do you want to Undo Checkout as well?",filename)))
{
if (SS_UndoCheckOut( filename ))
{
Sys_Printf ("(Undo Checkout performed on map)\n");
}
else
{
ErrorBox("Undo Checkout failed!\n");
}
}
}
else
{
// if !'need_save' here then the user has saved this out already, so prompt for check in...
//
if ( GetYesNo( va("Since you've finished with the file:\n\n\"%s\"\n\n...do you want to do a Check In?",filename)))
{
if ( SS_CheckIn( filename ))
{
//Sys_Printf ("(CheckIn performed on map)\n");
}
else
{
ErrorBox("CheckIn failed!\n");
}
}
}
}
}
}
}
}
BOOL CAssimilateDoc::OnOpenDocument_Actual(LPCTSTR lpszPathName, bool bCheckOut)
{
SS_DisposingOfCurrent(m_strPathName, !!IsModified());
if (bCheckOut)
{
// checkout the new file?
LPCSTR filename = lpszPathName; // compile-laziness :-)
if ( SS_FunctionsAvailable() )
{
if ( SS_IsUnderSourceControl( filename ) )
{
if ( SS_IsCheckedOut( filename ))
{
if ( !SS_IsCheckedOutByMe( filename ))
{
CString strCheckOuts;
int iCount;
if (SS_ListCheckOuts( filename, strCheckOuts, iCount ))
{
if (!GetYesNo( va("Warning: File \"%s\" is checked out by:\n\n%s\n... Continue loading? ",filename,(LPCSTR) strCheckOuts)))
{
return FALSE;
}
}
}
else
{
//InfoBox ("(You own this file under SourceSafe)\n");
}
}
else
{
if ( GetYesNo( va("The file \"%s\"\n\n...is under SourceSafe control, check it out now?",filename) ))
{
if (SS_CheckOut( filename ))
{
//InfoBox ("(File checked out ok)\n");
}
else
{
WarningBox( va("( Problem encountered during check out of file \"%s\" )",filename) );
}
}
}
}
else
{
//InfoBox ("(This file is not under SourceSafe control)\n");
}
}
}
if (!CDocument::OnOpenDocument(lpszPathName))
return FALSE;
return TRUE;
}
BOOL CAssimilateDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
return OnOpenDocument_Actual(lpszPathName, true) ;
}
void CAssimilateDoc::OnCloseDocument()
{
SS_DisposingOfCurrent(m_strPathName, !!IsModified());
CDocument::OnCloseDocument();
}
void CAssimilateDoc::OnViewFramedetailsonadditionalsequences()
{
gbViewFrameDetails_Additional = !gbViewFrameDetails_Additional;
UpdateAllViews(NULL, AS_FILESUPDATED, NULL);
}
void CAssimilateDoc::OnUpdateViewFramedetailsonadditionalsequences(CCmdUI* pCmdUI)
{
pCmdUI->SetCheck(gbViewFrameDetails_Additional);
}
void CAssimilateDoc::OnUpdateEditBuilddependant(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!!GetNumModels());
}
bool RunApp(LPCSTR psAppCommand)
{
CString strExec = psAppCommand; // eg "start q:\\bin_nt\\behaved.exe";
strExec.Replace("/","\\"); // otherwise it only works under NT...
char sBatchFilename[512];
GetTempPath(sizeof(sBatchFilename), sBatchFilename);
strcat(sBatchFilename,"~temp.bat");
FILE *handle = fopen(sBatchFilename,"wt");
fprintf(handle,strExec);
fprintf(handle,"\n");
fclose(handle);
STARTUPINFO startupinfo;
PROCESS_INFORMATION ProcessInformation;
GetStartupInfo (&startupinfo);
BOOL ret = CreateProcess(sBatchFilename,
//batpath, // pointer to name of executable module
NULL, // pointer to command line string
NULL, // pointer to process security attributes
NULL, // pointer to thread security attributes
FALSE, // handle inheritance flag
0 /*DETACHED_PROCESS*/, // creation flags
NULL, // pointer to new environment block
NULL, // pointer to current directory name
&startupinfo, // pointer to STARTUPINFO
&ProcessInformation // pointer to PROCESS_INFORMATION
);
// remove(sBatchFilename); // if you do this, the CreateProcess fails, presumably it needs it for a few seconds
return !!ret;
}
void CAssimilateDoc::OnEditLaunchmodviewoncurrent()
{
char sExecString[MAX_PATH];
sprintf(sExecString,"start %s.glm",Filename_WithoutExt(m_strPathName));
if (RunApp(sExecString))
{
// ok...
//
}
else
{
ErrorBox(va("CreateProcess() call \"%s\" failed!\n\n(let me know about this -Ste)",sExecString));
}
}
void CAssimilateDoc::OnUpdateEditLaunchmodviewoncurrent(CCmdUI* pCmdUI)
{
pCmdUI->Enable(!!GetNumModels());
}