• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 *****************************************************************************/
CPVRTPFXParserReadContext()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 *****************************************************************************/
~CPVRTPFXParserReadContext()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 *****************************************************************************/
IgnoreWhitespace(char ** pszString)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 *****************************************************************************/
ReadEOLToken(char * pToken)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 *****************************************************************************/
GetSemanticDataFromString(SPVRTSemanticDefaultData * pDataItem,const char * const pszArgumentString,ESemanticDefaultDataType eType,CPVRTString * pError)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 *****************************************************************************/
ConcatenateLinesUntil(CPVRTString & Out,int & nLine,const char * const * const ppszLines,const unsigned int nLimit,const char * const pszEnd)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 ****************************************************************************/
SPVRTPFXParserEffect()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 ****************************************************************************/
SPVRTPFXRenderPass()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 ****************************************************************************/
SPVRTPFXParserShader()378 SPVRTPFXParserShader::SPVRTPFXParserShader()
379 	:
380 	pszGLSLfile(NULL),
381 	pszGLSLBinaryFile(NULL),
382 	pszGLSLcode(NULL),
383 	pbGLSLBinary(NULL)
384 {
385 }
386 
~SPVRTPFXParserShader()387 SPVRTPFXParserShader::~SPVRTPFXParserShader()
388 {
389 	FREE(pszGLSLfile);
390 	FREE(pszGLSLcode);
391 	FREE(pszGLSLBinaryFile);
392 	FREE(pbGLSLBinary);
393 }
394 
SPVRTPFXParserShader(const SPVRTPFXParserShader & rhs)395 SPVRTPFXParserShader::SPVRTPFXParserShader(const SPVRTPFXParserShader& rhs)
396 {
397 	Copy(rhs);
398 }
399 
operator =(const SPVRTPFXParserShader & rhs)400 SPVRTPFXParserShader& SPVRTPFXParserShader::operator=(const SPVRTPFXParserShader& rhs)
401 {
402 	if(&rhs != this)
403 		Copy(rhs);
404 
405 	return *this;
406 }
407 
Copy(const SPVRTPFXParserShader & rhs)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 ****************************************************************************/
SPVRTSemanticDefaultData()426 SPVRTSemanticDefaultData::SPVRTSemanticDefaultData()
427 	:
428 	eType(eDataTypeNone)
429 {
430 }
431 
SPVRTSemanticDefaultData(const SPVRTSemanticDefaultData & rhs)432 SPVRTSemanticDefaultData::SPVRTSemanticDefaultData(const SPVRTSemanticDefaultData& rhs)
433 {
434 	Copy(rhs);
435 }
436 
operator =(const SPVRTSemanticDefaultData & rhs)437 SPVRTSemanticDefaultData& SPVRTSemanticDefaultData::operator=(const SPVRTSemanticDefaultData& rhs)
438 {
439 	if(&rhs != this)
440 		Copy(rhs);
441 	return *this;
442 }
443 
Copy(const SPVRTSemanticDefaultData & rhs)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 ****************************************************************************/
SPVRTPFXParserSemantic()455 SPVRTPFXParserSemantic::SPVRTPFXParserSemantic()
456 	:
457 	pszName(NULL),
458 	pszValue(NULL)
459 {
460 }
461 
~SPVRTPFXParserSemantic()462 SPVRTPFXParserSemantic::~SPVRTPFXParserSemantic()
463 {
464 	FREE(pszName);
465 	FREE(pszValue);
466 }
467 
SPVRTPFXParserSemantic(const SPVRTPFXParserSemantic & rhs)468 SPVRTPFXParserSemantic::SPVRTPFXParserSemantic(const SPVRTPFXParserSemantic& rhs)
469 {
470 	Copy(rhs);
471 }
472 
operator =(const SPVRTPFXParserSemantic & rhs)473 SPVRTPFXParserSemantic& SPVRTPFXParserSemantic::operator=(const SPVRTPFXParserSemantic& rhs)
474 {
475 	if(&rhs != this)
476 		Copy(rhs);
477 
478 	return *this;
479 }
480 
Copy(const SPVRTPFXParserSemantic & rhs)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 *****************************************************************************/
CPVRTPFXParser()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 *****************************************************************************/
~CPVRTPFXParser()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 *****************************************************************************/
Parse(CPVRTString * const pReturnError)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 *****************************************************************************/
ParseFromMemory(const char * const pszScript,CPVRTString * const pReturnError)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 *****************************************************************************/
ParseFromFile(const char * const pszFileName,CPVRTString * const pReturnError)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 *****************************************************************************/
SetViewportSize(unsigned int uiWidth,unsigned int uiHeight)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 *****************************************************************************/
RetrieveRenderPassDependencies(CPVRTArray<SPVRTPFXRenderPass * > & aRequiredRenderPasses,CPVRTArray<CPVRTStringHash> & aszActiveEffectStrings)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 *****************************************************************************/
GetEndTag(const char * pszTagName,int nStartLine,int * pnEndLine)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 *****************************************************************************/
ReduceWhitespace(char * line)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 *****************************************************************************/
FindParameter(char * aszSourceString,const CPVRTString & parameterTag,const CPVRTString & delimiter)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 *****************************************************************************/
ReadStringToken(char * pszSource,CPVRTString & output,CPVRTString & ErrorStr,int i,const char * pCaller)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 *****************************************************************************/
ParseHeader(int nStartLine,int nEndLine,CPVRTString * const pReturnError)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 *****************************************************************************/
ParseGenericSurface(int nStartLine,int nEndLine,SPVRTPFXParserTexture & Params,CPVRTArray<CPVRTHash> & KnownCmds,const char * pCaller,CPVRTString * const pReturnError)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 *****************************************************************************/
ParseTexture(int nStartLine,int nEndLine,CPVRTString * const pReturnError)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 *****************************************************************************/
ParseTarget(int nStartLine,int nEndLine,CPVRTString * const pReturnError)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 *****************************************************************************/
ParseTextures(int nStartLine,int nEndLine,CPVRTString * const pReturnError)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 *****************************************************************************/
ParseTextureFlags(const char * c_pszRemainingLine,unsigned int ** ppFlagsOut,unsigned int uiNumFlags,const char ** c_ppszFlagNames,unsigned int uiNumFlagNames,CPVRTString * const pReturnError,int iLineNum)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 *****************************************************************************/
ParseShader(int nStartLine,int nEndLine,CPVRTString * const pReturnError,SPVRTPFXParserShader & shader,const char * const pszBlockName)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 *****************************************************************************/
ParseSemantic(SPVRTPFXParserSemantic & semantic,const int nStartLine,CPVRTString * const pReturnError)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 *****************************************************************************/
ParseEffect(SPVRTPFXParserEffect & effect,const int nStartLine,const int nEndLine,CPVRTString * const pReturnError)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 *****************************************************************************/
DetermineRenderPassDependencies(CPVRTString * const pReturnError)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 *****************************************************************************/
FindTextureIndex(const CPVRTStringHash & TextureName,unsigned int uiEffect) const2742 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 *****************************************************************************/
GetNumberRenderPasses() const2761 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 *****************************************************************************/
GetRenderPass(unsigned int uiIndex) const2772 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 *****************************************************************************/
GetPFXFileName() const2783 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 *****************************************************************************/
GetPostProcessNames() const2793 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 *****************************************************************************/
GetNumberFragmentShaders() const2803 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 *****************************************************************************/
GetFragmentShader(unsigned int uiIndex)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 *****************************************************************************/
GetNumberVertexShaders() const2826 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 *****************************************************************************/
GetVertexShader(unsigned int uiIndex)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 *****************************************************************************/
GetNumberEffects() const2848 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 *****************************************************************************/
GetEffect(unsigned int uiIndex) const2859 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 *****************************************************************************/
GetNumberTextures() const2870 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 *****************************************************************************/
GetTexture(unsigned int uiIndex) const2881 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 *****************************************************************************/
FindEffectByName(const CPVRTStringHash & Name) const2893 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 *****************************************************************************/
FindTextureByName(const CPVRTStringHash & Name) const2915 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 *****************************************************************************/
PVRTPFXCreateStringCopy(char ** ppDst,const char * pSrc)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