#include "gosGml.hpp"

extern __int64 TimeParse;
//===========================================================================//
// File:	 GmlParser.cpp													 //
// Project:  gosGml				                                         //
// Contents:																 //
//---------------------------------------------------------------------------//
// Date      Who    Modification                                             //
// 4/10/00   DP     Created													 //
//---------------------------------------------------------------------------//
// Copyright (C) 2000 Microsoft Corp.						                 //
// All Rights reserved worldwide                                             //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL               //
//===========================================================================//

#include "Gmlparser.h"

GmlParser::GmlParser(char* fontPath, char* imagePath)
{
	FontPath=new char[mstrlen(fontPath)+1];
	mstrcpy(FontPath,fontPath);
	GmlFontStack=new GStack<GmlFont>;
	ImagePath=new char[mstrlen(imagePath)+1];
	mstrcpy(ImagePath,imagePath);
}

GmlParser::~GmlParser()
{
	delete[] ImagePath;
	delete[] FontPath;
	delete GmlFontStack;
}

void GmlParser::SetCallback(ParserCallback* PCB)
{
	TagCallback=PCB;
}

//parse the Gml, flushing when required
bool GmlParser::Parse(char* Gml, GQueue<GmlParsedString>* DisplayQueueT, TextDrawBuffer* TextBufferT, GmlFont* Default)
{
	#ifdef LAB_ONLY
		__int64 temp=GetCycles();
	#endif
	GmlFontStack->Push(Default);
	DisplayQueue=DisplayQueueT;
	TextBuffer=TextBufferT;

	bool StateA=false;
	bool StateP=false;
	bool StateBold=false;
	bool StateItalic=false;
	bool StateUL=false;
	bool StateOL=false;
	bool StateLI=false;

	int CountLI=0;
	int CountGmlFont=0;//this CountGmlFont is a BAD thing. it really should be removed.
	char* StartOfPlainText=Gml;
	char* StartOfATag=0;
	char* StartOfTag;
	char* EndOfTag;

	while(*Gml)
	{
		if(*Gml=='<')
		{
			//we have encountered a tag.
			StartOfTag=Gml;
			if(*(Gml+1)=='<')
			{
				BufferText(StartOfTag,StartOfPlainText);
				TextBuffer->Append("<",1);
				Gml+=2;
				StartOfPlainText=Gml;//Plain text starts again.
				continue;
			}
			EndOfTag=GrabTag(Gml);
			Gml=mbpinc(Gml);
			if(*EndOfTag!='>') //this needs work
				if(*EndOfTag==0 && *(EndOfTag-1)=='p')
				{
					BufferText(StartOfTag,StartOfPlainText);
					Flush(GmlParsedString_PlainText);
					Flush(GmlParsedString_br);
					break; //exit while loop
				}
				else
					STOP(( "gos_TextDrawGml: Corrupt end tag in Gml." )); //incomplete tags are bad. File is corrupted.
			switch(tolower(*Gml))
			{
				case '/':	//</p></a></font> etc
					Gml=mbpinc(Gml);
					switch(tolower(*(Gml)))
					{
						case 'a':
							{
								if(!StateA)
									STOP(("gos_TextDrawGml: found </a> without preceeding <a>."));
								StateA=false;
								BufferText(StartOfTag,StartOfPlainText);
								//Flush(GmlParsedString_PlainText);
								char* ValueStart=FindNextStrInTag(StartOfATag,"href");
								ValueStart=GrabNextQ(ValueStart);
								ValueStart=Chomp(ValueStart);
								char* lastQ=GrabNextQ(ValueStart);
								char* ValueEnd=RChomp(lastQ-1);
								int size = (ValueEnd - ValueStart);
								char * freeLater = new char[size+1];
								strncpy(freeLater, ValueStart, size);
								freeLater[size] = 0;
								ValueStart=FindNextStrInTag(StartOfATag,"name");
								if(!ValueStart)
									Flush(GmlParsedString_Link, freeLater); //flush the link item
								else
								{
									ValueStart=GrabNextQ(ValueStart);
									ValueStart=Chomp(ValueStart);
									lastQ=GrabNextQ(ValueStart);
									ValueEnd=RChomp(lastQ-1);
									size = (ValueEnd - ValueStart);
									char* freeLater2 = new char[size+1];
									strncpy(freeLater2, ValueStart, size);
									freeLater2[size] = 0;
									Flush(GmlParsedString_Link, freeLater, freeLater2);
								}
								Gml=EndOfTag+1;
								break;
							}
						case 's':	//</strong>
						case 'b':	//</bold> - turn off bold
							BufferText(StartOfTag,StartOfPlainText);
							Gml=EndOfTag+1;
							if(StateBold)
							{
								StateBold=false;
								TextBuffer->Append("/bold=0:",7); //move it into the buffer
							}
							break;
						case 'f':
							{//</font>
								if(!CountGmlFont)
									STOP(("gos_TextDrawGml: found </font> without preceeding <font>."));
								BufferText(StartOfTag,StartOfPlainText);
								Flush(GmlParsedString_PlainText); //flush the buffer because we are changing fonts
								CountGmlFont--;
								GmlFont* f=GmlFontStack->Pop();
								if(f) 
									delete f;
								Gml=EndOfTag+1;
								break;
							}
						case 'e':	//</EM>
						case 'i':	//italic
							BufferText(StartOfTag,StartOfPlainText);
							Gml=EndOfTag+1;
							if(StateItalic)
							{
								StateItalic=false;
								TextBuffer->Append("/italic=0:",9); //move it into the buffer
							}
							break;
						//This is taken out because the DHTML edit control which is used to generate the HTML for this... SUCKS!
						//DHTML edit control bug workaround
						//case 'l':	//</LI> - list line
						//	if(!StateLI)
						//		STOP(("gos_TextDrawGml: found unexpected </LI>."));
						//	StateLI=false;
						//	BufferText(StartOfTag,StartOfPlainText);
						//	Flush(GmlParsedString_PlainText);
						//	Flush(GmlParsedString_br);
						//	Gml=EndOfTag+1;
						//	break;
						case 'o':	//</OL> - numbered list
							if(!StateOL)
								STOP(("gos_TextDrawGml: found </ol> without preceeding <ol>."));
							StateOL=false;
							StateLI=false;	//DHTML edit control bug workaround
							BufferText(StartOfTag,StartOfPlainText);
							Flush(GmlParsedString_PlainText);
							Flush(GmlParsedString_br);
							Gml=EndOfTag+1;
							break;
						case 'p': //</p> - the end of a paragraph of text
							if(!StateP)
								STOP(("gos_TextDrawGml: Cannot have </p> without preceeding <p>."));
							StateP=false;
							Gml=EndOfTag+1;
							BufferText(StartOfTag,StartOfPlainText);
							Flush(GmlParsedString_PlainText);
							Flush(GmlParsedString_br);
							ChompToTag(Gml);
							break;
						case 'u':	//</UL> - numbered list
							if(!StateUL)
								STOP(("gos_TextDrawGml: found </ul> without preceeding <ul>."));
							StateUL=false;
							StateLI=false;	//DHTML edit control bug workaround
							BufferText(StartOfTag,StartOfPlainText);
							Flush(GmlParsedString_PlainText);
							Flush(GmlParsedString_br);
							Gml=EndOfTag+1;
							break;
						default:	//Unsupported/invalid tag
							#ifdef GmlParser_HaltOnBadString
								PAUSE(("gos_TextDrawGml: Invalid end tag."));
							#endif
								BufferText(StartOfTag,StartOfPlainText);
								Flush(GmlParsedString_PlainText);
								Gml=EndOfTag+1;
							break;
					}
					break;
				case 'a':	//<a> - This represents a link. Format for Href MUST BE href="str". spaces around = are irrevevant, but quotes are not!
					if(StateA)
						STOP(("gos_TextDrawGml: tags are NOT allowed in an <a>..</a> section."));
					StateA=true;
					BufferText(StartOfTag,StartOfPlainText);
					Flush(GmlParsedString_PlainText); //flush, because we will be changing the font for links
					StartOfATag=StartOfTag;
					Gml=EndOfTag+1;
					break;
				case 's':	//<strong>
				case 'b':	//<bold> - turn on bold
					BufferText(StartOfTag,StartOfPlainText);
					Gml=EndOfTag+1;
					if(!StateBold)
					{
						StateBold=true;
						TextBuffer->Append("/bold=1:",7); //move it into the buffer
					}
					break;
				case 'f':
					{
						//<font> - Change the font. This means we have to dump our buffer and make a new one since fonts can only be set at the beginning of a display.
						CountGmlFont++;
						BufferText(StartOfTag,StartOfPlainText);
						Flush(GmlParsedString_PlainText); //flush because we are changing fonts
						DealWithGmlFontTag(StartOfTag);
						Gml=EndOfTag+1;
						break;
					}
				case 'i':	//<italic>
					if(!memicmp("img",Gml,3))
					{
						BufferText(StartOfTag,StartOfPlainText);
						Flush(GmlParsedString_PlainText);
						Gml+=3;
						GmlImageParsedTag* ImageFile=ParseGmlImageTag(Gml);
						BufferText((ImageFile->FileName)+mstrlen(ImageFile->FileName),ImageFile->FileName);
						Flush(GmlParsedString_Image,ImageFile->Align);
						if(ImageFile->FileName)
							delete[] ImageFile->FileName;
						if(ImageFile->Alt)
							delete[] ImageFile->Alt;
						delete ImageFile;
						Gml=EndOfTag+1;
					}
					else//italic
					//if(!memcmp("italic",Gml,6))
					{
				case 'e':	//<EM>
						BufferText(StartOfTag,StartOfPlainText);
						Gml=EndOfTag+1;
						if(!StateItalic)
						{
							StateItalic=true;
							TextBuffer->Append("/italic=1:",9); //move it into the buffer
						}
					}
					break;
				case 'l':
					{	//<LI> - list line
						if(!StateUL&&!StateOL)
							STOP(("gos_TextDrawGml: found <LI> outside of a list."));
						BufferText(StartOfTag,StartOfPlainText);
						if(StateLI)
						{
							Flush(GmlParsedString_PlainText);
							Flush(GmlParsedString_br);
						}
						CountLI++;
						StateLI=true;
						if(StateOL) 
							Flush(GmlParsedString_NumList,CountLI);
						if(StateUL) 
							Flush(GmlParsedString_Bullet);
						Gml=EndOfTag+1;
					}
					break;
				case 'o':	//<OL> - numbered list
					if(StateUL||StateOL)
						STOP(("gos_TextDrawGml: Nested list tags not supported."));
					CountLI=0;
					StateOL=true;
					BufferText(StartOfTag,StartOfPlainText);
					Flush(GmlParsedString_br);
					Gml=EndOfTag+1;
					break;
				case 'p':	//<p> - the start of a paragraph of text
					if(StateP)
						STOP(("gos_TextDrawGml: Nested <p> tags not supported."));
					StateP=true;
					Gml=EndOfTag+1;//skip over the p>
					break;
				case 't': //<tab>
					BufferText(StartOfTag,StartOfPlainText);
					Flush(GmlParsedString_PlainText);
					Flush(GmlParsedString_Tab);
					Gml=EndOfTag+1;
					break;
				case 'u':	//<UL> - bullet list
					if(StateUL||StateOL)
						STOP(("gos_TextDrawGml: Nested list tags not supported."));
					StateUL=true;
					BufferText(StartOfTag,StartOfPlainText);
					Flush(GmlParsedString_br);
					Gml=EndOfTag+1;
					break;
				default:	//Unsupported/invalid tag
					#ifdef GmlParser_HaltOnBadString
						PAUSE(("gos_TextDrawGml: Invalid tag."));
					#endif
						BufferText(StartOfTag,StartOfPlainText);
						Flush(GmlParsedString_PlainText);
						Gml=EndOfTag+1;
					break;
			}
			ChompN(Gml);
			StartOfPlainText=Gml;//Plain text starts again.
		}
		else
			if(*Gml=='$' && *(Gml+1)=='$' && TagCallback)
			{
				char* Temp;
				EndOfTag=FindNext(Gml,' ');
				StartOfTag=MemToStr(Gml,(EndOfTag-Gml));
				Temp=(*TagCallback)(StartOfTag);
				delete[] StartOfTag;
				if(Temp)
				{
					BufferText((Gml-1),StartOfPlainText);
					BufferText(Temp+strlen(Temp),Temp);
				}
				delete[] Temp;
				Gml=EndOfTag;
			}
			else
			{
				//non-tag
				Gml=mbpinc(Gml);
			}
	}
	Flush(GmlParsedString_PlainText);
	delete GmlFontStack;
	GmlFontStack=new GStack<GmlFont>;
	#ifdef LAB_ONLY
		TimeParse+=GetCycles()-temp;
	#endif
	return true;
}

