// this include must remain at the top of every CPP file //Anything above this #include will be ignored by the compiler #include "../qcommon/exe_headers.h" #if !defined(GENERICPARSER2_H_INC) #include "GenericParser2.h" #endif #define _EXE #define MAX_TOKEN_SIZE 1024 static char token[MAX_TOKEN_SIZE]; static char *GetToken(char **text, bool allowLineBreaks, bool readUntilEOL = false) { char *pointer = *text; int length = 0; int c = 0; bool foundLineBreak; token[0] = 0; if (!pointer) { return token; } while(1) { foundLineBreak = false; while(1) { c = *pointer; if (c > ' ') { break; } if (!c) { *text = 0; return token; } if (c == '\n') { foundLineBreak = true; } pointer++; } if (foundLineBreak && !allowLineBreaks) { *text = pointer; return token; } c = *pointer; // skip single line comment if (c == '/' && pointer[1] == '/') { pointer += 2; while (*pointer && *pointer != '\n') { pointer++; } } // skip multi line comments else if (c == '/' && pointer[1] == '*') { pointer += 2; while (*pointer && (*pointer != '*' || pointer[1] != '/')) { pointer++; } if (*pointer) { pointer += 2; } } else { // found the start of a token break; } } if (c == '\"') { // handle a string pointer++; while (1) { c = *pointer++; if (c == '\"') { // token[length++] = c; break; } else if (!c) { break; } else if (length < MAX_TOKEN_SIZE) { token[length++] = c; } } } else if (readUntilEOL) { // absorb all characters until EOL while(c != '\n' && c != '\r') { if (c == '/' && ((*(pointer+1)) == '/' || (*(pointer+1)) == '*')) { break; } if (length < MAX_TOKEN_SIZE) { token[length++] = c; } pointer++; c = *pointer; } // remove trailing white space while(length && token[length-1] < ' ') { length--; } } else { while(c > ' ') { if (length < MAX_TOKEN_SIZE) { token[length++] = c; } pointer++; c = *pointer; } } if (token[0] == '\"') { // remove start quote length--; memmove(token, token+1, length); if (length && token[length-1] == '\"') { // remove end quote length--; } } if (length >= MAX_TOKEN_SIZE) { length = 0; } token[length] = 0; *text = (char *)pointer; return token; } CTextPool::CTextPool(int initSize) : mNext(0), mSize(initSize), mUsed(0) { #ifdef _EXE // mPool = (char *)Z_Malloc(mSize, TAG_GP2); mPool = (char *)Z_Malloc(mSize, TAG_TEXTPOOL, qtrue); #else mPool = (char *)trap_Z_Malloc(mSize, TAG_GP2); #endif } CTextPool::~CTextPool(void) { #ifdef _EXE Z_Free(mPool); #else trap_Z_Free(mPool); #endif } char *CTextPool::AllocText(char *text, bool addNULL, CTextPool **poolPtr) { int length = strlen(text) + (addNULL ? 1 : 0); if (mUsed + length + 1> mSize) { // extra 1 to put a null on the end if (poolPtr) { (*poolPtr)->SetNext(new CTextPool(mSize)); *poolPtr = (*poolPtr)->GetNext(); return (*poolPtr)->AllocText(text, addNULL); } return 0; } strcpy(mPool + mUsed, text); mUsed += length; mPool[mUsed] = 0; return mPool + mUsed - length; } void CleanTextPool(CTextPool *pool) { CTextPool *next; while(pool) { next = pool->GetNext(); delete pool; pool = next; } } CGPObject::CGPObject(const char *initName) : mName(initName), mNext(0), mInOrderNext(0), mInOrderPrevious(0) { } bool CGPObject::WriteText(CTextPool **textPool, const char *text) { if (strchr(text, ' ') || !text[0]) { (*textPool)->AllocText("\"", false, textPool); (*textPool)->AllocText((char *)text, false, textPool); (*textPool)->AllocText("\"", false, textPool); } else { (*textPool)->AllocText((char *)text, false, textPool); } return true; } CGPValue::CGPValue(const char *initName, const char *initValue) : CGPObject(initName), mList(0) { if (initValue) { AddValue(initValue); } } CGPValue::~CGPValue(void) { CGPObject *next; while(mList) { next = mList->GetNext(); delete mList; mList = next; } } CGPValue *CGPValue::Duplicate(CTextPool **textPool) { CGPValue *newValue; CGPObject *iterator; char *name; if (textPool) { name = (*textPool)->AllocText((char *)mName, true, textPool); } else { name = (char *)mName; } newValue = new CGPValue(name); iterator = mList; while(iterator) { if (textPool) { name = (*textPool)->AllocText((char *)iterator->GetName(), true, textPool); } else { name = (char *)iterator->GetName(); } newValue->AddValue(name); iterator = iterator->GetNext(); } return newValue; } bool CGPValue::IsList(void) { if (!mList || !mList->GetNext()) { return false; } return true; } const char *CGPValue::GetTopValue(void) { if (mList) { return mList->GetName(); } return 0; } void CGPValue::AddValue(const char *newValue, CTextPool **textPool) { if (textPool) { newValue = (*textPool)->AllocText((char *)newValue, true, textPool); } if (mList == 0) { mList = new CGPObject(newValue); mList->SetInOrderNext(mList); } else { mList->GetInOrderNext()->SetNext(new CGPObject(newValue)); mList->SetInOrderNext(mList->GetInOrderNext()->GetNext()); } } bool CGPValue::Parse(char **dataPtr, CTextPool **textPool) { char *token; char *value; while(1) { token = GetToken(dataPtr, true, true); if (!token[0]) { // end of data - error! return false; } else if (Q_stricmp(token, "]") == 0) { // ending brace for this list break; } value = (*textPool)->AllocText(token, true, textPool); AddValue(value); } return true; } bool CGPValue::Write(CTextPool **textPool, int depth) { int i; CGPObject *next; if (!mList) { return true; } for(i=0;iAllocText("\t", false, textPool); } WriteText(textPool, mName); if (!mList->GetNext()) { (*textPool)->AllocText("\t\t", false, textPool); mList->WriteText(textPool, mList->GetName()); (*textPool)->AllocText("\r\n", false, textPool); } else { (*textPool)->AllocText("\r\n", false, textPool); for(i=0;iAllocText("\t", false, textPool); } (*textPool)->AllocText("[\r\n", false, textPool); next = mList; while(next) { for(i=0;iAllocText("\t", false, textPool); } mList->WriteText(textPool, next->GetName()); (*textPool)->AllocText("\r\n", false, textPool); next = next->GetNext(); } for(i=0;iAllocText("\t", false, textPool); } (*textPool)->AllocText("]\r\n", false, textPool); } return true; } CGPGroup::CGPGroup(const char *initName, CGPGroup *initParent) : CGPObject(initName), mPairs(0), mInOrderPairs(0), mCurrentPair(0), mSubGroups(0), mInOrderSubGroups(0), mCurrentSubGroup(0), mParent(initParent), mWriteable(false) { } CGPGroup::~CGPGroup(void) { Clean(); } int CGPGroup::GetNumSubGroups(void) { int count; CGPGroup *group; count = 0; group = mSubGroups; do { count++; group = (CGPGroup *)group->GetNext(); } while(group); return(count); } int CGPGroup::GetNumPairs(void) { int count; CGPValue *pair; count = 0; pair = mPairs; do { count++; pair = (CGPValue *)pair->GetNext(); } while(pair); return(count); } void CGPGroup::Clean(void) { while(mPairs) { mCurrentPair = (CGPValue *)mPairs->GetNext(); delete mPairs; mPairs = mCurrentPair; } while(mSubGroups) { mCurrentSubGroup = (CGPGroup *)mSubGroups->GetNext(); delete mSubGroups; mSubGroups = mCurrentSubGroup; } mPairs = mInOrderPairs = mCurrentPair = 0; mSubGroups = mInOrderSubGroups = mCurrentSubGroup = 0; mParent = 0; mWriteable = false; } CGPGroup *CGPGroup::Duplicate(CTextPool **textPool, CGPGroup *initParent) { CGPGroup *newGroup, *subSub, *newSub; CGPValue *newPair, *subPair; char *name; if (textPool) { name = (*textPool)->AllocText((char *)mName, true, textPool); } else { name = (char *)mName; } newGroup = new CGPGroup(name); subSub = mSubGroups; while(subSub) { newSub = subSub->Duplicate(textPool, newGroup); newGroup->AddGroup(newSub); subSub = (CGPGroup *)subSub->GetNext(); } subPair = mPairs; while(subPair) { newPair = subPair->Duplicate(textPool); newGroup->AddPair(newPair); subPair = (CGPValue *)subPair->GetNext(); } return newGroup; } void CGPGroup::SortObject(CGPObject *object, CGPObject **unsortedList, CGPObject **sortedList, CGPObject **lastObject) { CGPObject *test, *last; if (!*unsortedList) { *unsortedList = *sortedList = object; } else { (*lastObject)->SetNext(object); test = *sortedList; last = 0; while(test) { if (Q_stricmp(object->GetName(), test->GetName()) < 0) { break; } last = test; test = test->GetInOrderNext(); } if (test) { test->SetInOrderPrevious(object); object->SetInOrderNext(test); } if (last) { last->SetInOrderNext(object); object->SetInOrderPrevious(last); } else { *sortedList = object; } } *lastObject = object; } CGPValue *CGPGroup::AddPair(const char *name, const char *value, CTextPool **textPool) { CGPValue *newPair; if (textPool) { name = (*textPool)->AllocText((char *)name, true, textPool); if (value) { value = (*textPool)->AllocText((char *)value, true, textPool); } } newPair = new CGPValue(name, value); AddPair(newPair); return newPair; } void CGPGroup::AddPair(CGPValue *NewPair) { SortObject(NewPair, (CGPObject **)&mPairs, (CGPObject **)&mInOrderPairs, (CGPObject **)&mCurrentPair); } CGPGroup *CGPGroup::AddGroup(const char *name, CTextPool **textPool) { CGPGroup *newGroup; if (textPool) { name = (*textPool)->AllocText((char *)name, true, textPool); } newGroup = new CGPGroup(name, this); AddGroup(newGroup); return newGroup; } void CGPGroup::AddGroup(CGPGroup *NewGroup) { SortObject(NewGroup, (CGPObject **)&mSubGroups, (CGPObject **)&mInOrderSubGroups, (CGPObject **)&mCurrentSubGroup); } CGPGroup *CGPGroup::FindSubGroup(const char *name) { CGPGroup *group; group = mSubGroups; while(group) { if(!Q_stricmp(name, group->GetName())) { return(group); } group = (CGPGroup *)group->GetNext(); } return(NULL); } bool CGPGroup::Parse(char **dataPtr, CTextPool **textPool) { char *token; char lastToken[MAX_TOKEN_SIZE]; CGPGroup *newSubGroup; CGPValue *newPair; while(1) { token = GetToken(dataPtr, true); if (!token[0]) { // end of data - error! if (mParent) { return false; } else { break; } } else if (Q_stricmp(token, "}") == 0) { // ending brace for this group break; } strcpy(lastToken, token); // read ahead to see what we are doing token = GetToken(dataPtr, true, true); if (Q_stricmp(token, "{") == 0) { // new sub group newSubGroup = AddGroup(lastToken, textPool); newSubGroup->SetWriteable(mWriteable); if (!newSubGroup->Parse(dataPtr, textPool)) { return false; } } else if (Q_stricmp(token, "[") == 0) { // new pair list newPair = AddPair(lastToken, 0, textPool); if (!newPair->Parse(dataPtr, textPool)) { return false; } } else { // new pair AddPair(lastToken, token, textPool); } } return true; } bool CGPGroup::Write(CTextPool **textPool, int depth) { int i; CGPValue *mPair = mPairs; CGPGroup *mSubGroup = mSubGroups; if (depth >= 0) { for(i=0;iAllocText("\t", false, textPool); } WriteText(textPool, mName); (*textPool)->AllocText("\r\n", false, textPool); for(i=0;iAllocText("\t", false, textPool); } (*textPool)->AllocText("{\r\n", false, textPool); } while(mPair) { mPair->Write(textPool, depth+1); mPair = (CGPValue *)mPair->GetNext(); } while(mSubGroup) { mSubGroup->Write(textPool, depth+1); mSubGroup = (CGPGroup *)mSubGroup->GetNext(); } if (depth >= 0) { for(i=0;iAllocText("\t", false, textPool); } (*textPool)->AllocText("}\r\n", false, textPool); } return true; } /************************************************************************************************ * CGPGroup::FindPair * This function will search for the pair with the specified key name. Multiple keys may be * searched if you specify "||" inbetween each key name in the string. The first key to be * found (from left to right) will be returned. * * Input * key: the name of the key(s) to be searched for. * * Output / Return * the group belonging to the first key found or 0 if no group was found. * ************************************************************************************************/ CGPValue *CGPGroup::FindPair(const char *key) { CGPValue *pair; int length; const char *pos, *separator, *next; pos = key; while(pos[0]) { separator = strstr(pos, "||"); if (separator) { length = separator - pos; next = separator + 2; } else { length = strlen(pos); next = pos + length; } pair = mPairs; while(pair) { if (strlen(pair->GetName()) == length && Q_stricmpn(pair->GetName(), pos, length) == 0) { return pair; } pair = pair->GetNext(); } pos = next; } return 0; } const char *CGPGroup::FindPairValue(const char *key, const char *defaultVal) { CGPValue *pair = FindPair(key); if (pair) { return pair->GetTopValue(); } return defaultVal; } CGenericParser2::CGenericParser2(void) : mTextPool(0), mWriteable(false) { } CGenericParser2::~CGenericParser2(void) { Clean(); } bool CGenericParser2::Parse(char **dataPtr, bool cleanFirst, bool writeable) { CTextPool *topPool; #ifdef _XBOX // Parsers are temporary structures. They exist mainly at load time. extern void Z_SetNewDeleteTemporary(bool bTemp); Z_SetNewDeleteTemporary(true); #endif if (cleanFirst) { Clean(); } if (!mTextPool) { mTextPool = new CTextPool; } SetWriteable(writeable); mTopLevel.SetWriteable(writeable); topPool = mTextPool; bool ret = mTopLevel.Parse(dataPtr, &topPool); #ifdef _XBOX Z_SetNewDeleteTemporary(false); #endif return ret; } void CGenericParser2::Clean(void) { mTopLevel.Clean(); CleanTextPool(mTextPool); mTextPool = 0; } bool CGenericParser2::Write(CTextPool *textPool) { return mTopLevel.Write(&textPool, -1); } // The following groups of routines are used for a C interface into GP2. // C++ users should just use the objects as normally and not call these routines below // // CGenericParser2 (void *) routines TGenericParser2 GP_Parse(char **dataPtr, bool cleanFirst, bool writeable) { CGenericParser2 *parse; parse = new CGenericParser2; if (parse->Parse(dataPtr, cleanFirst, writeable)) { return parse; } delete parse; return 0; } void GP_Clean(TGenericParser2 GP2) { if (!GP2) { return; } ((CGenericParser2 *)GP2)->Clean(); } void GP_Delete(TGenericParser2 *GP2) { if (!GP2 || !(*GP2)) { return; } delete ((CGenericParser2 *)(*GP2)); (*GP2) = 0; } TGPGroup GP_GetBaseParseGroup(TGenericParser2 GP2) { if (!GP2) { return 0; } return ((CGenericParser2 *)GP2)->GetBaseParseGroup(); } // CGPGroup (void *) routines const char *GPG_GetName(TGPGroup GPG) { if (!GPG) { return ""; } return ((CGPGroup *)GPG)->GetName(); } bool GPG_GetName(TGPGroup GPG, char *Value) { if (!GPG) { Value[0] = 0; return false; } strcpy(Value, ((CGPGroup *)GPG)->GetName()); return true; } TGPGroup GPG_GetNext(TGPGroup GPG) { if (!GPG) { return 0; } return ((CGPGroup *)GPG)->GetNext(); } TGPGroup GPG_GetInOrderNext(TGPGroup GPG) { if (!GPG) { return 0; } return ((CGPGroup *)GPG)->GetInOrderNext(); } TGPGroup GPG_GetInOrderPrevious(TGPGroup GPG) { if (!GPG) { return 0; } return ((CGPGroup *)GPG)->GetInOrderPrevious(); } TGPGroup GPG_GetPairs(TGPGroup GPG) { if (!GPG) { return 0; } return ((CGPGroup *)GPG)->GetPairs(); } TGPGroup GPG_GetInOrderPairs(TGPGroup GPG) { if (!GPG) { return 0; } return ((CGPGroup *)GPG)->GetInOrderPairs(); } TGPGroup GPG_GetSubGroups(TGPGroup GPG) { if (!GPG) { return 0; } return ((CGPGroup *)GPG)->GetSubGroups(); } TGPGroup GPG_GetInOrderSubGroups(TGPGroup GPG) { if (!GPG) { return 0; } return ((CGPGroup *)GPG)->GetInOrderSubGroups(); } TGPGroup GPG_FindSubGroup(TGPGroup GPG, const char *name) { if (!GPG) { return 0; } return ((CGPGroup *)GPG)->FindSubGroup(name); } TGPValue GPG_FindPair(TGPGroup GPG, const char *key) { if (!GPG) { return 0; } return ((CGPGroup *)GPG)->FindPair(key); } const char *GPG_FindPairValue(TGPGroup GPG, const char *key, const char *defaultVal) { if (!GPG) { return defaultVal; } return ((CGPGroup *)GPG)->FindPairValue(key, defaultVal); } bool GPG_FindPairValue(TGPGroup GPG, const char *key, const char *defaultVal, char *Value) { strcpy(Value, GPG_FindPairValue(GPG, key, defaultVal)); return true; } // CGPValue (void *) routines const char *GPV_GetName(TGPValue GPV) { if (!GPV) { return ""; } return ((CGPValue *)GPV)->GetName(); } bool GPV_GetName(TGPValue GPV, char *Value) { if (!GPV) { Value[0] = 0; return false; } strcpy(Value, ((CGPValue *)GPV)->GetName()); return true; } TGPValue GPV_GetNext(TGPValue GPV) { if (!GPV) { return 0; } return ((CGPValue *)GPV)->GetNext(); } TGPValue GPV_GetInOrderNext(TGPValue GPV) { if (!GPV) { return 0; } return ((CGPValue *)GPV)->GetInOrderNext(); } TGPValue GPV_GetInOrderPrevious(TGPValue GPV) { if (!GPV) { return 0; } return ((CGPValue *)GPV)->GetInOrderPrevious(); } bool GPV_IsList(TGPValue GPV) { if (!GPV) { return 0; } return ((CGPValue *)GPV)->IsList(); } const char *GPV_GetTopValue(TGPValue GPV) { if (!GPV) { return ""; } return ((CGPValue *)GPV)->GetTopValue(); } bool GPV_GetTopValue(TGPValue GPV, char *Value) { if (!GPV) { Value[0] = 0; return false; } strcpy(Value, ((CGPValue *)GPV)->GetTopValue()); return true; } TGPValue GPV_GetList(TGPValue GPV) { if (!GPV) { return 0; } return ((CGPValue *)GPV)->GetList(); }