blob: 023fb6e29bc03b6086bd6cffb51105e8ad616ff5 [file] [log] [blame]
/******************************************************************************
@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 &parameterTag, 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)
*****************************************************************************/