void GmlParser::Flush(int DType, int Extra)
{
	GmlParsedString* xps=new GmlParsedString;
	GmlFont* FontTemp=NULL;
	if(DType==GmlParsedString_PlainText || DType==GmlParsedString_Link)
		if(!TextBuffer->UsedSpace()) //if we got a whole lot of nothing
		{
			delete xps;
			return;
		}
		else
		{
			if(DType==GmlParsedString_PlainText)
			{
				GmlFont* f=GmlFontStack->Pop();
				if(f)
				{
					AppendGmlFont(f,TextBuffer);
					GmlFont* temp=new GmlFont(*f);
					FontTemp=temp;
					GmlFontStack->Push(f);
				}
				xps->Font=FontTemp;
			}
			else
			{
				xps->Font=new GmlFont;
			}
			xps->Text=TextBuffer->ExportBuffer();
			TextBuffer->ClearBuffer();
		}
	else
	{
		xps->Font=new GmlFont;
		xps->Text=TextBuffer->ExportBuffer();
		TextBuffer->ClearBuffer();
	}
	xps->DType=DType;
	xps->Extra=Extra;
	DisplayQueue->Push(xps);
}

void GmlParser::Flush(int DType, char* Dest, char* Alt)
{
	GmlParsedString* xps=new GmlParsedString;
	GmlFont* FontTemp=NULL;
	if(DType==GmlParsedString_PlainText || DType==GmlParsedString_Link)
		if(!TextBuffer->UsedSpace()) //if we got a whole lot of nothing
		{
			delete xps;
			return;
		}
		else
		{
			if(DType==GmlParsedString_PlainText)
			{
				GmlFont* f=GmlFontStack->Pop();
				if(f)
				{
					AppendGmlFont(f,TextBuffer);
					GmlFont* temp=new GmlFont(*f);
					FontTemp=temp;
					GmlFontStack->Push(f);
				}
				xps->Font=FontTemp;
			}
			else
			{
				xps->Font=new GmlFont;
			}
			xps->Text=TextBuffer->ExportBuffer();
			TextBuffer->ClearBuffer();
		}
	else
	{
		xps->Font=new GmlFont;
		xps->Text=TextBuffer->ExportBuffer();
		TextBuffer->ClearBuffer();
	}
	xps->Alt=Alt;
	xps->DType=DType;
	xps->LinkDest=Dest;
	DisplayQueue->Push(xps);
}

