PVRTPFXParser.cpp 71 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323
  1. /******************************************************************************
  2. @File PVRTPFXParser.cpp
  3. @Title PVRTPFXParser
  4. @Version
  5. @Copyright Copyright (C) Imagination Technologies Limited.
  6. @Platform Windows + Linux
  7. @Description PFX file parser.
  8. ******************************************************************************/
  9. /*****************************************************************************
  10. ** Includes
  11. ******************************************************************************/
  12. #include <string.h>
  13. #include <stdio.h>
  14. #include <stdlib.h>
  15. #include "PVRTGlobal.h"
  16. #include "PVRTContext.h"
  17. #include "PVRTMatrix.h"
  18. #include "PVRTFixedPoint.h"
  19. #include "PVRTMisc.h"
  20. #include "PVRTPFXParser.h"
  21. #include "PVRTResourceFile.h"
  22. #include "PVRTString.h"
  23. #include "PVRTMisc.h" // Used for POT functions
  24. /****************************************************************************
  25. ** CPVRTPFXParserReadContext Class
  26. ****************************************************************************/
  27. class CPVRTPFXParserReadContext
  28. {
  29. public:
  30. char **ppszEffectFile;
  31. int *pnFileLineNumber;
  32. unsigned int nNumLines, nMaxLines;
  33. public:
  34. CPVRTPFXParserReadContext();
  35. ~CPVRTPFXParserReadContext();
  36. };
  37. /*!***************************************************************************
  38. @Function CPVRTPFXParserReadContext
  39. @Description Initialises values.
  40. *****************************************************************************/
  41. CPVRTPFXParserReadContext::CPVRTPFXParserReadContext()
  42. {
  43. nMaxLines = 5000;
  44. nNumLines = 0;
  45. ppszEffectFile = new char*[nMaxLines];
  46. pnFileLineNumber = new int[nMaxLines];
  47. }
  48. /*!***************************************************************************
  49. @Function ~CPVRTPFXParserReadContext
  50. @Description Frees allocated memory
  51. *****************************************************************************/
  52. CPVRTPFXParserReadContext::~CPVRTPFXParserReadContext()
  53. {
  54. // free effect file
  55. for(unsigned int i = 0; i < nNumLines; i++)
  56. {
  57. FREE(ppszEffectFile[i]);
  58. }
  59. delete [] ppszEffectFile;
  60. delete [] pnFileLineNumber;
  61. }
  62. /*!***************************************************************************
  63. @Function IgnoreWhitespace
  64. @Input pszString
  65. @Output pszString
  66. @Description Skips space, tab, new-line and return characters.
  67. *****************************************************************************/
  68. void IgnoreWhitespace(char **pszString)
  69. {
  70. while( *pszString[0] == '\t' ||
  71. *pszString[0] == '\n' ||
  72. *pszString[0] == '\r' ||
  73. *pszString[0] == ' ' )
  74. {
  75. (*pszString)++;
  76. }
  77. }
  78. /*!***************************************************************************
  79. @Function GetSemanticDataFromString
  80. @Output pDataItem
  81. @Modified pszArgumentString
  82. @Input eType
  83. @Output pError error message
  84. @Return true if successful
  85. @Description Extracts the semantic data from the string and stores it
  86. in the output SPVRTSemanticDefaultData parameter.
  87. *****************************************************************************/
  88. bool GetSemanticDataFromString(SPVRTSemanticDefaultData *pDataItem, const char * const pszArgumentString, ESemanticDefaultDataType eType, CPVRTString *pError)
  89. {
  90. char *pszString = (char *)pszArgumentString;
  91. char *pszTmp;
  92. IgnoreWhitespace(&pszString);
  93. if(pszString[0] != '(')
  94. {
  95. *pError = CPVRTString("Missing '(' after ") + c_psSemanticDefaultDataTypeInfo[eType].pszName;
  96. return false;
  97. }
  98. pszString++;
  99. IgnoreWhitespace(&pszString);
  100. if(!strlen(pszString))
  101. {
  102. *pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments");
  103. return false;
  104. }
  105. pszTmp = pszString;
  106. switch(c_psSemanticDefaultDataTypeInfo[eType].eInternalType)
  107. {
  108. case eFloating:
  109. pDataItem->pfData[0] = (float)strtod(pszString, &pszTmp);
  110. break;
  111. case eInteger:
  112. pDataItem->pnData[0] = (int)strtol(pszString, &pszTmp, 10);
  113. break;
  114. case eBoolean:
  115. if(strncmp(pszString, "true", 4) == 0)
  116. {
  117. pDataItem->pbData[0] = true;
  118. pszTmp = &pszString[4];
  119. }
  120. else if(strncmp(pszString, "false", 5) == 0)
  121. {
  122. pDataItem->pbData[0] = false;
  123. pszTmp = &pszString[5];
  124. }
  125. break;
  126. }
  127. if(pszString == pszTmp)
  128. {
  129. size_t n = strcspn(pszString, ",\t ");
  130. char *pszError = (char *)malloc(n + 1);
  131. strcpy(pszError, "");
  132. strncat(pszError, pszString, n);
  133. *pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName;
  134. FREE(pszError);
  135. return false;
  136. }
  137. pszString = pszTmp;
  138. IgnoreWhitespace(&pszString);
  139. for(unsigned int i = 1; i < c_psSemanticDefaultDataTypeInfo[eType].nNumberDataItems; i++)
  140. {
  141. if(!strlen(pszString))
  142. {
  143. *pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments");
  144. return false;
  145. }
  146. if(pszString[0] != ',')
  147. {
  148. size_t n = strcspn(pszString, ",\t ");
  149. char *pszError = (char *)malloc(n + 1);
  150. strcpy(pszError, "");
  151. strncat(pszError, pszString, n);
  152. *pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName;
  153. FREE(pszError);
  154. return false;
  155. }
  156. pszString++;
  157. IgnoreWhitespace(&pszString);
  158. if(!strlen(pszString))
  159. {
  160. *pError = c_psSemanticDefaultDataTypeInfo[eType].pszName + CPVRTString(" missing arguments");
  161. return false;
  162. }
  163. pszTmp = pszString;
  164. switch(c_psSemanticDefaultDataTypeInfo[eType].eInternalType)
  165. {
  166. case eFloating:
  167. pDataItem->pfData[i] = (float)strtod(pszString, &pszTmp);
  168. break;
  169. case eInteger:
  170. pDataItem->pnData[i] = (int)strtol(pszString, &pszTmp, 10);
  171. break;
  172. case eBoolean:
  173. if(strncmp(pszString, "true", 4) == 0)
  174. {
  175. pDataItem->pbData[i] = true;
  176. pszTmp = &pszString[4];
  177. }
  178. else if(strncmp(pszString, "false", 5) == 0)
  179. {
  180. pDataItem->pbData[i] = false;
  181. pszTmp = &pszString[5];
  182. }
  183. break;
  184. }
  185. if(pszString == pszTmp)
  186. {
  187. size_t n = strcspn(pszString, ",\t ");
  188. char *pszError = (char *)malloc(n + 1);
  189. strcpy(pszError, "");
  190. strncat(pszError, pszString, n);
  191. *pError = CPVRTString("'") + pszError + "' unexpected for " + c_psSemanticDefaultDataTypeInfo[eType].pszName;
  192. FREE(pszError);
  193. return false;
  194. }
  195. pszString = pszTmp;
  196. IgnoreWhitespace(&pszString);
  197. }
  198. if(pszString[0] != ')')
  199. {
  200. size_t n = strcspn(pszString, "\t )");
  201. char *pszError = (char *)malloc(n + 1);
  202. strcpy(pszError, "");
  203. strncat(pszError, pszString, n);
  204. *pError = CPVRTString("'") + pszError + "' found when expecting ')' for " + c_psSemanticDefaultDataTypeInfo[eType].pszName;
  205. FREE(pszError);
  206. return false;
  207. }
  208. pszString++;
  209. IgnoreWhitespace(&pszString);
  210. if(strlen(pszString))
  211. {
  212. *pError = CPVRTString("'") + pszString + "' unexpected after ')'";
  213. return false;
  214. }
  215. return true;
  216. }
  217. /*!***************************************************************************
  218. @Function ConcatenateLinesUntil
  219. @Output pszOut output text
  220. @Output nLine end line number
  221. @Input nLine start line number
  222. @Input ppszLines input text - one array element per line
  223. @Input nLimit number of lines input
  224. @Input pszEnd end string
  225. @Return true if successful
  226. @Description Outputs a block of text starting from nLine and ending
  227. when the string pszEnd is found.
  228. *****************************************************************************/
  229. static bool ConcatenateLinesUntil(char *&pszOut, int &nLine, const char * const * const ppszLines, const unsigned int nLimit, const char * const pszEnd)
  230. {
  231. unsigned int i, j;
  232. size_t nLen;
  233. nLen = 0;
  234. for(i = nLine; i < nLimit; ++i)
  235. {
  236. if(strcmp(ppszLines[i], pszEnd) == 0)
  237. break;
  238. nLen += strlen(ppszLines[i]) + 1;
  239. }
  240. if(i == nLimit)
  241. {
  242. return false;
  243. }
  244. if(nLen)
  245. {
  246. ++nLen;
  247. pszOut = (char*)malloc(nLen * sizeof(*pszOut));
  248. *pszOut = 0;
  249. for(j = nLine; j < i; ++j)
  250. {
  251. strcat(pszOut, ppszLines[j]);
  252. strcat(pszOut, "\n");
  253. }
  254. }
  255. else
  256. {
  257. pszOut = 0;
  258. }
  259. nLine = i;
  260. return true;
  261. }
  262. /*!***************************************************************************
  263. @Function CPVRTPFXParser
  264. @Description Sets initial values.
  265. *****************************************************************************/
  266. CPVRTPFXParser::CPVRTPFXParser()
  267. {
  268. m_sHeader.pszVersion = NULL;
  269. m_sHeader.pszDescription = NULL;
  270. m_sHeader.pszCopyright = NULL;
  271. m_nMaxTextures = 20;
  272. m_nNumTextures = 0;
  273. m_psTexture = new SPVRTPFXParserTexture[m_nMaxTextures];
  274. m_nMaxVertShaders = 20;
  275. m_nNumVertShaders = 0;
  276. m_psVertexShader = new SPVRTPFXParserShader[m_nMaxVertShaders];
  277. m_nMaxFragShaders = 20;
  278. m_nNumFragShaders = 0;
  279. m_psFragmentShader = new SPVRTPFXParserShader[m_nMaxFragShaders];
  280. m_nMaxEffects = 20;
  281. m_nNumEffects = 0;
  282. m_psEffect = new SPVRTPFXParserEffect[m_nMaxEffects];
  283. m_nMaxRenders = 1; // Although more could be stored, Shaman only currently supports one additional render pass
  284. m_nNumRenderPasses = 0;
  285. m_psRenderPasses = new SPVRTPFXRenderPass[m_nMaxRenders];
  286. // NOTE: Temp hardcode viewport size
  287. m_uiViewportWidth = 640;
  288. m_uiViewportHeight = 480;
  289. }
  290. /*!***************************************************************************
  291. @Function ~CPVRTPFXParser
  292. @Description Frees memory used.
  293. *****************************************************************************/
  294. CPVRTPFXParser::~CPVRTPFXParser()
  295. {
  296. unsigned int i;
  297. // FREE header strings
  298. FREE(m_sHeader.pszVersion);
  299. FREE(m_sHeader.pszDescription);
  300. FREE(m_sHeader.pszCopyright);
  301. // free texture info
  302. for(i = 0; i < m_nNumTextures; ++i)
  303. {
  304. FREE(m_psTexture[i].pszName);
  305. FREE(m_psTexture[i].pszFile);
  306. }
  307. delete [] m_psTexture;
  308. // free shader strings
  309. for(i = 0; i < m_nNumFragShaders; ++i)
  310. {
  311. FREE(m_psFragmentShader[i].pszName);
  312. FREE(m_psFragmentShader[i].pszGLSLfile);
  313. FREE(m_psFragmentShader[i].pszGLSLcode);
  314. FREE(m_psFragmentShader[i].pszGLSLBinaryFile);
  315. FREE(m_psFragmentShader[i].pbGLSLBinary);
  316. }
  317. delete [] m_psFragmentShader;
  318. for(i = 0; i < m_nNumVertShaders; ++i)
  319. {
  320. FREE(m_psVertexShader[i].pszName);
  321. FREE(m_psVertexShader[i].pszGLSLfile);
  322. FREE(m_psVertexShader[i].pszGLSLcode);
  323. FREE(m_psVertexShader[i].pszGLSLBinaryFile);
  324. FREE(m_psVertexShader[i].pbGLSLBinary);
  325. }
  326. delete [] m_psVertexShader;
  327. // Free render pass info
  328. for(i=0; i < m_nNumRenderPasses; ++i)
  329. {
  330. delete[] m_psRenderPasses[i].pszSemanticName;
  331. m_psRenderPasses[i].pszSemanticName = NULL;
  332. delete[] m_psRenderPasses[i].pszNodeName;
  333. m_psRenderPasses[i].pszNodeName = NULL;
  334. }
  335. delete [] m_psRenderPasses;
  336. for(unsigned int nEffect = 0; nEffect < m_nNumEffects; ++nEffect)
  337. {
  338. // free uniform strings
  339. for(i=0; i < m_psEffect[nEffect].nNumUniforms; ++i)
  340. {
  341. FREE(m_psEffect[nEffect].psUniform[i].pszName);
  342. FREE(m_psEffect[nEffect].psUniform[i].pszValue);
  343. }
  344. delete [] m_psEffect[nEffect].psUniform;
  345. // free uniform strings
  346. for(i=0; i < m_psEffect[nEffect].nNumAttributes; ++i)
  347. {
  348. FREE(m_psEffect[nEffect].psAttribute[i].pszName);
  349. FREE(m_psEffect[nEffect].psAttribute[i].pszValue);
  350. }
  351. delete [] m_psEffect[nEffect].psAttribute;
  352. for(i=0; i < m_psEffect[nEffect].nNumTextures; ++i)
  353. {
  354. FREE(m_psEffect[nEffect].psTextures[i].pszName);
  355. }
  356. delete [] m_psEffect[nEffect].psTextures;
  357. FREE(m_psEffect[nEffect].pszFragmentShaderName);
  358. FREE(m_psEffect[nEffect].pszVertexShaderName);
  359. FREE(m_psEffect[nEffect].pszAnnotation);
  360. FREE(m_psEffect[nEffect].pszName);
  361. }
  362. delete [] m_psEffect;
  363. }
  364. /*!***************************************************************************
  365. @Function Parse
  366. @Output pReturnError error string
  367. @Return bool true for success parsing file
  368. @Description Parses a loaded PFX file.
  369. *****************************************************************************/
  370. bool CPVRTPFXParser::Parse(CPVRTString * const pReturnError)
  371. {
  372. int nEndLine = 0;
  373. int nHeaderCounter = 0, nTexturesCounter = 0;
  374. unsigned int i,j,k;
  375. m_nNumVertShaders = 0;
  376. m_nNumFragShaders = 0;
  377. m_nNumEffects = 0;
  378. // Loop through the file
  379. for(unsigned int nLine=0; nLine < m_psContext->nNumLines; nLine++)
  380. {
  381. // Skip blank lines
  382. if(!*m_psContext->ppszEffectFile[nLine])
  383. continue;
  384. if(strcmp("[HEADER]", m_psContext->ppszEffectFile[nLine]) == 0)
  385. {
  386. if(nHeaderCounter>0)
  387. {
  388. *pReturnError = PVRTStringFromFormattedStr("[HEADER] redefined on line %d\n", m_psContext->pnFileLineNumber[nLine]);
  389. return false;
  390. }
  391. if(GetEndTag("HEADER", nLine, &nEndLine))
  392. {
  393. if(ParseHeader(nLine, nEndLine, pReturnError))
  394. nHeaderCounter++;
  395. else
  396. return false;
  397. }
  398. else
  399. {
  400. *pReturnError = PVRTStringFromFormattedStr("Missing [/HEADER] tag after [HEADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
  401. return false;
  402. }
  403. nLine = nEndLine;
  404. }
  405. else if(strcmp("[TEXTURES]", m_psContext->ppszEffectFile[nLine]) == 0)
  406. {
  407. if(nTexturesCounter>0)
  408. {
  409. *pReturnError = PVRTStringFromFormattedStr("[TEXTURES] redefined on line %d\n", m_psContext->pnFileLineNumber[nLine]);
  410. return false;
  411. }
  412. if(GetEndTag("TEXTURES", nLine, &nEndLine))
  413. {
  414. if(ParseTextures(nLine, nEndLine, pReturnError))
  415. nTexturesCounter++;
  416. else
  417. return false;
  418. }
  419. else
  420. {
  421. *pReturnError = PVRTStringFromFormattedStr("Missing [/TEXTURES] tag after [TEXTURES] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
  422. return false;
  423. }
  424. nLine = nEndLine;
  425. }
  426. else if(strcmp("[VERTEXSHADER]", m_psContext->ppszEffectFile[nLine]) == 0)
  427. {
  428. if(GetEndTag("VERTEXSHADER", nLine, &nEndLine))
  429. {
  430. if(m_nNumVertShaders >= m_nMaxVertShaders)
  431. {
  432. *pReturnError = PVRTStringFromFormattedStr("%d vertex shaders read, can't store any more, on line %d\n", m_nNumVertShaders, m_psContext->pnFileLineNumber[nLine]);
  433. return false;
  434. }
  435. if(ParseShader(nLine, nEndLine, pReturnError, m_psVertexShader[m_nNumVertShaders], "VERTEXSHADER"))
  436. m_nNumVertShaders++;
  437. else
  438. {
  439. FREE(m_psVertexShader[m_nNumVertShaders].pszName);
  440. FREE(m_psVertexShader[m_nNumVertShaders].pszGLSLfile);
  441. FREE(m_psVertexShader[m_nNumVertShaders].pszGLSLcode);
  442. FREE(m_psVertexShader[m_nNumVertShaders].pszGLSLBinaryFile);
  443. return false;
  444. }
  445. }
  446. else
  447. {
  448. *pReturnError = PVRTStringFromFormattedStr("Missing [/VERTEXSHADER] tag after [VERTEXSHADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
  449. return false;
  450. }
  451. nLine = nEndLine;
  452. }
  453. else if(strcmp("[FRAGMENTSHADER]", m_psContext->ppszEffectFile[nLine]) == 0)
  454. {
  455. if(GetEndTag("FRAGMENTSHADER", nLine, &nEndLine))
  456. {
  457. if(m_nNumFragShaders >= m_nMaxFragShaders)
  458. {
  459. *pReturnError = PVRTStringFromFormattedStr("%d fragment shaders read, can't store any more, on line %d\n", m_nNumFragShaders, m_psContext->pnFileLineNumber[nLine]);
  460. return false;
  461. }
  462. if(ParseShader(nLine, nEndLine, pReturnError, m_psFragmentShader[m_nNumFragShaders], "FRAGMENTSHADER"))
  463. m_nNumFragShaders++;
  464. else
  465. {
  466. FREE(m_psFragmentShader[m_nNumFragShaders].pszName);
  467. FREE(m_psFragmentShader[m_nNumFragShaders].pszGLSLfile);
  468. FREE(m_psFragmentShader[m_nNumFragShaders].pszGLSLcode);
  469. FREE(m_psFragmentShader[m_nNumFragShaders].pszGLSLBinaryFile);
  470. return false;
  471. }
  472. }
  473. else
  474. {
  475. *pReturnError = PVRTStringFromFormattedStr("Missing [/FRAGMENTSHADER] tag after [FRAGMENTSHADER] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
  476. return false;
  477. }
  478. nLine = nEndLine;
  479. }
  480. else if(strcmp("[EFFECT]", m_psContext->ppszEffectFile[nLine]) == 0)
  481. {
  482. if(GetEndTag("EFFECT", nLine, &nEndLine))
  483. {
  484. if(m_nNumEffects >= m_nMaxEffects)
  485. {
  486. *pReturnError = PVRTStringFromFormattedStr("%d effects read, can't store any more, on line %d\n", m_nNumEffects, m_psContext->pnFileLineNumber[nLine]);
  487. return false;
  488. }
  489. if(ParseEffect(m_psEffect[m_nNumEffects], nLine, nEndLine, pReturnError))
  490. m_nNumEffects++;
  491. else
  492. {
  493. for(i=0; i < m_psEffect[m_nNumEffects].nNumUniforms; ++i)
  494. {
  495. FREE(m_psEffect[m_nNumEffects].psUniform[i].pszName);
  496. FREE(m_psEffect[m_nNumEffects].psUniform[i].pszValue);
  497. }
  498. delete [] m_psEffect[m_nNumEffects].psUniform;
  499. for(i=0; i < m_psEffect[m_nNumEffects].nNumAttributes; ++i)
  500. {
  501. FREE(m_psEffect[m_nNumEffects].psAttribute[i].pszName);
  502. FREE(m_psEffect[m_nNumEffects].psAttribute[i].pszValue);
  503. }
  504. delete [] m_psEffect[m_nNumEffects].psAttribute;
  505. for(i=0; i < m_psEffect[m_nNumEffects].nNumTextures; ++i)
  506. {
  507. FREE(m_psEffect[m_nNumEffects].psTextures[i].pszName);
  508. }
  509. delete [] m_psEffect[m_nNumEffects].psTextures;
  510. FREE(m_psEffect[m_nNumEffects].pszFragmentShaderName);
  511. FREE(m_psEffect[m_nNumEffects].pszVertexShaderName);
  512. FREE(m_psEffect[m_nNumEffects].pszAnnotation);
  513. FREE(m_psEffect[m_nNumEffects].pszName);
  514. return false;
  515. }
  516. }
  517. else
  518. {
  519. *pReturnError = PVRTStringFromFormattedStr("Missing [/EFFECT] tag after [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nLine]);
  520. return false;
  521. }
  522. nLine = nEndLine;
  523. }
  524. else
  525. {
  526. *pReturnError = PVRTStringFromFormattedStr("'%s' unexpected on line %d\n", m_psContext->ppszEffectFile[nLine], m_psContext->pnFileLineNumber[nLine]);
  527. return false;
  528. }
  529. }
  530. if(m_nNumEffects < 1)
  531. {
  532. *pReturnError = CPVRTString("No [EFFECT] found. PFX file must have at least one defined.\n");
  533. return false;
  534. }
  535. if(m_nNumFragShaders < 1)
  536. {
  537. *pReturnError = CPVRTString("No [FRAGMENTSHADER] found. PFX file must have at least one defined.\n");;
  538. return false;
  539. }
  540. if(m_nNumVertShaders < 1)
  541. {
  542. *pReturnError = CPVRTString("No [VERTEXSHADER] found. PFX file must have at least one defined.\n");
  543. return false;
  544. }
  545. for(i = 0; i < m_nNumEffects; ++i)
  546. {
  547. for(j = 0; j < m_psEffect[i].nNumTextures; ++j)
  548. {
  549. for(k = 0; k < m_nNumTextures; ++k)
  550. {
  551. if(strcmp(m_psEffect[i].psTextures[j].pszName, m_psTexture[k].pszName) == 0)
  552. break;
  553. }
  554. if(!m_nNumTextures || k == m_nNumTextures)
  555. {
  556. *pReturnError = PVRTStringFromFormattedStr("Error: TEXTURE '%s' is not defined in [TEXTURES].\n", m_psEffect[i].psTextures[j].pszName);
  557. return false;
  558. }
  559. }
  560. }
  561. return true;
  562. }
  563. /*!***************************************************************************
  564. @Function ParseFromMemory
  565. @Input pszScript PFX script
  566. @Output pReturnError error string
  567. @Return EPVRTError PVR_SUCCESS for success parsing file
  568. PVR_FAIL if file doesn't exist or is invalid
  569. @Description Parses a PFX script from memory.
  570. *****************************************************************************/
  571. EPVRTError CPVRTPFXParser::ParseFromMemory(const char * const pszScript, CPVRTString * const pReturnError)
  572. {
  573. CPVRTPFXParserReadContext context;
  574. char pszLine[512];
  575. const char *pszEnd, *pszCurr;
  576. int nLineCounter;
  577. unsigned int nLen;
  578. unsigned int nReduce;
  579. bool bDone;
  580. if(!pszScript)
  581. return PVR_FAIL;
  582. m_psContext = &context;
  583. // Find & process each line
  584. nLineCounter = 0;
  585. bDone = false;
  586. pszCurr = pszScript;
  587. while(!bDone)
  588. {
  589. nLineCounter++;
  590. while(*pszCurr == '\r')
  591. ++pszCurr;
  592. // Find length of line
  593. pszEnd = strchr(pszCurr, '\n');
  594. if(pszEnd)
  595. {
  596. nLen = (unsigned int)(pszEnd - pszCurr);
  597. }
  598. else
  599. {
  600. nLen = (unsigned int)strlen(pszCurr);
  601. bDone = true;
  602. }
  603. nReduce = 0; // Tells how far to go back because of '\r'.
  604. while(nLen - nReduce > 0 && pszCurr[nLen - 1 - nReduce] == '\r')
  605. nReduce++;
  606. // Ensure pszLine will not be not overrun
  607. if(nLen+1-nReduce > sizeof(pszLine) / sizeof(*pszLine))
  608. nLen = sizeof(pszLine) / sizeof(*pszLine) - 1 + nReduce;
  609. // Copy line into pszLine
  610. strncpy(pszLine, pszCurr, nLen - nReduce);
  611. pszLine[nLen - nReduce] = 0;
  612. pszCurr += nLen + 1;
  613. _ASSERT(strchr(pszLine, '\r') == 0);
  614. _ASSERT(strchr(pszLine, '\n') == 0);
  615. // Ignore comments
  616. char *tmp = strstr(pszLine, "//");
  617. if(tmp != NULL) *tmp = '\0';
  618. // Reduce whitespace to one character.
  619. ReduceWhitespace(pszLine);
  620. // Store the line, even if blank lines (to get correct errors from GLSL compiler).
  621. if(m_psContext->nNumLines < m_psContext->nMaxLines)
  622. {
  623. m_psContext->pnFileLineNumber[m_psContext->nNumLines] = nLineCounter;
  624. m_psContext->ppszEffectFile[m_psContext->nNumLines] = (char *)malloc((strlen(pszLine) + 1) * sizeof(char));
  625. strcpy(m_psContext->ppszEffectFile[m_psContext->nNumLines], pszLine);
  626. m_psContext->nNumLines++;
  627. }
  628. else
  629. {
  630. *pReturnError = PVRTStringFromFormattedStr("Too many lines of text in file (maximum is %d)\n", m_psContext->nMaxLines);
  631. return PVR_FAIL;
  632. }
  633. }
  634. return Parse(pReturnError) ? PVR_SUCCESS : PVR_FAIL;
  635. }
  636. /*!***************************************************************************
  637. @Function ParseFromFile
  638. @Input pszFileName PFX file name
  639. @Output pReturnError error string
  640. @Return EPVRTError PVR_SUCCESS for success parsing file
  641. PVR_FAIL if file doesn't exist or is invalid
  642. @Description Reads the PFX file and calls the parser.
  643. *****************************************************************************/
  644. EPVRTError CPVRTPFXParser::ParseFromFile(const char * const pszFileName, CPVRTString * const pReturnError)
  645. {
  646. CPVRTResourceFile PfxFile(pszFileName);
  647. if (!PfxFile.IsOpen())
  648. {
  649. *pReturnError = CPVRTString("Unable to open file ") + pszFileName;
  650. return PVR_FAIL;
  651. }
  652. return ParseFromMemory(PfxFile.StringPtr(), pReturnError);
  653. }
  654. /*!***************************************************************************
  655. @Function SetViewportSize
  656. @Input uiWidth New viewport width
  657. @Input uiHeight New viewport height
  658. @Return bool True on success
  659. @Description Allows the current viewport size to be set. This value
  660. is used for calculating relative texture resolutions
  661. *****************************************************************************/
  662. bool CPVRTPFXParser::SetViewportSize(unsigned int uiWidth, unsigned int uiHeight)
  663. {
  664. if(uiWidth > 0 && uiHeight > 0)
  665. {
  666. m_uiViewportWidth = uiWidth;
  667. m_uiViewportHeight = uiHeight;
  668. return true;
  669. }
  670. else
  671. {
  672. return false;
  673. }
  674. }
  675. /*!***************************************************************************
  676. @Function GetEndTag
  677. @Input pszTagName tag name
  678. @Input nStartLine start line
  679. @Output pnEndLine line end tag found
  680. @Return true if tag found
  681. @Description Searches for end tag pszTagName from line nStartLine.
  682. Returns true and outputs the line number of the end tag if
  683. found, otherwise returning false.
  684. *****************************************************************************/
  685. bool CPVRTPFXParser::GetEndTag(const char *pszTagName, int nStartLine, int *pnEndLine)
  686. {
  687. char pszEndTag[100];
  688. strcpy(pszEndTag, "[/");
  689. strcat(pszEndTag, pszTagName);
  690. strcat(pszEndTag, "]");
  691. for(unsigned int i = nStartLine; i < m_psContext->nNumLines; i++)
  692. {
  693. if(strcmp(pszEndTag, m_psContext->ppszEffectFile[i]) == 0)
  694. {
  695. *pnEndLine = i;
  696. return true;
  697. }
  698. }
  699. return false;
  700. }
  701. /*!***************************************************************************
  702. @Function ReduceWhitespace
  703. @Output line output text
  704. @Input line input text
  705. @Description Reduces all white space characters in the string to one
  706. blank space.
  707. *****************************************************************************/
  708. void CPVRTPFXParser::ReduceWhitespace(char *line)
  709. {
  710. // convert tabs and newlines to ' '
  711. char *tmp = strpbrk (line, "\t\n");
  712. while(tmp != NULL)
  713. {
  714. *tmp = ' ';
  715. tmp = strpbrk (line, "\t\n");
  716. }
  717. // remove all whitespace at start
  718. while(line[0] == ' ')
  719. {
  720. // move chars along to omit whitespace
  721. int counter = 0;
  722. do{
  723. line[counter] = line[counter+1];
  724. counter++;
  725. }while(line[counter] != '\0');
  726. }
  727. // step through chars of line remove multiple whitespace
  728. for(int i=0; i < (int)strlen(line); i++)
  729. {
  730. // whitespace found
  731. if(line[i] == ' ')
  732. {
  733. // count number of whitespace chars
  734. int numWhiteChars = 0;
  735. while(line[i+1+numWhiteChars] == ' ')
  736. {
  737. numWhiteChars++;
  738. }
  739. // multiple whitespace chars found
  740. if(numWhiteChars>0)
  741. {
  742. // move chars along to omit whitespace
  743. int counter=1;
  744. while(line[i+counter] != '\0')
  745. {
  746. line[i+counter] = line[i+numWhiteChars+counter];
  747. counter++;
  748. }
  749. }
  750. }
  751. }
  752. // remove all whitespace from end
  753. while(line[strlen(line)-1] == ' ')
  754. {
  755. // move chars along to omit whitespace
  756. line[strlen(line)-1] = '\0';
  757. }
  758. }
  759. /*!***************************************************************************
  760. @Function FindParameter
  761. @Output
  762. @Input
  763. @Description Finds the parameter after the specified delimiting character and
  764. returns the parameter as a string. An empty string is returned
  765. if a parameter cannot be found
  766. *****************************************************************************/
  767. CPVRTString CPVRTPFXParser::FindParameter(char *aszSourceString, const CPVRTString &parameterTag, const CPVRTString &delimiter)
  768. {
  769. CPVRTString returnString("");
  770. char* aszTagStart = strstr(aszSourceString, parameterTag.c_str());
  771. // Tag was found, so search for parameter
  772. if(aszTagStart)
  773. {
  774. char* aszDelimiterStart = strstr(aszTagStart, delimiter.c_str());
  775. char* aszSpaceStart = strstr(aszTagStart, " ");
  776. // Delimiter found
  777. if(aszDelimiterStart && (!aszSpaceStart ||(aszDelimiterStart < aszSpaceStart)))
  778. {
  779. // Create a string from the delimiter to the next space
  780. size_t strCount(strcspn(aszDelimiterStart, " "));
  781. aszDelimiterStart++; // Skip =
  782. returnString.assign(aszDelimiterStart, strCount-1);
  783. }
  784. }
  785. return returnString;
  786. }
  787. /*!***************************************************************************
  788. @Function ProcessKeywordParam
  789. @Output
  790. @Input
  791. @Description Processes the node name or vector position parameters that
  792. can be assigned to render pass keywords (e.g. ENVMAPCUBE=(11.0, 22.0, 0.0))
  793. *****************************************************************************/
  794. bool CPVRTPFXParser::ProcessKeywordParam(const CPVRTString &parameterString)
  795. {
  796. if(parameterString.compare("") == 0)
  797. {
  798. return false;
  799. }
  800. if(parameterString[0] == '"')
  801. {
  802. CPVRTString stringParam("");
  803. /*
  804. Start from the character after the first ".
  805. Create a string from this point until the next occurance of
  806. a " character.
  807. */
  808. for(int keywordElement = 1; keywordElement < (int)parameterString.length(); ++keywordElement)
  809. {
  810. if(parameterString[keywordElement] == '"')
  811. {
  812. // Reached the " delimiter, so break out of the loop
  813. break;
  814. }
  815. stringParam.append(parameterString[keywordElement]);
  816. }
  817. // Assign the node name and set the vector to (0,0,0)
  818. delete[] m_psRenderPasses[m_nNumRenderPasses].pszNodeName;
  819. m_psRenderPasses[m_nNumRenderPasses].pszNodeName = new char[(stringParam.length()+1)];
  820. strcpy(m_psRenderPasses[m_nNumRenderPasses].pszNodeName, stringParam.c_str());
  821. m_psRenderPasses[m_nNumRenderPasses].vecPos = PVRTVec3(0,0,0);
  822. }else if(parameterString[0] == '(')
  823. {
  824. // Create a string for each vector element (x,y,z)
  825. CPVRTString vectorParamString[3];
  826. int vecId(0);
  827. for(int keywordElement = 1; vecId < 3, keywordElement < (int)parameterString.length(); ++keywordElement)
  828. {
  829. if(parameterString[keywordElement] == ',')
  830. {
  831. // Comma found, so increment vecId and continue to next char
  832. vecId++;
  833. continue;
  834. }
  835. else if(parameterString[keywordElement] == ')')
  836. {
  837. break;
  838. }
  839. // Append the current char to the current vector string
  840. vectorParamString[vecId].append(parameterString[keywordElement]);
  841. }
  842. // Store the retrieved values and set the node name to NULL
  843. m_psRenderPasses[m_nNumRenderPasses].vecPos = PVRTVec3( (float)atof(vectorParamString[0].c_str()),
  844. (float)atof(vectorParamString[1].c_str()),
  845. (float)atof(vectorParamString[2].c_str()));
  846. delete[] m_psRenderPasses[m_nNumRenderPasses].pszNodeName;
  847. m_psRenderPasses[m_nNumRenderPasses].pszNodeName = NULL;
  848. }
  849. else
  850. {
  851. // Unrecognised data has been supplied
  852. return false;
  853. }
  854. return true;
  855. }
  856. /*!***************************************************************************
  857. @Function ParseHeader
  858. @Input nStartLine start line number
  859. @Input nEndLine end line number
  860. @Output pReturnError error string
  861. @Return bool true if parse is successful
  862. @Description Parses the HEADER section of the PFX file.
  863. *****************************************************************************/
  864. bool CPVRTPFXParser::ParseHeader(int nStartLine, int nEndLine, CPVRTString * const pReturnError)
  865. {
  866. for(int i = nStartLine+1; i < nEndLine; i++)
  867. {
  868. // Skip blank lines
  869. if(!*m_psContext->ppszEffectFile[i])
  870. continue;
  871. char *str = strtok (m_psContext->ppszEffectFile[i]," ");
  872. if(str != NULL)
  873. {
  874. if(strcmp(str, "VERSION") == 0)
  875. {
  876. str += (strlen(str)+1);
  877. m_sHeader.pszVersion = (char *)malloc((strlen(str) + 1) * sizeof(char));
  878. strcpy(m_sHeader.pszVersion, str);
  879. }
  880. else if(strcmp(str, "DESCRIPTION") == 0)
  881. {
  882. str += (strlen(str)+1);
  883. m_sHeader.pszDescription = (char *)malloc((strlen(str) + 1) * sizeof(char));
  884. strcpy(m_sHeader.pszDescription, str);
  885. }
  886. else if(strcmp(str, "COPYRIGHT") == 0)
  887. {
  888. str += (strlen(str)+1);
  889. m_sHeader.pszCopyright = (char *)malloc((strlen(str) + 1) * sizeof(char));
  890. strcpy(m_sHeader.pszCopyright, str);
  891. }
  892. else
  893. {
  894. *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [HEADER] on line %d\n", str, m_psContext->pnFileLineNumber[i]);
  895. return false;
  896. }
  897. }
  898. else
  899. {
  900. *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [HEADER] on line %d : %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  901. return false;
  902. }
  903. }
  904. // initialise empty strings
  905. if(m_sHeader.pszVersion == NULL)
  906. {
  907. m_sHeader.pszVersion = (char *)malloc(sizeof(char));
  908. strcpy(m_sHeader.pszVersion, "");
  909. }
  910. if(m_sHeader.pszDescription == NULL)
  911. {
  912. m_sHeader.pszDescription = (char *)malloc(sizeof(char));
  913. strcpy(m_sHeader.pszDescription, "");
  914. }
  915. if(m_sHeader.pszCopyright == NULL)
  916. {
  917. m_sHeader.pszCopyright = (char *)malloc(sizeof(char));
  918. strcpy(m_sHeader.pszCopyright, "");
  919. }
  920. return true;
  921. }
  922. /*!***************************************************************************
  923. @Function ParseTextures
  924. @Input nStartLine start line number
  925. @Input nEndLine end line number
  926. @Output pReturnError error string
  927. @Return bool true if parse is successful
  928. @Description Parses the TEXTURE section of the PFX file.
  929. *****************************************************************************/
  930. bool CPVRTPFXParser::ParseTextures(int nStartLine, int nEndLine, CPVRTString * const pReturnError)
  931. {
  932. m_nNumTextures = 0;
  933. char *pszName(NULL), *pszFile(NULL), *pszKeyword(NULL);
  934. char *pszRemaining(NULL), *pszTemp(NULL);
  935. bool bReturnVal(false);
  936. for(int i = nStartLine+1; i < nEndLine; i++)
  937. {
  938. // Check there are available slots for textures
  939. if(m_nNumTextures >= m_nMaxTextures)
  940. {
  941. *pReturnError = PVRTStringFromFormattedStr("Too many textures in [TEXTURES] on line %d\n", m_psContext->pnFileLineNumber[i]);
  942. bReturnVal = false;
  943. goto release_return;
  944. }
  945. // Skip blank lines
  946. if(!*m_psContext->ppszEffectFile[i])
  947. continue;
  948. char *str = strtok (m_psContext->ppszEffectFile[i]," ");
  949. if(str != NULL)
  950. {
  951. bool bRenderToTexture(false);
  952. unsigned int nMin(0), nMag(0), nMIP(0);
  953. unsigned int nWrapS(0), nWrapT(0), nWrapR(0);
  954. unsigned int uiWidth(m_uiViewportWidth), uiHeight(m_uiViewportHeight); // NOTE: Change/remove default values?
  955. unsigned int uiFlags(0);
  956. // Reset variables
  957. FREE(pszName) pszName = NULL;
  958. FREE(pszFile) pszFile = NULL;
  959. FREE(pszKeyword) pszKeyword = NULL;
  960. FREE(pszTemp) pszTemp = NULL;
  961. pszRemaining = NULL;
  962. // Compare against all valid keywords
  963. if((strcmp(str, "FILE") != 0) && (strcmp(str, "RENDER") != 0))
  964. {
  965. *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d\n", str, m_psContext->pnFileLineNumber[i]);
  966. goto release_return;
  967. }
  968. if((strcmp(str, "FILE") == 0)||(strcmp(str, "RENDER") == 0))
  969. {
  970. pszKeyword = (char *)malloc( ((int)strlen(str)+1) * sizeof(char));
  971. strcpy(pszKeyword, str);
  972. str = strtok (NULL, " ");
  973. if(str != NULL)
  974. {
  975. pszName = (char *)malloc( ((int)strlen(str)+1) * sizeof(char));
  976. strcpy(pszName, str);
  977. delete[] m_psRenderPasses[m_nNumRenderPasses].pszSemanticName; // Delete existing string
  978. m_psRenderPasses[m_nNumRenderPasses].pszSemanticName = new char[strlen(str)+1];
  979. strcpy(m_psRenderPasses[m_nNumRenderPasses].pszSemanticName, str);
  980. }
  981. else
  982. {
  983. *pReturnError = PVRTStringFromFormattedStr("Texture name missing in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  984. goto release_return;
  985. }
  986. }
  987. /*
  988. The pszRemaining string is used to look for remaining flags.
  989. This has the advantage of allowing flags to be order independent
  990. and makes it easier to ommit some flags, but still pick up others
  991. (the previous method made it diffifult to retrieve filtering info
  992. if flags before it were missing)
  993. */
  994. pszRemaining = strtok(NULL, "\n");
  995. if(strcmp(pszKeyword, "FILE") == 0)
  996. {
  997. // NOTE: Bit wasteful, but works
  998. pszTemp = (char *)malloc( ((int)strlen(pszRemaining)+1) * sizeof(char));
  999. strcpy(pszTemp, pszRemaining);
  1000. str = strtok (pszTemp, " ");
  1001. if(str != NULL)
  1002. {
  1003. pszFile = (char *)malloc( ((int)strlen(str)+1) * sizeof(char));
  1004. strcpy(pszFile, str);
  1005. }
  1006. else
  1007. {
  1008. *pReturnError = PVRTStringFromFormattedStr("Texture name missing in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  1009. bReturnVal = false;
  1010. goto release_return;
  1011. }
  1012. }
  1013. else if(strcmp(pszKeyword, "RENDER") == 0)
  1014. {
  1015. /*
  1016. TODO: When the parameters of the render texture have changed (such as format or resolution)
  1017. the previous texture should be destroyed and replaced with the updated one.
  1018. The texture ID may also need updating after this.
  1019. */
  1020. // Use shader name instead of file name, as a file name doesn't exist
  1021. pszFile = (char *)malloc( ((int)strlen(pszName)+1) * sizeof(char));
  1022. strcpy(pszFile, pszName);
  1023. bRenderToTexture = true;
  1024. // Check for known keywords
  1025. // TODO: Replace this with code that uses '=' delimiter (more flexible)
  1026. if(strstr(pszRemaining, "ENVMAPCUBE") != NULL)
  1027. {
  1028. if(!ProcessKeywordParam(FindParameter(pszRemaining, "ENVMAPCUBE", "=")))
  1029. {
  1030. *pReturnError = PVRTStringFromFormattedStr("Unknown or incorrectly formatted parameter has been given after ENVMAPCUBE on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  1031. bReturnVal = false;
  1032. goto release_return;
  1033. }
  1034. // The resolution applied here can be overridden by specifying a RES tag later
  1035. uiWidth = GetPOTLower(m_uiViewportWidth, 1);
  1036. uiHeight = GetPOTLower(m_uiViewportHeight, 1);
  1037. uiFlags &= PVRTEX_CUBEMAP;
  1038. m_psRenderPasses[m_nNumRenderPasses].eRenderPassType = eCubeMapRender;
  1039. }
  1040. else if(strstr(pszRemaining, "ENVMAPSPH") != NULL)
  1041. {
  1042. if(!ProcessKeywordParam(FindParameter(pszRemaining, "ENVMAPSPH", "=")))
  1043. {
  1044. *pReturnError = PVRTStringFromFormattedStr("Unknown or incorrectly formatted parameter has been given after ENVMAPSPH on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  1045. bReturnVal = false;
  1046. goto release_return;
  1047. }
  1048. // The resolution applied here can be overridden by specifying a RES tag later
  1049. uiWidth = GetPOTLower(m_uiViewportWidth, 1);
  1050. uiHeight = GetPOTLower(m_uiViewportHeight, 1);
  1051. m_psRenderPasses[m_nNumRenderPasses].eRenderPassType = eSphMapRender;
  1052. }
  1053. else if(strstr(pszRemaining, "CAMERA") != NULL)
  1054. {
  1055. CPVRTString paramString(FindParameter(pszRemaining, "CAMERA", "="));
  1056. if(paramString.compare("") != 0)
  1057. {
  1058. if(!ProcessKeywordParam(paramString))
  1059. {
  1060. *pReturnError = PVRTStringFromFormattedStr("Unknown or incorrectly formatted parameter has been given after CAMERA on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  1061. bReturnVal = false;
  1062. goto release_return;
  1063. }
  1064. }
  1065. uiWidth = m_uiViewportWidth;
  1066. uiHeight = m_uiViewportHeight;
  1067. m_psRenderPasses[m_nNumRenderPasses].eRenderPassType = eCameraRender;
  1068. }
  1069. else if(strstr(pszRemaining, "POSTPROCESS") != NULL)
  1070. {
  1071. /*
  1072. POSTPROCESS does not accept parameters as the camera position
  1073. should not be configurable
  1074. */
  1075. uiWidth = m_uiViewportWidth;
  1076. uiHeight = m_uiViewportHeight;
  1077. m_psRenderPasses[m_nNumRenderPasses].eRenderPassType = ePostProcessRender;
  1078. }
  1079. else
  1080. {
  1081. // Default
  1082. *pReturnError = PVRTStringFromFormattedStr("An unknown render pass type was specified on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  1083. bReturnVal = false;
  1084. goto release_return;
  1085. }
  1086. m_psRenderPasses[m_nNumRenderPasses].i32TextureNumber = m_nNumTextures;
  1087. // ----------------Texture format----------------//
  1088. // Find a FORMAT tag and isolate it's parameter string
  1089. CPVRTString formatParamString = FindParameter(pszRemaining, "FORMAT", "=");
  1090. // Check that a parameter string was found
  1091. if(formatParamString.compare("") != 0)
  1092. {
  1093. // NOTE: Add more formats as required
  1094. if(formatParamString.compare("RGBA_4444") == 0)
  1095. {
  1096. m_psRenderPasses[m_nNumRenderPasses].eFormat = OGL_RGBA_4444;
  1097. }
  1098. else if(formatParamString.compare("RGBA_5551") == 0)
  1099. {
  1100. m_psRenderPasses[m_nNumRenderPasses].eFormat = OGL_RGBA_5551;
  1101. }
  1102. else if(formatParamString.compare("RGBA_8888") == 0)
  1103. {
  1104. m_psRenderPasses[m_nNumRenderPasses].eFormat = OGL_RGBA_8888;
  1105. }
  1106. else if(formatParamString.compare("RGB_565") == 0)
  1107. {
  1108. m_psRenderPasses[m_nNumRenderPasses].eFormat = OGL_RGB_565;
  1109. }
  1110. else if(formatParamString.compare("RGB_555") == 0)
  1111. {
  1112. m_psRenderPasses[m_nNumRenderPasses].eFormat = OGL_RGB_555;
  1113. }
  1114. else if(formatParamString.compare("RGB_888") == 0)
  1115. {
  1116. m_psRenderPasses[m_nNumRenderPasses].eFormat = OGL_RGB_888;
  1117. }
  1118. else if(formatParamString.compare("I_8") == 0)
  1119. {
  1120. m_psRenderPasses[m_nNumRenderPasses].eFormat = OGL_I_8;
  1121. }
  1122. else if(formatParamString.compare("AI_88") == 0)
  1123. {
  1124. m_psRenderPasses[m_nNumRenderPasses].eFormat = OGL_AI_88;
  1125. }
  1126. else
  1127. {
  1128. *pReturnError = PVRTStringFromFormattedStr("Unrecognised texture format on line %d\n", m_psContext->pnFileLineNumber[i]);
  1129. bReturnVal = false;
  1130. goto release_return;
  1131. }
  1132. uiFlags &= m_psRenderPasses[m_nNumRenderPasses].eFormat;
  1133. }
  1134. // ----------------Texture resolution----------------//
  1135. // Search for the RES tag
  1136. CPVRTString resParamString = FindParameter(pszRemaining, "RES", "=");
  1137. // First, check for known keywords
  1138. if(resParamString.compare("") != 0)
  1139. {
  1140. if(strstr(resParamString.c_str(), "SCREENRES") != NULL)
  1141. {
  1142. uiWidth = m_uiViewportWidth;
  1143. uiHeight = m_uiViewportHeight;
  1144. }
  1145. else if(strstr(resParamString.c_str(), "SRESPOTLSQ") != NULL)
  1146. {
  1147. int i32StringLength((int)strlen("SRESPOTLSQ"));
  1148. // String is larger than param name, so extra info is appended
  1149. if((int)resParamString.length() > i32StringLength)
  1150. {
  1151. // Get a pointer to the start of the exponent
  1152. char *aszExponent = &resParamString[i32StringLength];
  1153. int i32Exponent = atoi(aszExponent);
  1154. uiWidth = GetPOTLower(m_uiViewportWidth, i32Exponent);
  1155. uiHeight = GetPOTLower(m_uiViewportHeight, i32Exponent);
  1156. }
  1157. else
  1158. {
  1159. // Set to next lowest POT res
  1160. uiWidth = GetPOTLower(m_uiViewportWidth, 0);
  1161. uiHeight = GetPOTLower(m_uiViewportHeight, 0);
  1162. }
  1163. /*
  1164. Make a square POT texture.
  1165. After calculating the POT of the width and height,
  1166. use the lowest value
  1167. */
  1168. if(uiWidth > uiHeight)
  1169. {
  1170. uiWidth = uiHeight;
  1171. }
  1172. else
  1173. {
  1174. uiHeight = uiWidth;
  1175. }
  1176. }
  1177. else if(strstr(resParamString.c_str(), "SRESPOTHSQ") != NULL)
  1178. {
  1179. int i32StringLength((int)strlen("SRESPOTHSQ"));
  1180. // String is larger than param name, so extra info is appended
  1181. if((int)resParamString.length() > i32StringLength)
  1182. {
  1183. // Get a pointer to the start of the exponent
  1184. char *aszExponent = &resParamString[i32StringLength];
  1185. int i32Exponent = atoi(aszExponent);
  1186. uiWidth = GetPOTHigher(m_uiViewportWidth, i32Exponent);
  1187. uiHeight = GetPOTHigher(m_uiViewportHeight, i32Exponent);
  1188. }
  1189. else
  1190. {
  1191. // Set to next higher POT res
  1192. uiWidth = GetPOTHigher(m_uiViewportWidth, 0);
  1193. uiHeight = GetPOTHigher(m_uiViewportHeight, 0);
  1194. }
  1195. /*
  1196. Make a square POT texture.
  1197. After calculating the POT of the width and height,
  1198. use the lowest value
  1199. */
  1200. if(uiWidth > uiHeight)
  1201. {
  1202. uiWidth = uiHeight;
  1203. }
  1204. else
  1205. {
  1206. uiHeight = uiWidth;
  1207. }
  1208. }
  1209. else if(strstr(resParamString.c_str(), "SRESPOTL") != NULL)
  1210. {
  1211. int i32StringLength((int)strlen("SRESPOTL"));
  1212. // String is larger than param name, so extra info is appended
  1213. if((int)resParamString.length() > i32StringLength)
  1214. {
  1215. // Get a pointer to the start of the exponent
  1216. char *aszExponent = &resParamString[i32StringLength];
  1217. int i32Exponent = atoi(aszExponent);
  1218. uiWidth = GetPOTLower(m_uiViewportWidth, i32Exponent);
  1219. uiHeight = GetPOTLower(m_uiViewportHeight, i32Exponent);
  1220. }
  1221. else
  1222. {
  1223. // Set to next lowest POT res
  1224. uiWidth = GetPOTLower(m_uiViewportWidth, 0);
  1225. uiHeight = GetPOTLower(m_uiViewportHeight, 0);
  1226. }
  1227. }
  1228. else if(strstr(resParamString.c_str(), "SRESPOTH") != NULL)
  1229. {
  1230. int i32StringLength((int)strlen("SRESPOTH"));
  1231. // String is larger than param name, so extra info is appended
  1232. if((int)resParamString.length() > i32StringLength)
  1233. {
  1234. // Get a pointer to the start of the exponent
  1235. char *aszExponent = &resParamString[i32StringLength];
  1236. int i32Exponent = atoi(aszExponent);
  1237. uiWidth = GetPOTHigher(m_uiViewportWidth, i32Exponent);
  1238. uiHeight = GetPOTHigher(m_uiViewportHeight, i32Exponent);
  1239. }
  1240. else
  1241. {
  1242. // Set to next higher POT res
  1243. uiWidth = GetPOTHigher(m_uiViewportWidth, 0);
  1244. uiHeight = GetPOTHigher(m_uiViewportHeight, 0);
  1245. }
  1246. }
  1247. else if(strstr(resParamString.c_str(), "SRESL") != NULL)
  1248. {
  1249. int i32StringLength((int)strlen("SRESL"));
  1250. int i32Divider(2);
  1251. // String is longer than param name, so divider has been provided
  1252. if((int)resParamString.length() > i32StringLength)
  1253. {
  1254. // Get a pointer to the start of the divider
  1255. char *aszDivider = &resParamString[i32StringLength];
  1256. i32Divider = atoi(aszDivider);
  1257. if(i32Divider <= 0)
  1258. {
  1259. *pReturnError = PVRTStringFromFormattedStr("Invalid divide value has been provided after SRESL on line %d\n", m_psContext->pnFileLineNumber[i]);
  1260. bReturnVal = false;
  1261. goto release_return;
  1262. }
  1263. }
  1264. uiWidth = m_uiViewportWidth / i32Divider;
  1265. uiHeight = m_uiViewportHeight / i32Divider;
  1266. }
  1267. else if(strstr(resParamString.c_str(), "SRESH") != NULL)
  1268. {
  1269. int i32StringLength((int)strlen("SRESH"));
  1270. int i32Multiplier(2);
  1271. // String is longer than param name, so divider has been provided
  1272. if((int)resParamString.length() > i32StringLength)
  1273. {
  1274. // Get a pointer to the start of the divider
  1275. char *aszMultiplier = &resParamString[i32StringLength];
  1276. i32Multiplier = atoi(aszMultiplier);
  1277. if(i32Multiplier <= 0)
  1278. {
  1279. *pReturnError = PVRTStringFromFormattedStr("Invalid multiply value has been provided after SRESH on line %d\n", m_psContext->pnFileLineNumber[i]);
  1280. bReturnVal = false;
  1281. goto release_return;
  1282. }
  1283. }
  1284. uiWidth = m_uiViewportWidth * i32Multiplier;
  1285. uiHeight = m_uiViewportHeight * i32Multiplier;
  1286. }
  1287. // Secondly, check for explicit resolutions
  1288. else if(strstr(resParamString.c_str(), "x") != NULL)
  1289. {
  1290. #if 0 // TODO: Fix this!
  1291. const char *pszXInString = strstr(resParamString.c_str(), "x");
  1292. int iWidthStrLength(resParamString.length() - strlen(pszXInString));
  1293. char *aszWidth = (char *)malloc( (iWidthStrLength+1) * sizeof(char));
  1294. strncpy(aszWidth, resParamString.c_str(), iWidthStrLength);
  1295. int iWidth(atoi(aszWidth));
  1296. FREE(aszWidth);
  1297. int iHeight(atoi(&resParamString[(int)strlen(pszXInString)+1]));
  1298. if(iWidth <= 0 || iHeight <= 0)
  1299. {
  1300. *pReturnError = PVRTStringFromFormattedStr("Invalid explicit resolution specified on line %d\n", m_psContext->pnFileLineNumber[i]);
  1301. bReturnVal = false;
  1302. goto release_return;
  1303. }
  1304. else
  1305. {
  1306. uiWidth = (unsigned int)iWidth;
  1307. uiHeight = (unsigned int)iHeight;
  1308. }
  1309. #endif
  1310. }
  1311. else
  1312. {
  1313. *pReturnError = PVRTStringFromFormattedStr("Unrecognised resolution on line %d\n", m_psContext->pnFileLineNumber[i]);
  1314. bReturnVal = false;
  1315. goto release_return;
  1316. }
  1317. }
  1318. }
  1319. if((strcmp(pszKeyword, "RENDER") == 0) || strcmp(pszKeyword, "FILE") == 0)
  1320. {
  1321. // ----------------Texture filtering----------------//
  1322. /*
  1323. Search the remaining string for the first occurance of LINEAR & NEAREST.
  1324. Make sure the pointer points to the first filtering parameter
  1325. */
  1326. char *pszLinear = strstr(pszRemaining, "LINEAR-");
  1327. char *pszNearest = strstr(pszRemaining, "NEAREST-");
  1328. char *pszFilterParams(NULL);
  1329. bool bLinearFirst(false);
  1330. if(pszNearest || pszLinear)
  1331. {
  1332. if((pszLinear < pszNearest) || !pszNearest)
  1333. {
  1334. pszFilterParams = pszLinear;
  1335. bLinearFirst = true;
  1336. }
  1337. else
  1338. {
  1339. pszFilterParams = pszNearest;
  1340. bLinearFirst = false;
  1341. }
  1342. }
  1343. if(bLinearFirst)
  1344. {
  1345. // LINEAR was found. Set minification value accordingly
  1346. nMin = 1;
  1347. }
  1348. else
  1349. {
  1350. nMin = 0; // Default to NEAREST
  1351. // Search for NEAREST to find the start of filtering flags
  1352. pszFilterParams = strstr(pszRemaining, "NEAREST-");
  1353. }
  1354. // Either LINEAR or NEAREST has been found in the remaining string
  1355. if(pszFilterParams)
  1356. {
  1357. // Move to the next param
  1358. pszFilterParams = strchr(pszFilterParams, '-');
  1359. if(pszFilterParams) pszFilterParams++;
  1360. if(pszFilterParams)
  1361. {
  1362. CPVRTString linearString("LINEAR-");
  1363. if(strncmp(pszFilterParams, linearString.c_str(), linearString.length()) == 0)
  1364. {
  1365. // LINEAR was found at the start of the string
  1366. nMag = 1;
  1367. }
  1368. else
  1369. {
  1370. nMag = 0;
  1371. }
  1372. // Move to the last param
  1373. pszFilterParams = strchr(pszFilterParams, '-');
  1374. if(pszFilterParams) pszFilterParams++;
  1375. if(pszFilterParams)
  1376. {
  1377. linearString = CPVRTString("LINEAR");
  1378. CPVRTString nearestString("NEAREST");
  1379. if(strncmp(pszFilterParams, linearString.c_str(), linearString.length()) == 0)
  1380. {
  1381. // LINEAR was found at the start of the string
  1382. nMIP = 2;
  1383. }
  1384. else if(strncmp(pszFilterParams, nearestString.c_str(), nearestString.length()) == 0)
  1385. {
  1386. nMIP = 1;
  1387. }
  1388. else
  1389. {
  1390. nMIP = 0;
  1391. }
  1392. }
  1393. }
  1394. }
  1395. // ----------------Wrap mode----------------//
  1396. nWrapS = 1;
  1397. nWrapT = 1;
  1398. nWrapR = 1;
  1399. /*
  1400. Search the remaining string for the first occurance of CLAMP & REPEAT.
  1401. Make sure the pointer points to the first wrap parameter
  1402. */
  1403. char *pszClamp = strstr(pszRemaining, "CLAMP-");
  1404. char *pszRepeat = strstr(pszRemaining, "REPEAT-");
  1405. char *pszWrapParams(NULL);
  1406. bool bClampFirst(false);
  1407. if(pszClamp || pszRepeat)
  1408. {
  1409. if((pszClamp < pszRepeat) || !pszRepeat)
  1410. {
  1411. // CLAMP occurs first
  1412. pszWrapParams = pszClamp;
  1413. bClampFirst = true;
  1414. }
  1415. else
  1416. {
  1417. // REPEAT occurs first
  1418. pszWrapParams = pszRepeat;
  1419. bClampFirst = false;
  1420. }
  1421. }
  1422. if(bClampFirst)
  1423. {
  1424. // CLAMP was found. Set minification value accordingly
  1425. nWrapS = 0;
  1426. }
  1427. else
  1428. {
  1429. nWrapS = 1; // Default to REPEAT
  1430. // Search for NEAREST to find the start of filtering flags
  1431. //pszWrapParams = strstr(pszRemaining, "REPEAT-");
  1432. }
  1433. if(pszWrapParams)
  1434. {
  1435. // Move to the next param
  1436. pszWrapParams = strchr(pszWrapParams, '-');
  1437. if(pszWrapParams) pszWrapParams++;
  1438. if(pszWrapParams)
  1439. {
  1440. CPVRTString clampString("CLAMP");
  1441. if(strncmp(pszWrapParams, clampString.c_str(), clampString.length()) == 0)
  1442. {
  1443. nWrapT = 0;
  1444. }
  1445. else
  1446. {
  1447. nWrapT = 1; // Default to REPEAT
  1448. }
  1449. // Move to the last param
  1450. pszWrapParams = strchr(pszWrapParams, '-');
  1451. if(pszWrapParams) pszWrapParams++;
  1452. if(pszWrapParams)
  1453. {
  1454. if(strncmp(pszWrapParams, clampString.c_str(), clampString.length()) == 0)
  1455. {
  1456. nWrapR = 0;
  1457. }
  1458. else
  1459. {
  1460. nWrapR = 1; // Default to REPEAT
  1461. }
  1462. }
  1463. }
  1464. }
  1465. FREE(m_psTexture[m_nNumTextures].pszName);
  1466. m_psTexture[m_nNumTextures].pszName = (char *)malloc( ((int)strlen(pszName)+1) * sizeof(char));
  1467. strcpy(m_psTexture[m_nNumTextures].pszName, pszName);
  1468. FREE(pszName);
  1469. FREE(m_psTexture[m_nNumTextures].pszFile);
  1470. m_psTexture[m_nNumTextures].pszFile = (char *)malloc( ((int)strlen(pszFile)+1) * sizeof(char));
  1471. strcpy(m_psTexture[m_nNumTextures].pszFile, pszFile);
  1472. FREE(pszFile);
  1473. m_psTexture[m_nNumTextures].bRenderToTexture = bRenderToTexture;
  1474. m_psTexture[m_nNumTextures].nMin = nMin;
  1475. m_psTexture[m_nNumTextures].nMag = nMag;
  1476. m_psTexture[m_nNumTextures].nMIP = nMIP;
  1477. m_psTexture[m_nNumTextures].nWrapS = nWrapS;
  1478. m_psTexture[m_nNumTextures].nWrapT = nWrapT;
  1479. m_psTexture[m_nNumTextures].nWrapR = nWrapR;
  1480. m_psTexture[m_nNumTextures].uiWidth = uiWidth;
  1481. m_psTexture[m_nNumTextures].uiHeight = uiHeight;
  1482. m_psTexture[m_nNumTextures].uiFlags = uiFlags;
  1483. ++m_nNumTextures;
  1484. if(strcmp(pszKeyword, "RENDER") == 0)
  1485. {
  1486. if(m_nNumRenderPasses < m_nMaxRenders)
  1487. {
  1488. m_nNumRenderPasses++;
  1489. }
  1490. else
  1491. {
  1492. *pReturnError = PVRTStringFromFormattedStr("Max number of render passes has been reached on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  1493. bReturnVal = false;
  1494. goto release_return;
  1495. }
  1496. }
  1497. }
  1498. else
  1499. {
  1500. *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [TEXTURES] on line %d\n", str, m_psContext->pnFileLineNumber[i]);;
  1501. bReturnVal = false;
  1502. goto release_return;
  1503. }
  1504. #if 0 // TODO: Test is broken. Should parser flag a problem when unessesary info has been provided?
  1505. str = strtok (NULL, " ");
  1506. if(str != NULL)
  1507. {
  1508. *pReturnError = PVRTStringFromFormattedStr("unexpected data in [TEXTURES] on line %d: '%s'\n", m_psContext->pnFileLineNumber[i], str);;
  1509. bReturnVal = false;
  1510. goto release_return;
  1511. }
  1512. #endif
  1513. }
  1514. else
  1515. {
  1516. *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [TEXTURES] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  1517. bReturnVal = false;
  1518. goto release_return;
  1519. //return false;
  1520. }
  1521. }
  1522. /*
  1523. Should only reach here if there have been no issues
  1524. */
  1525. bReturnVal = true;
  1526. // get true case to lead to error return free calls
  1527. //FREE(pszKeyword);
  1528. //return true;
  1529. release_return:
  1530. FREE(pszKeyword);
  1531. FREE(pszName);
  1532. FREE(pszFile);
  1533. //FREE(pszRemaining); // NOTE: Only a pointer - no need to free it!
  1534. FREE(pszTemp);
  1535. return bReturnVal;
  1536. }
  1537. /*!***************************************************************************
  1538. @Function ParseShader
  1539. @Input nStartLine start line number
  1540. @Input nEndLine end line number
  1541. @Output pReturnError error string
  1542. @Output shader shader data object
  1543. @Input pszBlockName name of block in PFX file
  1544. @Return bool true if parse is successful
  1545. @Description Parses the VERTEXSHADER or FRAGMENTSHADER section of the
  1546. PFX file.
  1547. *****************************************************************************/
  1548. bool CPVRTPFXParser::ParseShader(int nStartLine, int nEndLine, CPVRTString * const pReturnError, SPVRTPFXParserShader &shader, const char * const pszBlockName)
  1549. {
  1550. bool glslcode=0, glslfile=0, bName=0;
  1551. shader.pszName = NULL;
  1552. shader.bUseFileName = false;
  1553. shader.pszGLSLfile = NULL;
  1554. shader.pszGLSLcode = NULL;
  1555. shader.pszGLSLBinaryFile= NULL;
  1556. shader.pbGLSLBinary = NULL;
  1557. shader.nFirstLineNumber = 0;
  1558. for(int i = nStartLine+1; i < nEndLine; i++)
  1559. {
  1560. // Skip blank lines
  1561. if(!*m_psContext->ppszEffectFile[i])
  1562. continue;
  1563. char *str = strtok (m_psContext->ppszEffectFile[i]," ");
  1564. if(str != NULL)
  1565. {
  1566. // Check for [GLSL_CODE] tags first and remove those lines from loop.
  1567. if(strcmp(str, "[GLSL_CODE]") == 0)
  1568. {
  1569. if(glslcode)
  1570. {
  1571. *pReturnError = PVRTStringFromFormattedStr("[GLSL_CODE] redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
  1572. return false;
  1573. }
  1574. if(glslfile && shader.pbGLSLBinary==NULL )
  1575. {
  1576. *pReturnError = PVRTStringFromFormattedStr("[GLSL_CODE] not allowed with FILE in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
  1577. return false;
  1578. }
  1579. shader.nFirstLineNumber = m_psContext->pnFileLineNumber[i];
  1580. // Skip the block-start
  1581. i++;
  1582. if(!ConcatenateLinesUntil(
  1583. shader.pszGLSLcode,
  1584. i,
  1585. m_psContext->ppszEffectFile,
  1586. m_psContext->nNumLines,
  1587. "[/GLSL_CODE]"))
  1588. {
  1589. return false;
  1590. }
  1591. shader.bUseFileName = false;
  1592. glslcode = 1;
  1593. }
  1594. else if(strcmp(str, "NAME") == 0)
  1595. {
  1596. if(bName)
  1597. {
  1598. *pReturnError = PVRTStringFromFormattedStr("NAME redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
  1599. return false;
  1600. }
  1601. str = strtok (NULL, " ");
  1602. if(str == NULL)
  1603. {
  1604. *pReturnError = PVRTStringFromFormattedStr("NAME missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
  1605. return false;
  1606. }
  1607. shader.pszName = (char*)malloc((strlen(str)+1) * sizeof(char));
  1608. strcpy(shader.pszName, str);
  1609. bName = true;
  1610. }
  1611. else if(strcmp(str, "FILE") == 0)
  1612. {
  1613. if(glslfile)
  1614. {
  1615. *pReturnError = PVRTStringFromFormattedStr("FILE redefined in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
  1616. return false;
  1617. }
  1618. if(glslcode)
  1619. {
  1620. *pReturnError = PVRTStringFromFormattedStr("FILE not allowed with [GLSL_CODE] in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
  1621. return false;
  1622. }
  1623. str = strtok (NULL, " ");
  1624. if(str == NULL)
  1625. {
  1626. *pReturnError = PVRTStringFromFormattedStr("FILE missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
  1627. return false;
  1628. }
  1629. shader.pszGLSLfile = (char*)malloc((strlen(str)+1) * sizeof(char));
  1630. strcpy(shader.pszGLSLfile, str);
  1631. CPVRTResourceFile GLSLFile(str);
  1632. if(!GLSLFile.IsOpen())
  1633. {
  1634. *pReturnError = PVRTStringFromFormattedStr("Error loading file '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]);
  1635. return false;
  1636. }
  1637. shader.pszGLSLcode = new char[GLSLFile.Size() + 1];
  1638. strcpy(shader.pszGLSLcode, GLSLFile.StringPtr());
  1639. shader.bUseFileName = true;
  1640. glslfile = 1;
  1641. }
  1642. else if(strcmp(str, "BINARYFILE") == 0)
  1643. {
  1644. str = strtok (NULL, " ");
  1645. if(str == NULL)
  1646. {
  1647. *pReturnError = PVRTStringFromFormattedStr("BINARYFILE missing value in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[i]);
  1648. return false;
  1649. }
  1650. shader.pszGLSLBinaryFile = (char*)malloc((strlen(str)+1) * sizeof(char));
  1651. strcpy(shader.pszGLSLBinaryFile, str);
  1652. CPVRTResourceFile GLSLFile(str);
  1653. if(!GLSLFile.IsOpen())
  1654. {
  1655. *pReturnError = PVRTStringFromFormattedStr("Error loading file '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]);
  1656. return false;
  1657. }
  1658. shader.pbGLSLBinary = new char[GLSLFile.Size() + 1];
  1659. shader.nGLSLBinarySize = (unsigned int)GLSLFile.Size();
  1660. memcpy(shader.pbGLSLBinary, GLSLFile.StringPtr(), GLSLFile.Size());
  1661. shader.bUseFileName = true;
  1662. glslfile = 1;
  1663. }
  1664. else
  1665. {
  1666. *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [%s] on line %d\n", str, pszBlockName, m_psContext->pnFileLineNumber[i]);
  1667. return false;
  1668. }
  1669. str = strtok (NULL, " ");
  1670. if(str != NULL)
  1671. {
  1672. *pReturnError = PVRTStringFromFormattedStr("Unexpected data in [%s] on line %d: '%s'\n", pszBlockName, m_psContext->pnFileLineNumber[i], str);
  1673. return false;
  1674. }
  1675. }
  1676. else
  1677. {
  1678. *pReturnError = PVRTStringFromFormattedStr("Missing arguments in [%s] on line %d: %s\n", pszBlockName, m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  1679. return false;
  1680. }
  1681. }
  1682. if(!bName)
  1683. {
  1684. *pReturnError = PVRTStringFromFormattedStr("NAME not found in [%s] on line %d.\n", pszBlockName, m_psContext->pnFileLineNumber[nStartLine]);
  1685. return false;
  1686. }
  1687. if(!glslfile && !glslcode)
  1688. {
  1689. *pReturnError = PVRTStringFromFormattedStr("No Shader File or Shader Code specified in [%s] on line %d\n", pszBlockName, m_psContext->pnFileLineNumber[nStartLine]);
  1690. return false;
  1691. }
  1692. return true;
  1693. }
  1694. /*!***************************************************************************
  1695. @Function ParseSemantic
  1696. @Output semantic semantic data object
  1697. @Input nStartLine start line number
  1698. @Output pReturnError error string
  1699. @Return bool true if parse is successful
  1700. @Description Parses a semantic.
  1701. *****************************************************************************/
  1702. bool CPVRTPFXParser::ParseSemantic(SPVRTPFXParserSemantic &semantic, const int nStartLine, CPVRTString * const pReturnError)
  1703. {
  1704. char *str;
  1705. semantic.pszName = 0;
  1706. semantic.pszValue = 0;
  1707. semantic.sDefaultValue.eType = eDataTypeNone;
  1708. semantic.nIdx = 0;
  1709. str = strtok (NULL, " ");
  1710. if(str == NULL)
  1711. {
  1712. *pReturnError = PVRTStringFromFormattedStr("UNIFORM missing name in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
  1713. return false;
  1714. }
  1715. semantic.pszName = (char*)malloc((strlen(str)+1) * sizeof(char));
  1716. strcpy(semantic.pszName, str);
  1717. str = strtok (NULL, " ");
  1718. if(str == NULL)
  1719. {
  1720. *pReturnError = PVRTStringFromFormattedStr("UNIFORM missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
  1721. FREE(semantic.pszName);
  1722. return false;
  1723. }
  1724. /*
  1725. If the final digits of the semantic are a number they are
  1726. stripped off and used as the index, with the remainder
  1727. used as the semantic.
  1728. */
  1729. {
  1730. size_t idx, len;
  1731. len = strlen(str);
  1732. idx = len;
  1733. while(idx)
  1734. {
  1735. --idx;
  1736. if(strcspn(&str[idx], "0123456789") != 0)
  1737. {
  1738. break;
  1739. }
  1740. }
  1741. if(idx == 0)
  1742. {
  1743. *pReturnError = PVRTStringFromFormattedStr("Semantic contains only numbers in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
  1744. FREE(semantic.pszName);
  1745. return false;
  1746. }
  1747. ++idx;
  1748. // Store the semantic index
  1749. if(len == idx)
  1750. {
  1751. semantic.nIdx = 0;
  1752. }
  1753. else
  1754. {
  1755. semantic.nIdx = atoi(&str[idx]);
  1756. }
  1757. // Chop off the index from the string containing the semantic
  1758. str[idx] = 0;
  1759. }
  1760. // Store a copy of the semantic name
  1761. semantic.pszValue = (char*)malloc((strlen(str)+1) * sizeof(char));
  1762. strcpy(semantic.pszValue, str);
  1763. /*
  1764. Optional default semantic value
  1765. */
  1766. char pszString[2048];
  1767. strcpy(pszString,"");
  1768. str = strtok (NULL, " ");
  1769. if(str != NULL)
  1770. {
  1771. // Get all ramainning arguments
  1772. while(str != NULL)
  1773. {
  1774. strcat(pszString, str);
  1775. strcat(pszString, " ");
  1776. str = strtok (NULL, " ");
  1777. }
  1778. // default value
  1779. int i;
  1780. for(i = 0; i < eNumDefaultDataTypes; i++)
  1781. {
  1782. if(strncmp(pszString, c_psSemanticDefaultDataTypeInfo[i].pszName, strlen(c_psSemanticDefaultDataTypeInfo[i].pszName)) == 0)
  1783. {
  1784. if(!GetSemanticDataFromString( &semantic.sDefaultValue,
  1785. &pszString[strlen(c_psSemanticDefaultDataTypeInfo[i].pszName)],
  1786. c_psSemanticDefaultDataTypeInfo[i].eType,
  1787. pReturnError
  1788. ))
  1789. {
  1790. *pReturnError = PVRTStringFromFormattedStr(" on line %d.\n", m_psContext->pnFileLineNumber[nStartLine]);
  1791. FREE(semantic.pszValue);
  1792. FREE(semantic.pszName);
  1793. return false;
  1794. }
  1795. semantic.sDefaultValue.eType = c_psSemanticDefaultDataTypeInfo[i].eType;
  1796. break;
  1797. }
  1798. }
  1799. // invalid data type
  1800. if(i == eNumDefaultDataTypes)
  1801. {
  1802. *pReturnError = PVRTStringFromFormattedStr("'%s' unknown on line %d.\n", pszString, m_psContext->pnFileLineNumber[nStartLine]);
  1803. FREE(semantic.pszValue);
  1804. FREE(semantic.pszName);
  1805. return false;
  1806. }
  1807. }
  1808. return true;
  1809. }
  1810. /*!***************************************************************************
  1811. @Function ParseEffect
  1812. @Output effect effect data object
  1813. @Input nStartLine start line number
  1814. @Input nEndLine end line number
  1815. @Output pReturnError error string
  1816. @Return bool true if parse is successful
  1817. @Description Parses the EFFECT section of the PFX file.
  1818. *****************************************************************************/
  1819. bool CPVRTPFXParser::ParseEffect(SPVRTPFXParserEffect &effect, const int nStartLine, const int nEndLine, CPVRTString * const pReturnError)
  1820. {
  1821. bool bName = false;
  1822. bool bVertShader = false;
  1823. bool bFragShader = false;
  1824. effect.pszName = NULL;
  1825. effect.pszAnnotation = NULL;
  1826. effect.pszVertexShaderName = NULL;
  1827. effect.pszFragmentShaderName = NULL;
  1828. effect.nMaxTextures = 100;
  1829. effect.nNumTextures = 0;
  1830. effect.psTextures = new SPVRTPFXParserEffectTexture[effect.nMaxTextures];
  1831. effect.nMaxUniforms = 100;
  1832. effect.nNumUniforms = 0;
  1833. effect.psUniform = new SPVRTPFXParserSemantic[effect.nMaxUniforms];
  1834. effect.nMaxAttributes = 100;
  1835. effect.nNumAttributes = 0;
  1836. effect.psAttribute = new SPVRTPFXParserSemantic[effect.nMaxAttributes];
  1837. for(int i = nStartLine+1; i < nEndLine; i++)
  1838. {
  1839. // Skip blank lines
  1840. if(!*m_psContext->ppszEffectFile[i])
  1841. continue;
  1842. char *str = strtok (m_psContext->ppszEffectFile[i]," ");
  1843. if(str != NULL)
  1844. {
  1845. if(strcmp(str, "[ANNOTATION]") == 0)
  1846. {
  1847. if(effect.pszAnnotation)
  1848. {
  1849. *pReturnError = PVRTStringFromFormattedStr("ANNOTATION redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]);
  1850. return false;
  1851. }
  1852. i++; // Skip the block-start
  1853. if(!ConcatenateLinesUntil(
  1854. effect.pszAnnotation,
  1855. i,
  1856. m_psContext->ppszEffectFile,
  1857. m_psContext->nNumLines,
  1858. "[/ANNOTATION]"))
  1859. {
  1860. return false;
  1861. }
  1862. }
  1863. else if(strcmp(str, "VERTEXSHADER") == 0)
  1864. {
  1865. if(bVertShader)
  1866. {
  1867. *pReturnError = PVRTStringFromFormattedStr("VERTEXSHADER redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]);
  1868. return false;
  1869. }
  1870. str = strtok(NULL, " ");
  1871. if(str == NULL)
  1872. {
  1873. *pReturnError = PVRTStringFromFormattedStr("VERTEXSHADER missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
  1874. return false;
  1875. }
  1876. effect.pszVertexShaderName = (char*)malloc((strlen(str)+1) * sizeof(char));
  1877. strcpy(effect.pszVertexShaderName, str);
  1878. bVertShader = true;
  1879. }
  1880. else if(strcmp(str, "FRAGMENTSHADER") == 0)
  1881. {
  1882. if(bFragShader)
  1883. {
  1884. *pReturnError = PVRTStringFromFormattedStr("FRAGMENTSHADER redefined in [EFFECT] on line %d: \n", m_psContext->pnFileLineNumber[i]);
  1885. return false;
  1886. }
  1887. str = strtok(NULL, " ");
  1888. if(str == NULL)
  1889. {
  1890. *pReturnError = PVRTStringFromFormattedStr("FRAGMENTSHADER missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
  1891. return false;
  1892. }
  1893. effect.pszFragmentShaderName = (char*)malloc((strlen(str)+1) * sizeof(char));
  1894. strcpy(effect.pszFragmentShaderName, str);
  1895. bFragShader = true;
  1896. }
  1897. else if(strcmp(str, "TEXTURE") == 0)
  1898. {
  1899. if(effect.nNumTextures < effect.nMaxTextures)
  1900. {
  1901. // texture number
  1902. str = strtok(NULL, " ");
  1903. if(str != NULL)
  1904. effect.psTextures[effect.nNumTextures].nNumber = atoi(str);
  1905. else
  1906. {
  1907. *pReturnError = PVRTStringFromFormattedStr("TEXTURE missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
  1908. return false;
  1909. }
  1910. // texture name
  1911. str = strtok(NULL, " ");
  1912. if(str != NULL)
  1913. {
  1914. effect.psTextures[effect.nNumTextures].pszName = (char*)malloc(strlen(str) + 1);
  1915. strcpy(effect.psTextures[effect.nNumTextures].pszName, str);
  1916. }
  1917. else
  1918. {
  1919. *pReturnError = PVRTStringFromFormattedStr("TEXTURE missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
  1920. return false;
  1921. }
  1922. ++effect.nNumTextures;
  1923. }
  1924. else
  1925. {
  1926. *pReturnError = PVRTStringFromFormattedStr("Too many textures in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
  1927. return false;
  1928. }
  1929. }
  1930. else if(strcmp(str, "UNIFORM") == 0)
  1931. {
  1932. if(effect.nNumUniforms < effect.nMaxUniforms)
  1933. {
  1934. if(!ParseSemantic(effect.psUniform[effect.nNumUniforms], i, pReturnError))
  1935. return false;
  1936. effect.nNumUniforms++;
  1937. }
  1938. else
  1939. {
  1940. *pReturnError = PVRTStringFromFormattedStr("Too many uniforms in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
  1941. return false;
  1942. }
  1943. }
  1944. else if(strcmp(str, "ATTRIBUTE") == 0)
  1945. {
  1946. if(effect.nNumAttributes < effect.nMaxAttributes)
  1947. {
  1948. if(!ParseSemantic(effect.psAttribute[effect.nNumAttributes], i, pReturnError))
  1949. return false;
  1950. effect.nNumAttributes++;
  1951. }
  1952. else
  1953. {
  1954. *pReturnError = PVRTStringFromFormattedStr("Too many attributes in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[i]);
  1955. return false;
  1956. }
  1957. }
  1958. else if(strcmp(str, "NAME") == 0)
  1959. {
  1960. if(bName)
  1961. {
  1962. *pReturnError = PVRTStringFromFormattedStr("NAME redefined in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
  1963. return false;
  1964. }
  1965. str = strtok (NULL, " ");
  1966. if(str == NULL)
  1967. {
  1968. *pReturnError = PVRTStringFromFormattedStr("NAME missing value in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
  1969. return false;
  1970. }
  1971. effect.pszName = (char *)malloc((strlen(str)+1) * sizeof(char));
  1972. strcpy(effect.pszName, str);
  1973. bName = true;
  1974. }
  1975. else
  1976. {
  1977. *pReturnError = PVRTStringFromFormattedStr("Unknown keyword '%s' in [EFFECT] on line %d\n", str, m_psContext->pnFileLineNumber[i]);
  1978. return false;
  1979. }
  1980. }
  1981. else
  1982. {
  1983. *pReturnError = PVRTStringFromFormattedStr( "Missing arguments in [EFFECT] on line %d: %s\n", m_psContext->pnFileLineNumber[i], m_psContext->ppszEffectFile[i]);
  1984. return false;
  1985. }
  1986. }
  1987. if(!bName)
  1988. {
  1989. *pReturnError = PVRTStringFromFormattedStr("No 'NAME' found in [EFFECT] on line %d\n", m_psContext->pnFileLineNumber[nStartLine]);
  1990. return false;
  1991. }
  1992. if(!bVertShader)
  1993. {
  1994. *pReturnError = PVRTStringFromFormattedStr("No 'VERTEXSHADER' defined in [EFFECT] starting on line %d: \n", m_psContext->pnFileLineNumber[nStartLine-1]);
  1995. return false;
  1996. }
  1997. if(!bFragShader)
  1998. {
  1999. *pReturnError = PVRTStringFromFormattedStr("No 'FRAGMENTSHADER' defined in [EFFECT] starting on line %d: \n", m_psContext->pnFileLineNumber[nStartLine-1]);
  2000. return false;
  2001. }
  2002. return true;
  2003. }
  2004. /*!***************************************************************************
  2005. @Function DebugDump
  2006. @Return string A string containing debug information for the user
  2007. to handle
  2008. @Description Debug output.
  2009. *****************************************************************************/
  2010. CPVRTString CPVRTPFXParser::DebugDump() const
  2011. {
  2012. unsigned int i;
  2013. CPVRTString debug;
  2014. debug += CPVRTString("[HEADER]\n");
  2015. debug += PVRTStringFromFormattedStr("VERSION %s\n", m_sHeader.pszVersion);
  2016. debug += PVRTStringFromFormattedStr("DESCRIPTION %s\n", m_sHeader.pszDescription);
  2017. debug += PVRTStringFromFormattedStr("COPYRIGHT %s\n", m_sHeader.pszCopyright);
  2018. debug += CPVRTString("[/HEADER]\n\n");
  2019. debug += CPVRTString("[TEXTURES]\n");
  2020. for(i = 0; i < m_nNumTextures; ++i)
  2021. {
  2022. debug += PVRTStringFromFormattedStr("FILE %s %s\n", m_psTexture[i].pszName, m_psTexture[i].pszFile);
  2023. }
  2024. debug += CPVRTString("[/TEXTURES]\n\n");
  2025. debug += CPVRTString("[VERTEXSHADER]\n");
  2026. debug += PVRTStringFromFormattedStr("NAME %s\n", m_psVertexShader[0].pszName);
  2027. debug += PVRTStringFromFormattedStr("GLSLFILE %s\n", m_psVertexShader[0].pszGLSLfile);
  2028. debug += CPVRTString("[GLSL_CODE]\n");
  2029. debug += PVRTStringFromFormattedStr("%s", m_psVertexShader[0].pszGLSLcode);
  2030. debug += CPVRTString("[/GLSL_CODE]\n");
  2031. debug += CPVRTString("[/VERTEXSHADER]\n\n");
  2032. debug += CPVRTString("[FRAGMENTSHADER]\n");
  2033. debug += PVRTStringFromFormattedStr("NAME %s\n", m_psFragmentShader[0].pszName);
  2034. debug += PVRTStringFromFormattedStr("GLSLFILE %s\n", m_psFragmentShader[0].pszGLSLfile);
  2035. debug += CPVRTString("[GLSL_CODE]\n");
  2036. debug += PVRTStringFromFormattedStr("%s", m_psFragmentShader[0].pszGLSLcode);
  2037. debug += CPVRTString("[/GLSL_CODE]\n");
  2038. debug += CPVRTString("[/FRAGMENTSHADER]\n\n");
  2039. for(unsigned int nEffect = 0; nEffect < m_nNumEffects; ++nEffect)
  2040. {
  2041. debug += CPVRTString("[EFFECT]\n");
  2042. debug += PVRTStringFromFormattedStr("NAME %s\n", m_psEffect[nEffect].pszName);
  2043. debug += PVRTStringFromFormattedStr("[ANNOTATION]\n%s[/ANNOTATION]\n", m_psEffect[nEffect].pszAnnotation);
  2044. debug += PVRTStringFromFormattedStr("FRAGMENTSHADER %s\n", m_psEffect[nEffect].pszFragmentShaderName);
  2045. debug += PVRTStringFromFormattedStr("VERTEXSHADER %s\n", m_psEffect[nEffect].pszVertexShaderName);
  2046. for(i=0; i<m_psEffect[nEffect].nNumTextures; i++)
  2047. {
  2048. debug += PVRTStringFromFormattedStr("TEXTURE %d %s\n", m_psEffect[nEffect].psTextures[i].nNumber, m_psEffect[nEffect].psTextures[i].pszName);
  2049. }
  2050. for(i=0; i<m_psEffect[nEffect].nNumUniforms; i++)
  2051. {
  2052. debug += PVRTStringFromFormattedStr("UNIFORM %s %s%d\n", m_psEffect[nEffect].psUniform[i].pszName, m_psEffect[nEffect].psUniform[i].pszValue, m_psEffect[nEffect].psUniform[i].nIdx);
  2053. }
  2054. for(i=0; i<m_psEffect[nEffect].nNumAttributes; i++)
  2055. {
  2056. debug += PVRTStringFromFormattedStr("ATTRIBUTE %s %s%d\n", m_psEffect[nEffect].psAttribute[i].pszName, m_psEffect[nEffect].psAttribute[i].pszValue, m_psEffect[nEffect].psAttribute[i].nIdx);
  2057. }
  2058. debug += CPVRTString("[/EFFECT]\n\n");
  2059. }
  2060. return debug;
  2061. }
  2062. /*****************************************************************************
  2063. End of file (PVRTPFXParser.cpp)
  2064. *****************************************************************************/