|  | /****************************************************************************** | 
|  |  | 
|  | @File         PVRTPFXParser.cpp | 
|  |  | 
|  | @Title        PVRTPFXParser | 
|  |  | 
|  | @Version | 
|  |  | 
|  | @Copyright    Copyright (c) Imagination Technologies Limited. | 
|  |  | 
|  | @Platform     ANSI compatible | 
|  |  | 
|  | @Description  PFX file parser. | 
|  |  | 
|  | ******************************************************************************/ | 
|  |  | 
|  | /***************************************************************************** | 
|  | ** Includes | 
|  | ******************************************************************************/ | 
|  | #include <string.h> | 
|  | #include <stdio.h> | 
|  | #include <stdlib.h> | 
|  |  | 
|  | #include "PVRTGlobal.h" | 
|  | #include "PVRTContext.h" | 
|  | #include "PVRTMatrix.h" | 
|  | #include "PVRTFixedPoint.h" | 
|  | #include "PVRTMisc.h" | 
|  | #include "PVRTPFXParser.h" | 
|  | #include "PVRTResourceFile.h" | 
|  | #include "PVRTString.h" | 
|  | #include "PVRTMisc.h"		// Used for POT functions | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Constants | 
|  | ****************************************************************************/ | 
|  | const char* c_pszLinear   = "LINEAR"; | 
|  | const char* c_pszNearest  = "NEAREST"; | 
|  | const char* c_pszNone	  = "NONE"; | 
|  | const char* c_pszClamp    = "CLAMP"; | 
|  | const char* c_pszRepeat	  = "REPEAT"; | 
|  | const char* c_pszCurrentView = "PFX_CURRENTVIEW"; | 
|  |  | 
|  | const unsigned int CPVRTPFXParser::VIEWPORT_SIZE = 0xAAAA; | 
|  |  | 
|  | const char* c_ppszFilters[eFilter_Size] = | 
|  | { | 
|  | c_pszNearest,		// eFilter_Nearest | 
|  | c_pszLinear,		// eFilter_Linear | 
|  | c_pszNone,			// eFilter_None | 
|  | }; | 
|  | const char* c_ppszWraps[eWrap_Size] = | 
|  | { | 
|  | c_pszClamp,			// eWrap_Clamp | 
|  | c_pszRepeat			// eWrap_Repeat | 
|  | }; | 
|  |  | 
|  | #define NEWLINE_TOKENS "\r\n" | 
|  | #define DELIM_TOKENS " \t" | 
|  |  | 
|  | #define DEFAULT_EFFECT_NUM_TEX		100 | 
|  | #define DEFAULT_EFFECT_NUM_UNIFORM	100 | 
|  | #define DEFAULT_EFFECT_NUM_ATTRIB	100 | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** Data tables | 
|  | ****************************************************************************/ | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** CPVRTPFXParserReadContext Class | 
|  | ****************************************************************************/ | 
|  | class CPVRTPFXParserReadContext | 
|  | { | 
|  | public: | 
|  | char			**ppszEffectFile; | 
|  | int				*pnFileLineNumber; | 
|  | unsigned int	nNumLines, nMaxLines; | 
|  |  | 
|  | public: | 
|  | CPVRTPFXParserReadContext(); | 
|  | ~CPVRTPFXParserReadContext(); | 
|  | }; | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			CPVRTPFXParserReadContext | 
|  | @Description		Initialises values. | 
|  | *****************************************************************************/ | 
|  | CPVRTPFXParserReadContext::CPVRTPFXParserReadContext() | 
|  | { | 
|  | nMaxLines = 5000; | 
|  | nNumLines = 0; | 
|  | ppszEffectFile		= new char*[nMaxLines]; | 
|  | pnFileLineNumber	= new int[nMaxLines]; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			~CPVRTPFXParserReadContext | 
|  | @Description		Frees allocated memory | 
|  | *****************************************************************************/ | 
|  | CPVRTPFXParserReadContext::~CPVRTPFXParserReadContext() | 
|  | { | 
|  | // free effect file | 
|  | for(unsigned int i = 0; i < nNumLines; i++) | 
|  | { | 
|  | FREE(ppszEffectFile[i]); | 
|  | } | 
|  | delete [] ppszEffectFile; | 
|  | delete [] pnFileLineNumber; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			IgnoreWhitespace | 
|  | @Input				pszString | 
|  | @Output			pszString | 
|  | @Description		Skips space, tab, new-line and return characters. | 
|  | *****************************************************************************/ | 
|  | static void IgnoreWhitespace(char **pszString) | 
|  | { | 
|  | while(	*pszString[0] == '\t' || | 
|  | *pszString[0] == '\n' || | 
|  | *pszString[0] == '\r' || | 
|  | *pszString[0] == ' ' ) | 
|  | { | 
|  | (*pszString)++; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ReadEOLToken | 
|  | @Input				pToken | 
|  | @Output			char* | 
|  | @Description		Reads next strings to the end of the line and interperts as | 
|  | a token. | 
|  | *****************************************************************************/ | 
|  | static char* ReadEOLToken(char* pToken) | 
|  | { | 
|  | char* pReturn = NULL; | 
|  |  | 
|  | char szDelim[2] = {'\n', 0};				// try newline | 
|  | pReturn = strtok(pToken, szDelim); | 
|  | if(pReturn == NULL) | 
|  | { | 
|  | szDelim[0] = '\r'; | 
|  | pReturn = strtok (pToken, szDelim);		// try linefeed | 
|  | } | 
|  | return pReturn; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			GetSemanticDataFromString | 
|  | @Output			pDataItem | 
|  | @Modified			pszArgumentString | 
|  | @Input				eType | 
|  | @Output			pError				error message | 
|  | @Return			true if successful | 
|  | @Description		Extracts the semantic data from the string and stores it | 
|  | in the output SPVRTSemanticDefaultData parameter. | 
|  | *****************************************************************************/ | 
|  | static bool GetSemanticDataFromString(SPVRTSemanticDefaultData *pDataItem, const char * const pszArgumentString, ESemanticDefaultDataType eType, CPVRTString *pError) | 
|  | { | 
|  | char *pszString = (char *)pszArgumentString; | 
|  | char *pszTmp; | 
|  |  | 
|  | IgnoreWhitespace(&pszString); | 
|  |  | 
|  | if(pszString[0] != '(') | 
|  | { | 
|  | *pError = CPVRTString("Missing '(' after ") + c_psSemanticDefaultDataTypeInfo[eType].pszName; | 
|  | return false; | 
|  | } | 
|  | pszString++; | 
|  |  | 
|  | IgnoreWhitespace(&pszString); | 
|  |  | 
|  | if(!strlen(pszString)) | 
|  | { | 
|  | *pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | pszTmp = pszString; | 
|  | switch(c_psSemanticDefaultDataTypeInfo[eType].eInternalType) | 
|  | { | 
|  | case eFloating: | 
|  | pDataItem->pfData[0] = (float)strtod(pszString, &pszTmp); | 
|  | break; | 
|  | case eInteger: | 
|  | pDataItem->pnData[0] = (int)strtol(pszString, &pszTmp, 10); | 
|  | break; | 
|  | case eBoolean: | 
|  | if(strncmp(pszString, "true", 4) == 0) | 
|  | { | 
|  | pDataItem->pbData[0] = true; | 
|  | pszTmp = &pszString[4]; | 
|  | } | 
|  | else if(strncmp(pszString, "false", 5) == 0) | 
|  | { | 
|  | pDataItem->pbData[0] = false; | 
|  | pszTmp = &pszString[5]; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | if(pszString == pszTmp) | 
|  | { | 
|  | size_t n = strcspn(pszString, ",\t "); | 
|  | char *pszError = (char *)malloc(n + 1); | 
|  | strcpy(pszError, ""); | 
|  | strncat(pszError, pszString, n); | 
|  | *pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName; | 
|  | FREE(pszError); | 
|  | return false; | 
|  | } | 
|  | pszString = pszTmp; | 
|  |  | 
|  | IgnoreWhitespace(&pszString); | 
|  |  | 
|  | for(unsigned int i = 1; i < c_psSemanticDefaultDataTypeInfo[eType].nNumberDataItems; i++) | 
|  | { | 
|  | if(!strlen(pszString)) | 
|  | { | 
|  | *pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(pszString[0] != ',') | 
|  | { | 
|  | size_t n = strcspn(pszString, ",\t "); | 
|  | char *pszError = (char *)malloc(n + 1); | 
|  | strcpy(pszError, ""); | 
|  | strncat(pszError, pszString, n); | 
|  | *pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName; | 
|  | FREE(pszError); | 
|  | return false; | 
|  | } | 
|  | pszString++; | 
|  |  | 
|  | IgnoreWhitespace(&pszString); | 
|  |  | 
|  | if(!strlen(pszString)) | 
|  | { | 
|  | *pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | pszTmp = pszString; | 
|  | switch(c_psSemanticDefaultDataTypeInfo[eType].eInternalType) | 
|  | { | 
|  | case eFloating: | 
|  | pDataItem->pfData[i] = (float)strtod(pszString, &pszTmp); | 
|  | break; | 
|  | case eInteger: | 
|  | pDataItem->pnData[i] = (int)strtol(pszString, &pszTmp, 10); | 
|  | break; | 
|  | case eBoolean: | 
|  | if(strncmp(pszString, "true", 4) == 0) | 
|  | { | 
|  | pDataItem->pbData[i] = true; | 
|  | pszTmp = &pszString[4]; | 
|  | } | 
|  | else if(strncmp(pszString, "false", 5) == 0) | 
|  | { | 
|  | pDataItem->pbData[i] = false; | 
|  | pszTmp = &pszString[5]; | 
|  | } | 
|  | break; | 
|  | } | 
|  |  | 
|  | if(pszString == pszTmp) | 
|  | { | 
|  | size_t n = strcspn(pszString, ",\t "); | 
|  | char *pszError = (char *)malloc(n + 1); | 
|  | strcpy(pszError, ""); | 
|  | strncat(pszError, pszString, n); | 
|  | *pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName; | 
|  | FREE(pszError); | 
|  | return false; | 
|  | } | 
|  | pszString = pszTmp; | 
|  |  | 
|  | IgnoreWhitespace(&pszString); | 
|  | } | 
|  |  | 
|  | if(pszString[0] != ')') | 
|  | { | 
|  | size_t n = strcspn(pszString, "\t )"); | 
|  | char *pszError = (char *)malloc(n + 1); | 
|  | strcpy(pszError, ""); | 
|  | strncat(pszError, pszString, n); | 
|  | *pError = CPVRTString("'") + pszError + "' found when expecting ')' for " + c_psSemanticDefaultDataTypeInfo[eType].pszName; | 
|  | FREE(pszError); | 
|  | return false; | 
|  | } | 
|  | pszString++; | 
|  |  | 
|  | IgnoreWhitespace(&pszString); | 
|  |  | 
|  | if(strlen(pszString)) | 
|  | { | 
|  | *pError = CPVRTString("'") + pszString + "' unexpected after ')'"; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ConcatenateLinesUntil | 
|  | @Output			pszOut		output text | 
|  | @Output			nLine		end line number | 
|  | @Input				nLine		start line number | 
|  | @Input				ppszLines	input text - one array element per line | 
|  | @Input				nLimit		number of lines input | 
|  | @Input				pszEnd		end string | 
|  | @Return			true if successful | 
|  | @Description		Outputs a block of text starting from nLine and ending | 
|  | when the string pszEnd is found. | 
|  | *****************************************************************************/ | 
|  | static bool ConcatenateLinesUntil(CPVRTString& Out, int &nLine, const char * const * const ppszLines, const unsigned int nLimit, const char * const pszEnd) | 
|  | { | 
|  | unsigned int	i, j; | 
|  | size_t			nLen; | 
|  |  | 
|  | nLen = 0; | 
|  | for(i = nLine; i < nLimit; ++i) | 
|  | { | 
|  | if(strcmp(ppszLines[i], pszEnd) == 0) | 
|  | break; | 
|  | nLen += strlen(ppszLines[i]) + 1; | 
|  | } | 
|  | if(i == nLimit) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(nLen) | 
|  | { | 
|  | ++nLen; | 
|  |  | 
|  | Out.reserve(nLen); | 
|  |  | 
|  | for(j = nLine; j < i; ++j) | 
|  | { | 
|  | Out.append(ppszLines[j]); | 
|  | Out.append("\n"); | 
|  | } | 
|  | } | 
|  |  | 
|  | nLine = i; | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** SPVRTPFXParserEffect Struct | 
|  | ****************************************************************************/ | 
|  | SPVRTPFXParserEffect::SPVRTPFXParserEffect() : | 
|  | Uniforms(DEFAULT_EFFECT_NUM_UNIFORM), | 
|  | Attributes(DEFAULT_EFFECT_NUM_ATTRIB), | 
|  | Textures(DEFAULT_EFFECT_NUM_TEX) | 
|  | { | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** SPVRTPFXRenderPass Class | 
|  | ****************************************************************************/ | 
|  | SPVRTPFXRenderPass::SPVRTPFXRenderPass() : | 
|  | eRenderPassType(eNULL_PASS), | 
|  | eViewType(eVIEW_NONE), | 
|  | uiFormatFlags(0), | 
|  | pEffect(NULL), | 
|  | pTexture(NULL) | 
|  | { | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** SPVRTPFXParserShader Class | 
|  | ****************************************************************************/ | 
|  | SPVRTPFXParserShader::SPVRTPFXParserShader() | 
|  | : | 
|  | pszGLSLfile(NULL), | 
|  | pszGLSLBinaryFile(NULL), | 
|  | pszGLSLcode(NULL), | 
|  | pbGLSLBinary(NULL) | 
|  | { | 
|  | } | 
|  |  | 
|  | SPVRTPFXParserShader::~SPVRTPFXParserShader() | 
|  | { | 
|  | FREE(pszGLSLfile); | 
|  | FREE(pszGLSLcode); | 
|  | FREE(pszGLSLBinaryFile); | 
|  | FREE(pbGLSLBinary); | 
|  | } | 
|  |  | 
|  | SPVRTPFXParserShader::SPVRTPFXParserShader(const SPVRTPFXParserShader& rhs) | 
|  | { | 
|  | Copy(rhs); | 
|  | } | 
|  |  | 
|  | SPVRTPFXParserShader& SPVRTPFXParserShader::operator=(const SPVRTPFXParserShader& rhs) | 
|  | { | 
|  | if(&rhs != this) | 
|  | Copy(rhs); | 
|  |  | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | void SPVRTPFXParserShader::Copy(const SPVRTPFXParserShader& rhs) | 
|  | { | 
|  | Name = rhs.Name; | 
|  |  | 
|  | PVRTPFXCreateStringCopy(&pszGLSLfile, rhs.pszGLSLfile); | 
|  | PVRTPFXCreateStringCopy(&pszGLSLBinaryFile, rhs.pszGLSLBinaryFile); | 
|  | PVRTPFXCreateStringCopy(&pszGLSLcode, rhs.pszGLSLcode); | 
|  | PVRTPFXCreateStringCopy(&pbGLSLBinary, rhs.pbGLSLBinary); | 
|  |  | 
|  | bUseFileName	= rhs.bUseFileName; | 
|  | nGLSLBinarySize = rhs.nGLSLBinarySize; | 
|  | nFirstLineNumber= rhs.nFirstLineNumber; | 
|  | nLastLineNumber = rhs.nLastLineNumber; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** SPVRTSemanticDefaultData Struct | 
|  | ****************************************************************************/ | 
|  | SPVRTSemanticDefaultData::SPVRTSemanticDefaultData() | 
|  | : | 
|  | eType(eDataTypeNone) | 
|  | { | 
|  | } | 
|  |  | 
|  | SPVRTSemanticDefaultData::SPVRTSemanticDefaultData(const SPVRTSemanticDefaultData& rhs) | 
|  | { | 
|  | Copy(rhs); | 
|  | } | 
|  |  | 
|  | SPVRTSemanticDefaultData& SPVRTSemanticDefaultData::operator=(const SPVRTSemanticDefaultData& rhs) | 
|  | { | 
|  | if(&rhs != this) | 
|  | Copy(rhs); | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | void SPVRTSemanticDefaultData::Copy(const SPVRTSemanticDefaultData& rhs) | 
|  | { | 
|  | memcpy(pfData, rhs.pfData, sizeof(pfData)); | 
|  | memcpy(pnData, rhs.pnData, sizeof(pnData)); | 
|  | memcpy(pbData, rhs.pbData, sizeof(pbData)); | 
|  | eType = rhs.eType; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** SPVRTPFXParserSemantic Struct | 
|  | ****************************************************************************/ | 
|  | SPVRTPFXParserSemantic::SPVRTPFXParserSemantic() | 
|  | : | 
|  | pszName(NULL), | 
|  | pszValue(NULL) | 
|  | { | 
|  | } | 
|  |  | 
|  | SPVRTPFXParserSemantic::~SPVRTPFXParserSemantic() | 
|  | { | 
|  | FREE(pszName); | 
|  | FREE(pszValue); | 
|  | } | 
|  |  | 
|  | SPVRTPFXParserSemantic::SPVRTPFXParserSemantic(const SPVRTPFXParserSemantic& rhs) | 
|  | { | 
|  | Copy(rhs); | 
|  | } | 
|  |  | 
|  | SPVRTPFXParserSemantic& SPVRTPFXParserSemantic::operator=(const SPVRTPFXParserSemantic& rhs) | 
|  | { | 
|  | if(&rhs != this) | 
|  | Copy(rhs); | 
|  |  | 
|  | return *this; | 
|  | } | 
|  |  | 
|  | void SPVRTPFXParserSemantic::Copy(const SPVRTPFXParserSemantic& rhs) | 
|  | { | 
|  | PVRTPFXCreateStringCopy(&pszName, rhs.pszName); | 
|  | PVRTPFXCreateStringCopy(&pszValue, rhs.pszValue); | 
|  | nIdx			= rhs.nIdx; | 
|  | sDefaultValue   = rhs.sDefaultValue; | 
|  | } | 
|  |  | 
|  | /**************************************************************************** | 
|  | ** CPVRTPFXParser Class | 
|  | ****************************************************************************/ | 
|  | /*!*************************************************************************** | 
|  | @Function			CPVRTPFXParser | 
|  | @Description		Sets initial values. | 
|  | *****************************************************************************/ | 
|  | CPVRTPFXParser::CPVRTPFXParser() | 
|  | { | 
|  | m_szFileName.assign(""); | 
|  |  | 
|  | // NOTE: Temp hardcode viewport size | 
|  | m_uiViewportWidth = 640; | 
|  | m_uiViewportHeight = 480; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			~CPVRTPFXParser | 
|  | @Description		Frees memory used. | 
|  | *****************************************************************************/ | 
|  | CPVRTPFXParser::~CPVRTPFXParser() | 
|  | { | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			Parse | 
|  | @Output			pReturnError	error string | 
|  | @Return			bool			true for success parsing file | 
|  | @Description		Parses a loaded PFX file. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::Parse(CPVRTString * const pReturnError) | 
|  | { | 
|  | enum eCmd | 
|  | { | 
|  | eCmds_Header, | 
|  | eCmds_Texture, | 
|  | eCmds_Target, | 
|  | eCmds_Textures, | 
|  | eCmds_VertexShader, | 
|  | eCmds_FragmentShader, | 
|  | eCmds_Effect, | 
|  |  | 
|  | eCmds_Size | 
|  | }; | 
|  |  | 
|  | const CPVRTHash ParserCommands[] = | 
|  | { | 
|  | "[HEADER]",				// eCmds_Header | 
|  | "[TEXTURE]",			// eCmds_Texture | 
|  | "[TARGET]",				// eCmds_Target | 
|  | "[TEXTURES]",			// eCmds_Textures | 
|  | "[VERTEXSHADER]",		// eCmds_VertexShader | 
|  | "[FRAGMENTSHADER]",		// eCmds_FragmentShader | 
|  | "[EFFECT]",				// eCmds_Effect | 
|  | }; | 
|  | PVRTCOMPILEASSERT(ParserCommands, sizeof(ParserCommands) / sizeof(ParserCommands[0]) == eCmds_Size); | 
|  |  | 
|  | int nEndLine = 0; | 
|  | int nHeaderCounter = 0, nTexturesCounter = 0; | 
|  | unsigned int i,j,k; | 
|  |  | 
|  | // Loop through the file | 
|  | for(unsigned int nLine=0; nLine < m_psContext->nNumLines; nLine++) | 
|  | { | 
|  | // Skip blank lines | 
|  | if(!*m_psContext->ppszEffectFile[nLine]) | 
|  | continue; | 
|  |  | 
|  | CPVRTHash Cmd(m_psContext->ppszEffectFile[nLine]); | 
|  | if(Cmd == ParserCommands[eCmds_Header]) | 
|  | { | 
|  | if(nHeaderCounter>0) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("[HEADER] redefined on line %d\n", m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | if(GetEndTag("HEADER", nLine, &nEndLine)) | 
|  | { | 
|  | if(ParseHeader(nLine, nEndLine, pReturnError)) | 
|  | nHeaderCounter++; | 
|  | else | 
|  | return false; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing [/HEADER] tag after [HEADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | nLine = nEndLine; | 
|  | } | 
|  | else if(Cmd == ParserCommands[eCmds_Texture]) | 
|  | { | 
|  | if(GetEndTag("TEXTURE", nLine, &nEndLine)) | 
|  | { | 
|  | if(!ParseTexture(nLine, nEndLine, pReturnError)) | 
|  | return false; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing [/TEXTURE] tag after [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | nLine = nEndLine; | 
|  | } | 
|  | else if(Cmd == ParserCommands[eCmds_Target]) | 
|  | { | 
|  | if(GetEndTag("TARGET", nLine, &nEndLine)) | 
|  | { | 
|  | if(!ParseTarget(nLine, nEndLine, pReturnError)) | 
|  | return false; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing [/TARGET] tag after [TARGET] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | nLine = nEndLine; | 
|  | } | 
|  | else if(Cmd == ParserCommands[eCmds_Textures]) | 
|  | { | 
|  | if(nTexturesCounter>0) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("[TEXTURES] redefined on line %d\n", m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | if(GetEndTag("TEXTURES", nLine, &nEndLine)) | 
|  | { | 
|  | if(ParseTextures(nLine, nEndLine, pReturnError)) | 
|  | nTexturesCounter++; | 
|  | else | 
|  | return false; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing [/TEXTURES] tag after [TEXTURES] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | nLine = nEndLine; | 
|  | } | 
|  | else if(Cmd == ParserCommands[eCmds_VertexShader]) | 
|  | { | 
|  | if(GetEndTag("VERTEXSHADER", nLine, &nEndLine)) | 
|  | { | 
|  | SPVRTPFXParserShader VertexShader; | 
|  | if(ParseShader(nLine, nEndLine, pReturnError, VertexShader, "VERTEXSHADER")) | 
|  | m_psVertexShader.Append(VertexShader); | 
|  | else | 
|  | return false; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing [/VERTEXSHADER] tag after [VERTEXSHADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | nLine = nEndLine; | 
|  | } | 
|  | else if(Cmd == ParserCommands[eCmds_FragmentShader]) | 
|  | { | 
|  | if(GetEndTag("FRAGMENTSHADER", nLine, &nEndLine)) | 
|  | { | 
|  | SPVRTPFXParserShader FragShader; | 
|  | if(ParseShader(nLine, nEndLine, pReturnError, FragShader, "FRAGMENTSHADER")) | 
|  | m_psFragmentShader.Append(FragShader); | 
|  | else | 
|  | return false; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing [/FRAGMENTSHADER] tag after [FRAGMENTSHADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | nLine = nEndLine; | 
|  | } | 
|  | else if(Cmd == ParserCommands[eCmds_Effect]) | 
|  | { | 
|  | if(GetEndTag("EFFECT", nLine, &nEndLine)) | 
|  | { | 
|  | SPVRTPFXParserEffect Effect; | 
|  | if(ParseEffect(Effect, nLine, nEndLine, pReturnError)) | 
|  | m_psEffect.Append(Effect); | 
|  | else | 
|  | return false; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing [/EFFECT] tag after [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | nLine = nEndLine; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("'%s' unexpected on line %d\n", m_psContext->ppszEffectFile[nLine], m_psContext->pnFileLineNumber[nLine]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(m_psEffect.GetSize() < 1) | 
|  | { | 
|  | *pReturnError = CPVRTString("No [EFFECT] found. PFX file must have at least one defined.\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(m_psFragmentShader.GetSize() < 1) | 
|  | { | 
|  | *pReturnError = CPVRTString("No [FRAGMENTSHADER] found. PFX file must have at least one defined.\n");; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(m_psVertexShader.GetSize() < 1) | 
|  | { | 
|  | *pReturnError = CPVRTString("No [VERTEXSHADER] found. PFX file must have at least one defined.\n"); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Loop Effects | 
|  | for(i = 0; i < m_psEffect.GetSize(); ++i) | 
|  | { | 
|  | // Loop Textures in Effects | 
|  | for(j = 0; j < m_psEffect[i].Textures.GetSize(); ++j) | 
|  | { | 
|  | // Loop Textures in whole PFX | 
|  | unsigned int uiTexSize = m_psTexture.GetSize(); | 
|  | for(k = 0; k < uiTexSize; ++k) | 
|  | { | 
|  | if(m_psTexture[k]->Name == m_psEffect[i].Textures[j].Name) | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Texture mismatch. Report error. | 
|  | if(!uiTexSize || k == uiTexSize) | 
|  | { | 
|  | *pReturnError = "Error: TEXTURE '" + m_psEffect[i].Textures[j].Name.String() + "' is not defined in [TEXTURES].\n"; | 
|  | return false; | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | DetermineRenderPassDependencies(pReturnError); | 
|  | if(pReturnError->compare("")) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseFromMemory | 
|  | @Input				pszScript		PFX script | 
|  | @Output			pReturnError	error string | 
|  | @Return			EPVRTError		PVR_SUCCESS for success parsing file | 
|  | PVR_FAIL if file doesn't exist or is invalid | 
|  | @Description		Parses a PFX script from memory. | 
|  | *****************************************************************************/ | 
|  | EPVRTError CPVRTPFXParser::ParseFromMemory(const char * const pszScript, CPVRTString * const pReturnError) | 
|  | { | 
|  | CPVRTPFXParserReadContext	context; | 
|  | char			pszLine[512]; | 
|  | const char		*pszEnd, *pszCurr; | 
|  | int				nLineCounter; | 
|  | unsigned int	nLen; | 
|  | unsigned int	nReduce; | 
|  | bool			bDone; | 
|  |  | 
|  | if(!pszScript) | 
|  | return PVR_FAIL; | 
|  |  | 
|  | m_psContext = &context; | 
|  |  | 
|  | // Find & process each line | 
|  | nLineCounter	= 0; | 
|  | bDone			= false; | 
|  | pszCurr			= pszScript; | 
|  | while(!bDone) | 
|  | { | 
|  | nLineCounter++; | 
|  |  | 
|  | while(*pszCurr == '\r') | 
|  | ++pszCurr; | 
|  |  | 
|  | // Find length of line | 
|  | pszEnd = strchr(pszCurr, '\n'); | 
|  | if(pszEnd) | 
|  | { | 
|  | nLen = (unsigned int)(pszEnd - pszCurr); | 
|  | } | 
|  | else | 
|  | { | 
|  | nLen = (unsigned int)strlen(pszCurr); | 
|  | bDone = true; | 
|  | } | 
|  |  | 
|  | nReduce = 0; // Tells how far to go back because of '\r'. | 
|  | while(nLen - nReduce > 0 && pszCurr[nLen - 1 - nReduce] == '\r') | 
|  | nReduce++; | 
|  |  | 
|  | // Ensure pszLine will not be not overrun | 
|  | if(nLen+1-nReduce > sizeof(pszLine) / sizeof(*pszLine)) | 
|  | nLen = sizeof(pszLine) / sizeof(*pszLine) - 1 + nReduce; | 
|  |  | 
|  | // Copy line into pszLine | 
|  | strncpy(pszLine, pszCurr, nLen - nReduce); | 
|  | pszLine[nLen - nReduce] = 0; | 
|  | pszCurr += nLen + 1; | 
|  |  | 
|  | _ASSERT(strchr(pszLine, '\r') == 0); | 
|  | _ASSERT(strchr(pszLine, '\n') == 0); | 
|  |  | 
|  | // Ignore comments | 
|  | char *tmp = strstr(pszLine, "//"); | 
|  | if(tmp != NULL)	*tmp = '\0'; | 
|  |  | 
|  | // Reduce whitespace to one character. | 
|  | ReduceWhitespace(pszLine); | 
|  |  | 
|  | // Store the line, even if blank lines (to get correct errors from GLSL compiler). | 
|  | if(m_psContext->nNumLines < m_psContext->nMaxLines) | 
|  | { | 
|  | m_psContext->pnFileLineNumber[m_psContext->nNumLines] = nLineCounter; | 
|  | m_psContext->ppszEffectFile[m_psContext->nNumLines] = (char *)malloc((strlen(pszLine) + 1) * sizeof(char)); | 
|  | strcpy(m_psContext->ppszEffectFile[m_psContext->nNumLines], pszLine); | 
|  | m_psContext->nNumLines++; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Too many lines of text in file (maximum is %d)\n", m_psContext->nMaxLines); | 
|  | return PVR_FAIL; | 
|  | } | 
|  | } | 
|  |  | 
|  | return Parse(pReturnError) ? PVR_SUCCESS : PVR_FAIL; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseFromFile | 
|  | @Input				pszFileName		PFX file name | 
|  | @Output			pReturnError	error string | 
|  | @Return			EPVRTError		PVR_SUCCESS for success parsing file | 
|  | PVR_FAIL if file doesn't exist or is invalid | 
|  | @Description		Reads the PFX file and calls the parser. | 
|  | *****************************************************************************/ | 
|  | EPVRTError CPVRTPFXParser::ParseFromFile(const char * const pszFileName, CPVRTString * const pReturnError) | 
|  | { | 
|  | CPVRTResourceFile PfxFile(pszFileName); | 
|  | if (!PfxFile.IsOpen()) | 
|  | { | 
|  | *pReturnError = CPVRTString("Unable to open file ") + pszFileName; | 
|  | return PVR_FAIL; | 
|  | } | 
|  |  | 
|  | CPVRTString PfxFileString; | 
|  | const char* pPfxData = (const char*) PfxFile.DataPtr(); | 
|  |  | 
|  | // Is our shader resource file data null terminated? | 
|  | if(pPfxData[PfxFile.Size()-1] != '\0') | 
|  | { | 
|  | // If not create a temporary null-terminated string | 
|  | PfxFileString.assign(pPfxData, PfxFile.Size()); | 
|  | pPfxData = PfxFileString.c_str(); | 
|  | } | 
|  |  | 
|  | m_szFileName.assign(pszFileName); | 
|  |  | 
|  | return ParseFromMemory(pPfxData, pReturnError); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			SetViewportSize | 
|  | @Input				uiWidth				New viewport width | 
|  | @Input				uiHeight			New viewport height | 
|  | @Return			bool				True on success | 
|  | @Description		Allows the current viewport size to be set. This value | 
|  | is used for calculating relative texture resolutions | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::SetViewportSize(unsigned int uiWidth, unsigned int uiHeight) | 
|  | { | 
|  | if(uiWidth > 0 && uiHeight > 0) | 
|  | { | 
|  | m_uiViewportWidth = uiWidth; | 
|  | m_uiViewportHeight = uiHeight; | 
|  | return true; | 
|  | } | 
|  | else | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | /*!*************************************************************************** | 
|  | @Function		RetrieveRenderPassDependencies | 
|  | @Output			aRequiredRenderPasses | 
|  | @Output			aszActiveEffectStrings | 
|  | @Return			bool | 
|  | @Description	Returns a list of dependencies associated with the pass. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::RetrieveRenderPassDependencies(CPVRTArray<SPVRTPFXRenderPass*> &aRequiredRenderPasses, CPVRTArray<CPVRTStringHash> &aszActiveEffectStrings) | 
|  | { | 
|  | unsigned int ui(0), uj(0), uk(0), ul(0); | 
|  | const SPVRTPFXParserEffect* pTempEffect(NULL); | 
|  |  | 
|  | if(aRequiredRenderPasses.GetSize() > 0) | 
|  | { | 
|  | /* aRequiredRenderPasses should be empty when it is passed in */ | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for(ui = 0; ui < (unsigned int)aszActiveEffectStrings.GetSize(); ++ui) | 
|  | { | 
|  | if(aszActiveEffectStrings[ui].String().empty()) | 
|  | { | 
|  | // Empty strings are not valid | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Find the specified effect | 
|  | for(uj = 0, pTempEffect = NULL; uj < (unsigned int)m_psEffect.GetSize(); ++uj) | 
|  | { | 
|  | if(aszActiveEffectStrings[ui] == m_psEffect[uj].Name) | 
|  | { | 
|  | // Effect found | 
|  | pTempEffect = &m_psEffect[uj]; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(pTempEffect == NULL) | 
|  | { | 
|  | // Effect not found | 
|  | return false; | 
|  | } | 
|  |  | 
|  | for(uj = 0; uj < m_renderPassSkipGraph.GetNumNodes(); ++uj) | 
|  | { | 
|  | if(m_renderPassSkipGraph[uj]->pEffect == pTempEffect) | 
|  | { | 
|  | m_renderPassSkipGraph.RetreiveSortedDependencyList(aRequiredRenderPasses, uj); | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | The effect wasn't a post-process. Check to see if it has any non-post-process dependencies, | 
|  | e.g. RENDER CAMERA textures. | 
|  | */ | 
|  | // Loop Effects | 
|  | for(uj = 0; uj < (unsigned int)m_psEffect.GetSize(); ++uj) | 
|  | { | 
|  | if(aszActiveEffectStrings[ui] != m_psEffect[uj].Name) | 
|  | continue; | 
|  |  | 
|  | // Loop Textures in Effect | 
|  | for(uk = 0; uk < m_psEffect[uj].Textures.GetSize();++uk) | 
|  | { | 
|  | // Loop Render Passes for whole PFX | 
|  | for(ul = 0; ul < m_RenderPasses.GetSize(); ++ul) | 
|  | { | 
|  | // Check that the name of this render pass output texture matches a provided texture in an Effect | 
|  | if(m_RenderPasses[ul].pTexture->Name == m_psEffect[uj].Textures[uk].Name) | 
|  | aRequiredRenderPasses.Append(&m_RenderPasses[ul]); | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  | /*!*************************************************************************** | 
|  | @Function			GetEndTag | 
|  | @Input				pszTagName		tag name | 
|  | @Input				nStartLine		start line | 
|  | @Output			pnEndLine		line end tag found | 
|  | @Return			true if tag found | 
|  | @Description		Searches for end tag pszTagName from line nStartLine. | 
|  | Returns true and outputs the line number of the end tag if | 
|  | found, otherwise returning false. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::GetEndTag(const char* pszTagName, int nStartLine, int *pnEndLine) | 
|  | { | 
|  | char pszEndTag[100]; | 
|  | strcpy(pszEndTag, "[/"); | 
|  | strcat(pszEndTag, pszTagName); | 
|  | strcat(pszEndTag, "]"); | 
|  |  | 
|  | for(unsigned int i = nStartLine; i < m_psContext->nNumLines; i++) | 
|  | { | 
|  | if(strcmp(pszEndTag, m_psContext->ppszEffectFile[i]) == 0) | 
|  | { | 
|  | *pnEndLine = i; | 
|  | return true; | 
|  | } | 
|  | } | 
|  |  | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ReduceWhitespace | 
|  | @Output			line		output text | 
|  | @Input				line		input text | 
|  | @Description		Reduces all white space characters in the string to one | 
|  | blank space. | 
|  | *****************************************************************************/ | 
|  | void CPVRTPFXParser::ReduceWhitespace(char *line) | 
|  | { | 
|  | // convert tabs and newlines to ' ' | 
|  | char *tmp = strpbrk (line, "\t\n"); | 
|  | while(tmp != NULL) | 
|  | { | 
|  | *tmp = ' '; | 
|  | tmp = strpbrk (line, "\t\n"); | 
|  | } | 
|  |  | 
|  | // remove all whitespace at start | 
|  | while(line[0] == ' ') | 
|  | { | 
|  | // move chars along to omit whitespace | 
|  | int counter = 0; | 
|  | do{ | 
|  | line[counter] = line[counter+1]; | 
|  | counter++; | 
|  | }while(line[counter] != '\0'); | 
|  | } | 
|  |  | 
|  | // step through chars of line remove multiple whitespace | 
|  | for(int i=0; i < (int)strlen(line); i++) | 
|  | { | 
|  | // whitespace found | 
|  | if(line[i] == ' ') | 
|  | { | 
|  | // count number of whitespace chars | 
|  | int numWhiteChars = 0; | 
|  | while(line[i+1+numWhiteChars] == ' ') | 
|  | { | 
|  | numWhiteChars++; | 
|  | } | 
|  |  | 
|  | // multiple whitespace chars found | 
|  | if(numWhiteChars>0) | 
|  | { | 
|  | // move chars along to omit whitespace | 
|  | int counter=1; | 
|  | while(line[i+counter] != '\0') | 
|  | { | 
|  | line[i+counter] = line[i+numWhiteChars+counter]; | 
|  | counter++; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | // If there is no string then do not remove terminating white symbols | 
|  | if(!strlen(line)) | 
|  | return; | 
|  |  | 
|  | // remove all whitespace from end | 
|  | while(line[strlen(line)-1] == ' ') | 
|  | { | 
|  | // move chars along to omit whitespace | 
|  | line[strlen(line)-1] = '\0'; | 
|  | } | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			FindParameter | 
|  | @Output | 
|  | @Input | 
|  | @Description		Finds the parameter after the specified delimiting character and | 
|  | returns the parameter as a string. An empty string is returned | 
|  | if a parameter cannot be found | 
|  |  | 
|  | *****************************************************************************/ | 
|  | CPVRTString CPVRTPFXParser::FindParameter(char *aszSourceString, const CPVRTString ¶meterTag, const CPVRTString &delimiter) | 
|  | { | 
|  | CPVRTString returnString(""); | 
|  | char* aszTagStart = strstr(aszSourceString, parameterTag.c_str()); | 
|  |  | 
|  | // Tag was found, so search for parameter | 
|  | if(aszTagStart) | 
|  | { | 
|  | char* aszDelimiterStart = strstr(aszTagStart, delimiter.c_str()); | 
|  | char* aszSpaceStart = strstr(aszTagStart, " "); | 
|  |  | 
|  | // Delimiter found | 
|  | if(aszDelimiterStart && (!aszSpaceStart ||(aszDelimiterStart < aszSpaceStart))) | 
|  | { | 
|  | // Create a string from the delimiter to the next space | 
|  | size_t strCount(strcspn(aszDelimiterStart, " ")); | 
|  | aszDelimiterStart++;	// Skip = | 
|  | returnString.assign(aszDelimiterStart, strCount-1); | 
|  | } | 
|  | } | 
|  |  | 
|  | return returnString; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		ReadStringToken | 
|  | @Input			pszSource			Parameter string to process | 
|  | @Output			output				Processed string | 
|  | @Output			ErrorStr			String containing errors | 
|  | @Return								Returns true on success | 
|  | @Description	Processes the null terminated char array as if it's a | 
|  | formatted string array. Quote marks are determined to be | 
|  | start and end of strings. If no quote marks are found the | 
|  | string is delimited by whitespace. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ReadStringToken(char* pszSource, CPVRTString& output, CPVRTString &ErrorStr, int i, const char* pCaller) | 
|  | { | 
|  | if(*pszSource == '\"')		// Quote marks. Continue parsing until end mark or NULL | 
|  | { | 
|  | pszSource++;		// Skip past first quote | 
|  | while(*pszSource != '\"') | 
|  | { | 
|  | if(*pszSource == '\0') | 
|  | { | 
|  | ErrorStr = PVRTStringFromFormattedStr("Incomplete argument in [%s] on line %d: %s\n", pCaller,m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | output.push_back(*pszSource); | 
|  | pszSource++; | 
|  | } | 
|  |  | 
|  | pszSource++;		// Skip past final quote. | 
|  | } | 
|  | else		// No quotes. Read until space | 
|  | { | 
|  | pszSource = strtok(pszSource, DELIM_TOKENS NEWLINE_TOKENS); | 
|  | output = pszSource; | 
|  |  | 
|  | pszSource += strlen(pszSource); | 
|  | } | 
|  |  | 
|  | // Check that there's nothing left on this line | 
|  | pszSource = strtok(pszSource, NEWLINE_TOKENS); | 
|  | if(pszSource) | 
|  | { | 
|  | ErrorStr = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d: %s\n", pszSource, pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseHeader | 
|  | @Input				nStartLine		start line number | 
|  | @Input				nEndLine		end line number | 
|  | @Output			pReturnError	error string | 
|  | @Return			bool			true if parse is successful | 
|  | @Description		Parses the HEADER section of the PFX file. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ParseHeader(int nStartLine, int nEndLine, CPVRTString * const pReturnError) | 
|  | { | 
|  | enum eCmd | 
|  | { | 
|  | eCmds_Version, | 
|  | eCmds_Description, | 
|  | eCmds_Copyright, | 
|  |  | 
|  | eCmds_Size | 
|  | }; | 
|  |  | 
|  | const CPVRTHash HeaderCommands[] = | 
|  | { | 
|  | "VERSION",			// eCmds_Version | 
|  | "DESCRIPTION",		// eCmds_Description | 
|  | "COPYRIGHT",		// eCmds_Copyright | 
|  | }; | 
|  | PVRTCOMPILEASSERT(HeaderCommands, sizeof(HeaderCommands) / sizeof(HeaderCommands[0]) == eCmds_Size); | 
|  |  | 
|  | for(int i = nStartLine+1; i < nEndLine; i++) | 
|  | { | 
|  | // Skip blank lines | 
|  | if(!*m_psContext->ppszEffectFile[i]) | 
|  | continue; | 
|  |  | 
|  | char *str = strtok (m_psContext->ppszEffectFile[i]," "); | 
|  | if(str != NULL) | 
|  | { | 
|  | CPVRTHash Cmd(str); | 
|  | if(Cmd == HeaderCommands[eCmds_Version]) | 
|  | { | 
|  | str += (strlen(str)+1); | 
|  | m_sHeader.Version = str; | 
|  | } | 
|  | else if(Cmd == HeaderCommands[eCmds_Description]) | 
|  | { | 
|  | str += (strlen(str)+1); | 
|  | m_sHeader.Description = str; | 
|  | } | 
|  | else if(Cmd == HeaderCommands[eCmds_Copyright]) | 
|  | { | 
|  | str += (strlen(str)+1); | 
|  | m_sHeader.Copyright = str; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [HEADER] on line %d\n", str, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [HEADER] on line %d : %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseGenericSurface | 
|  | @Input				nStartLine		start line number | 
|  | @Input				nEndLine		end line number | 
|  | @Output				uiWrapS | 
|  | @Output				uiWrapT | 
|  | @Output				uiWrapR | 
|  | @Output				uiMin | 
|  | @Output				uiMag | 
|  | @Output				uiMip | 
|  | @Output				pReturnError	error string | 
|  | @Return				bool			true if parse is successful | 
|  | @Description		Parses generic data from TARGET and TEXTURE blocks. Namely | 
|  | wrapping and filter commands. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ParseGenericSurface(int nStartLine, int nEndLine, SPVRTPFXParserTexture& Params, CPVRTArray<CPVRTHash>& KnownCmds, | 
|  | const char* pCaller, CPVRTString * const pReturnError) | 
|  | { | 
|  | const unsigned int INVALID_TYPE = 0xAC1DBEEF; | 
|  |  | 
|  | enum eCmd | 
|  | { | 
|  | eCmds_Min, | 
|  | eCmds_Mag, | 
|  | eCmds_Mip, | 
|  | eCmds_WrapS, | 
|  | eCmds_WrapT, | 
|  | eCmds_WrapR, | 
|  | eCmds_Filter, | 
|  | eCmds_Wrap, | 
|  | eCmds_Resolution, | 
|  | eCmds_Surface, | 
|  |  | 
|  | eCmds_Size | 
|  | }; | 
|  |  | 
|  | const CPVRTHash GenericSurfCommands[] = | 
|  | { | 
|  | "MINIFICATION",			// eCmds_Min | 
|  | "MAGNIFICATION",		// eCmds_Mag | 
|  | "MIPMAP",				// eCmds_Mip | 
|  | "WRAP_S",				// eCmds_WrapS | 
|  | "WRAP_T",				// eCmds_WrapT | 
|  | "WRAP_R",				// eCmds_WrapR | 
|  | "FILTER",				// eCmds_Filter | 
|  | "WRAP",					// eCmds_Wrap | 
|  | "RESOLUTION",			// eCmds_Resolution | 
|  | "SURFACETYPE",			// eCmds_Surface | 
|  | }; | 
|  | PVRTCOMPILEASSERT(GenericSurfCommands, sizeof(GenericSurfCommands) / sizeof(GenericSurfCommands[0]) == eCmds_Size); | 
|  |  | 
|  | struct SSurfacePair | 
|  | { | 
|  | CPVRTHash Name; | 
|  | PVRTPixelType eType; | 
|  | unsigned int BufferType; | 
|  | }; | 
|  |  | 
|  | const SSurfacePair SurfacePairs[] = | 
|  | { | 
|  | { "RGBA8888",	OGL_RGBA_8888,	PVRPFXTEX_COLOUR }, | 
|  | { "RGBA4444",	OGL_RGBA_4444,	PVRPFXTEX_COLOUR }, | 
|  | { "RGB888",		OGL_RGB_888,	PVRPFXTEX_COLOUR }, | 
|  | { "RGB565",		OGL_RGB_565,	PVRPFXTEX_COLOUR }, | 
|  | { "INTENSITY8",	OGL_I_8,		PVRPFXTEX_COLOUR }, | 
|  | { "DEPTH24",	OGL_RGB_888,	PVRPFXTEX_DEPTH }, | 
|  | { "DEPTH16",	OGL_RGB_565,	PVRPFXTEX_DEPTH }, | 
|  | { "DEPTH8",		OGL_I_8,		PVRPFXTEX_DEPTH }, | 
|  | }; | 
|  | const unsigned int uiNumSurfTypes = sizeof(SurfacePairs) / sizeof(SurfacePairs[0]); | 
|  |  | 
|  | for(int i = nStartLine+1; i < nEndLine; i++) | 
|  | { | 
|  | // Skip blank lines | 
|  | if(!*m_psContext->ppszEffectFile[i]) | 
|  | continue; | 
|  |  | 
|  | // Need to make a copy so we can use strtok and not affect subsequent parsing | 
|  | size_t lineLen = strlen(m_psContext->ppszEffectFile[i]); | 
|  | char* pBlockCopy = new char[lineLen+1]; | 
|  | strcpy(pBlockCopy, m_psContext->ppszEffectFile[i]); | 
|  |  | 
|  | char *str = strtok (pBlockCopy, NEWLINE_TOKENS DELIM_TOKENS); | 
|  | if(!str) | 
|  | { | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | CPVRTHash Cmd(str); | 
|  | const char** ppFilters  = NULL; | 
|  | bool bKnown = false; | 
|  |  | 
|  | // --- Verbose filtering flags | 
|  | if(Cmd == GenericSurfCommands[eCmds_Min] || Cmd == GenericSurfCommands[eCmds_Mag] || Cmd == GenericSurfCommands[eCmds_Mip]) | 
|  | { | 
|  | ppFilters = c_ppszFilters; | 
|  | bKnown     = true; | 
|  | } | 
|  | // --- Verbose wrapping flags | 
|  | else if(Cmd == GenericSurfCommands[eCmds_WrapS] || Cmd == GenericSurfCommands[eCmds_WrapT] || Cmd == GenericSurfCommands[eCmds_WrapR]) | 
|  | { | 
|  | ppFilters = c_ppszWraps; | 
|  | bKnown     = true; | 
|  | } | 
|  | // --- Inline filtering flags | 
|  | else if(Cmd == GenericSurfCommands[eCmds_Filter]) | 
|  | { | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | 
|  | if(!pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing FILTER arguments in [%s] on line %d: %s\n", pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | unsigned int* pFlags[3] = | 
|  | { | 
|  | &Params.nMin, | 
|  | &Params.nMag, | 
|  | &Params.nMIP, | 
|  | }; | 
|  |  | 
|  | if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszFilters, eFilter_Size, pReturnError, i)) | 
|  | { | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bKnown     = true; | 
|  | } | 
|  | // --- Inline wrapping flags | 
|  | else if(Cmd == GenericSurfCommands[eCmds_Wrap]) | 
|  | { | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | 
|  | if(!pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing WRAP arguments in [%s] on line %d: %s\n", pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | unsigned int* pFlags[3] = | 
|  | { | 
|  | &Params.nWrapS, | 
|  | &Params.nWrapT, | 
|  | &Params.nWrapR, | 
|  | }; | 
|  |  | 
|  | if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszWraps, eWrap_Size, pReturnError, i)) | 
|  | { | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bKnown     = true; | 
|  | } | 
|  | // --- Resolution | 
|  | else if(Cmd == GenericSurfCommands[eCmds_Resolution]) | 
|  | { | 
|  | char* pszRemaining; | 
|  |  | 
|  | unsigned int* uiVals[2] = { &Params.uiWidth, &Params.uiHeight }; | 
|  |  | 
|  | // There should be precisely TWO arguments for resolution (width and height) | 
|  | for(unsigned int uiIndex = 0; uiIndex < 2; ++uiIndex) | 
|  | { | 
|  | pszRemaining = strtok(NULL, DELIM_TOKENS NEWLINE_TOKENS); | 
|  | if(!pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing RESOLUTION argument(s) (requires width AND height) in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]); | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | int val = atoi(pszRemaining); | 
|  |  | 
|  | 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. | 
|  | ||  (val < 0)) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Invalid RESOLUTION argument \"%s\" in [TEXTURE] on line %d\n", pszRemaining, m_psContext->pnFileLineNumber[i]); | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *(uiVals[uiIndex]) = (unsigned int)val; | 
|  | } | 
|  |  | 
|  | bKnown     = true; | 
|  | } | 
|  | // --- Surface type | 
|  | else if(Cmd == GenericSurfCommands[eCmds_Surface]) | 
|  | { | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | 
|  | if(!pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing SURFACETYPE arguments in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]); | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | CPVRTHash hashType(pszRemaining); | 
|  | for(unsigned int uiIndex = 0; uiIndex < uiNumSurfTypes; ++uiIndex) | 
|  | { | 
|  | if(hashType == SurfacePairs[uiIndex].Name) | 
|  | { | 
|  | Params.uiFlags =  SurfacePairs[uiIndex].eType | SurfacePairs[uiIndex].BufferType; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | bKnown     = true; | 
|  | } | 
|  |  | 
|  | // Valid Verbose command | 
|  | if(ppFilters) | 
|  | { | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | 
|  | if(!pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [%s] on line %d: %s\n", pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | unsigned int Type = INVALID_TYPE; | 
|  | for(unsigned int uiIndex = 0; uiIndex < 3; ++uiIndex) | 
|  | { | 
|  | if(strcmp(pszRemaining, ppFilters[uiIndex]) == 0) | 
|  | { | 
|  | Type = uiIndex;			// Yup, it's valid. | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tell the user it's invalid. | 
|  | if(Type == INVALID_TYPE) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d: %s\n", pszRemaining, pCaller, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(Cmd == GenericSurfCommands[eCmds_Min])			Params.nMin = Type; | 
|  | else if(Cmd == GenericSurfCommands[eCmds_Mag])		Params.nMag = Type; | 
|  | else if(Cmd == GenericSurfCommands[eCmds_Mip])		Params.nMIP = Type; | 
|  | else if(Cmd == GenericSurfCommands[eCmds_WrapR])	Params.nWrapR = Type; | 
|  | else if(Cmd == GenericSurfCommands[eCmds_WrapS])	Params.nWrapS = Type; | 
|  | else if(Cmd == GenericSurfCommands[eCmds_WrapT])	Params.nWrapT = Type; | 
|  | } | 
|  |  | 
|  | if(bKnown) | 
|  | { | 
|  | KnownCmds.Append(Cmd); | 
|  |  | 
|  | // Make sure nothing else exists on the line that hasn't been parsed. | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS); | 
|  | if(pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unexpected keyword '%s' in [%s] on line %d: %s\n", pszRemaining, pCaller, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | delete[] pBlockCopy; | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | delete [] pBlockCopy; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseTexture | 
|  | @Input				nStartLine		start line number | 
|  | @Input				nEndLine		end line number | 
|  | @Output				pReturnError	error string | 
|  | @Return				bool			true if parse is successful | 
|  | @Description		Parses the TEXTURE section of the PFX file. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ParseTexture(int nStartLine, int nEndLine, CPVRTString * const pReturnError) | 
|  | { | 
|  | enum eCmd | 
|  | { | 
|  | eCmds_Name, | 
|  | eCmds_Path, | 
|  | eCmds_View, | 
|  | eCmds_Camera, | 
|  |  | 
|  | eCmds_Size | 
|  | }; | 
|  |  | 
|  | const CPVRTHash TextureCmds[] = | 
|  | { | 
|  | "NAME",				// eTextureCmds_Name | 
|  | "PATH",				// eTextureCmds_Path | 
|  | "VIEW",				// eTextureCmds_View | 
|  | "CAMERA",			// eTextureCmds_Camera | 
|  | }; | 
|  | PVRTCOMPILEASSERT(TextureCmds, sizeof(TextureCmds) / sizeof(TextureCmds[0]) == eCmds_Size); | 
|  |  | 
|  | SPVRTPFXParserTexture TexDesc; | 
|  | TexDesc.nMin = eFilter_Default; | 
|  | TexDesc.nMag = eFilter_Default; | 
|  | TexDesc.nMIP = eFilter_MipDefault; | 
|  | TexDesc.nWrapS = eWrap_Default; | 
|  | TexDesc.nWrapT = eWrap_Default; | 
|  | TexDesc.nWrapR = eWrap_Default; | 
|  | TexDesc.uiWidth  = VIEWPORT_SIZE; | 
|  | TexDesc.uiHeight = VIEWPORT_SIZE; | 
|  | TexDesc.uiFlags  = OGL_RGBA_8888 | PVRPFXTEX_COLOUR; | 
|  |  | 
|  | CPVRTArray<CPVRTHash> KnownCmds; | 
|  | if(!ParseGenericSurface(nStartLine, nEndLine, TexDesc, KnownCmds, "TEXTURE", pReturnError)) | 
|  | return false; | 
|  |  | 
|  | CPVRTString texName, filePath, viewName; | 
|  | for(int i = nStartLine+1; i < nEndLine; i++) | 
|  | { | 
|  | // Skip blank lines | 
|  | if(!*m_psContext->ppszEffectFile[i]) | 
|  | continue; | 
|  |  | 
|  | char *str = strtok (m_psContext->ppszEffectFile[i], NEWLINE_TOKENS DELIM_TOKENS); | 
|  | if(!str) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | CPVRTHash texCmd(str); | 
|  | // --- Texture Name | 
|  | if(texCmd == TextureCmds[eCmds_Name]) | 
|  | { | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | 
|  | if(!pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing NAME arguments in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | texName = pszRemaining; | 
|  | } | 
|  | // --- Texture Path | 
|  | else if(texCmd == TextureCmds[eCmds_Path]) | 
|  | { | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS); | 
|  | if(!pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing PATH arguments in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(!ReadStringToken(pszRemaining, filePath, *pReturnError, i, "TEXTURE")) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | // --- View/Camera Name | 
|  | else if(texCmd == TextureCmds[eCmds_View] || texCmd == TextureCmds[eCmds_Camera]) | 
|  | { | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS);		// String component. Get the rest of the line. | 
|  | if(!pszRemaining || strlen(pszRemaining) == 0) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing VIEW argument in [TEXTURE] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(!ReadStringToken(pszRemaining, viewName, *pReturnError, i, "TEXTURE")) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else if(KnownCmds.Contains(texCmd)) | 
|  | { | 
|  | // Remove from 'unknown' list. | 
|  | for(unsigned int uiIndex = 0; uiIndex < KnownCmds.GetSize(); ++uiIndex) | 
|  | { | 
|  | if(KnownCmds[uiIndex] == texCmd) | 
|  | { | 
|  | KnownCmds.Remove(uiIndex); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | continue;		// This line has already been processed. | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURE] on line %d: %s\n", str, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS); | 
|  | if(pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unexpected keyword '%s' in [TEXTURE] on line %d: %s\n", pszRemaining, m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(texName.empty()) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("No NAME tag specified in [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  | if(!filePath.empty() && !viewName.empty()) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Both PATH and VIEW tags specified in [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  | if(filePath.empty() && viewName.empty()) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("No PATH or VIEW tag specified in [TEXTURE] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | bool bRTT = (viewName.empty() ? false : true); | 
|  | if(bRTT) | 
|  | { | 
|  | filePath = texName;									// RTT doesn't have a physical file. | 
|  | } | 
|  |  | 
|  | // Create a new texture and copy over the vals. | 
|  | SPVRTPFXParserTexture* pTex = new SPVRTPFXParserTexture(); | 
|  | pTex->Name				= CPVRTStringHash(texName); | 
|  | pTex->FileName			= CPVRTStringHash(filePath); | 
|  | pTex->bRenderToTexture	= bRTT; | 
|  | pTex->nMin				= TexDesc.nMin; | 
|  | pTex->nMag				= TexDesc.nMag; | 
|  | pTex->nMIP				= TexDesc.nMIP; | 
|  | pTex->nWrapS			= TexDesc.nWrapS; | 
|  | pTex->nWrapT			= TexDesc.nWrapT; | 
|  | pTex->nWrapR			= TexDesc.nWrapR; | 
|  | pTex->uiWidth			= TexDesc.uiWidth; | 
|  | pTex->uiHeight			= TexDesc.uiHeight; | 
|  | pTex->uiFlags			= TexDesc.uiFlags; | 
|  | m_psTexture.Append(pTex); | 
|  |  | 
|  | if(bRTT) | 
|  | { | 
|  | unsigned int uiPassIdx = m_RenderPasses.Append(); | 
|  | m_RenderPasses[uiPassIdx].SemanticName = texName; | 
|  |  | 
|  | if(viewName == c_pszCurrentView) | 
|  | { | 
|  | m_RenderPasses[uiPassIdx].eViewType	 = eVIEW_CURRENT; | 
|  | } | 
|  | else | 
|  | { | 
|  | m_RenderPasses[uiPassIdx].eViewType	 = eVIEW_POD_CAMERA; | 
|  | m_RenderPasses[uiPassIdx].NodeName	 = viewName; | 
|  | } | 
|  |  | 
|  | m_RenderPasses[uiPassIdx].eRenderPassType = eCAMERA_PASS;			// Textures are always 'camera' passes | 
|  |  | 
|  | // Set render pass texture to the newly created texture. | 
|  | m_RenderPasses[uiPassIdx].pTexture		 = pTex; | 
|  | m_RenderPasses[uiPassIdx].uiFormatFlags  = TexDesc.uiFlags; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseTarget | 
|  | @Input				nStartLine		start line number | 
|  | @Input				nEndLine		end line number | 
|  | @Output				pReturnError	error string | 
|  | @Return				bool			true if parse is successful | 
|  | @Description		Parses the TARGET section of the PFX file. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ParseTarget(int nStartLine, int nEndLine, CPVRTString * const pReturnError) | 
|  | { | 
|  | enum eCmd | 
|  | { | 
|  | eCmds_Name, | 
|  |  | 
|  | eCmds_Size | 
|  | }; | 
|  |  | 
|  | const CPVRTHash TargetCommands[] = | 
|  | { | 
|  | "NAME",				// eCmds_Name | 
|  | }; | 
|  | PVRTCOMPILEASSERT(TargetCommands, sizeof(TargetCommands) / sizeof(TargetCommands[0]) == eCmds_Size); | 
|  |  | 
|  | CPVRTString targetName; | 
|  | SPVRTPFXParserTexture TexDesc; | 
|  | TexDesc.nMin = eFilter_Default; | 
|  | TexDesc.nMag = eFilter_Default; | 
|  | TexDesc.nMIP = eFilter_MipDefault; | 
|  | TexDesc.nWrapS = eWrap_Default; | 
|  | TexDesc.nWrapT = eWrap_Default; | 
|  | TexDesc.nWrapR = eWrap_Default; | 
|  | TexDesc.uiWidth  = VIEWPORT_SIZE; | 
|  | TexDesc.uiHeight = VIEWPORT_SIZE; | 
|  | TexDesc.uiFlags  = OGL_RGBA_8888 | PVRPFXTEX_COLOUR; | 
|  |  | 
|  | CPVRTArray<CPVRTHash> KnownCmds; | 
|  | if(!ParseGenericSurface(nStartLine, nEndLine, TexDesc, KnownCmds, "TARGET", pReturnError)) | 
|  | return false; | 
|  |  | 
|  | for(int i = nStartLine+1; i < nEndLine; i++) | 
|  | { | 
|  | // Skip blank lines | 
|  | if(!*m_psContext->ppszEffectFile[i]) | 
|  | continue; | 
|  |  | 
|  | char *str = strtok (m_psContext->ppszEffectFile[i], NEWLINE_TOKENS DELIM_TOKENS); | 
|  | if(!str) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | CPVRTHash texCmd(str); | 
|  | // --- Target Name | 
|  | if(texCmd == TargetCommands[eCmds_Name]) | 
|  | { | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS DELIM_TOKENS); | 
|  | if(!pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing NAME arguments in [TARGET] on line %d\n", m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | targetName = pszRemaining; | 
|  | } | 
|  | else if(KnownCmds.Contains(texCmd)) | 
|  | { | 
|  | // Remove from 'unknown' list. | 
|  | for(unsigned int uiIndex = 0; uiIndex < KnownCmds.GetSize(); ++uiIndex) | 
|  | { | 
|  | if(KnownCmds[uiIndex] == texCmd) | 
|  | { | 
|  | KnownCmds.Remove(uiIndex); | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | continue;		// This line has already been processed. | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TARGET] on line %d\n", str, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | char* pszRemaining = strtok(NULL, NEWLINE_TOKENS); | 
|  | if(pszRemaining) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unexpected keyword '%s' in [TARGET] on line %d\n", pszRemaining, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Create a new texture and copy over the vals. | 
|  | SPVRTPFXParserTexture* pTex = new SPVRTPFXParserTexture(); | 
|  | pTex->Name				= CPVRTStringHash(targetName); | 
|  | pTex->FileName			= CPVRTStringHash(targetName); | 
|  | pTex->bRenderToTexture	= true; | 
|  | pTex->nMin				= TexDesc.nMin; | 
|  | pTex->nMag				= TexDesc.nMag; | 
|  | pTex->nMIP				= TexDesc.nMIP; | 
|  | pTex->nWrapS			= TexDesc.nWrapS; | 
|  | pTex->nWrapT			= TexDesc.nWrapT; | 
|  | pTex->nWrapR			= TexDesc.nWrapR; | 
|  | pTex->uiWidth			= TexDesc.uiWidth; | 
|  | pTex->uiHeight			= TexDesc.uiHeight; | 
|  | pTex->uiFlags			= TexDesc.uiFlags; | 
|  | m_psTexture.Append(pTex); | 
|  |  | 
|  | // Copy to render pass struct | 
|  | unsigned int uiPassIdx = m_RenderPasses.Append(); | 
|  | m_RenderPasses[uiPassIdx].SemanticName		= targetName; | 
|  | m_RenderPasses[uiPassIdx].eViewType			= eVIEW_NONE; | 
|  | m_RenderPasses[uiPassIdx].eRenderPassType	= ePOSTPROCESS_PASS;			// Targets are always post-process passes. | 
|  | m_RenderPasses[uiPassIdx].pTexture			= pTex; | 
|  | m_RenderPasses[uiPassIdx].uiFormatFlags		= TexDesc.uiFlags; | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseTextures		** DEPRECATED ** | 
|  | @Input				nStartLine		start line number | 
|  | @Input				nEndLine		end line number | 
|  | @Output			pReturnError	error string | 
|  | @Return			bool			true if parse is successful | 
|  | @Description		Parses the TEXTURE section of the PFX file. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ParseTextures(int nStartLine, int nEndLine, CPVRTString * const pReturnError) | 
|  | { | 
|  | char *pszName(NULL), *pszFile(NULL), *pszKeyword(NULL); | 
|  | char *pszRemaining(NULL), *pszTemp(NULL); | 
|  | bool bReturnVal(false); | 
|  |  | 
|  | for(int i = nStartLine+1; i < nEndLine; i++) | 
|  | { | 
|  | // Skip blank lines | 
|  | if(!*m_psContext->ppszEffectFile[i]) | 
|  | continue; | 
|  |  | 
|  | char *str = strtok (m_psContext->ppszEffectFile[i]," "); | 
|  | if(str != NULL) | 
|  | { | 
|  | // Set defaults | 
|  | unsigned int	uiMin(eFilter_Default), uiMag(eFilter_Default), uiMip(eFilter_MipDefault); | 
|  | unsigned int	uiWrapS(eWrap_Default), uiWrapT(eWrap_Default), uiWrapR(eWrap_Default); | 
|  | unsigned int	uiFlags = 0; | 
|  |  | 
|  | unsigned int uiWidth	= CPVRTPFXParser::VIEWPORT_SIZE; | 
|  | unsigned int uiHeight	= CPVRTPFXParser::VIEWPORT_SIZE; | 
|  |  | 
|  | // Reset variables | 
|  | FREE(pszName)		pszName = NULL; | 
|  | FREE(pszFile)		pszFile = NULL; | 
|  | FREE(pszKeyword)	pszKeyword = NULL; | 
|  | FREE(pszTemp)		pszTemp = NULL; | 
|  | pszRemaining		= NULL; | 
|  |  | 
|  | // Compare against all valid keywords | 
|  | if((strcmp(str, "FILE") != 0) && (strcmp(str, "RENDER") != 0)) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d\n", str, m_psContext->pnFileLineNumber[i]); | 
|  | goto fail_release_return; | 
|  | } | 
|  |  | 
|  | #if 1 | 
|  | if((strcmp(str, "RENDER") == 0)) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("RENDER tag no longer supported in [TEXTURES] block. Use new [TARGET] block instead\n"); | 
|  | goto fail_release_return; | 
|  | } | 
|  | #endif | 
|  |  | 
|  | pszKeyword = (char *)malloc( ((int)strlen(str)+1) * sizeof(char)); | 
|  | strcpy(pszKeyword, str); | 
|  |  | 
|  | str = strtok (NULL, " "); | 
|  | if(str != NULL) | 
|  | { | 
|  | pszName = (char *)malloc( ((int)strlen(str)+1) * sizeof(char)); | 
|  | strcpy(pszName, str); | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Texture name missing in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | 
|  | goto fail_release_return; | 
|  | } | 
|  |  | 
|  | /* | 
|  | The pszRemaining string is used to look for remaining flags. | 
|  | This has the advantage of allowing flags to be order independent | 
|  | and makes it easier to ommit some flags, but still pick up others | 
|  | (the previous method made it diffifult to retrieve filtering info | 
|  | if flags before it were missing) | 
|  | */ | 
|  | pszRemaining  = strtok(NULL, "\n"); | 
|  |  | 
|  | if(pszRemaining == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Incomplete definition in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | 
|  | goto fail_release_return; | 
|  | } | 
|  | else if(strcmp(pszKeyword, "FILE") == 0) | 
|  | { | 
|  | pszTemp = (char *)malloc( ((int)strlen(pszRemaining)+1) * sizeof(char)); | 
|  | strcpy(pszTemp, pszRemaining); | 
|  | str = strtok (pszTemp, " "); | 
|  |  | 
|  | if(str != NULL) | 
|  | { | 
|  | pszFile = (char *)malloc( ((int)strlen(str)+1) * sizeof(char)); | 
|  | strcpy(pszFile, str); | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Texture name missing in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | 
|  | goto fail_release_return; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(strcmp(pszKeyword, "FILE") == 0) | 
|  | { | 
|  | // --- Filter flags | 
|  | { | 
|  | unsigned int* pFlags[3] = | 
|  | { | 
|  | &uiMin, | 
|  | &uiMag, | 
|  | &uiMip, | 
|  | }; | 
|  |  | 
|  | if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszFilters, eFilter_Size, pReturnError, i)) | 
|  | goto fail_release_return; | 
|  | } | 
|  |  | 
|  | // --- Wrap flags | 
|  | { | 
|  | unsigned int* pFlags[3] = | 
|  | { | 
|  | &uiWrapS, | 
|  | &uiWrapT, | 
|  | &uiWrapR, | 
|  | }; | 
|  |  | 
|  | if(!ParseTextureFlags(pszRemaining, pFlags, 3, c_ppszWraps, eWrap_Size, pReturnError, i)) | 
|  | goto fail_release_return; | 
|  | } | 
|  |  | 
|  | SPVRTPFXParserTexture* pTex = new SPVRTPFXParserTexture(); | 
|  | pTex->Name				= CPVRTStringHash(pszName); | 
|  | pTex->FileName			= CPVRTStringHash(pszFile); | 
|  | pTex->bRenderToTexture	= false; | 
|  | pTex->nMin				= uiMin; | 
|  | pTex->nMag				= uiMag; | 
|  | pTex->nMIP				= uiMip; | 
|  | pTex->nWrapS			= uiWrapS; | 
|  | pTex->nWrapT			= uiWrapT; | 
|  | pTex->nWrapR			= uiWrapR; | 
|  | pTex->uiWidth			= uiWidth; | 
|  | pTex->uiHeight			= uiHeight; | 
|  | pTex->uiFlags			= uiFlags; | 
|  | m_psTexture.Append(pTex); | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d\n", str, m_psContext->pnFileLineNumber[i]);; | 
|  | goto fail_release_return; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i],  m_psContext->ppszEffectFile[i]); | 
|  | goto fail_release_return; | 
|  | } | 
|  | } | 
|  |  | 
|  | /* | 
|  | Should only reach here if there have been no issues | 
|  | */ | 
|  | bReturnVal = true; | 
|  | goto release_return; | 
|  |  | 
|  | fail_release_return: | 
|  | bReturnVal = false; | 
|  | release_return: | 
|  | FREE(pszKeyword); | 
|  | FREE(pszName); | 
|  | FREE(pszFile); | 
|  | FREE(pszTemp); | 
|  | return bReturnVal; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		ParseTextureFlags | 
|  | @Input			c_pszCursor | 
|  | @Output			pFlagsOut | 
|  | @Input			uiNumFlags | 
|  | @Input			ppszFlagNames | 
|  | @Input			uiNumFlagNames | 
|  | @Input			pReturnError | 
|  | @Input			iLineNum | 
|  | @Return			bool | 
|  | @Description	Parses the texture flag sections. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ParseTextureFlags(	const char* c_pszRemainingLine, unsigned int** ppFlagsOut, unsigned int uiNumFlags, const char** c_ppszFlagNames, unsigned int uiNumFlagNames, | 
|  | CPVRTString * const pReturnError, int iLineNum) | 
|  | { | 
|  | const unsigned int INVALID_TYPE = 0xAC1DBEEF; | 
|  | unsigned int uiIndex; | 
|  | const char* c_pszCursor; | 
|  | const char* c_pszResult; | 
|  |  | 
|  | // --- Find the first flag | 
|  | uiIndex = 0; | 
|  | c_pszCursor = strstr(c_pszRemainingLine, c_ppszFlagNames[uiIndex++]); | 
|  | while(uiIndex < uiNumFlagNames) | 
|  | { | 
|  | c_pszResult = strstr(c_pszRemainingLine, c_ppszFlagNames[uiIndex++]); | 
|  | if(((c_pszResult < c_pszCursor) || !c_pszCursor) && c_pszResult) | 
|  | c_pszCursor = c_pszResult; | 
|  | } | 
|  |  | 
|  | if(!c_pszCursor) | 
|  | return true;		// No error, but just return as no flags specified. | 
|  |  | 
|  | // Quick error check - make sure that the first flag found is valid. | 
|  | if(c_pszCursor != c_pszRemainingLine) | 
|  | { | 
|  | if(*(c_pszCursor-1) == '-')		// Yeah this shouldn't be there. Must be invalid first tag. | 
|  | { | 
|  | char szBuffer[128];		// Find out the tag. | 
|  | memset(szBuffer, 0, sizeof(szBuffer)); | 
|  | const char* pszStart = c_pszCursor-1; | 
|  | while(pszStart != c_pszRemainingLine && *pszStart != ' ')		pszStart--; | 
|  | pszStart++;	// Escape the space. | 
|  | unsigned int uiNumChars = (unsigned int) ((c_pszCursor-1) - pszStart); | 
|  | strncpy(szBuffer, pszStart, uiNumChars); | 
|  |  | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d: %s\n", szBuffer, m_psContext->pnFileLineNumber[iLineNum], m_psContext->ppszEffectFile[iLineNum]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | unsigned int uiFlagsFound = 0; | 
|  | unsigned int uiBufferIdx; | 
|  | char szBuffer[128];		// Buffer to hold the token | 
|  |  | 
|  | while(*c_pszCursor != ' ' && *c_pszCursor != 0 && uiFlagsFound < uiNumFlags) | 
|  | { | 
|  | memset(szBuffer, 0, sizeof(szBuffer));		// Clear the buffer | 
|  | uiBufferIdx = 0; | 
|  |  | 
|  | while(*c_pszCursor != '-' && *c_pszCursor != 0 && *c_pszCursor != ' ' && uiBufferIdx < 128)		// - = delim. token | 
|  | szBuffer[uiBufferIdx++] = *c_pszCursor++; | 
|  |  | 
|  | // Check if the buffer content is a valid flag name. | 
|  | unsigned int Type = INVALID_TYPE; | 
|  | for(unsigned int uiIndex = 0; uiIndex < uiNumFlagNames; ++uiIndex) | 
|  | { | 
|  | if(strcmp(szBuffer, c_ppszFlagNames[uiIndex]) == 0) | 
|  | { | 
|  | 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. | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Tell the user it's invalid. | 
|  | if(Type == INVALID_TYPE) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d: %s\n", szBuffer, m_psContext->pnFileLineNumber[iLineNum], m_psContext->ppszEffectFile[iLineNum]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // Set the flag to the enum type. | 
|  | *ppFlagsOut[uiFlagsFound++] = Type; | 
|  |  | 
|  | if(*c_pszCursor == '-')	c_pszCursor++; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseShader | 
|  | @Input				nStartLine		start line number | 
|  | @Input				nEndLine		end line number | 
|  | @Output			pReturnError	error string | 
|  | @Output			shader			shader data object | 
|  | @Input				pszBlockName	name of block in PFX file | 
|  | @Return			bool			true if parse is successful | 
|  | @Description		Parses the VERTEXSHADER or FRAGMENTSHADER section of the | 
|  | PFX file. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ParseShader(int nStartLine, int nEndLine, CPVRTString * const pReturnError, SPVRTPFXParserShader &shader, const char * const pszBlockName) | 
|  | { | 
|  | enum eCmd | 
|  | { | 
|  | eCmds_GLSLCode, | 
|  | eCmds_Name, | 
|  | eCmds_File, | 
|  | eCmds_BinaryFile, | 
|  |  | 
|  | eCmds_Size | 
|  | }; | 
|  |  | 
|  | const CPVRTHash ShaderCommands[] = | 
|  | { | 
|  | "[GLSL_CODE]", | 
|  | "NAME", | 
|  | "FILE", | 
|  | "BINARYFILE", | 
|  | }; | 
|  | PVRTCOMPILEASSERT(ShaderCommands, sizeof(ShaderCommands) / sizeof(ShaderCommands[0]) == eCmds_Size); | 
|  |  | 
|  | bool glslcode=0, glslfile=0, bName=0; | 
|  |  | 
|  | shader.bUseFileName		= false; | 
|  | shader.pszGLSLfile		= NULL; | 
|  | shader.pszGLSLcode		= NULL; | 
|  | shader.pszGLSLBinaryFile= NULL; | 
|  | shader.pbGLSLBinary		= NULL; | 
|  | shader.nFirstLineNumber	= 0; | 
|  | shader.nLastLineNumber  = 0; | 
|  |  | 
|  | for(int i = nStartLine+1; i < nEndLine; i++) | 
|  | { | 
|  | // Skip blank lines | 
|  | if(!*m_psContext->ppszEffectFile[i]) | 
|  | continue; | 
|  |  | 
|  | char *str = strtok (m_psContext->ppszEffectFile[i]," "); | 
|  | if(str != NULL) | 
|  | { | 
|  | CPVRTHash Cmd(str); | 
|  |  | 
|  | // Check for [GLSL_CODE] tags first and remove those lines from loop. | 
|  | if(Cmd == ShaderCommands[eCmds_GLSLCode]) | 
|  | { | 
|  | if(glslcode) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("[GLSL_CODE] redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | if(glslfile && shader.pbGLSLBinary==NULL ) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("[GLSL_CODE] not allowed with FILE in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | shader.nFirstLineNumber = m_psContext->pnFileLineNumber[i]; | 
|  |  | 
|  | // Skip the block-start | 
|  | i++; | 
|  |  | 
|  | CPVRTString GLSLCode; | 
|  | if(!ConcatenateLinesUntil( | 
|  | GLSLCode, | 
|  | i, | 
|  | m_psContext->ppszEffectFile, | 
|  | m_psContext->nNumLines, | 
|  | "[/GLSL_CODE]")) | 
|  | { | 
|  | return false; | 
|  | } | 
|  |  | 
|  | shader.nLastLineNumber = m_psContext->pnFileLineNumber[i]; | 
|  |  | 
|  | shader.pszGLSLcode = (char*)malloc((GLSLCode.size()+1) * sizeof(char)); | 
|  | strcpy(shader.pszGLSLcode, GLSLCode.c_str()); | 
|  |  | 
|  | shader.bUseFileName = false; | 
|  | glslcode = 1; | 
|  | } | 
|  | else if(Cmd == ShaderCommands[eCmds_Name]) | 
|  | { | 
|  | if(bName) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("NAME redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | str = ReadEOLToken(NULL); | 
|  |  | 
|  | if(str == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("NAME missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | shader.Name.assign(str); | 
|  | bName = true; | 
|  | } | 
|  | else if(Cmd == ShaderCommands[eCmds_File]) | 
|  | { | 
|  | if(glslfile) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("FILE redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | if(glslcode) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("FILE not allowed with [GLSL_CODE] in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | str = ReadEOLToken(NULL); | 
|  |  | 
|  | if(str == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("FILE missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | shader.pszGLSLfile = (char*)malloc((strlen(str)+1) * sizeof(char)); | 
|  | strcpy(shader.pszGLSLfile, str); | 
|  |  | 
|  | CPVRTResourceFile GLSLFile(str); | 
|  |  | 
|  | if(!GLSLFile.IsOpen()) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Error loading file '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | shader.pszGLSLcode = (char*)malloc((GLSLFile.Size()+1) * sizeof(char)); | 
|  | memcpy(shader.pszGLSLcode, (const char*) GLSLFile.DataPtr(), GLSLFile.Size()); | 
|  | shader.pszGLSLcode[GLSLFile.Size()] = '\0'; | 
|  |  | 
|  | shader.nFirstLineNumber = m_psContext->pnFileLineNumber[i];		// Mark position where GLSL file is defined. | 
|  |  | 
|  | shader.bUseFileName = true; | 
|  | glslfile = 1; | 
|  | } | 
|  | else if(Cmd == ShaderCommands[eCmds_BinaryFile]) | 
|  | { | 
|  | str = ReadEOLToken(NULL); | 
|  |  | 
|  | if(str == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("BINARYFILE missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | shader.pszGLSLBinaryFile = (char*)malloc((strlen(str)+1) * sizeof(char)); | 
|  | strcpy(shader.pszGLSLBinaryFile, str); | 
|  |  | 
|  | CPVRTResourceFile GLSLFile(str); | 
|  |  | 
|  | if(!GLSLFile.IsOpen()) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Error loading file '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | shader.pbGLSLBinary = new char[GLSLFile.Size()]; | 
|  | shader.nGLSLBinarySize = (unsigned int)GLSLFile.Size(); | 
|  | memcpy(shader.pbGLSLBinary, GLSLFile.DataPtr(), GLSLFile.Size()); | 
|  |  | 
|  | shader.bUseFileName = true; | 
|  | glslfile = 1; | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | str = strtok (NULL, " "); | 
|  | if(str != NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unexpected data in [%s] on line %d: '%s'\n", pszBlockName, m_psContext->pnFileLineNumber[i], str); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [%s] on line %d: %s\n", pszBlockName, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(!bName) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("NAME not found in [%s] on line %d.\n", pszBlockName, m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | if(!glslfile && !glslcode) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("No Shader File or Shader Code specified in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseSemantic | 
|  | @Output			semantic		semantic data object | 
|  | @Input				nStartLine		start line number | 
|  | @Output			pReturnError	error string | 
|  | @Return			bool			true if parse is successful | 
|  | @Description		Parses a semantic. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ParseSemantic(SPVRTPFXParserSemantic &semantic, const int nStartLine, CPVRTString * const pReturnError) | 
|  | { | 
|  | char *str; | 
|  |  | 
|  | semantic.pszName = 0; | 
|  | semantic.pszValue = 0; | 
|  | semantic.sDefaultValue.eType = eDataTypeNone; | 
|  | semantic.nIdx = 0; | 
|  |  | 
|  | str = strtok (NULL, " "); | 
|  | if(str == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("UNIFORM missing name in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  | semantic.pszName = (char*)malloc((strlen(str)+1) * sizeof(char)); | 
|  | strcpy(semantic.pszName, str); | 
|  |  | 
|  | str = strtok (NULL, " "); | 
|  | if(str == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("UNIFORM missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  |  | 
|  | FREE(semantic.pszName); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | /* | 
|  | If the final digits of the semantic are a number they are | 
|  | stripped off and used as the index, with the remainder | 
|  | used as the semantic. | 
|  | */ | 
|  | { | 
|  | size_t idx, len; | 
|  | len = strlen(str); | 
|  |  | 
|  | idx = len; | 
|  | while(idx) | 
|  | { | 
|  | --idx; | 
|  | if(strcspn(&str[idx], "0123456789") != 0) | 
|  | { | 
|  | break; | 
|  | } | 
|  | } | 
|  | if(idx == 0) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Semantic contains only numbers in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  |  | 
|  | FREE(semantic.pszName); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | ++idx; | 
|  | // Store the semantic index | 
|  | if(len == idx) | 
|  | { | 
|  | semantic.nIdx = 0; | 
|  | } | 
|  | else | 
|  | { | 
|  | semantic.nIdx = atoi(&str[idx]); | 
|  | } | 
|  |  | 
|  | // Chop off the index from the string containing the semantic | 
|  | str[idx] = 0; | 
|  | } | 
|  |  | 
|  | // Store a copy of the semantic name | 
|  | semantic.pszValue = (char*)malloc((strlen(str)+1) * sizeof(char)); | 
|  | strcpy(semantic.pszValue, str); | 
|  |  | 
|  | /* | 
|  | Optional default semantic value | 
|  | */ | 
|  | char pszString[2048]; | 
|  | strcpy(pszString,""); | 
|  | str = strtok (NULL, " "); | 
|  | if(str != NULL) | 
|  | { | 
|  | // Get all ramainning arguments | 
|  | while(str != NULL) | 
|  | { | 
|  | strcat(pszString, str); | 
|  | strcat(pszString, " "); | 
|  | str = strtok (NULL, " "); | 
|  | } | 
|  |  | 
|  | // default value | 
|  | int i; | 
|  | for(i = 0; i < eNumDefaultDataTypes; i++) | 
|  | { | 
|  | if(strncmp(pszString, c_psSemanticDefaultDataTypeInfo[i].pszName, strlen(c_psSemanticDefaultDataTypeInfo[i].pszName)) == 0) | 
|  | { | 
|  | if(!GetSemanticDataFromString(	&semantic.sDefaultValue, | 
|  | &pszString[strlen(c_psSemanticDefaultDataTypeInfo[i].pszName)], | 
|  | c_psSemanticDefaultDataTypeInfo[i].eType, | 
|  | pReturnError | 
|  | )) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr(" on line %d.\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  |  | 
|  | FREE(semantic.pszValue); | 
|  | FREE(semantic.pszName); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | semantic.sDefaultValue.eType = c_psSemanticDefaultDataTypeInfo[i].eType; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | // invalid data type | 
|  | if(i == eNumDefaultDataTypes) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("'%s' unknown on line %d.\n", pszString, m_psContext->pnFileLineNumber[nStartLine]); | 
|  |  | 
|  | FREE(semantic.pszValue); | 
|  | FREE(semantic.pszName); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			ParseEffect | 
|  | @Output			effect			effect data object | 
|  | @Input				nStartLine		start line number | 
|  | @Input				nEndLine		end line number | 
|  | @Output			pReturnError	error string | 
|  | @Return			bool			true if parse is successful | 
|  | @Description		Parses the EFFECT section of the PFX file. | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::ParseEffect(SPVRTPFXParserEffect &effect, const int nStartLine, const int nEndLine, CPVRTString * const pReturnError) | 
|  | { | 
|  | enum eCmds | 
|  | { | 
|  | eCmds_Annotation, | 
|  | eCmds_VertexShader, | 
|  | eCmds_FragmentShader, | 
|  | eCmds_Texture, | 
|  | eCmds_Uniform, | 
|  | eCmds_Attribute, | 
|  | eCmds_Name, | 
|  | eCmds_Target, | 
|  |  | 
|  | eCmds_Size | 
|  | }; | 
|  |  | 
|  | const CPVRTHash EffectCommands[] = | 
|  | { | 
|  | "[ANNOTATION]", | 
|  | "VERTEXSHADER", | 
|  | "FRAGMENTSHADER", | 
|  | "TEXTURE", | 
|  | "UNIFORM", | 
|  | "ATTRIBUTE", | 
|  | "NAME", | 
|  | "TARGET", | 
|  | }; | 
|  | PVRTCOMPILEASSERT(EffectCommands, sizeof(EffectCommands) / sizeof(EffectCommands[0]) == eCmds_Size); | 
|  |  | 
|  | bool bName = false; | 
|  | bool bVertShader = false; | 
|  | bool bFragShader = false; | 
|  |  | 
|  | for(int i = nStartLine+1; i < nEndLine; i++) | 
|  | { | 
|  | // Skip blank lines | 
|  | if(!*m_psContext->ppszEffectFile[i]) | 
|  | continue; | 
|  |  | 
|  | char *str = strtok (m_psContext->ppszEffectFile[i]," "); | 
|  | if(str != NULL) | 
|  | { | 
|  | CPVRTHash Cmd(str); | 
|  |  | 
|  | if(Cmd == EffectCommands[eCmds_Annotation]) | 
|  | { | 
|  | if(!effect.Annotation.empty()) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("ANNOTATION redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | i++;		// Skip the block-start | 
|  | if(!ConcatenateLinesUntil( | 
|  | effect.Annotation, | 
|  | i, | 
|  | m_psContext->ppszEffectFile, | 
|  | m_psContext->nNumLines, | 
|  | "[/ANNOTATION]")) | 
|  | { | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else if(Cmd == EffectCommands[eCmds_VertexShader]) | 
|  | { | 
|  | if(bVertShader) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("VERTEXSHADER redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | str = ReadEOLToken(NULL); | 
|  |  | 
|  | if(str == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("VERTEXSHADER missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | effect.VertexShaderName.assign(str); | 
|  |  | 
|  | bVertShader = true; | 
|  | } | 
|  | else if(Cmd == EffectCommands[eCmds_FragmentShader]) | 
|  | { | 
|  | if(bFragShader) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("FRAGMENTSHADER redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | str = ReadEOLToken(NULL); | 
|  |  | 
|  | if(str == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("FRAGMENTSHADER missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | effect.FragmentShaderName.assign(str); | 
|  |  | 
|  | bFragShader = true; | 
|  | } | 
|  | else if(Cmd == EffectCommands[eCmds_Texture]) | 
|  | { | 
|  | unsigned int uiTexIdx = effect.Textures.Append(); | 
|  | // texture number | 
|  | str = strtok(NULL, " "); | 
|  | if(str != NULL) | 
|  | effect.Textures[uiTexIdx].nNumber = atoi(str); | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("TEXTURE missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | // texture name | 
|  | str = strtok(NULL, " "); | 
|  | if(str != NULL) | 
|  | { | 
|  | effect.Textures[uiTexIdx].Name = CPVRTStringHash(str); | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("TEXTURE missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else if(Cmd == EffectCommands[eCmds_Uniform]) | 
|  | { | 
|  | unsigned int uiUniformIdx = effect.Uniforms.Append(); | 
|  | if(!ParseSemantic(effect.Uniforms[uiUniformIdx], i, pReturnError)) | 
|  | return false; | 
|  |  | 
|  | } | 
|  | else if(Cmd == EffectCommands[eCmds_Attribute]) | 
|  | { | 
|  | unsigned int uiAttribIdx = effect.Attributes.Append(); | 
|  | if(!ParseSemantic(effect.Attributes[uiAttribIdx], i, pReturnError)) | 
|  | return false; | 
|  | } | 
|  | else if(Cmd == EffectCommands[eCmds_Name]) | 
|  | { | 
|  | if(bName) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("NAME redefined in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | str = strtok (NULL, " "); | 
|  | if(str == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("NAME missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | effect.Name.assign(str); | 
|  | bName = true; | 
|  | } | 
|  | else if(Cmd == EffectCommands[eCmds_Target]) | 
|  | { | 
|  | unsigned int uiIndex = effect.Targets.Append(); | 
|  |  | 
|  | // Target requires 2 components | 
|  | CPVRTString* pVals[] = { &effect.Targets[uiIndex].BufferType, &effect.Targets[uiIndex].TargetName }; | 
|  |  | 
|  | for(unsigned int uiVal = 0; uiVal < 2; ++uiVal) | 
|  | { | 
|  | str = strtok (NULL, " "); | 
|  | if(str == NULL) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("TARGET missing value(s) in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | *(pVals[uiVal]) = str; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [EFFECT] on line %d\n", str, m_psContext->pnFileLineNumber[i]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  | else | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr( "Missing arguments in [EFFECT] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  | // Check that every TEXTURE has a matching UNIFORM | 
|  | for(unsigned int uiTex = 0; uiTex < effect.Textures.GetSize(); ++uiTex) | 
|  | { | 
|  | unsigned int uiTexUnit			= effect.Textures[uiTex].nNumber; | 
|  | const CPVRTStringHash& texName  = effect.Textures[uiTex].Name; | 
|  | // Find UNIFORM associated with the TexUnit (e.g TEXTURE0). | 
|  | bool bFound = false; | 
|  | for(unsigned int uiUniform = 0; uiUniform < effect.Uniforms.GetSize(); ++uiUniform) | 
|  | { | 
|  | const SPVRTPFXParserSemantic& Sem = effect.Uniforms[uiUniform]; | 
|  | if(strcmp(Sem.pszValue, "TEXTURE") == 0 && Sem.nIdx == uiTexUnit) | 
|  | { | 
|  | bFound = true; | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(!bFound) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("TEXTURE %s missing matching UNIFORM in [EFFECT] on line %d\n", texName.c_str(), m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  | } | 
|  |  | 
|  |  | 
|  | if(!bName) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("No 'NAME' found in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]); | 
|  | return false; | 
|  | } | 
|  | if(!bVertShader) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("No 'VERTEXSHADER' defined in [EFFECT] starting on line %d: \n", m_psContext->pnFileLineNumber[nStartLine-1]); | 
|  | return false; | 
|  | } | 
|  | if(!bFragShader) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("No 'FRAGMENTSHADER' defined in [EFFECT] starting on line %d: \n", m_psContext->pnFileLineNumber[nStartLine-1]); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function			DetermineRenderPassDependencies | 
|  | @Return			True if dependency tree is valid. False if there are errors | 
|  | in the dependency tree (e.g. recursion) | 
|  | @Description		Looks through all of the effects in the .pfx and determines | 
|  | the order of render passes that have been declared with | 
|  | the RENDER tag (found in [TEXTURES] | 
|  | *****************************************************************************/ | 
|  | bool CPVRTPFXParser::DetermineRenderPassDependencies(CPVRTString * const pReturnError) | 
|  | { | 
|  | unsigned int	ui(0), uj(0), uk(0); | 
|  |  | 
|  | if(m_RenderPasses.GetSize() == 0) | 
|  | return true; | 
|  |  | 
|  | // --- Add all render pass nodes to the skip graph. | 
|  | for(ui = 0; ui < m_RenderPasses.GetSize(); ++ui) | 
|  | { | 
|  | SPVRTPFXRenderPass& Pass = m_RenderPasses[ui]; | 
|  | bool bFound = false; | 
|  |  | 
|  | // Search all EFFECT blocks for matching TARGET. This is for post-processes behavior. | 
|  | for(unsigned int uiEffect = 0; uiEffect < m_psEffect.GetSize(); ++uiEffect) | 
|  | { | 
|  | SPVRTPFXParserEffect& Effect = m_psEffect[uiEffect]; | 
|  |  | 
|  | // Search all TARGETs in this effect | 
|  | for(unsigned int uiTargets = 0; uiTargets < Effect.Targets.GetSize(); ++uiTargets) | 
|  | { | 
|  | const SPVRTTargetPair& Target = Effect.Targets[uiTargets]; | 
|  | if(Target.TargetName == Pass.SemanticName) | 
|  | { | 
|  | // Match. This EFFECT block matches the pass name. | 
|  | Pass.pEffect = &Effect; | 
|  | bFound = true; | 
|  |  | 
|  | // This is now a post-process pass. Set relevant values. | 
|  | Pass.eRenderPassType = ePOSTPROCESS_PASS; | 
|  | m_aszPostProcessNames.Append(Pass.SemanticName); | 
|  |  | 
|  | // Check that the surface type and output match are relevant (i.e DEPTH != RGBA8888). | 
|  | if( (Target.BufferType.find_first_of("DEPTH") != CPVRTString::npos && !(Pass.uiFormatFlags & PVRPFXTEX_DEPTH)) | 
|  | ||	(Target.BufferType.find_first_of("COLOR") != CPVRTString::npos && !(Pass.uiFormatFlags & PVRPFXTEX_COLOUR)) ) | 
|  | { | 
|  | *pReturnError = PVRTStringFromFormattedStr("Surface type mismatch in [EFFECT]. \"%s\" has different type than \"%s\"\n", Target.TargetName.c_str(), Pass.SemanticName.c_str()); | 
|  | return false; | 
|  | } | 
|  |  | 
|  | break; | 
|  | } | 
|  | } | 
|  |  | 
|  | if(bFound) | 
|  | break; | 
|  | } | 
|  |  | 
|  | // Add a pointer to the post process | 
|  | m_renderPassSkipGraph.AddNode(&Pass); | 
|  | } | 
|  |  | 
|  |  | 
|  | // --- Loop through all created render passes in the skip graph and determine their dependencies | 
|  | for(ui = 0; ui < m_renderPassSkipGraph.GetNumNodes(); ++ui) | 
|  | { | 
|  | //	Loop through all other nodes in the skip graph | 
|  | SPVRTPFXRenderPass* pPass			= m_renderPassSkipGraph[ui]; | 
|  | SPVRTPFXRenderPass* pTestPass       = NULL; | 
|  |  | 
|  | for(uj = 0; uj < m_RenderPasses.GetSize(); ++uj) | 
|  | { | 
|  | pTestPass = m_renderPassSkipGraph[uj]; | 
|  |  | 
|  | // No self compare | 
|  | if(pPass == pTestPass) | 
|  | continue; | 
|  |  | 
|  | // No effect associated. | 
|  | if(!pPass->pEffect) | 
|  | continue; | 
|  |  | 
|  | // Is the node a render pass I rely on? | 
|  | for(uk = 0; uk < pPass->pEffect->Textures.GetSize(); ++uk) | 
|  | { | 
|  | /* | 
|  | If the texture names match, add a new node | 
|  | */ | 
|  | if(pTestPass->pTexture->Name == pPass->pEffect->Textures[uk].Name) | 
|  | { | 
|  | m_renderPassSkipGraph.AddNodeDependency(pPass, pTestPass); | 
|  | break; | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | return true; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		FindTextureIndex | 
|  | @Input			TextureName | 
|  | @Return			unsigned int	Index in to the effect.Texture array. | 
|  | @Description	Returns the index in to the texture array within the effect | 
|  | block where the given texture resides. | 
|  | *****************************************************************************/ | 
|  | unsigned int CPVRTPFXParser::FindTextureIndex( const CPVRTStringHash& TextureName, unsigned int uiEffect ) const | 
|  | { | 
|  | for(unsigned int uiIndex = 0; uiIndex < m_psEffect[uiEffect].Textures.GetSize(); ++uiIndex) | 
|  | { | 
|  | const SPVRTPFXParserEffectTexture& Tex = m_psEffect[uiEffect].Textures[uiIndex]; | 
|  | if(Tex.Name == TextureName) | 
|  | { | 
|  | return uiIndex; | 
|  | } | 
|  | } | 
|  |  | 
|  | return 0xFFFFFFFF; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetNumberRenderPasses | 
|  | @Return			unsigned int | 
|  | @Description	Returns the number of render passes within this PFX. | 
|  | *****************************************************************************/ | 
|  | unsigned int CPVRTPFXParser::GetNumberRenderPasses() const | 
|  | { | 
|  | return m_RenderPasses.GetSize(); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetNumberRenderPasses | 
|  | @Input			unsigned int		The render pass index. | 
|  | @Return			SPVRTPFXRenderPass* | 
|  | @Description	Returns the given render pass. | 
|  | *****************************************************************************/ | 
|  | const SPVRTPFXRenderPass& CPVRTPFXParser::GetRenderPass( unsigned int uiIndex ) const | 
|  | { | 
|  | _ASSERT(uiIndex >= 0 && uiIndex < GetNumberRenderPasses()); | 
|  | return m_RenderPasses[uiIndex]; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetPFXFileName | 
|  | @Return			const CPVRTString & | 
|  | @Description	Returns the PFX file name associated with this object. | 
|  | *****************************************************************************/ | 
|  | const CPVRTString& CPVRTPFXParser::GetPFXFileName() const | 
|  | { | 
|  | return m_szFileName; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetPostProcessNames | 
|  | @Return			const CPVRTArray<CPVRTString>& | 
|  | @Description	Returns a list of prost process effect names. | 
|  | *****************************************************************************/ | 
|  | const CPVRTArray<CPVRTString>& CPVRTPFXParser::GetPostProcessNames() const | 
|  | { | 
|  | return m_aszPostProcessNames; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetNumberFragmentShaders | 
|  | @Return			unsigned int	Number of fragment shaders. | 
|  | @Description	Returns the number of fragment shaders referenced in the PFX. | 
|  | *****************************************************************************/ | 
|  | unsigned int CPVRTPFXParser::GetNumberFragmentShaders() const | 
|  | { | 
|  | return m_psFragmentShader.GetSize(); | 
|  | } | 
|  |  | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetFragmentShader | 
|  | @Input			unsigned int		The index of this shader. | 
|  | @Return			const SPVRTPFXParserShader&		The PFX fragment shader. | 
|  | @Description	Returns a given fragment shader. | 
|  | *****************************************************************************/ | 
|  | SPVRTPFXParserShader& CPVRTPFXParser::GetFragmentShader( unsigned int uiIndex ) | 
|  | { | 
|  | _ASSERT(uiIndex < GetNumberFragmentShaders()); | 
|  | return m_psFragmentShader[uiIndex]; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetNumberVertexShaders | 
|  | @Return			unsigned int	Number of vertex shaders. | 
|  | @Description	Returns the number of vertex shaders referenced in the PFX. | 
|  | *****************************************************************************/ | 
|  | unsigned int CPVRTPFXParser::GetNumberVertexShaders() const | 
|  | { | 
|  | return m_psVertexShader.GetSize(); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetVertexShader | 
|  | @Input			unsigned int		The index of this shader. | 
|  | @Return			const SPVRTPFXParserShader&		The PFX vertex shader. | 
|  | @Description	Returns a given vertex shader. | 
|  | *****************************************************************************/ | 
|  | SPVRTPFXParserShader& CPVRTPFXParser::GetVertexShader( unsigned int uiIndex ) | 
|  | { | 
|  | _ASSERT(uiIndex < GetNumberVertexShaders()); | 
|  | return m_psVertexShader[uiIndex]; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetNumberEffects | 
|  | @Return			unsigned int	Number of effects. | 
|  | @Description	Returns the number of effects referenced in the PFX. | 
|  | *****************************************************************************/ | 
|  | unsigned int CPVRTPFXParser::GetNumberEffects() const | 
|  | { | 
|  | return m_psEffect.GetSize(); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetEffect | 
|  | @Input			uiIndex		The index of this effect. | 
|  | @Return			The PFX effect. | 
|  | @Description	Returns a given effect. | 
|  | *****************************************************************************/ | 
|  | const SPVRTPFXParserEffect& CPVRTPFXParser::GetEffect(unsigned int uiIndex) const | 
|  | { | 
|  | _ASSERT(uiIndex < GetNumberEffects()); | 
|  | return m_psEffect[uiIndex]; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetNumberTextures | 
|  | @Return			unsigned int	Number of effects. | 
|  | @Description	Returns the number of textures referenced in the PFX. | 
|  | *****************************************************************************/ | 
|  | unsigned int CPVRTPFXParser::GetNumberTextures() const | 
|  | { | 
|  | return m_psTexture.GetSize(); | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		GetTexture | 
|  | @Input			unsigned int		The index of this texture | 
|  | @Return			const SPVRTPFXParserEffect&		The PFX texture. | 
|  | @Description	Returns a given texture. | 
|  | *****************************************************************************/ | 
|  | const SPVRTPFXParserTexture* CPVRTPFXParser::GetTexture( unsigned int uiIndex ) const | 
|  | { | 
|  | _ASSERT(uiIndex < GetNumberTextures()); | 
|  | return m_psTexture[uiIndex]; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		FindEffectByName | 
|  | @Input			Name | 
|  | @Return			int | 
|  | @Description	Returns the index of the given string. Returns -1 on failure. | 
|  | *****************************************************************************/ | 
|  | int CPVRTPFXParser::FindEffectByName(const CPVRTStringHash& Name) const | 
|  | { | 
|  | if(Name.Hash() == 0) | 
|  | return -1; | 
|  |  | 
|  | for(unsigned int uiIndex = 0; uiIndex < GetNumberEffects(); ++uiIndex) | 
|  | { | 
|  | if(GetEffect(uiIndex).Name == Name) | 
|  | { | 
|  | return (int)uiIndex; | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		FindTextureByName | 
|  | @Input			Name		Name of the texture. | 
|  | @Return			int | 
|  | @Description	Returns the index of the given texture. Returns -1 on failure. | 
|  | *****************************************************************************/ | 
|  | int CPVRTPFXParser::FindTextureByName(const CPVRTStringHash& Name) const | 
|  | { | 
|  | if(Name.Hash() == 0) | 
|  | return -1; | 
|  |  | 
|  | for(unsigned int uiIndex = 0; uiIndex < GetNumberTextures(); ++uiIndex) | 
|  | { | 
|  | if(GetTexture(uiIndex)->Name == Name) | 
|  | { | 
|  | return (int)uiIndex; | 
|  | } | 
|  | } | 
|  |  | 
|  | return -1; | 
|  | } | 
|  |  | 
|  | /*!*************************************************************************** | 
|  | @Function		PVRTPFXCreateStringCopy | 
|  | @Return			void | 
|  | @Description	Safely copies a C string. | 
|  | *****************************************************************************/ | 
|  | void PVRTPFXCreateStringCopy(char** ppDst, const char* pSrc) | 
|  | { | 
|  | if(pSrc) | 
|  | { | 
|  | FREE(*ppDst); | 
|  | *ppDst = (char*)malloc((strlen(pSrc)+1) * sizeof(char)); | 
|  | strcpy(*ppDst, pSrc); | 
|  | } | 
|  | } | 
|  |  | 
|  | /***************************************************************************** | 
|  | End of file (PVRTPFXParser.cpp) | 
|  | *****************************************************************************/ | 
|  |  |