//takes in a image tag and parses it
//if you suck and stick redundant flags in the font tag then it's a memory leak and I don't care.|0$R.
GmlImageParsedTag* GmlParser::ParseGmlImageTag(char* StartOfTag)
{
	StartOfTag=mbpinc(StartOfTag);
	char* StartOfData;
	char* EndOfData=StartOfTag;
	char* ImgFile=NULL;
	GmlImageParsedTag* IPT=new GmlImageParsedTag;
	while(FindEQ(EndOfData,StartOfData))
	{
		switch(tolower(*EndOfData))
		{
			case 's': //src
				{
					//src="the name of the image file.ext"
					EndOfData=FindNext(StartOfData,'"'); //we are assuming that src tags are opened and closed with "
					ImgFile=new char[(EndOfData-StartOfData)+1+mstrlen(ImagePath)];
					mstrcpy(ImgFile,ImagePath);
					memcpy(ImgFile+mstrlen(ImagePath),StartOfData,(EndOfData-StartOfData));
					*(ImgFile+mstrlen(ImagePath)+(EndOfData-StartOfData))=0;
					IPT->FileName=ImgFile;
					break;
				}
			case 'a': //align or alt
				{
					if(*(EndOfData+2)=='t')
					{
			case 'n':	char* EndOfAlt = FindNext(StartOfData,'"');
						IPT->Alt=new char[EndOfAlt-StartOfData+1];
						memcpy(IPT->Alt, StartOfData, EndOfAlt-StartOfData);
						*(IPT->Alt+(int)(EndOfAlt-StartOfData))=0;
						EndOfData=StartOfData;
					}
					else
					{
						switch(tolower(*StartOfData))
						{
							case 'r':
								IPT->Align=GmlParsedImage_AlignRight;
								break;
							case 'l':
								IPT->Align=GmlParsedImage_AlignLeft;
								break;
						}
						EndOfData=StartOfData;
					}
					break;
				}
			default:
				PAUSE(( "Invalid flag in img tag" ));
				break;
		}
	}
	return IPT;
}

