Home | History | Annotate | Download | only in Tools
      1 /******************************************************************************
      2 
      3  @File         PVRTPFXParser.cpp
      4 
      5  @Title        PVRTPFXParser
      6 
      7  @Version
      8 
      9  @Copyright    Copyright (c) Imagination Technologies Limited.
     10 
     11  @Platform     ANSI compatible
     12 
     13  @Description  PFX file parser.
     14 
     15 ******************************************************************************/
     16 
     17 /*****************************************************************************
     18 ** Includes
     19 ******************************************************************************/
     20 #include <string.h>
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 
     24 #include "PVRTGlobal.h"
     25 #include "PVRTContext.h"
     26 #include "PVRTMatrix.h"
     27 #include "PVRTFixedPoint.h"
     28 #include "PVRTMisc.h"
     29 #include "PVRTPFXParser.h"
     30 #include "PVRTResourceFile.h"
     31 #include "PVRTString.h"
     32 #include "PVRTMisc.h"		// Used for POT functions
     33 
     34 /****************************************************************************
     35 ** Constants
     36 ****************************************************************************/
     37 const char* c_pszLinear   = "LINEAR";
     38 const char* c_pszNearest  = "NEAREST";
     39 const char* c_pszNone	  = "NONE";
     40 const char* c_pszClamp    = "CLAMP";
     41 const char* c_pszRepeat	  = "REPEAT";
     42 const char* c_pszCurrentView = "PFX_CURRENTVIEW";
     43 
     44 const unsigned int CPVRTPFXParser::VIEWPORT_SIZE = 0xAAAA;
     45 
     46 const char* c_ppszFilters[eFilter_Size] =
     47 {
     48 	c_pszNearest,		// eFilter_Nearest
     49 	c_pszLinear,		// eFilter_Linear
     50 	c_pszNone,			// eFilter_None
     51 };
     52 const char* c_ppszWraps[eWrap_Size] =
     53 {
     54 	c_pszClamp,			// eWrap_Clamp
     55 	c_pszRepeat			// eWrap_Repeat
     56 };
     57 
     58 #define NEWLINE_TOKENS "\r\n"
     59 #define DELIM_TOKENS " \t"
     60 
     61 #define DEFAULT_EFFECT_NUM_TEX		100
     62 #define DEFAULT_EFFECT_NUM_UNIFORM	100
     63 #define DEFAULT_EFFECT_NUM_ATTRIB	100
     64 
     65 /****************************************************************************
     66 ** Data tables
     67 ****************************************************************************/
     68 
     69 /****************************************************************************
     70 ** CPVRTPFXParserReadContext Class
     71 ****************************************************************************/
     72 class CPVRTPFXParserReadContext
     73 {
     74 public:
     75 	char			**ppszEffectFile;
     76 	int				*pnFileLineNumber;
     77 	unsigned int	nNumLines, nMaxLines;
     78 
     79 public:
     80 	CPVRTPFXParserReadContext();
     81 	~CPVRTPFXParserReadContext();
     82 };
     83 
     84 /*!***************************************************************************
     85  @Function			CPVRTPFXParserReadContext
     86  @Description		Initialises values.
     87 *****************************************************************************/
     88 CPVRTPFXParserReadContext::CPVRTPFXParserReadContext()
     89 {
     90 	nMaxLines = 5000;
     91 	nNumLines = 0;
     92 	ppszEffectFile		= new char*[nMaxLines];
     93 	pnFileLineNumber	= new int[nMaxLines];
     94 }
     95 
     96 /*!***************************************************************************
     97  @Function			~CPVRTPFXParserReadContext
     98  @Description		Frees allocated memory
     99 *****************************************************************************/
    100 CPVRTPFXParserReadContext::~CPVRTPFXParserReadContext()
    101 {
    102 	// free effect file
    103 	for(unsigned int i = 0; i < nNumLines; i++)
    104 	{
    105 		FREE(ppszEffectFile[i]);
    106 	}
    107 	delete [] ppszEffectFile;
    108 	delete [] pnFileLineNumber;
    109 }
    110 
    111 /*!***************************************************************************
    112  @Function			IgnoreWhitespace
    113  @Input				pszString
    114  @Output			pszString
    115  @Description		Skips space, tab, new-line and return characters.
    116 *****************************************************************************/
    117 static void IgnoreWhitespace(char **pszString)
    118 {
    119 	while(	*pszString[0] == '\t' ||
    120 			*pszString[0] == '\n' ||
    121 			*pszString[0] == '\r' ||
    122 			*pszString[0] == ' ' )
    123 	{
    124 		(*pszString)++;
    125 	}
    126 }
    127 
    128 /*!***************************************************************************
    129  @Function			ReadEOLToken
    130  @Input				pToken
    131  @Output			char*
    132  @Description		Reads next strings to the end of the line and interperts as
    133 					a token.
    134 *****************************************************************************/
    135 static char* ReadEOLToken(char* pToken)
    136 {
    137 	char* pReturn = NULL;
    138 
    139 	char szDelim[2] = {'\n', 0};				// try newline
    140 	pReturn = strtok(pToken, szDelim);
    141 	if(pReturn == NULL)
    142 	{
    143 		szDelim[0] = '\r';
    144 		pReturn = strtok (pToken, szDelim);		// try linefeed
    145 	}
    146 	return pReturn;
    147 }
    148 
    149 /*!***************************************************************************
    150  @Function			GetSemanticDataFromString
    151  @Output			pDataItem
    152  @Modified			pszArgumentString
    153  @Input				eType
    154  @Output			pError				error message
    155  @Return			true if successful
    156  @Description		Extracts the semantic data from the string and stores it
    157 					in the output SPVRTSemanticDefaultData parameter.
    158 *****************************************************************************/
    159 static bool GetSemanticDataFromString(SPVRTSemanticDefaultData *pDataItem, const char * const pszArgumentString, ESemanticDefaultDataType eType, CPVRTString *pError)
    160 {
    161 	char *pszString = (char *)pszArgumentString;
    162 	char *pszTmp;
    163 
    164 	IgnoreWhitespace(&pszString);
    165 
    166 	if(pszString[0] != '(')
    167 	{
    168 		*pError = CPVRTString("Missing '(' after ") + c_psSemanticDefaultDataTypeInfo[eType].pszName;
    169 		return false;
    170 	}
    171 	pszString++;
    172 
    173 	IgnoreWhitespace(&pszString);
    174 
    175 	if(!strlen(pszString))
    176 	{
    177 		*pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments");
    178 		return false;
    179 	}
    180 
    181 	pszTmp = pszString;
    182 	switch(c_psSemanticDefaultDataTypeInfo[eType].eInternalType)
    183 	{
    184 		case eFloating:
    185 			pDataItem->pfData[0] = (float)strtod(pszString, &pszTmp);
    186 			break;
    187 		case eInteger:
    188 			pDataItem->pnData[0] = (int)strtol(pszString, &pszTmp, 10);
    189 			break;
    190 		case eBoolean:
    191 			if(strncmp(pszString, "true", 4) == 0)
    192 			{
    193 				pDataItem->pbData[0] = true;
    194 				pszTmp = &pszString[4];
    195 			}
    196 			else if(strncmp(pszString, "false", 5) == 0)
    197 			{
    198 				pDataItem->pbData[0] = false;
    199 				pszTmp = &pszString[5];
    200 			}
    201 			break;
    202 	}
    203 
    204 	if(pszString == pszTmp)
    205 	{
    206 		size_t n = strcspn(pszString, ",\t ");
    207 		char *pszError = (char *)malloc(n + 1);
    208 		strcpy(pszError, "");
    209 		strncat(pszError, pszString, n);
    210 		*pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName;
    211 		FREE(pszError);
    212 		return false;
    213 	}
    214 	pszString = pszTmp;
    215 
    216 	IgnoreWhitespace(&pszString);
    217 
    218 	for(unsigned int i = 1; i < c_psSemanticDefaultDataTypeInfo[eType].nNumberDataItems; i++)
    219 	{
    220 		if(!strlen(pszString))
    221 		{
    222 			*pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments");
    223 			return false;
    224 		}
    225 
    226 		if(pszString[0] != ',')
    227 		{
    228 			size_t n = strcspn(pszString, ",\t ");
    229 			char *pszError = (char *)malloc(n + 1);
    230 			strcpy(pszError, "");
    231 			strncat(pszError, pszString, n);
    232 			*pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName;
    233 			FREE(pszError);
    234 			return false;
    235 		}
    236 		pszString++;
    237 
    238 		IgnoreWhitespace(&pszString);
    239 
    240 		if(!strlen(pszString))
    241 		{
    242 			*pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments");
    243 			return false;
    244 		}
    245 
    246 		pszTmp = pszString;
    247 		switch(c_psSemanticDefaultDataTypeInfo[eType].eInternalType)
    248 		{
    249 			case eFloating:
    250 				pDataItem->pfData[i] = (float)strtod(pszString, &pszTmp);
    251 				break;
    252 			case eInteger:
    253 				pDataItem->pnData[i] = (int)strtol(pszString, &pszTmp, 10);
    254 				break;
    255 			case eBoolean:
    256 				if(strncmp(pszString, "true", 4) == 0)
    257 				{
    258 					pDataItem->pbData[i] = true;
    259 					pszTmp = &pszString[4];
    260 				}
    261 				else if(strncmp(pszString, "false", 5) == 0)
    262 				{
    263 					pDataItem->pbData[i] = false;
    264 					pszTmp = &pszString[5];
    265 				}
    266 				break;
    267 		}
    268 
    269 		if(pszString == pszTmp)
    270 		{
    271 			size_t n = strcspn(pszString, ",\t ");
    272 			char *pszError = (char *)malloc(n + 1);
    273 			strcpy(pszError, "");
    274 			strncat(pszError, pszString, n);
    275 			*pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName;
    276 			FREE(pszError);
    277 			return false;
    278 		}
    279 		pszString = pszTmp;
    280 
    281 		IgnoreWhitespace(&pszString);
    282 	}
    283 
    284 	if(pszString[0] != ')')
    285 	{
    286 		size_t n = strcspn(pszString, "\t )");
    287 		char *pszError = (char *)malloc(n + 1);
    288 		strcpy(pszError, "");
    289 		strncat(pszError, pszString, n);
    290 		*pError = CPVRTString("'") + pszError + "' found when expecting ')' for " + c_psSemanticDefaultDataTypeInfo[eType].pszName;
    291 		FREE(pszError);
    292 		return false;
    293 	}
    294 	pszString++;
    295 
    296 	IgnoreWhitespace(&pszString);
    297 
    298 	if(strlen(pszString))
    299 	{
    300 		*pError = CPVRTString("'") + pszString + "' unexpected after ')'";
    301 		return false;
    302 	}
    303 
    304 	return true;
    305 }
    306 
    307 /*!***************************************************************************
    308  @Function			ConcatenateLinesUntil
    309  @Output			pszOut		output text
    310  @Output			nLine		end line number
    311  @Input				nLine		start line number
    312  @Input				ppszLines	input text - one array element per line
    313  @Input				nLimit		number of lines input
    314  @Input				pszEnd		end string
    315  @Return			true if successful
    316  @Description		Outputs a block of text starting from nLine and ending
    317 					when the string pszEnd is found.
    318 *****************************************************************************/
    319 static bool ConcatenateLinesUntil(CPVRTString& Out, int &nLine, const char * const * const ppszLines, const unsigned int nLimit, const char * const pszEnd)
    320 {
    321 	unsigned int	i, j;
    322 	size_t			nLen;
    323 
    324 	nLen = 0;
    325 	for(i = nLine; i < nLimit; ++i)
    326 	{
    327 		if(strcmp(ppszLines[i], pszEnd) == 0)
    328 			break;
    329 		nLen += strlen(ppszLines[i]) + 1;
    330 	}
    331 	if(i == nLimit)
    332 	{
    333 		return false;
    334 	}
    335 
    336 	if(nLen)
    337 	{
    338 		++nLen;
    339 
    340 		Out.reserve(nLen);
    341 
    342 		for(j = nLine; j < i; ++j)
    343 		{
    344 			Out.append(ppszLines[j]);
    345 			Out.append("\n");
    346 		}
    347 	}
    348 
    349 	nLine = i;
    350 	return true;
    351 }
    352 
    353 /****************************************************************************
    354 ** SPVRTPFXParserEffect Struct
    355 ****************************************************************************/
    356 SPVRTPFXParserEffect::SPVRTPFXParserEffect() :
    357 	Uniforms(DEFAULT_EFFECT_NUM_UNIFORM),
    358 	Attributes(DEFAULT_EFFECT_NUM_ATTRIB),
    359 	Textures(DEFAULT_EFFECT_NUM_TEX)
    360 {
    361 }
    362 
    363 /****************************************************************************
    364 ** SPVRTPFXRenderPass Class
    365 ****************************************************************************/
    366 SPVRTPFXRenderPass::SPVRTPFXRenderPass() :
    367 	eRenderPassType(eNULL_PASS),
    368 	eViewType(eVIEW_NONE),
    369 	uiFormatFlags(0),
    370 	pEffect(NULL),
    371 	pTexture(NULL)
    372 {
    373 }
    374 
    375 /****************************************************************************
    376 ** SPVRTPFXParserShader Class
    377 ****************************************************************************/
    378 SPVRTPFXParserShader::SPVRTPFXParserShader()
    379 	:
    380 	pszGLSLfile(NULL),
    381 	pszGLSLBinaryFile(NULL),
    382 	pszGLSLcode(NULL),
    383 	pbGLSLBinary(NULL)
    384 {
    385 }
    386 
    387 SPVRTPFXParserShader::~SPVRTPFXParserShader()
    388 {
    389 	FREE(pszGLSLfile);
    390 	FREE(pszGLSLcode);
    391 	FREE(pszGLSLBinaryFile);
    392 	FREE(pbGLSLBinary);
    393 }
    394 
    395 SPVRTPFXParserShader::SPVRTPFXParserShader(const SPVRTPFXParserShader& rhs)
    396 {
    397 	Copy(rhs);
    398 }
    399 
    400 SPVRTPFXParserShader& SPVRTPFXParserShader::operator=(const SPVRTPFXParserShader& rhs)
    401 {
    402 	if(&rhs != this)
    403 		Copy(rhs);
    404 
    405 	return *this;
    406 }
    407 
    408 void SPVRTPFXParserShader::Copy(const SPVRTPFXParserShader& rhs)
    409 {
    410 	Name = rhs.Name;
    411 
    412 	PVRTPFXCreateStringCopy(&pszGLSLfile, rhs.pszGLSLfile);
    413 	PVRTPFXCreateStringCopy(&pszGLSLBinaryFile, rhs.pszGLSLBinaryFile);
    414 	PVRTPFXCreateStringCopy(&pszGLSLcode, rhs.pszGLSLcode);
    415 	PVRTPFXCreateStringCopy(&pbGLSLBinary, rhs.pbGLSLBinary);
    416 
    417 	bUseFileName	= rhs.bUseFileName;
    418 	nGLSLBinarySize = rhs.nGLSLBinarySize;
    419 	nFirstLineNumber= rhs.nFirstLineNumber;
    420 	nLastLineNumber = rhs.nLastLineNumber;
    421 }
    422 
    423 /****************************************************************************
    424 ** SPVRTSemanticDefaultData Struct
    425 ****************************************************************************/
    426 SPVRTSemanticDefaultData::SPVRTSemanticDefaultData()
    427 	:
    428 	eType(eDataTypeNone)
    429 {
    430 }
    431 
    432 SPVRTSemanticDefaultData::SPVRTSemanticDefaultData(const SPVRTSemanticDefaultData& rhs)
    433 {
    434 	Copy(rhs);
    435 }
    436 
    437 SPVRTSemanticDefaultData& SPVRTSemanticDefaultData::operator=(const SPVRTSemanticDefaultData& rhs)
    438 {
    439 	if(&rhs != this)
    440 		Copy(rhs);
    441 	return *this;
    442 }
    443 
    444 void SPVRTSemanticDefaultData::Copy(const SPVRTSemanticDefaultData& rhs)
    445 {
    446 	memcpy(pfData, rhs.pfData, sizeof(pfData));
    447 	memcpy(pnData, rhs.pnData, sizeof(pnData));
    448 	memcpy(pbData, rhs.pbData, sizeof(pbData));
    449 	eType = rhs.eType;
    450 }
    451 
    452 /****************************************************************************
    453 ** SPVRTPFXParserSemantic Struct
    454 ****************************************************************************/
    455 SPVRTPFXParserSemantic::SPVRTPFXParserSemantic()
    456 	:
    457 	pszName(NULL),
    458 	pszValue(NULL)
    459 {
    460 }
    461 
    462 SPVRTPFXParserSemantic::~SPVRTPFXParserSemantic()
    463 {
    464 	FREE(pszName);
    465 	FREE(pszValue);
    466 }
    467 
    468 SPVRTPFXParserSemantic::SPVRTPFXParserSemantic(const SPVRTPFXParserSemantic& rhs)
    469 {
    470 	Copy(rhs);
    471 }
    472 
    473 SPVRTPFXParserSemantic& SPVRTPFXParserSemantic::operator=(const SPVRTPFXParserSemantic& rhs)
    474 {
    475 	if(&rhs != this)
    476 		Copy(rhs);
    477 
    478 	return *this;
    479 }
    480 
    481 void SPVRTPFXParserSemantic::Copy(const SPVRTPFXParserSemantic& rhs)
    482 {
    483 	PVRTPFXCreateStringCopy(&pszName, rhs.pszName);
    484 	PVRTPFXCreateStringCopy(&pszValue, rhs.pszValue);
    485 	nIdx			= rhs.nIdx;
    486 	sDefaultValue   = rhs.sDefaultValue;
    487 }
    488 
    489 /****************************************************************************
    490 ** CPVRTPFXParser Class
    491 ****************************************************************************/
    492 /*!***************************************************************************
    493  @Function			CPVRTPFXParser
    494  @Description		Sets initial values.
    495 *****************************************************************************/
    496 CPVRTPFXParser::CPVRTPFXParser()
    497 {
    498 	m_szFileName.assign("");
    499 
    500 	// NOTE: Temp hardcode viewport size
    501 	m_uiViewportWidth = 640;
    502 	m_uiViewportHeight = 480;
    503 }
    504 
    505 /*!***************************************************************************
    506  @Function			~CPVRTPFXParser
    507  @Description		Frees memory used.
    508 *****************************************************************************/
    509 CPVRTPFXParser::~CPVRTPFXParser()
    510 {
    511 }
    512 
    513 /*!***************************************************************************
    514  @Function			Parse
    515  @Output			pReturnError	error string
    516  @Return			bool			true for success parsing file
    517  @Description		Parses a loaded PFX file.
    518 *****************************************************************************/
    519 bool CPVRTPFXParser::Parse(CPVRTString * const pReturnError)
    520 {
    521 	enum eCmd
    522 	{
    523 		eCmds_Header,
    524 		eCmds_Texture,
    525 		eCmds_Target,
    526 		eCmds_Textures,
    527 		eCmds_VertexShader,
    528 		eCmds_FragmentShader,
    529 		eCmds_Effect,
    530 
    531 		eCmds_Size
    532 	};
    533 
    534 	const CPVRTHash ParserCommands[] =
    535 	{
    536 		"[HEADER]",				// eCmds_Header
    537 		"[TEXTURE]",			// eCmds_Texture
    538 		"[TARGET]",				// eCmds_Target
    539 		"[TEXTURES]",			// eCmds_Textures
    540 		"[VERTEXSHADER]",		// eCmds_VertexShader
    541 		"[FRAGMENTSHADER]",		// eCmds_FragmentShader
    542 		"[EFFECT]",				// eCmds_Effect
    543 	};
    544 	PVRTCOMPILEASSERT(ParserCommands, sizeof(ParserCommands) / sizeof(ParserCommands[0]) == eCmds_Size);
    545 
    546 	int nEndLine = 0;
    547 	int nHeaderCounter = 0, nTexturesCounter = 0;
    548 	unsigned int i,j,k;
    549 
    550 	// Loop through the file
    551 	for(unsigned int nLine=0; nLine < m_psContext->nNumLines; nLine++)
    552 	{
    553 		// Skip blank lines
    554 		if(!*m_psContext->ppszEffectFile[nLine])
    555 			continue;
    556 
    557 		CPVRTHash Cmd(m_psContext->ppszEffectFile[nLine]);
    558 		if(Cmd == ParserCommands[eCmds_Header])
    559 		{
    560 			if(nHeaderCounter>0)
    561 			{
    562 				*pReturnError = PVRTStringFromFormattedStr("[HEADER] redefined on line %d\n", m_psContext->pnFileLineNumber[nLine]);
    563 				return false;
    564 			}
    565 			if(GetEndTag("HEADER", nLine, &nEndLine))
    566 			{
    567 				if(ParseHeader(nLine, nEndLine, pReturnError))
    568 					nHeaderCounter++;
    569 				else
    570 					return false;
    571 			}
    572 			else
    573 			{
    574 				*pReturnError = PVRTStringFromFormattedStr("Missing [/HEADER] tag after [HEADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
    575 				return false;
    576 			}
    577 			nLine = nEndLine;
    578 		}
    579 		else if(Cmd == ParserCommands[eCmds_Texture])
    580 		{
    581 			if(GetEndTag("TEXTURE", nLine, &nEndLine))
    582 			{
    583 				if(!ParseTexture(nLine, nEndLine, pReturnError))
    584 					return false;
    585 			}
    586 			else
    587 			{
    588 				*pReturnError = PVRTStringFromFormattedStr("Missing [/TEXTURE] tag after [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
    589 				return false;
    590 			}
    591 			nLine = nEndLine;
    592 		}
    593 		else if(Cmd == ParserCommands[eCmds_Target])
    594 		{
    595 			if(GetEndTag("TARGET", nLine, &nEndLine))
    596 			{
    597 				if(!ParseTarget(nLine, nEndLine, pReturnError))
    598 					return false;
    599 			}
    600 			else
    601 			{
    602 				*pReturnError = PVRTStringFromFormattedStr("Missing [/TARGET] tag after [TARGET] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
    603 				return false;
    604 			}
    605 			nLine = nEndLine;
    606 		}
    607 		else if(Cmd == ParserCommands[eCmds_Textures])
    608 		{
    609 			if(nTexturesCounter>0)
    610 			{
    611 				*pReturnError = PVRTStringFromFormattedStr("[TEXTURES] redefined on line %d\n", m_psContext->pnFileLineNumber[nLine]);
    612 				return false;
    613 			}
    614 			if(GetEndTag("TEXTURES", nLine, &nEndLine))
    615 			{
    616 				if(ParseTextures(nLine, nEndLine, pReturnError))
    617 					nTexturesCounter++;
    618 				else
    619 					return false;
    620 			}
    621 			else
    622 			{
    623 				*pReturnError = PVRTStringFromFormattedStr("Missing [/TEXTURES] tag after [TEXTURES] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
    624 				return false;
    625 			}
    626 			nLine = nEndLine;
    627 		}
    628 		else if(Cmd == ParserCommands[eCmds_VertexShader])
    629 		{
    630 			if(GetEndTag("VERTEXSHADER", nLine, &nEndLine))
    631 			{
    632 				SPVRTPFXParserShader VertexShader;
    633 				if(ParseShader(nLine, nEndLine, pReturnError, VertexShader, "VERTEXSHADER"))
    634 					m_psVertexShader.Append(VertexShader);
    635 				else
    636 					return false;
    637 			}
    638 			else
    639 			{
    640 				*pReturnError = PVRTStringFromFormattedStr("Missing [/VERTEXSHADER] tag after [VERTEXSHADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
    641 				return false;
    642 			}
    643 			nLine = nEndLine;
    644 		}
    645 		else if(Cmd == ParserCommands[eCmds_FragmentShader])
    646 		{
    647 			if(GetEndTag("FRAGMENTSHADER", nLine, &nEndLine))
    648 			{
    649 				SPVRTPFXParserShader FragShader;
    650 				if(ParseShader(nLine, nEndLine, pReturnError, FragShader, "FRAGMENTSHADER"))
    651 					m_psFragmentShader.Append(FragShader);
    652 				else
    653 					return false;
    654 			}
    655 			else
    656 			{
    657 				*pReturnError = PVRTStringFromFormattedStr("Missing [/FRAGMENTSHADER] tag after [FRAGMENTSHADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
    658 				return false;
    659 			}
    660 			nLine = nEndLine;
    661 		}
    662 		else if(Cmd == ParserCommands[eCmds_Effect])
    663 		{
    664 			if(GetEndTag("EFFECT", nLine, &nEndLine))
    665 			{
    666 				SPVRTPFXParserEffect Effect;
    667 				if(ParseEffect(Effect, nLine, nEndLine, pReturnError))
    668 					m_psEffect.Append(Effect);
    669 				else
    670 					return false;
    671 			}
    672 			else
    673 			{
    674 				*pReturnError = PVRTStringFromFormattedStr("Missing [/EFFECT] tag after [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
    675 				return false;
    676 			}
    677 			nLine = nEndLine;
    678 		}
    679 		else
    680 		{
    681 			*pReturnError = PVRTStringFromFormattedStr("'%s' unexpected on line %d\n", m_psContext->ppszEffectFile[nLine], m_psContext->pnFileLineNumber[nLine]);
    682 			return false;
    683 		}
    684 	}
    685 
    686 	if(m_psEffect.GetSize() < 1)
    687 	{
    688 		*pReturnError = CPVRTString("No [EFFECT] found. PFX file must have at least one defined.\n");
    689 		return false;
    690 	}
    691 
    692 	if(m_psFragmentShader.GetSize() < 1)
    693 	{
    694 		*pReturnError = CPVRTString("No [FRAGMENTSHADER] found. PFX file must have at least one defined.\n");;
    695 		return false;
    696 	}
    697 
    698 	if(m_psVertexShader.GetSize() < 1)
    699 	{
    700 		*pReturnError = CPVRTString("No [VERTEXSHADER] found. PFX file must have at least one defined.\n");
    701 		return false;
    702 	}
    703 
    704 	// Loop Effects
    705 	for(i = 0; i < m_psEffect.GetSize(); ++i)
    706 	{
    707 		// Loop Textures in Effects
    708 		for(j = 0; j < m_psEffect[i].Textures.GetSize(); ++j)
    709 		{
    710 			// Loop Textures in whole PFX
    711 			unsigned int uiTexSize = m_psTexture.GetSize();
    712 			for(k = 0; k < uiTexSize; ++k)
    713 			{
    714 				if(m_psTexture[k]->Name == m_psEffect[i].Textures[j].Name)
    715 					break;
    716 			}
    717 
    718 			// Texture mismatch. Report error.
    719 			if(!uiTexSize || k == uiTexSize)
    720 			{
    721 				*pReturnError = "Error: TEXTURE '" + m_psEffect[i].Textures[j].Name.String() + "' is not defined in [TEXTURES].\n";
    722 				return false;
    723 			}
    724 		}
    725 	}
    726 
    727 	DetermineRenderPassDependencies(pReturnError);
    728 	if(pReturnError->compare(""))
    729 	{
    730 		return false;
    731 	}
    732 
    733 	return true;
    734 }
    735 
    736 /*!***************************************************************************
    737  @Function			ParseFromMemory
    738  @Input				pszScript		PFX script
    739  @Output			pReturnError	error string
    740  @Return			EPVRTError		PVR_SUCCESS for success parsing file
    741 									PVR_FAIL if file doesn't exist or is invalid
    742  @Description		Parses a PFX script from memory.
    743 *****************************************************************************/
    744 EPVRTError CPVRTPFXParser::ParseFromMemory(const char * const pszScript, CPVRTString * const pReturnError)
    745 {
    746 	CPVRTPFXParserReadContext	context;
    747 	char			pszLine[512];
    748 	const char		*pszEnd, *pszCurr;
    749 	int				nLineCounter;
    750 	unsigned int	nLen;
    751 	unsigned int	nReduce;
    752 	bool			bDone;
    753 
    754 	if(!pszScript)
    755 		return PVR_FAIL;
    756 
    757 	m_psContext = &context;
    758 
    759 	// Find & process each line
    760 	nLineCounter	= 0;
    761 	bDone			= false;
    762 	pszCurr			= pszScript;
    763 	while(!bDone)
    764 	{
    765 		nLineCounter++;
    766 
    767 		while(*pszCurr == '\r')
    768 			++pszCurr;
    769 
    770 		// Find length of line
    771 		pszEnd = strchr(pszCurr, '\n');
    772 		if(pszEnd)
    773 		{
    774 			nLen = (unsigned int)(pszEnd - pszCurr);
    775 		}
    776 		else
    777 		{
    778 			nLen = (unsigned int)strlen(pszCurr);
    779 			bDone = true;
    780 		}
    781 
    782 		nReduce = 0; // Tells how far to go back because of '\r'.
    783 		while(nLen - nReduce > 0 && pszCurr[nLen - 1 - nReduce] == '\r')
    784 			nReduce++;
    785 
    786 		// Ensure pszLine will not be not overrun
    787 		if(nLen+1-nReduce > sizeof(pszLine) / sizeof(*pszLine))
    788 			nLen = sizeof(pszLine) / sizeof(*pszLine) - 1 + nReduce;
    789 
    790 		// Copy line into pszLine
    791 		strncpy(pszLine, pszCurr, nLen - nReduce);
    792 		pszLine[nLen - nReduce] = 0;
    793 		pszCurr += nLen + 1;
    794 
    795 		_ASSERT(strchr(pszLine, '\r') == 0);
    796 		_ASSERT(strchr(pszLine, '\n') == 0);
    797 
    798 		// Ignore comments
    799 		char *tmp = strstr(pszLine, "//");
    800 		if(tmp != NULL)	*tmp = '\0';
    801 
    802 		// Reduce whitespace to one character.
    803 		ReduceWhitespace(pszLine);
    804 
    805 		// Store the line, even if blank lines (to get correct errors from GLSL compiler).
    806 		if(m_psContext->nNumLines < m_psContext->nMaxLines)
    807 		{
    808 			m_psContext->pnFileLineNumber[m_psContext->nNumLines] = nLineCounter;
    809 			m_psContext->ppszEffectFile[m_psContext->nNumLines] = (char *)malloc((strlen(pszLine) + 1) * sizeof(char));
    810 			strcpy(m_psContext->ppszEffectFile[m_psContext->nNumLines], pszLine);
    811 			m_psContext->nNumLines++;
    812 		}
    813 		else
    814 		{
    815 			*pReturnError = PVRTStringFromFormattedStr("Too many lines of text in file (maximum is %d)\n", m_psContext->nMaxLines);
    816 			return PVR_FAIL;
    817 		}
    818 	}
    819 
    820 	return Parse(pReturnError) ? PVR_SUCCESS : PVR_FAIL;
    821 }
    822 
    823 /*!***************************************************************************
    824  @Function			ParseFromFile
    825  @Input				pszFileName		PFX file name
    826  @Output			pReturnError	error string
    827  @Return			EPVRTError		PVR_SUCCESS for success parsing file
    828 									PVR_FAIL if file doesn't exist or is invalid
    829  @Description		Reads the PFX file and calls the parser.
    830 *****************************************************************************/
    831 EPVRTError CPVRTPFXParser::ParseFromFile(const char * const pszFileName, CPVRTString * const pReturnError)
    832 {
    833 	CPVRTResourceFile PfxFile(pszFileName);
    834 	if (!PfxFile.IsOpen())
    835 	{
    836 		*pReturnError = CPVRTString("Unable to open file ") + pszFileName;
    837 		return PVR_FAIL;
    838 	}
    839 
    840 	CPVRTString PfxFileString;
    841 	const char* pPfxData = (const char*) PfxFile.DataPtr();
    842 
    843 	// Is our shader resource file data null terminated?
    844 	if(pPfxData[PfxFile.Size()-1] != '\0')
    845 	{
    846 		// If not create a temporary null-terminated string
    847 		PfxFileString.assign(pPfxData, PfxFile.Size());
    848 		pPfxData = PfxFileString.c_str();
    849 	}
    850 
    851 	m_szFileName.assign(pszFileName);
    852 
    853 	return ParseFromMemory(pPfxData, pReturnError);
    854 }
    855 
    856 /*!***************************************************************************
    857  @Function			SetViewportSize
    858  @Input				uiWidth				New viewport width
    859  @Input				uiHeight			New viewport height
    860  @Return			bool				True on success
    861  @Description		Allows the current viewport size to be set. This value
    862 					is used for calculating relative texture resolutions
    863 *****************************************************************************/
    864 bool CPVRTPFXParser::SetViewportSize(unsigned int uiWidth, unsigned int uiHeight)
    865 {
    866 	if(uiWidth > 0 && uiHeight > 0)
    867 	{
    868 		m_uiViewportWidth = uiWidth;
    869 		m_uiViewportHeight = uiHeight;
    870 		return true;
    871 	}
    872 	else
    873 	{
    874 		return false;
    875 	}
    876 }
    877 /*!***************************************************************************
    878 @Function		RetrieveRenderPassDependencies
    879 @Output			aRequiredRenderPasses
    880 @Output			aszActiveEffectStrings
    881 @Return			bool
    882 @Description	Returns a list of dependencies associated with the pass.
    883 *****************************************************************************/
    884 bool CPVRTPFXParser::RetrieveRenderPassDependencies(CPVRTArray<SPVRTPFXRenderPass*> &aRequiredRenderPasses, CPVRTArray<CPVRTStringHash> &aszActiveEffectStrings)
    885 {
    886 	unsigned int ui(0), uj(0), uk(0), ul(0);
    887 	const SPVRTPFXParserEffect* pTempEffect(NULL);
    888 
    889 	if(aRequiredRenderPasses.GetSize() > 0)
    890 	{
    891 		/* aRequiredRenderPasses should be empty when it is passed in */
    892 		return false;
    893 	}
    894 
    895 	for(ui = 0; ui < (unsigned int)aszActiveEffectStrings.GetSize(); ++ui)
    896 	{
    897 		if(aszActiveEffectStrings[ui].String().empty())
    898 		{
    899 			// Empty strings are not valid
    900 			return false;
    901 		}
    902 
    903 		// Find the specified effect
    904 		for(uj = 0, pTempEffect = NULL; uj < (unsigned int)m_psEffect.GetSize(); ++uj)
    905 		{
    906 			if(aszActiveEffectStrings[ui] == m_psEffect[uj].Name)
    907 			{
    908 				// Effect found
    909 				pTempEffect = &m_psEffect[uj];
    910 				break;
    911 			}
    912 		}
    913 
    914 		if(pTempEffect == NULL)
    915 		{
    916 			// Effect not found
    917 			return false;
    918 		}
    919 
    920 		for(uj = 0; uj < m_renderPassSkipGraph.GetNumNodes(); ++uj)
    921 		{
    922 			if(m_renderPassSkipGraph[uj]->pEffect == pTempEffect)
    923 			{
    924 				m_renderPassSkipGraph.RetreiveSortedDependencyList(aRequiredRenderPasses, uj);
    925 				return true;
    926 			}
    927 		}
    928 
    929 		/*
    930 			The effect wasn't a post-process. Check to see if it has any non-post-process dependencies,
    931 			e.g. RENDER CAMERA textures.
    932 		*/
    933 		// Loop Effects
    934 		for(uj = 0; uj < (unsigned int)m_psEffect.GetSize(); ++uj)
    935 		{
    936 			if(aszActiveEffectStrings[ui] != m_psEffect[uj].Name)
    937 				continue;
    938 
    939 			// Loop Textures in Effect
    940 			for(uk = 0; uk < m_psEffect[uj].Textures.GetSize();++uk)
    941 			{
    942 				// Loop Render Passes for whole PFX
    943 				for(ul = 0; ul < m_RenderPasses.GetSize(); ++ul)
    944 				{
    945 					// Check that the name of this render pass output texture matches a provided texture in an Effect
    946 					if(m_RenderPasses[ul].pTexture->Name == m_psEffect[uj].Textures[uk].Name)
    947 						aRequiredRenderPasses.Append(&m_RenderPasses[ul]);
    948 				}
    949 			}
    950 
    951 			return true;
    952 		}
    953 	}
    954 
    955 	return false;
    956 }
    957 /*!***************************************************************************
    958  @Function			GetEndTag
    959  @Input				pszTagName		tag name
    960  @Input				nStartLine		start line
    961  @Output			pnEndLine		line end tag found
    962  @Return			true if tag found
    963  @Description		Searches for end tag pszTagName from line nStartLine.
    964 					Returns true and outputs the line number of the end tag if
    965 					found, otherwise returning false.
    966 *****************************************************************************/
    967 bool CPVRTPFXParser::GetEndTag(const char* pszTagName, int nStartLine, int *pnEndLine)
    968 {
    969 	char pszEndTag[100];
    970 	strcpy(pszEndTag, "[/");
    971 	strcat(pszEndTag, pszTagName);
    972 	strcat(pszEndTag, "]");
    973 
    974 	for(unsigned int i = nStartLine; i < m_psContext->nNumLines; i++)
    975 	{
    976 		if(strcmp(pszEndTag, m_psContext->ppszEffectFile[i]) == 0)
    977 		{
    978 			*pnEndLine = i;
    979 			return true;
    980 		}
    981 	}
    982 
    983 	return false;
    984 }
    985 
    986 /*!***************************************************************************
    987  @Function			ReduceWhitespace
    988  @Output			line		output text
    989  @Input				line		input text
    990  @Description		Reduces all white space characters in the string to one
    991 					blank space.
    992 *****************************************************************************/
    993 void CPVRTPFXParser::ReduceWhitespace(char *line)
    994 {
    995 	// convert tabs and newlines to ' '
    996 	char *tmp = strpbrk (line, "\t\n");
    997 	while(tmp != NULL)
    998 	{
    999 		*tmp = ' ';
   1000 		tmp = strpbrk (line, "\t\n");
   1001 	}
   1002 
   1003 	// remove all whitespace at start
   1004 	while(line[0] == ' ')
   1005 	{
   1006 		// move chars along to omit whitespace
   1007 		int counter = 0;
   1008 		do{
   1009 			line[counter] = line[counter+1];
   1010 			counter++;
   1011 		}while(line[counter] != '\0');
   1012 	}
   1013 
   1014 	// step through chars of line remove multiple whitespace
   1015 	for(int i=0; i < (int)strlen(line); i++)
   1016 	{
   1017 		// whitespace found
   1018 		if(line[i] == ' ')
   1019 		{
   1020 			// count number of whitespace chars
   1021 			int numWhiteChars = 0;
   1022 			while(line[i+1+numWhiteChars] == ' ')
   1023 			{
   1024 				numWhiteChars++;
   1025 			}
   1026 
   1027 			// multiple whitespace chars found
   1028 			if(numWhiteChars>0)
   1029 			{
   1030 				// move chars along to omit whitespace
   1031 				int counter=1;
   1032 				while(line[i+counter] != '\0')
   1033 				{
   1034 					line[i+counter] = line[i+numWhiteChars+counter];
   1035 					counter++;
   1036 				}
   1037 			}
   1038 		}
   1039 	}
   1040 
   1041 	// If there is no string then do not remove terminating white symbols
   1042 	if(!strlen(line))
   1043 	    return;
   1044 
   1045 	// remove all whitespace from end
   1046 	while(line[strlen(line)-1] == ' ')
   1047 	{
   1048 		// move chars along to omit whitespace
   1049 		line[strlen(line)-1] = '\0';
   1050 	}
   1051 }
   1052 
   1053 /*!***************************************************************************
   1054  @Function			FindParameter
   1055  @Output
   1056  @Input
   1057  @Description		Finds the parameter after the specified delimiting character and
   1058 					returns the parameter as a string. An empty string is returned
   1059 					if a parameter cannot be found
   1060 
   1061 *****************************************************************************/
   1062 CPVRTString CPVRTPFXParser::FindParameter(char *aszSourceString, const CPVRTString &parameterTag, const CPVRTString &delimiter)
   1063 {
   1064 	CPVRTString returnString("");
   1065 	char* aszTagStart = strstr(aszSourceString, parameterTag.c_str());
   1066 
   1067 	// Tag was found, so search for parameter
   1068 	if(aszTagStart)
   1069 	{
   1070 		char* aszDelimiterStart = strstr(aszTagStart, delimiter.c_str());
   1071 		char* aszSpaceStart = strstr(aszTagStart, " ");
   1072 
   1073 		// Delimiter found
   1074 		if(aszDelimiterStart && (!aszSpaceStart ||(aszDelimiterStart < aszSpaceStart)))
   1075 		{
   1076 			// Create a string from the delimiter to the next space
   1077 			size_t strCount(strcspn(aszDelimiterStart, " "));
   1078 			aszDelimiterStart++;	// Skip =
   1079 			returnString.assign(aszDelimiterStart, strCount-1);
   1080 		}
   1081 	}
   1082 
   1083 	return returnString;
   1084 }
   1085 
   1086 /*!***************************************************************************
   1087 @Function		ReadStringToken
   1088 @Input			pszSource			Parameter string to process
   1089 @Output			output				Processed string
   1090 @Output			ErrorStr			String containing errors
   1091 @Return								Returns true on success
   1092 @Description	Processes the null terminated char array as if it's a
   1093 				formatted string array. Quote marks are determined to be
   1094 				start and end of strings. If no quote marks are found the
   1095 				string is delimited by whitespace.
   1096 *****************************************************************************/
   1097 bool CPVRTPFXParser::ReadStringToken(char* pszSource, CPVRTString& output, CPVRTString &ErrorStr, int i, const char* pCaller)
   1098 {
   1099 	if(*pszSource == '\"')		// Quote marks. Continue parsing until end mark or NULL
   1100 	{
   1101 		pszSource++;		// Skip past first quote
   1102 		while(*pszSource != '\"')
   1103 		{
   1104 			if(*pszSource == '\0')
   1105 			{
   1106 				ErrorStr = PVRTStringFromFormattedStr("Incomplete argument in [%s] on line %d: %s\n", pCaller,m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1107 				return false;
   1108 			}
   1109 
   1110 			output.push_back(*pszSource);
   1111 			pszSource++;
   1112 		}
   1113 
   1114 		pszSource++;		// Skip past final quote.
   1115 	}
   1116 	else		// No quotes. Read until space
   1117 	{
   1118 		pszSource = strtok(pszSource, DELIM_TOKENS NEWLINE_TOKENS);
   1119 		output = pszSource;
   1120 
   1121 		pszSource += strlen(pszSource);
   1122 	}
   1123 
   1124 	// Check that there's nothing left on this line
   1125 	pszSource = strtok(pszSource, NEWLINE_TOKENS);
   1126 	if(pszSource)
   1127 	{
   1128 		ErrorStr = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d: %s\n", pszSource, pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1129 		return false;
   1130 	}
   1131 
   1132 	return true;
   1133 }
   1134 
   1135 /*!***************************************************************************
   1136  @Function			ParseHeader
   1137  @Input				nStartLine		start line number
   1138  @Input				nEndLine		end line number
   1139  @Output			pReturnError	error string
   1140  @Return			bool			true if parse is successful
   1141  @Description		Parses the HEADER section of the PFX file.
   1142 *****************************************************************************/
   1143 bool CPVRTPFXParser::ParseHeader(int nStartLine, int nEndLine, CPVRTString * const pReturnError)
   1144 {
   1145 	enum eCmd
   1146 	{
   1147 		eCmds_Version,
   1148 		eCmds_Description,
   1149 		eCmds_Copyright,
   1150 
   1151 		eCmds_Size
   1152 	};
   1153 
   1154 	const CPVRTHash HeaderCommands[] =
   1155 	{
   1156 		"VERSION",			// eCmds_Version
   1157 		"DESCRIPTION",		// eCmds_Description
   1158 		"COPYRIGHT",		// eCmds_Copyright
   1159 	};
   1160 	PVRTCOMPILEASSERT(HeaderCommands, sizeof(HeaderCommands) / sizeof(HeaderCommands[0]) == eCmds_Size);
   1161 
   1162 	for(int i = nStartLine+1; i < nEndLine; i++)
   1163 	{
   1164 		// Skip blank lines
   1165 		if(!*m_psContext->ppszEffectFile[i])
   1166 			continue;
   1167 
   1168 		char *str = strtok (m_psContext->ppszEffectFile[i]," ");
   1169 		if(str != NULL)
   1170 		{
   1171 			CPVRTHash Cmd(str);
   1172 			if(Cmd == HeaderCommands[eCmds_Version])
   1173 			{
   1174 				str += (strlen(str)+1);
   1175 				m_sHeader.Version = str;
   1176 			}
   1177 			else if(Cmd == HeaderCommands[eCmds_Description])
   1178 			{
   1179 				str += (strlen(str)+1);
   1180 				m_sHeader.Description = str;
   1181 			}
   1182 			else if(Cmd == HeaderCommands[eCmds_Copyright])
   1183 			{
   1184 				str += (strlen(str)+1);
   1185 				m_sHeader.Copyright = str;
   1186 			}
   1187 			else
   1188 			{
   1189 				*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [HEADER] on line %d\n", str, m_psContext->pnFileLineNumber[i]);
   1190 				return false;
   1191 			}
   1192 		}
   1193 		else
   1194 		{
   1195 			*pReturnError = PVRTStringFromFormattedStr("Missing arguments in [HEADER] on line %d : %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1196 			return false;
   1197 		}
   1198 	}
   1199 
   1200 	return true;
   1201 }
   1202 
   1203 /*!***************************************************************************
   1204 @Function			ParseGenericSurface
   1205 @Input				nStartLine		start line number
   1206 @Input				nEndLine		end line number
   1207 @Output				uiWrapS
   1208 @Output				uiWrapT
   1209 @Output				uiWrapR
   1210 @Output				uiMin
   1211 @Output				uiMag
   1212 @Output				uiMip
   1213 @Output				pReturnError	error string
   1214 @Return				bool			true if parse is successful
   1215 @Description		Parses generic data from TARGET and TEXTURE blocks. Namely
   1216 					wrapping and filter commands.
   1217 *****************************************************************************/
   1218 bool CPVRTPFXParser::ParseGenericSurface(int nStartLine, int nEndLine, SPVRTPFXParserTexture& Params, CPVRTArray<CPVRTHash>& KnownCmds,
   1219 										 const char* pCaller, CPVRTString * const pReturnError)
   1220 {
   1221 	const unsigned int INVALID_TYPE = 0xAC1DBEEF;
   1222 
   1223 	enum eCmd
   1224 	{
   1225 		eCmds_Min,
   1226 		eCmds_Mag,
   1227 		eCmds_Mip,
   1228 		eCmds_WrapS,
   1229 		eCmds_WrapT,
   1230 		eCmds_WrapR,
   1231 		eCmds_Filter,
   1232 		eCmds_Wrap,
   1233 		eCmds_Resolution,
   1234 		eCmds_Surface,
   1235 
   1236 		eCmds_Size
   1237 	};
   1238 
   1239 	const CPVRTHash GenericSurfCommands[] =
   1240 	{
   1241 		"MINIFICATION",			// eCmds_Min
   1242 		"MAGNIFICATION",		// eCmds_Mag
   1243 		"MIPMAP",				// eCmds_Mip
   1244 		"WRAP_S",				// eCmds_WrapS
   1245 		"WRAP_T",				// eCmds_WrapT
   1246 		"WRAP_R",				// eCmds_WrapR
   1247 		"FILTER",				// eCmds_Filter
   1248 		"WRAP",					// eCmds_Wrap
   1249 		"RESOLUTION",			// eCmds_Resolution
   1250 		"SURFACETYPE",			// eCmds_Surface
   1251 	};
   1252 	PVRTCOMPILEASSERT(GenericSurfCommands, sizeof(GenericSurfCommands) / sizeof(GenericSurfCommands[0]) == eCmds_Size);
   1253 
   1254 	struct SSurfacePair
   1255 	{
   1256 		CPVRTHash Name;
   1257 		PVRTPixelType eType;
   1258 		unsigned int BufferType;
   1259 	};
   1260 
   1261 	const SSurfacePair SurfacePairs[] =
   1262 	{
   1263 		{ "RGBA8888",	OGL_RGBA_8888,	PVRPFXTEX_COLOUR },
   1264 		{ "RGBA4444",	OGL_RGBA_4444,	PVRPFXTEX_COLOUR },
   1265 		{ "RGB888",		OGL_RGB_888,	PVRPFXTEX_COLOUR },
   1266 		{ "RGB565",		OGL_RGB_565,	PVRPFXTEX_COLOUR },
   1267 		{ "INTENSITY8",	OGL_I_8,		PVRPFXTEX_COLOUR },
   1268 		{ "DEPTH24",	OGL_RGB_888,	PVRPFXTEX_DEPTH },
   1269 		{ "DEPTH16",	OGL_RGB_565,	PVRPFXTEX_DEPTH },
   1270 		{ "DEPTH8",		OGL_I_8,		PVRPFXTEX_DEPTH },
   1271 	};
   1272 	const unsigned int uiNumSurfTypes = sizeof(SurfacePairs) / sizeof(SurfacePairs[0]);
   1273 
   1274 	for(int i = nStartLine+1; i < nEndLine; i++)
   1275 	{
   1276 		// Skip blank lines
   1277 		if(!*m_psContext->ppszEffectFile[i])
   1278 			continue;
   1279 
   1280 		// Need to make a copy so we can use strtok and not affect subsequent parsing
   1281 		size_t lineLen = strlen(m_psContext->ppszEffectFile[i]);
   1282 		char* pBlockCopy = new char[lineLen+1];
   1283 		strcpy(pBlockCopy, m_psContext->ppszEffectFile[i]);
   1284 
   1285 		char *str = strtok (pBlockCopy, NEWLINE_TOKENS DELIM_TOKENS);
   1286 		if(!str)
   1287 		{
   1288 			delete[] pBlockCopy;
   1289 			return false;
   1290 		}
   1291 
   1292 		CPVRTHash Cmd(str);
   1293 		const char** ppFilters  = NULL;
   1294 		bool bKnown = false;
   1295 
   1296 		// --- Verbose filtering flags
   1297 		if(Cmd == GenericSurfCommands[eCmds_Min] || Cmd == GenericSurfCommands[eCmds_Mag] || Cmd == GenericSurfCommands[eCmds_Mip])
   1298 		{
   1299 			ppFilters = c_ppszFilters;
   1300 			bKnown     = true;
   1301 		}
   1302 		// --- Verbose wrapping flags
   1303 		else if(Cmd == GenericSurfCommands[eCmds_WrapS] || Cmd == GenericSurfCommands[eCmds_WrapT] || Cmd == GenericSurfCommands[eCmds_WrapR])
   1304 		{
   1305 			ppFilters = c_ppszWraps;
   1306 			bKnown     = true;
   1307 		}
   1308 		// --- Inline filtering flags
   1309 		else if(Cmd == GenericSurfCommands[eCmds_Filter])
   1310 		{
   1311 			char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS);
   1312 			if(!pszRemaining)
   1313 			{
   1314 				*pReturnError = PVRTStringFromFormattedStr("Missing FILTER arguments in [%s] on line %d: %s\n", pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1315 				delete[] pBlockCopy;
   1316 				return false;
   1317 			}
   1318 
   1319 			unsigned int* pFlags[3] =
   1320 			{
   1321 				&Params.nMin,
   1322 				&Params.nMag,
   1323 				&Params.nMIP,
   1324 			};
   1325 
   1326 			if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszFilters, eFilter_Size, pReturnError, i))
   1327 			{
   1328 				delete[] pBlockCopy;
   1329 				return false;
   1330 			}
   1331 
   1332 			bKnown     = true;
   1333 		}
   1334 		// --- Inline wrapping flags
   1335 		else if(Cmd == GenericSurfCommands[eCmds_Wrap])
   1336 		{
   1337 			char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS);
   1338 			if(!pszRemaining)
   1339 			{
   1340 				*pReturnError = PVRTStringFromFormattedStr("Missing WRAP arguments in [%s] on line %d: %s\n", pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1341 				delete[] pBlockCopy;
   1342 				return false;
   1343 			}
   1344 
   1345 			unsigned int* pFlags[3] =
   1346 			{
   1347 				&Params.nWrapS,
   1348 				&Params.nWrapT,
   1349 				&Params.nWrapR,
   1350 			};
   1351 
   1352 			if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszWraps, eWrap_Size, pReturnError, i))
   1353 			{
   1354 				delete[] pBlockCopy;
   1355 				return false;
   1356 			}
   1357 
   1358 			bKnown     = true;
   1359 		}
   1360 		// --- Resolution
   1361 		else if(Cmd == GenericSurfCommands[eCmds_Resolution])
   1362 		{
   1363 			char* pszRemaining;
   1364 
   1365 			unsigned int* uiVals[2] = { &Params.uiWidth, &Params.uiHeight };
   1366 
   1367 			// There should be precisely TWO arguments for resolution (width and height)
   1368 			for(unsigned int uiIndex = 0; uiIndex < 2; ++uiIndex)
   1369 			{
   1370 				pszRemaining = strtok(NULL, DELIM_TOKENS NEWLINE_TOKENS);
   1371 				if(!pszRemaining)
   1372 				{
   1373 					*pReturnError = PVRTStringFromFormattedStr("Missing RESOLUTION argument(s) (requires width AND height) in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]);
   1374 					delete[] pBlockCopy;
   1375 					return false;
   1376 				}
   1377 
   1378 				int val = atoi(pszRemaining);
   1379 
   1380 				if( (val == 0 && *pszRemaining != '0')			// Make sure they haven't explicitly set the value to be 0 as this might be a valid use-case.
   1381 					||  (val < 0))
   1382 				{
   1383 					*pReturnError = PVRTStringFromFormattedStr("Invalid RESOLUTION argument \"%s\" in [TEXTURE] on line %d\n", pszRemaining, m_psContext->pnFileLineNumber[i]);
   1384 					delete[] pBlockCopy;
   1385 					return false;
   1386 				}
   1387 
   1388 				*(uiVals[uiIndex]) = (unsigned int)val;
   1389 			}
   1390 
   1391 			bKnown     = true;
   1392 		}
   1393 		// --- Surface type
   1394 		else if(Cmd == GenericSurfCommands[eCmds_Surface])
   1395 		{
   1396 			char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS);
   1397 			if(!pszRemaining)
   1398 			{
   1399 				*pReturnError = PVRTStringFromFormattedStr("Missing SURFACETYPE arguments in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]);
   1400 				delete[] pBlockCopy;
   1401 				return false;
   1402 			}
   1403 
   1404 			CPVRTHash hashType(pszRemaining);
   1405 			for(unsigned int uiIndex = 0; uiIndex < uiNumSurfTypes; ++uiIndex)
   1406 			{
   1407 				if(hashType == SurfacePairs[uiIndex].Name)
   1408 				{
   1409 					Params.uiFlags =  SurfacePairs[uiIndex].eType | SurfacePairs[uiIndex].BufferType;
   1410 					break;
   1411 				}
   1412 			}
   1413 
   1414 			bKnown     = true;
   1415 		}
   1416 
   1417 		// Valid Verbose command
   1418 		if(ppFilters)
   1419 		{
   1420 			char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS);
   1421 			if(!pszRemaining)
   1422 			{
   1423 				*pReturnError = PVRTStringFromFormattedStr("Missing arguments in [%s] on line %d: %s\n", pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1424 				delete[] pBlockCopy;
   1425 				return false;
   1426 			}
   1427 
   1428 			unsigned int Type = INVALID_TYPE;
   1429 			for(unsigned int uiIndex = 0; uiIndex < 3; ++uiIndex)
   1430 			{
   1431 				if(strcmp(pszRemaining, ppFilters[uiIndex]) == 0)
   1432 				{
   1433 					Type = uiIndex;			// Yup, it's valid.
   1434 					break;
   1435 				}
   1436 			}
   1437 
   1438 			// Tell the user it's invalid.
   1439 			if(Type == INVALID_TYPE)
   1440 			{
   1441 				*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d: %s\n", pszRemaining, pCaller, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
   1442 				delete[] pBlockCopy;
   1443 				return false;
   1444 			}
   1445 
   1446 			if(Cmd == GenericSurfCommands[eCmds_Min])			Params.nMin = Type;
   1447 			else if(Cmd == GenericSurfCommands[eCmds_Mag])		Params.nMag = Type;
   1448 			else if(Cmd == GenericSurfCommands[eCmds_Mip])		Params.nMIP = Type;
   1449 			else if(Cmd == GenericSurfCommands[eCmds_WrapR])	Params.nWrapR = Type;
   1450 			else if(Cmd == GenericSurfCommands[eCmds_WrapS])	Params.nWrapS = Type;
   1451 			else if(Cmd == GenericSurfCommands[eCmds_WrapT])	Params.nWrapT = Type;
   1452 		}
   1453 
   1454 		if(bKnown)
   1455 		{
   1456 			KnownCmds.Append(Cmd);
   1457 
   1458 			// Make sure nothing else exists on the line that hasn't been parsed.
   1459 			char* pszRemaining = strtok(NULL, NEWLINE_TOKENS);
   1460 			if(pszRemaining)
   1461 			{
   1462 				*pReturnError = PVRTStringFromFormattedStr("Unexpected keyword '%s' in [%s] on line %d: %s\n", pszRemaining, pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1463 				delete[] pBlockCopy;
   1464 				return false;
   1465 			}
   1466 		}
   1467 
   1468 		delete [] pBlockCopy;
   1469 	}
   1470 
   1471 	return true;
   1472 }
   1473 
   1474 /*!***************************************************************************
   1475 @Function			ParseTexture
   1476 @Input				nStartLine		start line number
   1477 @Input				nEndLine		end line number
   1478 @Output				pReturnError	error string
   1479 @Return				bool			true if parse is successful
   1480 @Description		Parses the TEXTURE section of the PFX file.
   1481 *****************************************************************************/
   1482 bool CPVRTPFXParser::ParseTexture(int nStartLine, int nEndLine, CPVRTString * const pReturnError)
   1483 {
   1484 	enum eCmd
   1485 	{
   1486 		eCmds_Name,
   1487 		eCmds_Path,
   1488 		eCmds_View,
   1489 		eCmds_Camera,
   1490 
   1491 		eCmds_Size
   1492 	};
   1493 
   1494 	const CPVRTHash TextureCmds[] =
   1495 	{
   1496 		"NAME",				// eTextureCmds_Name
   1497 		"PATH",				// eTextureCmds_Path
   1498 		"VIEW",				// eTextureCmds_View
   1499 		"CAMERA",			// eTextureCmds_Camera
   1500 	};
   1501 	PVRTCOMPILEASSERT(TextureCmds, sizeof(TextureCmds) / sizeof(TextureCmds[0]) == eCmds_Size);
   1502 
   1503 	SPVRTPFXParserTexture TexDesc;
   1504 	TexDesc.nMin = eFilter_Default;
   1505 	TexDesc.nMag = eFilter_Default;
   1506 	TexDesc.nMIP = eFilter_MipDefault;
   1507 	TexDesc.nWrapS = eWrap_Default;
   1508 	TexDesc.nWrapT = eWrap_Default;
   1509 	TexDesc.nWrapR = eWrap_Default;
   1510 	TexDesc.uiWidth  = VIEWPORT_SIZE;
   1511 	TexDesc.uiHeight = VIEWPORT_SIZE;
   1512 	TexDesc.uiFlags  = OGL_RGBA_8888 | PVRPFXTEX_COLOUR;
   1513 
   1514 	CPVRTArray<CPVRTHash> KnownCmds;
   1515 	if(!ParseGenericSurface(nStartLine, nEndLine, TexDesc, KnownCmds, "TEXTURE", pReturnError))
   1516 		return false;
   1517 
   1518 	CPVRTString texName, filePath, viewName;
   1519 	for(int i = nStartLine+1; i < nEndLine; i++)
   1520 	{
   1521 		// Skip blank lines
   1522 		if(!*m_psContext->ppszEffectFile[i])
   1523 			continue;
   1524 
   1525 		char *str = strtok (m_psContext->ppszEffectFile[i], NEWLINE_TOKENS DELIM_TOKENS);
   1526 		if(!str)
   1527 		{
   1528 			*pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1529 			return false;
   1530 		}
   1531 
   1532 		CPVRTHash texCmd(str);
   1533 		// --- Texture Name
   1534 		if(texCmd == TextureCmds[eCmds_Name])
   1535 		{
   1536 			char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS);
   1537 			if(!pszRemaining)
   1538 			{
   1539 				*pReturnError = PVRTStringFromFormattedStr("Missing NAME arguments in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1540 				return false;
   1541 			}
   1542 
   1543 			texName = pszRemaining;
   1544 		}
   1545 		// --- Texture Path
   1546 		else if(texCmd == TextureCmds[eCmds_Path])
   1547 		{
   1548 			char* pszRemaining = strtok(NULL, NEWLINE_TOKENS);
   1549 			if(!pszRemaining)
   1550 			{
   1551 				*pReturnError = PVRTStringFromFormattedStr("Missing PATH arguments in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1552 				return false;
   1553 			}
   1554 
   1555 			if(!ReadStringToken(pszRemaining, filePath, *pReturnError, i, "TEXTURE"))
   1556 			{
   1557 				return false;
   1558 			}
   1559 		}
   1560 		// --- View/Camera Name
   1561 		else if(texCmd == TextureCmds[eCmds_View] || texCmd == TextureCmds[eCmds_Camera])
   1562 		{
   1563 			char* pszRemaining = strtok(NULL, NEWLINE_TOKENS);		// String component. Get the rest of the line.
   1564 			if(!pszRemaining || strlen(pszRemaining) == 0)
   1565 			{
   1566 				*pReturnError = PVRTStringFromFormattedStr("Missing VIEW argument in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1567 				return false;
   1568 			}
   1569 
   1570 			if(!ReadStringToken(pszRemaining, viewName, *pReturnError, i, "TEXTURE"))
   1571 			{
   1572 				return false;
   1573 			}
   1574 		}
   1575 		else if(KnownCmds.Contains(texCmd))
   1576 		{
   1577 			// Remove from 'unknown' list.
   1578 			for(unsigned int uiIndex = 0; uiIndex < KnownCmds.GetSize(); ++uiIndex)
   1579 			{
   1580 				if(KnownCmds[uiIndex] == texCmd)
   1581 				{
   1582 					KnownCmds.Remove(uiIndex);
   1583 					break;
   1584 				}
   1585 			}
   1586 
   1587 			continue;		// This line has already been processed.
   1588 		}
   1589 		else
   1590 		{
   1591 			*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURE] on line %d: %s\n", str, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1592 			return false;
   1593 		}
   1594 
   1595 		char* pszRemaining = strtok(NULL, NEWLINE_TOKENS);
   1596 		if(pszRemaining)
   1597 		{
   1598 			*pReturnError = PVRTStringFromFormattedStr("Unexpected keyword '%s' in [TEXTURE] on line %d: %s\n", pszRemaining, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1599 			return false;
   1600 		}
   1601 	}
   1602 
   1603 	if(texName.empty())
   1604 	{
   1605 		*pReturnError = PVRTStringFromFormattedStr("No NAME tag specified in [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   1606 		return false;
   1607 	}
   1608 	if(!filePath.empty() && !viewName.empty())
   1609 	{
   1610 		*pReturnError = PVRTStringFromFormattedStr("Both PATH and VIEW tags specified in [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   1611 		return false;
   1612 	}
   1613 	if(filePath.empty() && viewName.empty())
   1614 	{
   1615 		*pReturnError = PVRTStringFromFormattedStr("No PATH or VIEW tag specified in [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   1616 		return false;
   1617 	}
   1618 
   1619 	bool bRTT = (viewName.empty() ? false : true);
   1620 	if(bRTT)
   1621 	{
   1622 		filePath = texName;									// RTT doesn't have a physical file.
   1623 	}
   1624 
   1625 	// Create a new texture and copy over the vals.
   1626 	SPVRTPFXParserTexture* pTex = new SPVRTPFXParserTexture();
   1627 	pTex->Name				= CPVRTStringHash(texName);
   1628 	pTex->FileName			= CPVRTStringHash(filePath);
   1629 	pTex->bRenderToTexture	= bRTT;
   1630 	pTex->nMin				= TexDesc.nMin;
   1631 	pTex->nMag				= TexDesc.nMag;
   1632 	pTex->nMIP				= TexDesc.nMIP;
   1633 	pTex->nWrapS			= TexDesc.nWrapS;
   1634 	pTex->nWrapT			= TexDesc.nWrapT;
   1635 	pTex->nWrapR			= TexDesc.nWrapR;
   1636 	pTex->uiWidth			= TexDesc.uiWidth;
   1637 	pTex->uiHeight			= TexDesc.uiHeight;
   1638 	pTex->uiFlags			= TexDesc.uiFlags;
   1639 	m_psTexture.Append(pTex);
   1640 
   1641 	if(bRTT)
   1642 	{
   1643 		unsigned int uiPassIdx = m_RenderPasses.Append();
   1644 		m_RenderPasses[uiPassIdx].SemanticName = texName;
   1645 
   1646 		if(viewName == c_pszCurrentView)
   1647 		{
   1648 			m_RenderPasses[uiPassIdx].eViewType	 = eVIEW_CURRENT;
   1649 		}
   1650 		else
   1651 		{
   1652 			m_RenderPasses[uiPassIdx].eViewType	 = eVIEW_POD_CAMERA;
   1653 			m_RenderPasses[uiPassIdx].NodeName	 = viewName;
   1654 		}
   1655 
   1656 		m_RenderPasses[uiPassIdx].eRenderPassType = eCAMERA_PASS;			// Textures are always 'camera' passes
   1657 
   1658 		// Set render pass texture to the newly created texture.
   1659 		m_RenderPasses[uiPassIdx].pTexture		 = pTex;
   1660 		m_RenderPasses[uiPassIdx].uiFormatFlags  = TexDesc.uiFlags;
   1661 	}
   1662 
   1663 	return true;
   1664 }
   1665 
   1666 /*!***************************************************************************
   1667 @Function			ParseTarget
   1668 @Input				nStartLine		start line number
   1669 @Input				nEndLine		end line number
   1670 @Output				pReturnError	error string
   1671 @Return				bool			true if parse is successful
   1672 @Description		Parses the TARGET section of the PFX file.
   1673 *****************************************************************************/
   1674 bool CPVRTPFXParser::ParseTarget(int nStartLine, int nEndLine, CPVRTString * const pReturnError)
   1675 {
   1676 	enum eCmd
   1677 	{
   1678 		eCmds_Name,
   1679 
   1680 		eCmds_Size
   1681 	};
   1682 
   1683 	const CPVRTHash TargetCommands[] =
   1684 	{
   1685 		"NAME",				// eCmds_Name
   1686 	};
   1687 	PVRTCOMPILEASSERT(TargetCommands, sizeof(TargetCommands) / sizeof(TargetCommands[0]) == eCmds_Size);
   1688 
   1689 	CPVRTString targetName;
   1690 	SPVRTPFXParserTexture TexDesc;
   1691 	TexDesc.nMin = eFilter_Default;
   1692 	TexDesc.nMag = eFilter_Default;
   1693 	TexDesc.nMIP = eFilter_MipDefault;
   1694 	TexDesc.nWrapS = eWrap_Default;
   1695 	TexDesc.nWrapT = eWrap_Default;
   1696 	TexDesc.nWrapR = eWrap_Default;
   1697 	TexDesc.uiWidth  = VIEWPORT_SIZE;
   1698 	TexDesc.uiHeight = VIEWPORT_SIZE;
   1699 	TexDesc.uiFlags  = OGL_RGBA_8888 | PVRPFXTEX_COLOUR;
   1700 
   1701 	CPVRTArray<CPVRTHash> KnownCmds;
   1702 	if(!ParseGenericSurface(nStartLine, nEndLine, TexDesc, KnownCmds, "TARGET", pReturnError))
   1703 		return false;
   1704 
   1705 	for(int i = nStartLine+1; i < nEndLine; i++)
   1706 	{
   1707 		// Skip blank lines
   1708 		if(!*m_psContext->ppszEffectFile[i])
   1709 			continue;
   1710 
   1711 		char *str = strtok (m_psContext->ppszEffectFile[i], NEWLINE_TOKENS DELIM_TOKENS);
   1712 		if(!str)
   1713 		{
   1714 			*pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]);
   1715 			return false;
   1716 		}
   1717 
   1718 		CPVRTHash texCmd(str);
   1719 		// --- Target Name
   1720 		if(texCmd == TargetCommands[eCmds_Name])
   1721 		{
   1722 			char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS);
   1723 			if(!pszRemaining)
   1724 			{
   1725 				*pReturnError = PVRTStringFromFormattedStr("Missing NAME arguments in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]);
   1726 				return false;
   1727 			}
   1728 
   1729 			targetName = pszRemaining;
   1730 		}
   1731 		else if(KnownCmds.Contains(texCmd))
   1732 		{
   1733 			// Remove from 'unknown' list.
   1734 			for(unsigned int uiIndex = 0; uiIndex < KnownCmds.GetSize(); ++uiIndex)
   1735 			{
   1736 				if(KnownCmds[uiIndex] == texCmd)
   1737 				{
   1738 					KnownCmds.Remove(uiIndex);
   1739 					break;
   1740 				}
   1741 			}
   1742 
   1743 			continue;		// This line has already been processed.
   1744 		}
   1745 		else
   1746 		{
   1747 			*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TARGET] on line %d\n", str, m_psContext->pnFileLineNumber[i]);
   1748 			return false;
   1749 		}
   1750 
   1751 		char* pszRemaining = strtok(NULL, NEWLINE_TOKENS);
   1752 		if(pszRemaining)
   1753 		{
   1754 			*pReturnError = PVRTStringFromFormattedStr("Unexpected keyword '%s' in [TARGET] on line %d\n", pszRemaining, m_psContext->pnFileLineNumber[i]);
   1755 			return false;
   1756 		}
   1757 	}
   1758 
   1759 	// Create a new texture and copy over the vals.
   1760 	SPVRTPFXParserTexture* pTex = new SPVRTPFXParserTexture();
   1761 	pTex->Name				= CPVRTStringHash(targetName);
   1762 	pTex->FileName			= CPVRTStringHash(targetName);
   1763 	pTex->bRenderToTexture	= true;
   1764 	pTex->nMin				= TexDesc.nMin;
   1765 	pTex->nMag				= TexDesc.nMag;
   1766 	pTex->nMIP				= TexDesc.nMIP;
   1767 	pTex->nWrapS			= TexDesc.nWrapS;
   1768 	pTex->nWrapT			= TexDesc.nWrapT;
   1769 	pTex->nWrapR			= TexDesc.nWrapR;
   1770 	pTex->uiWidth			= TexDesc.uiWidth;
   1771 	pTex->uiHeight			= TexDesc.uiHeight;
   1772 	pTex->uiFlags			= TexDesc.uiFlags;
   1773 	m_psTexture.Append(pTex);
   1774 
   1775 	// Copy to render pass struct
   1776 	unsigned int uiPassIdx = m_RenderPasses.Append();
   1777 	m_RenderPasses[uiPassIdx].SemanticName		= targetName;
   1778 	m_RenderPasses[uiPassIdx].eViewType			= eVIEW_NONE;
   1779 	m_RenderPasses[uiPassIdx].eRenderPassType	= ePOSTPROCESS_PASS;			// Targets are always post-process passes.
   1780 	m_RenderPasses[uiPassIdx].pTexture			= pTex;
   1781 	m_RenderPasses[uiPassIdx].uiFormatFlags		= TexDesc.uiFlags;
   1782 
   1783 	return true;
   1784 }
   1785 
   1786 /*!***************************************************************************
   1787  @Function			ParseTextures		** DEPRECATED **
   1788  @Input				nStartLine		start line number
   1789  @Input				nEndLine		end line number
   1790  @Output			pReturnError	error string
   1791  @Return			bool			true if parse is successful
   1792  @Description		Parses the TEXTURE section of the PFX file.
   1793 *****************************************************************************/
   1794 bool CPVRTPFXParser::ParseTextures(int nStartLine, int nEndLine, CPVRTString * const pReturnError)
   1795 {
   1796 	char *pszName(NULL), *pszFile(NULL), *pszKeyword(NULL);
   1797 	char *pszRemaining(NULL), *pszTemp(NULL);
   1798 	bool bReturnVal(false);
   1799 
   1800 	for(int i = nStartLine+1; i < nEndLine; i++)
   1801 	{
   1802 		// Skip blank lines
   1803 		if(!*m_psContext->ppszEffectFile[i])
   1804 			continue;
   1805 
   1806 		char *str = strtok (m_psContext->ppszEffectFile[i]," ");
   1807 		if(str != NULL)
   1808 		{
   1809 			// Set defaults
   1810 			unsigned int	uiMin(eFilter_Default), uiMag(eFilter_Default), uiMip(eFilter_MipDefault);
   1811 			unsigned int	uiWrapS(eWrap_Default), uiWrapT(eWrap_Default), uiWrapR(eWrap_Default);
   1812 			unsigned int	uiFlags = 0;
   1813 
   1814 			unsigned int uiWidth	= CPVRTPFXParser::VIEWPORT_SIZE;
   1815 			unsigned int uiHeight	= CPVRTPFXParser::VIEWPORT_SIZE;
   1816 
   1817 			// Reset variables
   1818 			FREE(pszName)		pszName = NULL;
   1819 			FREE(pszFile)		pszFile = NULL;
   1820 			FREE(pszKeyword)	pszKeyword = NULL;
   1821 			FREE(pszTemp)		pszTemp = NULL;
   1822 			pszRemaining		= NULL;
   1823 
   1824 			// Compare against all valid keywords
   1825 			if((strcmp(str, "FILE") != 0) && (strcmp(str, "RENDER") != 0))
   1826 			{
   1827 				*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d\n", str, m_psContext->pnFileLineNumber[i]);
   1828 				goto fail_release_return;
   1829 			}
   1830 
   1831 #if 1
   1832 			if((strcmp(str, "RENDER") == 0))
   1833 			{
   1834 				*pReturnError = PVRTStringFromFormattedStr("RENDER tag no longer supported in [TEXTURES] block. Use new [TARGET] block instead\n");
   1835 				goto fail_release_return;
   1836 			}
   1837 #endif
   1838 
   1839 			pszKeyword = (char *)malloc( ((int)strlen(str)+1) * sizeof(char));
   1840 			strcpy(pszKeyword, str);
   1841 
   1842 			str = strtok (NULL, " ");
   1843 			if(str != NULL)
   1844 			{
   1845 				pszName = (char *)malloc( ((int)strlen(str)+1) * sizeof(char));
   1846 				strcpy(pszName, str);
   1847 			}
   1848 			else
   1849 			{
   1850 				*pReturnError = PVRTStringFromFormattedStr("Texture name missing in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
   1851 				goto fail_release_return;
   1852 			}
   1853 
   1854 			/*
   1855 				The pszRemaining string is used to look for remaining flags.
   1856 				This has the advantage of allowing flags to be order independent
   1857 				and makes it easier to ommit some flags, but still pick up others
   1858 				(the previous method made it diffifult to retrieve filtering info
   1859 				if flags before it were missing)
   1860 			*/
   1861 			pszRemaining  = strtok(NULL, "\n");
   1862 
   1863 			if(pszRemaining == NULL)
   1864 			{
   1865 				*pReturnError = PVRTStringFromFormattedStr("Incomplete definition in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
   1866 				goto fail_release_return;
   1867 			}
   1868 			else if(strcmp(pszKeyword, "FILE") == 0)
   1869 			{
   1870 				pszTemp = (char *)malloc( ((int)strlen(pszRemaining)+1) * sizeof(char));
   1871 				strcpy(pszTemp, pszRemaining);
   1872 				str = strtok (pszTemp, " ");
   1873 
   1874 				if(str != NULL)
   1875 				{
   1876 					pszFile = (char *)malloc( ((int)strlen(str)+1) * sizeof(char));
   1877 					strcpy(pszFile, str);
   1878 				}
   1879 				else
   1880 				{
   1881 					*pReturnError = PVRTStringFromFormattedStr("Texture name missing in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
   1882 					goto fail_release_return;
   1883 				}
   1884 			}
   1885 
   1886 			if(strcmp(pszKeyword, "FILE") == 0)
   1887 			{
   1888 				// --- Filter flags
   1889 				{
   1890 					unsigned int* pFlags[3] =
   1891 					{
   1892 						&uiMin,
   1893 						&uiMag,
   1894 						&uiMip,
   1895 					};
   1896 
   1897 					if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszFilters, eFilter_Size, pReturnError, i))
   1898 						goto fail_release_return;
   1899 				}
   1900 
   1901 				// --- Wrap flags
   1902 				{
   1903 					unsigned int* pFlags[3] =
   1904 					{
   1905 						&uiWrapS,
   1906 						&uiWrapT,
   1907 						&uiWrapR,
   1908 					};
   1909 
   1910 					if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszWraps, eWrap_Size, pReturnError, i))
   1911 						goto fail_release_return;
   1912 				}
   1913 
   1914 				SPVRTPFXParserTexture* pTex = new SPVRTPFXParserTexture();
   1915 				pTex->Name				= CPVRTStringHash(pszName);
   1916 				pTex->FileName			= CPVRTStringHash(pszFile);
   1917 				pTex->bRenderToTexture	= false;
   1918 				pTex->nMin				= uiMin;
   1919 				pTex->nMag				= uiMag;
   1920 				pTex->nMIP				= uiMip;
   1921 				pTex->nWrapS			= uiWrapS;
   1922 				pTex->nWrapT			= uiWrapT;
   1923 				pTex->nWrapR			= uiWrapR;
   1924 				pTex->uiWidth			= uiWidth;
   1925 				pTex->uiHeight			= uiHeight;
   1926 				pTex->uiFlags			= uiFlags;
   1927 				m_psTexture.Append(pTex);
   1928 			}
   1929 			else
   1930 			{
   1931 				*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d\n", str, m_psContext->pnFileLineNumber[i]);;
   1932 				goto fail_release_return;
   1933 			}
   1934 		}
   1935 		else
   1936 		{
   1937 			*pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]);
   1938 			goto fail_release_return;
   1939 		}
   1940 	}
   1941 
   1942 	/*
   1943 		Should only reach here if there have been no issues
   1944 	*/
   1945 	bReturnVal = true;
   1946 	goto release_return;
   1947 
   1948 fail_release_return:
   1949 	bReturnVal = false;
   1950 release_return:
   1951 	FREE(pszKeyword);
   1952 	FREE(pszName);
   1953 	FREE(pszFile);
   1954 	FREE(pszTemp);
   1955 	return bReturnVal;
   1956 }
   1957 
   1958 /*!***************************************************************************
   1959 @Function		ParseTextureFlags
   1960 @Input			c_pszCursor
   1961 @Output			pFlagsOut
   1962 @Input			uiNumFlags
   1963 @Input			ppszFlagNames
   1964 @Input			uiNumFlagNames
   1965 @Input			pReturnError
   1966 @Input			iLineNum
   1967 @Return			bool
   1968 @Description	Parses the texture flag sections.
   1969 *****************************************************************************/
   1970 bool CPVRTPFXParser::ParseTextureFlags(	const char* c_pszRemainingLine, unsigned int** ppFlagsOut, unsigned int uiNumFlags, const char** c_ppszFlagNames, unsigned int uiNumFlagNames,
   1971 										CPVRTString * const pReturnError, int iLineNum)
   1972 {
   1973 	const unsigned int INVALID_TYPE = 0xAC1DBEEF;
   1974 	unsigned int uiIndex;
   1975 	const char* c_pszCursor;
   1976 	const char* c_pszResult;
   1977 
   1978 	// --- Find the first flag
   1979 	uiIndex = 0;
   1980 	c_pszCursor = strstr(c_pszRemainingLine, c_ppszFlagNames[uiIndex++]);
   1981 	while(uiIndex < uiNumFlagNames)
   1982 	{
   1983 		c_pszResult = strstr(c_pszRemainingLine, c_ppszFlagNames[uiIndex++]);
   1984 		if(((c_pszResult < c_pszCursor) || !c_pszCursor) && c_pszResult)
   1985 			c_pszCursor = c_pszResult;
   1986 	}
   1987 
   1988 	if(!c_pszCursor)
   1989 		return true;		// No error, but just return as no flags specified.
   1990 
   1991 	// Quick error check - make sure that the first flag found is valid.
   1992 	if(c_pszCursor != c_pszRemainingLine)
   1993 	{
   1994 		if(*(c_pszCursor-1) == '-')		// Yeah this shouldn't be there. Must be invalid first tag.
   1995 		{
   1996 			char szBuffer[128];		// Find out the tag.
   1997 			memset(szBuffer, 0, sizeof(szBuffer));
   1998 			const char* pszStart = c_pszCursor-1;
   1999 			while(pszStart != c_pszRemainingLine && *pszStart != ' ')		pszStart--;
   2000 			pszStart++;	// Escape the space.
   2001 			unsigned int uiNumChars = (unsigned int) ((c_pszCursor-1) - pszStart);
   2002 			strncpy(szBuffer, pszStart, uiNumChars);
   2003 
   2004 			*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d: %s\n", szBuffer, m_psContext->pnFileLineNumber[iLineNum], m_psContext->ppszEffectFile[iLineNum]);
   2005 			return false;
   2006 		}
   2007 	}
   2008 
   2009 	unsigned int uiFlagsFound = 0;
   2010 	unsigned int uiBufferIdx;
   2011 	char szBuffer[128];		// Buffer to hold the token
   2012 
   2013 	while(*c_pszCursor != ' ' && *c_pszCursor != 0 && uiFlagsFound < uiNumFlags)
   2014 	{
   2015 		memset(szBuffer, 0, sizeof(szBuffer));		// Clear the buffer
   2016 		uiBufferIdx = 0;
   2017 
   2018 		while(*c_pszCursor != '-' && *c_pszCursor != 0 && *c_pszCursor != ' ' && uiBufferIdx < 128)		// - = delim. token
   2019 			szBuffer[uiBufferIdx++] = *c_pszCursor++;
   2020 
   2021 		// Check if the buffer content is a valid flag name.
   2022 		unsigned int Type = INVALID_TYPE;
   2023 		for(unsigned int uiIndex = 0; uiIndex < uiNumFlagNames; ++uiIndex)
   2024 		{
   2025 			if(strcmp(szBuffer, c_ppszFlagNames[uiIndex]) == 0)
   2026 			{
   2027 				Type = uiIndex;			// Yup, it's valid. uiIndex here would translate to one of the enums that matches the string array of flag names passed in.
   2028 				break;
   2029 			}
   2030 		}
   2031 
   2032 		// Tell the user it's invalid.
   2033 		if(Type == INVALID_TYPE)
   2034 		{
   2035 			*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d: %s\n", szBuffer, m_psContext->pnFileLineNumber[iLineNum], m_psContext->ppszEffectFile[iLineNum]);
   2036 			return false;
   2037 		}
   2038 
   2039 		// Set the flag to the enum type.
   2040 		*ppFlagsOut[uiFlagsFound++] = Type;
   2041 
   2042 		if(*c_pszCursor == '-')	c_pszCursor++;
   2043 	}
   2044 
   2045 	return true;
   2046 }
   2047 
   2048 /*!***************************************************************************
   2049  @Function			ParseShader
   2050  @Input				nStartLine		start line number
   2051  @Input				nEndLine		end line number
   2052  @Output			pReturnError	error string
   2053  @Output			shader			shader data object
   2054  @Input				pszBlockName	name of block in PFX file
   2055  @Return			bool			true if parse is successful
   2056  @Description		Parses the VERTEXSHADER or FRAGMENTSHADER section of the
   2057 					PFX file.
   2058 *****************************************************************************/
   2059 bool CPVRTPFXParser::ParseShader(int nStartLine, int nEndLine, CPVRTString * const pReturnError, SPVRTPFXParserShader &shader, const char * const pszBlockName)
   2060 {
   2061 	enum eCmd
   2062 	{
   2063 		eCmds_GLSLCode,
   2064 		eCmds_Name,
   2065 		eCmds_File,
   2066 		eCmds_BinaryFile,
   2067 
   2068 		eCmds_Size
   2069 	};
   2070 
   2071 	const CPVRTHash ShaderCommands[] =
   2072 	{
   2073 		"[GLSL_CODE]",
   2074 		"NAME",
   2075 		"FILE",
   2076 		"BINARYFILE",
   2077 	};
   2078 	PVRTCOMPILEASSERT(ShaderCommands, sizeof(ShaderCommands) / sizeof(ShaderCommands[0]) == eCmds_Size);
   2079 
   2080 	bool glslcode=0, glslfile=0, bName=0;
   2081 
   2082 	shader.bUseFileName		= false;
   2083 	shader.pszGLSLfile		= NULL;
   2084 	shader.pszGLSLcode		= NULL;
   2085 	shader.pszGLSLBinaryFile= NULL;
   2086 	shader.pbGLSLBinary		= NULL;
   2087 	shader.nFirstLineNumber	= 0;
   2088 	shader.nLastLineNumber  = 0;
   2089 
   2090 	for(int i = nStartLine+1; i < nEndLine; i++)
   2091 	{
   2092 		// Skip blank lines
   2093 		if(!*m_psContext->ppszEffectFile[i])
   2094 			continue;
   2095 
   2096 		char *str = strtok (m_psContext->ppszEffectFile[i]," ");
   2097 		if(str != NULL)
   2098 		{
   2099 			CPVRTHash Cmd(str);
   2100 
   2101 			// Check for [GLSL_CODE] tags first and remove those lines from loop.
   2102 			if(Cmd == ShaderCommands[eCmds_GLSLCode])
   2103 			{
   2104 				if(glslcode)
   2105 				{
   2106 					*pReturnError = PVRTStringFromFormattedStr("[GLSL_CODE] redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
   2107 					return false;
   2108 				}
   2109 				if(glslfile && shader.pbGLSLBinary==NULL )
   2110 				{
   2111 					*pReturnError = PVRTStringFromFormattedStr("[GLSL_CODE] not allowed with FILE in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
   2112 					return false;
   2113 				}
   2114 
   2115 				shader.nFirstLineNumber = m_psContext->pnFileLineNumber[i];
   2116 
   2117 				// Skip the block-start
   2118 				i++;
   2119 
   2120 				CPVRTString GLSLCode;
   2121 				if(!ConcatenateLinesUntil(
   2122 					GLSLCode,
   2123 					i,
   2124 					m_psContext->ppszEffectFile,
   2125 					m_psContext->nNumLines,
   2126 					"[/GLSL_CODE]"))
   2127 				{
   2128 					return false;
   2129 				}
   2130 
   2131 				shader.nLastLineNumber = m_psContext->pnFileLineNumber[i];
   2132 
   2133 				shader.pszGLSLcode = (char*)malloc((GLSLCode.size()+1) * sizeof(char));
   2134 				strcpy(shader.pszGLSLcode, GLSLCode.c_str());
   2135 
   2136 				shader.bUseFileName = false;
   2137 				glslcode = 1;
   2138 			}
   2139 			else if(Cmd == ShaderCommands[eCmds_Name])
   2140 			{
   2141 				if(bName)
   2142 				{
   2143 					*pReturnError = PVRTStringFromFormattedStr("NAME redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
   2144 					return false;
   2145 				}
   2146 
   2147 				str = ReadEOLToken(NULL);
   2148 
   2149 				if(str == NULL)
   2150 				{
   2151 					*pReturnError = PVRTStringFromFormattedStr("NAME missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
   2152 					return false;
   2153 				}
   2154 
   2155 				shader.Name.assign(str);
   2156 				bName = true;
   2157 			}
   2158 			else if(Cmd == ShaderCommands[eCmds_File])
   2159 			{
   2160 				if(glslfile)
   2161 				{
   2162 					*pReturnError = PVRTStringFromFormattedStr("FILE redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
   2163 					return false;
   2164 				}
   2165 				if(glslcode)
   2166 				{
   2167 					*pReturnError = PVRTStringFromFormattedStr("FILE not allowed with [GLSL_CODE] in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
   2168 					return false;
   2169 				}
   2170 
   2171 				str = ReadEOLToken(NULL);
   2172 
   2173 				if(str == NULL)
   2174 				{
   2175 					*pReturnError = PVRTStringFromFormattedStr("FILE missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
   2176 					return false;
   2177 				}
   2178 
   2179 				shader.pszGLSLfile = (char*)malloc((strlen(str)+1) * sizeof(char));
   2180 				strcpy(shader.pszGLSLfile, str);
   2181 
   2182 				CPVRTResourceFile GLSLFile(str);
   2183 
   2184 				if(!GLSLFile.IsOpen())
   2185 				{
   2186 					*pReturnError = PVRTStringFromFormattedStr("Error loading file '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]);
   2187 					return false;
   2188 				}
   2189 				shader.pszGLSLcode = (char*)malloc((GLSLFile.Size()+1) * sizeof(char));
   2190 				memcpy(shader.pszGLSLcode, (const char*) GLSLFile.DataPtr(), GLSLFile.Size());
   2191 				shader.pszGLSLcode[GLSLFile.Size()] = '\0';
   2192 
   2193 				shader.nFirstLineNumber = m_psContext->pnFileLineNumber[i];		// Mark position where GLSL file is defined.
   2194 
   2195 				shader.bUseFileName = true;
   2196 				glslfile = 1;
   2197 			}
   2198 			else if(Cmd == ShaderCommands[eCmds_BinaryFile])
   2199 			{
   2200 				str = ReadEOLToken(NULL);
   2201 
   2202 				if(str == NULL)
   2203 				{
   2204 					*pReturnError = PVRTStringFromFormattedStr("BINARYFILE missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
   2205 					return false;
   2206 				}
   2207 
   2208 				shader.pszGLSLBinaryFile = (char*)malloc((strlen(str)+1) * sizeof(char));
   2209 				strcpy(shader.pszGLSLBinaryFile, str);
   2210 
   2211 				CPVRTResourceFile GLSLFile(str);
   2212 
   2213 				if(!GLSLFile.IsOpen())
   2214 				{
   2215 					*pReturnError = PVRTStringFromFormattedStr("Error loading file '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]);
   2216 					return false;
   2217 				}
   2218 				shader.pbGLSLBinary = new char[GLSLFile.Size()];
   2219 				shader.nGLSLBinarySize = (unsigned int)GLSLFile.Size();
   2220 				memcpy(shader.pbGLSLBinary, GLSLFile.DataPtr(), GLSLFile.Size());
   2221 
   2222 				shader.bUseFileName = true;
   2223 				glslfile = 1;
   2224 			}
   2225 			else
   2226 			{
   2227 				*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]);
   2228 				return false;
   2229 			}
   2230 
   2231 			str = strtok (NULL, " ");
   2232 			if(str != NULL)
   2233 			{
   2234 				*pReturnError = PVRTStringFromFormattedStr("Unexpected data in [%s] on line %d: '%s'\n", pszBlockName, m_psContext->pnFileLineNumber[i], str);
   2235 				return false;
   2236 			}
   2237 		}
   2238 		else
   2239 		{
   2240 			*pReturnError = PVRTStringFromFormattedStr("Missing arguments in [%s] on line %d: %s\n", pszBlockName, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
   2241 			return false;
   2242 		}
   2243 	}
   2244 
   2245 	if(!bName)
   2246 	{
   2247 		*pReturnError = PVRTStringFromFormattedStr("NAME not found in [%s] on line %d.\n", pszBlockName, m_psContext->pnFileLineNumber[nStartLine]);
   2248 		return false;
   2249 	}
   2250 
   2251 	if(!glslfile && !glslcode)
   2252 	{
   2253 		*pReturnError = PVRTStringFromFormattedStr("No Shader File or Shader Code specified in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[nStartLine]);
   2254 		return false;
   2255 	}
   2256 
   2257 	return true;
   2258 }
   2259 
   2260 /*!***************************************************************************
   2261  @Function			ParseSemantic
   2262  @Output			semantic		semantic data object
   2263  @Input				nStartLine		start line number
   2264  @Output			pReturnError	error string
   2265  @Return			bool			true if parse is successful
   2266  @Description		Parses a semantic.
   2267 *****************************************************************************/
   2268 bool CPVRTPFXParser::ParseSemantic(SPVRTPFXParserSemantic &semantic, const int nStartLine, CPVRTString * const pReturnError)
   2269 {
   2270 	char *str;
   2271 
   2272 	semantic.pszName = 0;
   2273 	semantic.pszValue = 0;
   2274 	semantic.sDefaultValue.eType = eDataTypeNone;
   2275 	semantic.nIdx = 0;
   2276 
   2277 	str = strtok (NULL, " ");
   2278 	if(str == NULL)
   2279 	{
   2280 		*pReturnError = PVRTStringFromFormattedStr("UNIFORM missing name in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   2281 		return false;
   2282 	}
   2283 	semantic.pszName = (char*)malloc((strlen(str)+1) * sizeof(char));
   2284 	strcpy(semantic.pszName, str);
   2285 
   2286 	str = strtok (NULL, " ");
   2287 	if(str == NULL)
   2288 	{
   2289 		*pReturnError = PVRTStringFromFormattedStr("UNIFORM missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   2290 
   2291 		FREE(semantic.pszName);
   2292 		return false;
   2293 	}
   2294 
   2295 	/*
   2296 		If the final digits of the semantic are a number they are
   2297 		stripped off and used as the index, with the remainder
   2298 		used as the semantic.
   2299 	*/
   2300 	{
   2301 		size_t idx, len;
   2302 		len = strlen(str);
   2303 
   2304 		idx = len;
   2305 		while(idx)
   2306 		{
   2307 			--idx;
   2308 			if(strcspn(&str[idx], "0123456789") != 0)
   2309 			{
   2310 				break;
   2311 			}
   2312 		}
   2313 		if(idx == 0)
   2314 		{
   2315 			*pReturnError = PVRTStringFromFormattedStr("Semantic contains only numbers in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   2316 
   2317 			FREE(semantic.pszName);
   2318 			return false;
   2319 		}
   2320 
   2321 		++idx;
   2322 		// Store the semantic index
   2323 		if(len == idx)
   2324 		{
   2325 			semantic.nIdx = 0;
   2326 		}
   2327 		else
   2328 		{
   2329 			semantic.nIdx = atoi(&str[idx]);
   2330 		}
   2331 
   2332 		// Chop off the index from the string containing the semantic
   2333 		str[idx] = 0;
   2334 	}
   2335 
   2336 	// Store a copy of the semantic name
   2337 	semantic.pszValue = (char*)malloc((strlen(str)+1) * sizeof(char));
   2338 	strcpy(semantic.pszValue, str);
   2339 
   2340 	/*
   2341 		Optional default semantic value
   2342 	*/
   2343 	char pszString[2048];
   2344 	strcpy(pszString,"");
   2345 	str = strtok (NULL, " ");
   2346 	if(str != NULL)
   2347 	{
   2348 		// Get all ramainning arguments
   2349 		while(str != NULL)
   2350 		{
   2351 			strcat(pszString, str);
   2352 			strcat(pszString, " ");
   2353 			str = strtok (NULL, " ");
   2354 		}
   2355 
   2356 		// default value
   2357 		int i;
   2358 		for(i = 0; i < eNumDefaultDataTypes; i++)
   2359 		{
   2360 			if(strncmp(pszString, c_psSemanticDefaultDataTypeInfo[i].pszName, strlen(c_psSemanticDefaultDataTypeInfo[i].pszName)) == 0)
   2361 			{
   2362 				if(!GetSemanticDataFromString(	&semantic.sDefaultValue,
   2363 												&pszString[strlen(c_psSemanticDefaultDataTypeInfo[i].pszName)],
   2364 												c_psSemanticDefaultDataTypeInfo[i].eType,
   2365 												pReturnError
   2366 												))
   2367 				{
   2368 					*pReturnError = PVRTStringFromFormattedStr(" on line %d.\n", m_psContext->pnFileLineNumber[nStartLine]);
   2369 
   2370 					FREE(semantic.pszValue);
   2371 					FREE(semantic.pszName);
   2372 					return false;
   2373 				}
   2374 
   2375 				semantic.sDefaultValue.eType = c_psSemanticDefaultDataTypeInfo[i].eType;
   2376 				break;
   2377 			}
   2378 		}
   2379 
   2380 		// invalid data type
   2381 		if(i == eNumDefaultDataTypes)
   2382 		{
   2383 			*pReturnError = PVRTStringFromFormattedStr("'%s' unknown on line %d.\n", pszString, m_psContext->pnFileLineNumber[nStartLine]);
   2384 
   2385 			FREE(semantic.pszValue);
   2386 			FREE(semantic.pszName);
   2387 			return false;
   2388 		}
   2389 
   2390 	}
   2391 
   2392 	return true;
   2393 }
   2394 
   2395 /*!***************************************************************************
   2396  @Function			ParseEffect
   2397  @Output			effect			effect data object
   2398  @Input				nStartLine		start line number
   2399  @Input				nEndLine		end line number
   2400  @Output			pReturnError	error string
   2401  @Return			bool			true if parse is successful
   2402  @Description		Parses the EFFECT section of the PFX file.
   2403 *****************************************************************************/
   2404 bool CPVRTPFXParser::ParseEffect(SPVRTPFXParserEffect &effect, const int nStartLine, const int nEndLine, CPVRTString * const pReturnError)
   2405 {
   2406 	enum eCmds
   2407 	{
   2408 		eCmds_Annotation,
   2409 		eCmds_VertexShader,
   2410 		eCmds_FragmentShader,
   2411 		eCmds_Texture,
   2412 		eCmds_Uniform,
   2413 		eCmds_Attribute,
   2414 		eCmds_Name,
   2415 		eCmds_Target,
   2416 
   2417 		eCmds_Size
   2418 	};
   2419 
   2420 	const CPVRTHash EffectCommands[] =
   2421 	{
   2422 		"[ANNOTATION]",
   2423 		"VERTEXSHADER",
   2424 		"FRAGMENTSHADER",
   2425 		"TEXTURE",
   2426 		"UNIFORM",
   2427 		"ATTRIBUTE",
   2428 		"NAME",
   2429 		"TARGET",
   2430 	};
   2431 	PVRTCOMPILEASSERT(EffectCommands, sizeof(EffectCommands) / sizeof(EffectCommands[0]) == eCmds_Size);
   2432 
   2433 	bool bName = false;
   2434 	bool bVertShader = false;
   2435 	bool bFragShader = false;
   2436 
   2437 	for(int i = nStartLine+1; i < nEndLine; i++)
   2438 	{
   2439 		// Skip blank lines
   2440 		if(!*m_psContext->ppszEffectFile[i])
   2441 			continue;
   2442 
   2443 		char *str = strtok (m_psContext->ppszEffectFile[i]," ");
   2444 		if(str != NULL)
   2445 		{
   2446 			CPVRTHash Cmd(str);
   2447 
   2448 			if(Cmd == EffectCommands[eCmds_Annotation])
   2449 			{
   2450 				if(!effect.Annotation.empty())
   2451 				{
   2452 					*pReturnError = PVRTStringFromFormattedStr("ANNOTATION redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]);
   2453 					return false;
   2454 				}
   2455 
   2456 				i++;		// Skip the block-start
   2457 				if(!ConcatenateLinesUntil(
   2458 					effect.Annotation,
   2459 					i,
   2460 					m_psContext->ppszEffectFile,
   2461 					m_psContext->nNumLines,
   2462 					"[/ANNOTATION]"))
   2463 				{
   2464 					return false;
   2465 				}
   2466 			}
   2467 			else if(Cmd == EffectCommands[eCmds_VertexShader])
   2468 			{
   2469 				if(bVertShader)
   2470 				{
   2471 					*pReturnError = PVRTStringFromFormattedStr("VERTEXSHADER redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]);
   2472 					return false;
   2473 				}
   2474 
   2475 				str = ReadEOLToken(NULL);
   2476 
   2477 				if(str == NULL)
   2478 				{
   2479 					*pReturnError = PVRTStringFromFormattedStr("VERTEXSHADER missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
   2480 					return false;
   2481 				}
   2482 				effect.VertexShaderName.assign(str);
   2483 
   2484 				bVertShader = true;
   2485 			}
   2486 			else if(Cmd == EffectCommands[eCmds_FragmentShader])
   2487 			{
   2488 				if(bFragShader)
   2489 				{
   2490 					*pReturnError = PVRTStringFromFormattedStr("FRAGMENTSHADER redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]);
   2491 					return false;
   2492 				}
   2493 
   2494 				str = ReadEOLToken(NULL);
   2495 
   2496 				if(str == NULL)
   2497 				{
   2498 					*pReturnError = PVRTStringFromFormattedStr("FRAGMENTSHADER missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
   2499 					return false;
   2500 				}
   2501 				effect.FragmentShaderName.assign(str);
   2502 
   2503 				bFragShader = true;
   2504 			}
   2505 			else if(Cmd == EffectCommands[eCmds_Texture])
   2506 			{
   2507 				unsigned int uiTexIdx = effect.Textures.Append();
   2508 				// texture number
   2509 				str = strtok(NULL, " ");
   2510 				if(str != NULL)
   2511 					effect.Textures[uiTexIdx].nNumber = atoi(str);
   2512 				else
   2513 				{
   2514 					*pReturnError = PVRTStringFromFormattedStr("TEXTURE missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
   2515 					return false;
   2516 				}
   2517 
   2518 				// texture name
   2519 				str = strtok(NULL, " ");
   2520 				if(str != NULL)
   2521 				{
   2522 					effect.Textures[uiTexIdx].Name = CPVRTStringHash(str);
   2523 				}
   2524 				else
   2525 				{
   2526 					*pReturnError = PVRTStringFromFormattedStr("TEXTURE missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
   2527 					return false;
   2528 				}
   2529 			}
   2530 			else if(Cmd == EffectCommands[eCmds_Uniform])
   2531 			{
   2532 				unsigned int uiUniformIdx = effect.Uniforms.Append();
   2533 				if(!ParseSemantic(effect.Uniforms[uiUniformIdx], i, pReturnError))
   2534 					return false;
   2535 
   2536 			}
   2537 			else if(Cmd == EffectCommands[eCmds_Attribute])
   2538 			{
   2539 				unsigned int uiAttribIdx = effect.Attributes.Append();
   2540 				if(!ParseSemantic(effect.Attributes[uiAttribIdx], i, pReturnError))
   2541 					return false;
   2542 			}
   2543 			else if(Cmd == EffectCommands[eCmds_Name])
   2544 			{
   2545 				if(bName)
   2546 				{
   2547 					*pReturnError = PVRTStringFromFormattedStr("NAME redefined in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   2548 					return false;
   2549 				}
   2550 
   2551 				str = strtok (NULL, " ");
   2552 				if(str == NULL)
   2553 				{
   2554 					*pReturnError = PVRTStringFromFormattedStr("NAME missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   2555 					return false;
   2556 				}
   2557 
   2558 				effect.Name.assign(str);
   2559 				bName = true;
   2560 			}
   2561 			else if(Cmd == EffectCommands[eCmds_Target])
   2562 			{
   2563 				unsigned int uiIndex = effect.Targets.Append();
   2564 
   2565 				// Target requires 2 components
   2566 				CPVRTString* pVals[] = { &effect.Targets[uiIndex].BufferType, &effect.Targets[uiIndex].TargetName };
   2567 
   2568 				for(unsigned int uiVal = 0; uiVal < 2; ++uiVal)
   2569 				{
   2570 					str = strtok (NULL, " ");
   2571 					if(str == NULL)
   2572 					{
   2573 						*pReturnError = PVRTStringFromFormattedStr("TARGET missing value(s) in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   2574 						return false;
   2575 					}
   2576 
   2577 					*(pVals[uiVal]) = str;
   2578 				}
   2579 			}
   2580 			else
   2581 			{
   2582 				*pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [EFFECT] on line %d\n", str, m_psContext->pnFileLineNumber[i]);
   2583 				return false;
   2584 			}
   2585 		}
   2586 		else
   2587 		{
   2588 			*pReturnError = PVRTStringFromFormattedStr( "Missing arguments in [EFFECT] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
   2589 			return false;
   2590 		}
   2591 	}
   2592 
   2593 	// Check that every TEXTURE has a matching UNIFORM
   2594 	for(unsigned int uiTex = 0; uiTex < effect.Textures.GetSize(); ++uiTex)
   2595 	{
   2596 		unsigned int uiTexUnit			= effect.Textures[uiTex].nNumber;
   2597 		const CPVRTStringHash& texName  = effect.Textures[uiTex].Name;
   2598 		// Find UNIFORM associated with the TexUnit (e.g TEXTURE0).
   2599 		bool bFound = false;
   2600 		for(unsigned int uiUniform = 0; uiUniform < effect.Uniforms.GetSize(); ++uiUniform)
   2601 		{
   2602 			const SPVRTPFXParserSemantic& Sem = effect.Uniforms[uiUniform];
   2603 			if(strcmp(Sem.pszValue, "TEXTURE") == 0 && Sem.nIdx == uiTexUnit)
   2604 			{
   2605 				bFound = true;
   2606 				break;
   2607 			}
   2608 		}
   2609 
   2610 		if(!bFound)
   2611 		{
   2612 			*pReturnError = PVRTStringFromFormattedStr("TEXTURE %s missing matching UNIFORM in [EFFECT] on line %d\n", texName.c_str(), m_psContext->pnFileLineNumber[nStartLine]);
   2613 			return false;
   2614 		}
   2615 	}
   2616 
   2617 
   2618 	if(!bName)
   2619 	{
   2620 		*pReturnError = PVRTStringFromFormattedStr("No 'NAME' found in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
   2621 		return false;
   2622 	}
   2623 	if(!bVertShader)
   2624 	{
   2625 		*pReturnError = PVRTStringFromFormattedStr("No 'VERTEXSHADER' defined in [EFFECT] starting on line %d: \n", m_psContext->pnFileLineNumber[nStartLine-1]);
   2626 		return false;
   2627 	}
   2628 	if(!bFragShader)
   2629 	{
   2630 		*pReturnError = PVRTStringFromFormattedStr("No 'FRAGMENTSHADER' defined in [EFFECT] starting on line %d: \n", m_psContext->pnFileLineNumber[nStartLine-1]);
   2631 		return false;
   2632 	}
   2633 
   2634 	return true;
   2635 }
   2636 
   2637 /*!***************************************************************************
   2638  @Function			DetermineRenderPassDependencies
   2639  @Return			True if dependency tree is valid. False if there are errors
   2640 					in the dependency tree (e.g. recursion)
   2641  @Description		Looks through all of the effects in the .pfx and determines
   2642 					the order of render passes that have been declared with
   2643 					the RENDER tag (found in [TEXTURES]
   2644 *****************************************************************************/
   2645 bool CPVRTPFXParser::DetermineRenderPassDependencies(CPVRTString * const pReturnError)
   2646 {
   2647 	unsigned int	ui(0), uj(0), uk(0);
   2648 
   2649 	if(m_RenderPasses.GetSize() == 0)
   2650 		return true;
   2651 
   2652 	// --- Add all render pass nodes to the skip graph.
   2653 	for(ui = 0; ui < m_RenderPasses.GetSize(); ++ui)
   2654 	{
   2655 		SPVRTPFXRenderPass& Pass = m_RenderPasses[ui];
   2656 		bool bFound = false;
   2657 
   2658 		// Search all EFFECT blocks for matching TARGET. This is for post-processes behavior.
   2659 		for(unsigned int uiEffect = 0; uiEffect < m_psEffect.GetSize(); ++uiEffect)
   2660 		{
   2661 			SPVRTPFXParserEffect& Effect = m_psEffect[uiEffect];
   2662 
   2663 			// Search all TARGETs in this effect
   2664 			for(unsigned int uiTargets = 0; uiTargets < Effect.Targets.GetSize(); ++uiTargets)
   2665 			{
   2666 				const SPVRTTargetPair& Target = Effect.Targets[uiTargets];
   2667 				if(Target.TargetName == Pass.SemanticName)
   2668 				{
   2669 					// Match. This EFFECT block matches the pass name.
   2670 					Pass.pEffect = &Effect;
   2671 					bFound = true;
   2672 
   2673 					// This is now a post-process pass. Set relevant values.
   2674 					Pass.eRenderPassType = ePOSTPROCESS_PASS;
   2675 					m_aszPostProcessNames.Append(Pass.SemanticName);
   2676 
   2677 					// Check that the surface type and output match are relevant (i.e DEPTH != RGBA8888).
   2678 					if( (Target.BufferType.find_first_of("DEPTH") != CPVRTString::npos && !(Pass.uiFormatFlags & PVRPFXTEX_DEPTH))
   2679 					||	(Target.BufferType.find_first_of("COLOR") != CPVRTString::npos && !(Pass.uiFormatFlags & PVRPFXTEX_COLOUR)) )
   2680 					{
   2681 						*pReturnError = PVRTStringFromFormattedStr("Surface type mismatch in [EFFECT]. \"%s\" has different type than \"%s\"\n", Target.TargetName.c_str(), Pass.SemanticName.c_str());
   2682 						return false;
   2683 					}
   2684 
   2685 					break;
   2686 				}
   2687 			}
   2688 
   2689 			if(bFound)
   2690 				break;
   2691 		}
   2692 
   2693 		// Add a pointer to the post process
   2694 		m_renderPassSkipGraph.AddNode(&Pass);
   2695 	}
   2696 
   2697 
   2698 	// --- Loop through all created render passes in the skip graph and determine their dependencies
   2699 	for(ui = 0; ui < m_renderPassSkipGraph.GetNumNodes(); ++ui)
   2700 	{
   2701 		//	Loop through all other nodes in the skip graph
   2702 		SPVRTPFXRenderPass* pPass			= m_renderPassSkipGraph[ui];
   2703 		SPVRTPFXRenderPass* pTestPass       = NULL;
   2704 
   2705 		for(uj = 0; uj < m_RenderPasses.GetSize(); ++uj)
   2706 		{
   2707 			pTestPass = m_renderPassSkipGraph[uj];
   2708 
   2709 			// No self compare
   2710 			if(pPass == pTestPass)
   2711 				continue;
   2712 
   2713 			// No effect associated.
   2714 			if(!pPass->pEffect)
   2715 				continue;
   2716 
   2717 			// Is the node a render pass I rely on?
   2718 			for(uk = 0; uk < pPass->pEffect->Textures.GetSize(); ++uk)
   2719 			{
   2720 				/*
   2721 					If the texture names match, add a new node
   2722 				*/
   2723 				if(pTestPass->pTexture->Name == pPass->pEffect->Textures[uk].Name)
   2724 				{
   2725 					m_renderPassSkipGraph.AddNodeDependency(pPass, pTestPass);
   2726 					break;
   2727 				}
   2728 			}
   2729 		}
   2730 	}
   2731 
   2732 	return true;
   2733 }
   2734 
   2735 /*!***************************************************************************
   2736 @Function		FindTextureIndex
   2737 @Input			TextureName
   2738 @Return			unsigned int	Index in to the effect.Texture array.
   2739 @Description	Returns the index in to the texture array within the effect
   2740 				block where the given texture resides.
   2741 *****************************************************************************/
   2742 unsigned int CPVRTPFXParser::FindTextureIndex( const CPVRTStringHash& TextureName, unsigned int uiEffect ) const
   2743 {
   2744 	for(unsigned int uiIndex = 0; uiIndex < m_psEffect[uiEffect].Textures.GetSize(); ++uiIndex)
   2745 	{
   2746 		const SPVRTPFXParserEffectTexture& Tex = m_psEffect[uiEffect].Textures[uiIndex];
   2747 		if(Tex.Name == TextureName)
   2748 		{
   2749 			return uiIndex;
   2750 		}
   2751 	}
   2752 
   2753 	return 0xFFFFFFFF;
   2754 }
   2755 
   2756 /*!***************************************************************************
   2757 @Function		GetNumberRenderPasses
   2758 @Return			unsigned int
   2759 @Description	Returns the number of render passes within this PFX.
   2760 *****************************************************************************/
   2761 unsigned int CPVRTPFXParser::GetNumberRenderPasses() const
   2762 {
   2763 	return m_RenderPasses.GetSize();
   2764 }
   2765 
   2766 /*!***************************************************************************
   2767 @Function		GetNumberRenderPasses
   2768 @Input			unsigned int		The render pass index.
   2769 @Return			SPVRTPFXRenderPass*
   2770 @Description	Returns the given render pass.
   2771 *****************************************************************************/
   2772 const SPVRTPFXRenderPass& CPVRTPFXParser::GetRenderPass( unsigned int uiIndex ) const
   2773 {
   2774 	_ASSERT(uiIndex >= 0 && uiIndex < GetNumberRenderPasses());
   2775 	return m_RenderPasses[uiIndex];
   2776 }
   2777 
   2778 /*!***************************************************************************
   2779 @Function		GetPFXFileName
   2780 @Return			const CPVRTString &
   2781 @Description	Returns the PFX file name associated with this object.
   2782 *****************************************************************************/
   2783 const CPVRTString& CPVRTPFXParser::GetPFXFileName() const
   2784 {
   2785 	return m_szFileName;
   2786 }
   2787 
   2788 /*!***************************************************************************
   2789 @Function		GetPostProcessNames
   2790 @Return			const CPVRTArray<CPVRTString>&
   2791 @Description	Returns a list of prost process effect names.
   2792 *****************************************************************************/
   2793 const CPVRTArray<CPVRTString>& CPVRTPFXParser::GetPostProcessNames() const
   2794 {
   2795 	return m_aszPostProcessNames;
   2796 }
   2797 
   2798 /*!***************************************************************************
   2799 @Function		GetNumberFragmentShaders
   2800 @Return			unsigned int	Number of fragment shaders.
   2801 @Description	Returns the number of fragment shaders referenced in the PFX.
   2802 *****************************************************************************/
   2803 unsigned int CPVRTPFXParser::GetNumberFragmentShaders() const
   2804 {
   2805 	return m_psFragmentShader.GetSize();
   2806 }
   2807 
   2808 
   2809 /*!***************************************************************************
   2810 @Function		GetFragmentShader
   2811 @Input			unsigned int		The index of this shader.
   2812 @Return			const SPVRTPFXParserShader&		The PFX fragment shader.
   2813 @Description	Returns a given fragment shader.
   2814 *****************************************************************************/
   2815 SPVRTPFXParserShader& CPVRTPFXParser::GetFragmentShader( unsigned int uiIndex )
   2816 {
   2817 	_ASSERT(uiIndex < GetNumberFragmentShaders());
   2818 	return m_psFragmentShader[uiIndex];
   2819 }
   2820 
   2821 /*!***************************************************************************
   2822 @Function		GetNumberVertexShaders
   2823 @Return			unsigned int	Number of vertex shaders.
   2824 @Description	Returns the number of vertex shaders referenced in the PFX.
   2825 *****************************************************************************/
   2826 unsigned int CPVRTPFXParser::GetNumberVertexShaders() const
   2827 {
   2828 	return m_psVertexShader.GetSize();
   2829 }
   2830 
   2831 /*!***************************************************************************
   2832 @Function		GetVertexShader
   2833 @Input			unsigned int		The index of this shader.
   2834 @Return			const SPVRTPFXParserShader&		The PFX vertex shader.
   2835 @Description	Returns a given vertex shader.
   2836 *****************************************************************************/
   2837 SPVRTPFXParserShader& CPVRTPFXParser::GetVertexShader( unsigned int uiIndex )
   2838 {
   2839 	_ASSERT(uiIndex < GetNumberVertexShaders());
   2840 	return m_psVertexShader[uiIndex];
   2841 }
   2842 
   2843 /*!***************************************************************************
   2844 @Function		GetNumberEffects
   2845 @Return			unsigned int	Number of effects.
   2846 @Description	Returns the number of effects referenced in the PFX.
   2847 *****************************************************************************/
   2848 unsigned int CPVRTPFXParser::GetNumberEffects() const
   2849 {
   2850 	return m_psEffect.GetSize();
   2851 }
   2852 
   2853 /*!***************************************************************************
   2854 @Function		GetEffect
   2855 @Input			uiIndex		The index of this effect.
   2856 @Return			The PFX effect.
   2857 @Description	Returns a given effect.
   2858 *****************************************************************************/
   2859 const SPVRTPFXParserEffect& CPVRTPFXParser::GetEffect(unsigned int uiIndex) const
   2860 {
   2861 	_ASSERT(uiIndex < GetNumberEffects());
   2862 	return m_psEffect[uiIndex];
   2863 }
   2864 
   2865 /*!***************************************************************************
   2866 @Function		GetNumberTextures
   2867 @Return			unsigned int	Number of effects.
   2868 @Description	Returns the number of textures referenced in the PFX.
   2869 *****************************************************************************/
   2870 unsigned int CPVRTPFXParser::GetNumberTextures() const
   2871 {
   2872 	return m_psTexture.GetSize();
   2873 }
   2874 
   2875 /*!***************************************************************************
   2876 @Function		GetTexture
   2877 @Input			unsigned int		The index of this texture
   2878 @Return			const SPVRTPFXParserEffect&		The PFX texture.
   2879 @Description	Returns a given texture.
   2880 *****************************************************************************/
   2881 const SPVRTPFXParserTexture* CPVRTPFXParser::GetTexture( unsigned int uiIndex ) const
   2882 {
   2883 	_ASSERT(uiIndex < GetNumberTextures());
   2884 	return m_psTexture[uiIndex];
   2885 }
   2886 
   2887 /*!***************************************************************************
   2888 @Function		FindEffectByName
   2889 @Input			Name
   2890 @Return			int
   2891 @Description	Returns the index of the given string. Returns -1 on failure.
   2892 *****************************************************************************/
   2893 int CPVRTPFXParser::FindEffectByName(const CPVRTStringHash& Name) const
   2894 {
   2895 	if(Name.Hash() == 0)
   2896 		return -1;
   2897 
   2898 	for(unsigned int uiIndex = 0; uiIndex < GetNumberEffects(); ++uiIndex)
   2899 	{
   2900 		if(GetEffect(uiIndex).Name == Name)
   2901 		{
   2902 			return (int)uiIndex;
   2903 		}
   2904 	}
   2905 
   2906 	return -1;
   2907 }
   2908 
   2909 /*!***************************************************************************
   2910 @Function		FindTextureByName
   2911 @Input			Name		Name of the texture.
   2912 @Return			int
   2913 @Description	Returns the index of the given texture. Returns -1 on failure.
   2914 *****************************************************************************/
   2915 int CPVRTPFXParser::FindTextureByName(const CPVRTStringHash& Name) const
   2916 {
   2917 	if(Name.Hash() == 0)
   2918 		return -1;
   2919 
   2920 	for(unsigned int uiIndex = 0; uiIndex < GetNumberTextures(); ++uiIndex)
   2921 	{
   2922 		if(GetTexture(uiIndex)->Name == Name)
   2923 		{
   2924 			return (int)uiIndex;
   2925 		}
   2926 	}
   2927 
   2928 	return -1;
   2929 }
   2930 
   2931 /*!***************************************************************************
   2932 @Function		PVRTPFXCreateStringCopy
   2933 @Return			void
   2934 @Description	Safely copies a C string.
   2935 *****************************************************************************/
   2936 void PVRTPFXCreateStringCopy(char** ppDst, const char* pSrc)
   2937 {
   2938 	if(pSrc)
   2939 	{
   2940 		FREE(*ppDst);
   2941 		*ppDst = (char*)malloc((strlen(pSrc)+1) * sizeof(char));
   2942 		strcpy(*ppDst, pSrc);
   2943 	}
   2944 }
   2945 
   2946 /*****************************************************************************
   2947  End of file (PVRTPFXParser.cpp)
   2948 *****************************************************************************/
   2949 
   2950