|
|
|
/*==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 <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
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 "plSDL.h"
|
|
|
|
#include "plFile/hsFiles.h"
|
|
|
|
#include "plFile/plStreamSource.h"
|
|
|
|
#include "pnNetCommon/pnNetCommon.h"
|
|
|
|
#include "pnNetCommon/plNetApp.h"
|
|
|
|
|
|
|
|
static const int kTokenLen=256;
|
|
|
|
|
|
|
|
void plSDLParser::DebugMsg(const char* fmt, ...) const
|
|
|
|
{
|
|
|
|
return;
|
|
|
|
plNetApp* netApp = plSDLMgr::GetInstance()->GetNetApp();
|
|
|
|
|
|
|
|
va_list args;
|
|
|
|
va_start(args, fmt);
|
|
|
|
|
|
|
|
if (netApp)
|
|
|
|
{
|
|
|
|
hsLogEntry(netApp->DebugMsgV(fmt, args));
|
|
|
|
}
|
|
|
|
else
|
|
|
|
DebugMsgV(fmt, args);
|
|
|
|
va_end(args);
|
|
|
|
}
|
|
|
|
|
|
|
|
void plSDLParser::DebugMsgV(const char* fmt, va_list args) const
|
|
|
|
{
|
|
|
|
if (strlen(fmt)==nil)
|
|
|
|
return;
|
|
|
|
hsStatusMessage(plString::IFormat(fmt,args).c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// parsing stateDesc
|
|
|
|
// read name, version
|
|
|
|
// return true to skip the next token read
|
|
|
|
//
|
|
|
|
bool plSDLParser::IParseStateDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc) const
|
|
|
|
{
|
|
|
|
plSDL::DescriptorList* descList = &plSDLMgr::GetInstance()->fDescriptors;
|
|
|
|
|
|
|
|
bool ok = true;
|
|
|
|
|
|
|
|
//
|
|
|
|
// NAME
|
|
|
|
//
|
|
|
|
// curDesc=plSDLMgr::GetInstance()->FindDescriptor(token, plSDL::kLatestVersion);
|
|
|
|
// if (!curDesc)
|
|
|
|
{
|
|
|
|
curDesc = new plStateDescriptor;
|
|
|
|
curDesc->SetName(token);
|
|
|
|
|
|
|
|
DebugMsg("SDL: DESC name=%s", token);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// {
|
|
|
|
//
|
|
|
|
stream->GetToken(token, kTokenLen); // skip '{'
|
|
|
|
|
|
|
|
//
|
|
|
|
// VERSION
|
|
|
|
//
|
|
|
|
if (stream->GetToken(token, kTokenLen))
|
|
|
|
{
|
|
|
|
if (!strcmp(token, "VERSION"))
|
|
|
|
{
|
|
|
|
// read desc version
|
|
|
|
hsAssert(curDesc, plString::Format("Syntax problem with .sdl file, fileName=%s", fileName).c_str());
|
|
|
|
if (stream->GetToken(token, kTokenLen))
|
|
|
|
{
|
|
|
|
int v=atoi(token);
|
|
|
|
curDesc->SetVersion(v);
|
|
|
|
DebugMsg("\tVersion=%d", v);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsAssert(false, plString::Format("Error parsing state desc, missing VERSION, fileName=%s",
|
|
|
|
fileName).c_str());
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsAssert(false, plString::Format("Error parsing state desc, fileName=%s", fileName).c_str());
|
|
|
|
ok = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ok )
|
|
|
|
{
|
|
|
|
ok = ( plSDLMgr::GetInstance()->FindDescriptor(curDesc->GetName(), curDesc->GetVersion())==nil );
|
|
|
|
if ( !ok )
|
|
|
|
{
|
|
|
|
plString err = plString::Format( "Found duplicate SDL descriptor for %s version %d.\nFailed to parse file: %s",
|
|
|
|
curDesc->GetName().c_str(), curDesc->GetVersion(), fileName );
|
|
|
|
plNetApp::StaticErrorMsg( err.c_str() );
|
|
|
|
hsAssert( false, err.c_str() );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( ok )
|
|
|
|
{
|
|
|
|
descList->push_back(curDesc);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
delete curDesc;
|
|
|
|
curDesc = nil;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// Parse a variable descriptor.
|
|
|
|
// read type, name, count [default]
|
|
|
|
// return true to skip the next token read
|
|
|
|
//
|
|
|
|
bool plSDLParser::IParseVarDesc(const char* fileName, hsStream* stream, char token[], plStateDescriptor*& curDesc,
|
|
|
|
plVarDescriptor*& curVar) const
|
|
|
|
{
|
|
|
|
hsAssert(curDesc, plString::Format("Syntax problem with .sdl file, fileName=%s", fileName).c_str());
|
|
|
|
if ( !curDesc )
|
|
|
|
return false;
|
|
|
|
|
|
|
|
bool skipNext=false;
|
|
|
|
plString dbgStr;
|
|
|
|
static char seps[] = "( ,)[]";
|
|
|
|
// read type, name, cnt, [default]
|
|
|
|
|
|
|
|
//
|
|
|
|
// TYPE
|
|
|
|
// create new state var, make current
|
|
|
|
//
|
|
|
|
if (*token == '$')
|
|
|
|
{
|
|
|
|
// nested sdls
|
|
|
|
char* sdlName = token+1;
|
|
|
|
plStateDescriptor* stateDesc = plSDLMgr::GetInstance()->FindDescriptor(sdlName, plSDL::kLatestVersion);
|
|
|
|
hsAssert(stateDesc, plString::Format("can't find nested state desc reference %s, fileName=%s",
|
|
|
|
sdlName, fileName).c_str());
|
|
|
|
curVar = new plSDVarDescriptor(stateDesc);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
curVar = new plSimpleVarDescriptor;
|
|
|
|
|
|
|
|
curDesc->AddVar(curVar);
|
|
|
|
bool ok=curVar->SetType(token);
|
|
|
|
hsAssert(ok, plString::Format("Variable 'type' syntax problem with .sdl file, type=%s, fileName=%s", token, fileName).c_str());
|
|
|
|
dbgStr = plString::Format("\tVAR Type=%s ", token);
|
|
|
|
|
|
|
|
//
|
|
|
|
// NAME (foo[1])
|
|
|
|
//
|
|
|
|
if (stream->GetToken(token, kTokenLen))
|
|
|
|
{
|
|
|
|
hsAssert(strstr(token, "[") && strstr(token, "]"), plString::Format("invalid var syntax, missing [x], fileName=%s",
|
|
|
|
fileName).c_str());
|
|
|
|
char* ptr = strtok( token, seps ); // skip [
|
|
|
|
|
|
|
|
hsAssert(curVar, plString::Format("Missing current var. Syntax problem with .sdl file, fileName=%s", fileName).c_str());
|
|
|
|
curVar->SetName(token);
|
|
|
|
//
|
|
|
|
// COUNT
|
|
|
|
//
|
|
|
|
char* cntTok=strtok(nil, seps); // kill ]
|
|
|
|
int cnt = cntTok ? atoi(cntTok) : 0;
|
|
|
|
curVar->SetCount(cnt);
|
|
|
|
if (cnt==0)
|
|
|
|
curVar->SetVariableLength(true);
|
|
|
|
dbgStr += plString::Format("Name=%s[%d]", curVar->GetName().c_str(), cnt);
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// optional tokens: DEFAULT, INTERNAL
|
|
|
|
//
|
|
|
|
while (stream->GetToken(token, kTokenLen))
|
|
|
|
{
|
|
|
|
if (!strcmp(token, "DEFAULT"))
|
|
|
|
{
|
|
|
|
hsAssert(curVar, plString::Format("Syntax problem with .sdl file, fileName=%s", fileName).c_str());
|
|
|
|
// read state var type
|
|
|
|
|
|
|
|
plString defaultStr;
|
|
|
|
plSimpleVarDescriptor* sVar=(plSimpleVarDescriptor*)curVar;
|
|
|
|
if (sVar)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
for(i=0;i<sVar->GetAtomicCount();i++)
|
|
|
|
{
|
|
|
|
if (stream->GetToken(token, kTokenLen))
|
|
|
|
{
|
|
|
|
defaultStr += token;
|
|
|
|
if (i!=sVar->GetAtomicCount()-1)
|
|
|
|
defaultStr += ",";
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
if (!defaultStr.IsEmpty())
|
|
|
|
{
|
|
|
|
curVar->SetDefault(defaultStr);
|
|
|
|
dbgStr += " DEFAULT=" + defaultStr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (!strcmp(token, "DISPLAYOPTION"))
|
|
|
|
{
|
|
|
|
hsAssert(curVar, plString::Format("Syntax problem with .sdl file, fileName=%s", fileName).c_str());
|
|
|
|
dbgStr += plString(" ") + token;
|
|
|
|
|
|
|
|
bool read=stream->GetToken(token, kTokenLen);
|
|
|
|
if (read)
|
|
|
|
{
|
|
|
|
plString oldOptions=curVar->GetDisplayOptions();
|
|
|
|
if (!oldOptions.IsEmpty())
|
|
|
|
oldOptions += ",";
|
|
|
|
oldOptions += token;
|
|
|
|
curVar->SetDisplayOptions(oldOptions);
|
|
|
|
dbgStr += plString("=") + token;
|
|
|
|
if (!stricmp(token, "hidden"))
|
|
|
|
curVar->SetInternal(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsAssert(false, plString::Format("missing displayOption string, fileName=%s", fileName).c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (!strcmp(token, "DEFAULTOPTION"))
|
|
|
|
{
|
|
|
|
hsAssert(curVar, plString::Format("Syntax problem with .sdl file, fileName=%s", fileName).c_str());
|
|
|
|
dbgStr += plString(" ") + token;
|
|
|
|
|
|
|
|
bool read=stream->GetToken(token, kTokenLen);
|
|
|
|
if (read)
|
|
|
|
{
|
|
|
|
dbgStr += plString("=") + token;
|
|
|
|
if (!stricmp(token, "vault"))
|
|
|
|
curVar->SetAlwaysNew(true);
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
hsAssert(false, plString::Format("missing defaultOption string, fileName=%s", fileName).c_str());
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
#if 1 // delete me in May 2003
|
|
|
|
else
|
|
|
|
if (!strcmp(token, "INTERNAL"))
|
|
|
|
{
|
|
|
|
hsAssert(curVar, plString::Format("Syntax problem with .sdl file, fileName=%s", fileName).c_str());
|
|
|
|
curVar->SetInternal(true);
|
|
|
|
dbgStr += plString(" ") + token;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (!strcmp(token, "PHASED"))
|
|
|
|
{
|
|
|
|
hsAssert(curVar, plString::Format("Syntax problem with .sdl file, fileName=%s", fileName).c_str());
|
|
|
|
curVar->SetAlwaysNew(true);
|
|
|
|
dbgStr += plString(" ") + token;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
else
|
|
|
|
{
|
|
|
|
skipNext=true;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
DebugMsg(dbgStr.c_str());
|
|
|
|
|
|
|
|
return skipNext;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// create state descriptor from sdl file.
|
|
|
|
// return false on err.
|
|
|
|
//
|
|
|
|
bool plSDLParser::ILoadSDLFile(const char* fileName) const
|
|
|
|
{
|
|
|
|
DebugMsg("Parsing SDL file %s", fileName);
|
|
|
|
|
|
|
|
wchar_t* temp = hsStringToWString(fileName);
|
|
|
|
hsStream* stream = plStreamSource::GetInstance()->GetFile(temp);
|
|
|
|
delete [] temp;
|
|
|
|
if (!stream)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
stream->Rewind();
|
|
|
|
|
|
|
|
plVarDescriptor* curVar=nil;
|
|
|
|
plStateDescriptor* curDesc=nil;
|
|
|
|
char token[kTokenLen];
|
|
|
|
bool parsingStateDesc=false;
|
|
|
|
bool skip=false;
|
|
|
|
while (1)
|
|
|
|
{
|
|
|
|
if (!skip)
|
|
|
|
{
|
|
|
|
if (!stream->GetToken(token, kTokenLen))
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
skip=false;
|
|
|
|
|
|
|
|
if (!strcmp(token, "VAR"))
|
|
|
|
{
|
|
|
|
parsingStateDesc=false;
|
|
|
|
curVar=nil; // start fresh
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(token, "STATEDESC"))
|
|
|
|
{
|
|
|
|
parsingStateDesc=true;
|
|
|
|
curDesc=nil; // start fresh
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(token, "}"))
|
|
|
|
{
|
|
|
|
if ( curDesc )
|
|
|
|
curDesc->SetFilename( fileName );
|
|
|
|
parsingStateDesc=false;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (parsingStateDesc)
|
|
|
|
{
|
|
|
|
skip=IParseStateDesc(fileName, stream, token, curDesc);
|
|
|
|
if ( !curDesc )
|
|
|
|
break; // failed to parse state desc
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
skip=IParseVarDesc(fileName, stream, token, curDesc, curVar);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// If the very last char is a } without a \n, then it won't be handled above for some reason, so we have to catch it here.
|
|
|
|
if ( curDesc )
|
|
|
|
curDesc->SetFilename( fileName );
|
|
|
|
|
|
|
|
// do not close or delete the stream, we do not own it
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//
|
|
|
|
// load all .sdl files in sdl directory, and create descriptors for each.
|
|
|
|
// return false on error
|
|
|
|
//
|
|
|
|
bool plSDLParser::IReadDescriptors() const
|
|
|
|
{
|
|
|
|
std::string sdlDir = plSDLMgr::GetInstance()->GetSDLDir();
|
|
|
|
DebugMsg("SDL: Reading latest descriptors from directory %s", sdlDir.c_str());
|
|
|
|
|
|
|
|
wchar_t* temp = hsStringToWString(sdlDir.c_str());
|
|
|
|
std::wstring wSDLDir = temp;
|
|
|
|
delete [] temp;
|
|
|
|
|
|
|
|
// Get the names of all the sdl files
|
|
|
|
std::vector<std::wstring> files = plStreamSource::GetInstance()->GetListOfNames(wSDLDir, L".sdl");
|
|
|
|
|
|
|
|
bool ret=true;
|
|
|
|
int cnt=0;
|
|
|
|
for (int i = 0; i < files.size(); i++)
|
|
|
|
{
|
|
|
|
char* str = hsWStringToString(files[i].c_str());
|
|
|
|
if (!ILoadSDLFile(str))
|
|
|
|
{
|
|
|
|
plNetApp* netApp = plSDLMgr::GetInstance()->GetNetApp();
|
|
|
|
if (netApp)
|
|
|
|
netApp->ErrorMsg("Error loading SDL file %s", str);
|
|
|
|
else
|
|
|
|
hsStatusMessageF("Error loading SDL file %s", str);
|
|
|
|
ret=false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
cnt++;
|
|
|
|
delete [] str;
|
|
|
|
}
|
|
|
|
DebugMsg("Done reading SDL files");
|
|
|
|
|
|
|
|
if (!cnt)
|
|
|
|
ret=false;
|
|
|
|
|
|
|
|
return ret;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
//
|
|
|
|
// reads sdl folder, creates descriptor list
|
|
|
|
//
|
|
|
|
bool plSDLParser::Parse() const
|
|
|
|
{
|
|
|
|
return IReadDescriptors();
|
|
|
|
}
|