//The following all deal with FONT tags.


void GmlParser::DealWithGmlFontTag(char*& Text)
{
	GmlFont* F=ParseGmlFontTag(Text);
	GmlFont* OF;
	OF=GmlFontStack->Pop();
	if(OF)
	{
		if(!F->Size)
		{
			F->Size= new char[mstrlen(OF->Size)+1]; 	
			mstrcpy(F->Size,OF->Size); 		
			F->iSize=OF->iSize;
		}
		if(!F->Color)
		{
			F->Color=new char[mstrlen(OF->Color)+1]; 
			mstrcpy(F->Color,OF->Color);
		}
		if(!F->Face)
		{
			F->Face= new char[mstrlen(OF->Face)+1]; 	
			mstrcpy(F->Face,OF->Face);
		}
		GmlFontStack->Push(OF);
	}
	GmlFontStack->Push(F);
}

//takes in a font tag and parses it into GmlParser font struct
//if you suck and stick redundant flags in the font tag then it's a memory leak and I don't care.|0$R.
GmlFont* GmlParser::ParseGmlFontTag(char* StartOfTag)
{
	StartOfTag=mbpinc(StartOfTag);
	GmlFont* FTag=new GmlFont;
	char* StartOfData;
	char* EndOfTag=GrabTag(StartOfTag);
	char* EndOfData=StartOfTag;
	while(FindEQ(EndOfData,StartOfData))
	{
		switch(tolower(*EndOfData))
		{
			case 'c':
				{
					//color #RRGGBB format, hex
					FTag->Color= new char[9];
					mstrcpy(FTag->Color,"FF000000");  //AARRGGBB alpha, red, green, blue
					EndOfData=FindMNextInTag(StartOfData,' ','"');
					//while(*EndOfData&&(*EndOfData=='"'||*EndOfData==' '||*EndOfData=='>')&&*EndOfData!='<') EndOfData--;
					while(*StartOfData&&(*StartOfData=='"'||*StartOfData=='#'||*StartOfData==' '||*StartOfData=='=')&&*StartOfData!='>') StartOfData=mbpinc(StartOfData);
					memcpy(FTag->Color+2,StartOfData,6);
					if(EndOfData>EndOfTag) EndOfData=EndOfTag;
					break;
				}
			case 'f':
				{
					//face "face name, face B, face C..." -- we are only using the first face name. gomen. BUG
					EndOfData=FindNext(StartOfData,'"'); //we are assuming that fonts are opened and closed with "
					FTag->Face=new char[(EndOfData-StartOfData)+1+mstrlen(FontPath)+4]; //+4 for the .d3f
					//memcpy(FTag->Face,StartOfData,(EndOfData-StartOfData));
					mstrcpy(FTag->Face,FontPath);
					memcpy(FTag->Face+mstrlen(FontPath),StartOfData,(EndOfData-StartOfData));
					memcpy(FTag->Face+mstrlen(FontPath)+(EndOfData-StartOfData),".d3f",4);
					*(FTag->Face+17+(EndOfData-StartOfData))=0;
					break;
				}
			case 's':
				{
					//size
					EndOfData=FindMNextInTag(StartOfData,' ','"');
					while(*EndOfData&&(*EndOfData=='"'||*EndOfData==' '||*EndOfData=='>')&&*EndOfData!='<') EndOfData--;
					//at this point, EndOfData is pointing to the last character in the size, for example size=12, EndOfData points to the 2
					while(*StartOfData&&(*StartOfData=='"'||*StartOfData==' '||*StartOfData=='=')&&*StartOfData!='>') StartOfData=mbpinc(StartOfData);
					//at this point, StartOfData is pointing to the first character in the size, for example size=12, StartOfData points to the 1
					FTag->Size=new char[(EndOfData-StartOfData)+2];
					FTag->Size[(EndOfData-StartOfData)+1]=0;
					memcpy(FTag->Size,StartOfData,(EndOfData-StartOfData)+1);
					if(FTag->Size[(EndOfData-StartOfData)+1]!=0) 
						STOP (( "GmlParser::ParseGmlFontTag corrupt size tag (overflow)" ));
					FTag->iSize=atoi(FTag->Size);
					mstrcpy(FTag->Size,"1");//stupid hack here
					break;
				}
			default:
				STOP(( "Invalid flag in font tag" ));
				break;
		}
	}
	return FTag;
}

void GmlParser::AppendGmlFont(GmlFont* f,TextDrawBuffer* TextBuffer)
{
	char *temp1=NULL,*temp2=NULL,*temp3=NULL,*TempStr;
	int tl=0,ts=0,tc=0,tf=0;

	if(f->Size)
	{
		tl+=ts=8+mstrlen(f->Size);
		temp1=new char[9+mstrlen(f->Size)];
		mstrcpy(temp1,"/size=<");
		mstrcpy(temp1+7,f->Size);
		mstrcpy(temp1+ts-1,">");
	}
	if(f->Color)
	{
		tl+=tc=7+mstrlen(f->Color);
		temp2=new char[8+mstrlen(f->Color)];
		mstrcpy(temp2,"/color=");
		mstrcpy(temp2+7,f->Color);
	}
	if(f->Face)
	{
		tl+=tf=8+mstrlen(f->Face);
		temp3=new char[9+mstrlen(f->Face)];
		mstrcpy(temp3,"/font=<");
		mstrcpy(temp3+7,f->Face);
		mstrcpy(temp3+tf-1,">");
	}
	if(tl)
	{
		TempStr=new char[tl+1];
		if(temp3)
		{
			mstrcpy(TempStr,temp3); 
			delete[] temp3;
		}
		if(temp1)
		{
			mstrcpy(TempStr+tf,temp1); 
			delete[] temp1;
		}
		if(temp2)
		{
			mstrcpy(TempStr+ts+tf,temp2); 
			delete[] temp2;
		}
		TextBuffer->PreInsert(TempStr,tl);//we preinsert all together so that it doesn't barf part of it but not all of it.
		delete[] TempStr;
	}
}






//the following all deal with strings, and finding things in them.


//Chomp variations


//remove leading whitespace from string
char* GmlParser::Chomp(char* Text)
{
	while(*Text==' '||*Text=='	') 
		Text=mbpinc(Text);
	return Text;
}

char* GmlParser::RChomp(char* Text)
{
	while(*Text==' '||*Text=='	') 
		Text=mbpdec(Text);
	return Text;
}

//remove leading whitespace and \n from string
char* GmlParser::ChompN(char* Text)
{
	while(*Text==' '||*Text=='	'||*Text=='\n') Text=mbpinc(Text);
	return Text;
}

//remove leading whitespace and \n from string
char* GmlParser::ChompToTag(char* Text)
{
	bool cont;
	do
	{
		cont=false;
		while(*Text&&*Text!='<')
			Text=mbpinc(Text);
		if(*Text=='<'&&*(Text+1)=='<')
		{
			cont=true;
			Text=mbpinc(mbpinc(Text));
		}
	} while(cont);
	return Text;
}


//find variations


char* GmlParser::FindNext(char* Text, char Target) {
	while(*Text && *Text != Target)
		Text=mbpinc(Text);
	return Text;
}

char* GmlParser::FindPrev(char* Text, char Target, char* Limit)
{
	while(Text>Limit && *Text != Target)
		Text=mbpdec(Text);
	return Text;
}

char* GmlParser::FindNextInTag(char* Text, char Target)
{
	while(*Text && *Text != Target && *Text != '>')
		Text=mbpinc(Text);
	return Text;
}

char* GmlParser::FindPrevInTag(char* Text, char Target, char* Limit)
{
	while(Text>Limit && *Text != Target && *Text != '<')
		Text=mbpdec(Text);
	return Text;
}

char* GmlParser::FindMNext(char* Text, char Target, char Target2)
{
	while(*Text && *Text != Target && *Text != Target2)
		Text=mbpinc(Text);
	return Text;
}

char* GmlParser::FindMPrev(char* Text, char Target, char Target2, char* Limit)
{
	while(Text>Limit && *Text != Target && *Text != Target2)
		Text=mbpdec(Text);
	return Text;
}

char* GmlParser::FindMNextInTag(char* Text, char Target, char Target2)
{
	while(*Text && *Text != Target && *Text != Target2 && *Text != '>')
		Text=mbpinc(Text);
	return Text;
}

char* GmlParser::FindMPrevInTag(char* Text, char Target, char Target2, char* Limit)
{
	while(Text>Limit && *Text != Target && *Text != Target2 && *Text != '<')
		Text=mbpdec(Text);
	return Text;
}

void GmlParser::BufferText(char* StartOfTag, char*StartOfPlainText)
{
	if(StartOfTag!=StartOfPlainText)
		TextBuffer->Append(StartOfPlainText,(int)StartOfTag-(int)StartOfPlainText); //move it into the buffer
}

//wrapper
char* GmlParser::GrabNextQ(char* Text)
{
	return mbpinc(FindNextInTag(Text,'"'));
}

//wrapper, may crash
char* GmlParser::GrabPrevQ(char* Text)
{
	return FindPrevInTag(Text,'"',0)-1;
}

char* GmlParser::FindNextStr(char* Text, char* Str)
{
	bool done=false;
	int StrL=mstrlen(Str);
	do
	{
		Text = FindNext(Text,*Str);
		if(*Text==0 || memcmp(Text,Str,StrL)==0) 
			done=true;
	} while(!done);
	return Text;
}

//this just isn't safe. BAD THINGS if not found!!!
char* GmlParser::FindPrevStr(char* Text, char* Str)
{
	bool done=false;
	int StrL=mstrlen(Str);
	do
	{
		Text = FindPrev(Text,*Str,0);
		if(memcmp(Text,Str,StrL)==0) 
			done=true;
	} while(!done);
	return Text;
}

char* GmlParser::FindNextStrInTag(char* Text, char* Str)
{
	bool done=false;
	int StrL=mstrlen(Str);
	do
	{
		Text = FindNextInTag(Text,*Str);
		if(*Text=='>' || memcmp(Text,Str,StrL)==0) 
			done=true;
	} while(!done);
	return Text;
}

char* GmlParser::FindNextUnQuotedStr(char* Text, char* Str)
{
	bool done=false;
	int StrL=mstrlen(Str);
	do
	{
		Text = FindMNext(Text,*Str,'"');
		if(*Text == '"') 
			Text=FindNext(Text,'"')+1;
		else
			if(memcmp(Text,Str,StrL)==0)
				done=true;
	} while(!done);
	return Text;
}

//NOT SAFE, don't use
char* GmlParser::FindPrevUnQuotedStr(char* Text, char* Str)
{
	bool done=false;
	int StrL=mstrlen(Str);
	do
	{
		Text = FindMPrev(Text,*Str,'"',0);
		if(*Text == '"') 
			Text=FindPrev(Text,'"',0)-1;
		else
		{
			if(memcmp(Text,Str,StrL)==0)
				done=true;
		}
	} while(!done);
	return Text;
}

//GrabTag
//This function takes a string and returns the end of the tag. simple.
//If there is no end of tag before end of string, returns the end of string.
//
char* GmlParser::GrabTag(char* Text)
{
	//tiny optimization. After all, the beginning of the tag can't be the end of the tag.
	return FindNext(Text=mbpinc(Text), '>');
}

//we could mod this to keep track of last space so we don't have to go back for it.
bool GmlParser::FindEQ(char*& StartOfTag,char*& StartOfData)
{
	char* StartOfFlag=StartOfTag;
	StartOfData=StartOfFlag=FindNextInTag(StartOfFlag,'=');
	if(*StartOfFlag=='>') //if cannot find = before end of tag
		return false;
	StartOfData=mbpinc(StartOfData); //get past =
	Chomp(StartOfData);	//remove spaces
	if(*StartOfData=='"')//if " then ++
		StartOfData=mbpinc(StartOfData);
	StartOfFlag-=3;//get past any ' ' around the '='. all tags are damned well more than 2 characters anyways.
	StartOfFlag=FindPrev(StartOfFlag,' ',StartOfTag);
	if(*StartOfFlag==' ')
		StartOfFlag=mbpinc(StartOfFlag);
	StartOfTag=StartOfFlag;
	return true;
}