/*
								+----------------------------------+
								|                                  |
								| *** ÜberLame material system *** |
								|                                  |
								|   Copyright © -tHE SWINe- 2007   |
								|                                  |
								|           Material.cpp           |
								|                                  |
								+----------------------------------+
*/

/**
 *	@file dev/Material.cpp
 *	@date 2007
 *	@author -tHE SWINe-
 *	@brief ÜberLame material system
 *
 *	@date 2008-08-08
 *
 *	added \#ifdef for windows 64, added \#define for GL_GLEXT_LEGACY (for linux builds)
 *
 *	@date 2009-05-23
 *
 *	removed all instances of std::vector::reserve and replaced them by stl_ut::Reserve_*
 *
 *	@date 2009-10-20
 *
 *	fixed some warnings when compiling under VC 2005, implemented "Security
 *	Enhancements in the CRT" for VC 2008. compare against MyProjects_2009-10-19_
 *
 */

#include "../NewFix.h"

#include "../CallStack.h"
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <algorithm>
#include "../OpenGL20.h"
#include "../OpenGLState.h"
#include "../Texture.h"
#include "../Shader2.h"
#include "../ShaderSys.h"
#include "Material.h"
#include "../StlUtils.h"

#if defined(_MSC_VER) && !defined(__MWERKS__) && !defined(for) && _MSC_VER <= 1200
#define for if(0) {} else for
#endif // _MSC_VER && !__MWERKS__ && !for && _MSC_VER <= 1200
// msvc 'for' scoping hack

/*
 *								=== CMaterialLexer::TToken ===
 */

/*
 *	CMaterialLexer::TToken::TToken()
 *		- default constructor; sets line and column to 0 and type to token_EOF
 */
CMaterialLexer::TToken::TToken()
	:n_line(0), n_col(0), n_type(token_EOF)
{
	t_value.p_s_value = 0;
}

/*
 *	CMaterialLexer::TToken::TToken(int _n_line, int _n_col, int _n_type)
 *		- constructor for tokens with no data
 *		- sets line to _n_line, column to _n_col and type to _n_type
 */
CMaterialLexer::TToken::TToken(int _n_line, int _n_col, int _n_type)
	:n_line(_n_line), n_col(_n_col), n_type(_n_type)
{
	t_value.p_s_value = 0;
}

/*
 *	CMaterialLexer::TToken::TToken(int _n_line, int _n_col, int _n_type, int n_value)
 *		- constructor for tokens with integer data
 *		- sets line to _n_line, column to _n_col and type to _n_type
 *		- sets int value to n_value
 */
CMaterialLexer::TToken::TToken(int _n_line, int _n_col, int _n_type, int n_value)
	:n_line(_n_line), n_col(_n_col), n_type(_n_type)
{
	t_value.n_value = n_value;
}

/*
 *	CMaterialLexer::TToken::TToken(int _n_line, int _n_col, int _n_type, float f_value)
 *		- constructor for tokens with floating point data
 *		- sets line to _n_line, column to _n_col and type to _n_type
 *		- sets float value to f_value
 */
CMaterialLexer::TToken::TToken(int _n_line, int _n_col, int _n_type, float f_value)
	:n_line(_n_line), n_col(_n_col), n_type(_n_type)
{
	t_value.f_value = f_value;
}

/*
 *	CMaterialLexer::TToken::TToken(int _n_line, int _n_col,
 *		int _n_type, const char *p_s_value)
 *		- constructor for tokens with string data
 *		- sets line to _n_line, column to _n_col and type to _n_type
 *		- sets string value to p_s_value (a new string is allocated and copied,
 *		  check wheter allocation was successful)
 */
CMaterialLexer::TToken::TToken(int _n_line, int _n_col, int _n_type, const char *p_s_value)
	:n_line(_n_line), n_col(_n_col), n_type(_n_type)
{
	if(n_type >= token_Node && n_type <= token_CData && p_s_value) {
		if(t_value.p_s_value = new(std::nothrow) char[strlen(p_s_value) + 1]) {
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
			strcpy_s(t_value.p_s_value, (strlen(p_s_value) + 1)  *sizeof(char), p_s_value);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			strcpy(t_value.p_s_value, p_s_value);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		}
	} else
		t_value.p_s_value = 0;
}

/*
 *	CMaterialLexer::TToken::TToken(const TToken &r_t_token)
 *		- copy-constructor
 *		- note in case token is supposed to contain string data, a new string
 *		  is allocated and copied, check wheter allocation was successful
 */
CMaterialLexer::TToken::TToken(const TToken &r_t_token)
	:n_line(r_t_token.n_line), n_col(r_t_token.n_col), n_type(r_t_token.n_type)
{
	t_value = r_t_token.t_value;
	if(n_type >= token_Node && n_type <= token_CData && t_value.p_s_value) {
		if(t_value.p_s_value = new(std::nothrow) char[strlen(r_t_token.t_value.p_s_value) + 1]) {
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
			strcpy_s(t_value.p_s_value, (strlen(r_t_token.t_value.p_s_value) + 1) * sizeof(char),
				r_t_token.t_value.p_s_value);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			strcpy(t_value.p_s_value, r_t_token.t_value.p_s_value);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		}
	}
}

/*
 *	CMaterialLexer::TToken::~TToken()
 *		- default destructor; deallocates string data in case it was present
 */
CMaterialLexer::TToken::~TToken()
{
	if(n_type >= token_Node && n_type <= token_CData && t_value.p_s_value)
		delete[] t_value.p_s_value;
}

/*
 *	bool CMaterialLexer::TToken::operator =(const TToken &r_t_token)
 *		- copy-operator
 *		- returns true on success, false in case there was
 *		  not enough memory for string data
 */
bool CMaterialLexer::TToken::operator =(const TToken &r_t_token)
{
	if(&r_t_token == this)
		return true;
	// handle self-assignment

	if(n_type >= token_Node && n_type <= token_CData && t_value.p_s_value)
		delete[] t_value.p_s_value;
	// delete previous string

	n_line = r_t_token.n_line;
	n_col = r_t_token.n_col;
	n_type = r_t_token.n_type;
	t_value = r_t_token.t_value;
	// copy token data

	if(n_type >= token_Node && n_type <= token_CData && t_value.p_s_value) {
		if(t_value.p_s_value = new(std::nothrow) char[strlen(r_t_token.t_value.p_s_value) + 1]) {
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
			strcpy_s(t_value.p_s_value, (strlen(r_t_token.t_value.p_s_value) +
				1) * sizeof(char), r_t_token.t_value.p_s_value);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			strcpy(t_value.p_s_value, r_t_token.t_value.p_s_value);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		} else
			return false;
	}
	// copy new string

	return true;
}

#ifdef MATERIAL_LEXER_TOKEN_DUMP

/*
 *	void CMaterialLexer::TToken::Dump() const
 *		- prints token contents
 *		- note this is compiled only if MATERIAL_LEXER_TOKEN_DUMP is defined
 */
void CMaterialLexer::TToken::Dump() const
{
	const char *p_type_name_list[] = {
		"token_White",
		"token_Colon",
		"token_Semicolon",
		"token_LeftPar",
		"token_RightPar",
		"token_Begin",
		"token_End",
		"token_Node",
		"token_String",
		"token_CData",
		"token_Bool",
		"token_Int",
		"token_GLenum",
		"token_Float",
		"token_Pass",
		"token_Texture1D",
		"token_Texture2D",
		"token_Texture3D",
		"token_TextureCube",
		"token_URI",
		"token_Reflect",
		"token_Refract",
		"token_Custom",
		"token_DepthMap",
		"token_EOF"
	};

	if(n_type == token_Bool || n_type == token_Int) {
		printf("TToken(n_line = %d, n_col = %d, n_type = %s, n_value = %d)\n",
			n_line, n_col, p_type_name_list[n_type], t_value.n_value);
	} else if(n_type == token_Float) {
		printf("TToken(n_line = %d, n_col = %d, n_type = %s, f_value = %g)\n",
			n_line, n_col, p_type_name_list[n_type], t_value.f_value);
	} else if(n_type == token_String || n_type == token_Node || n_type == token_CData) {
		printf("TToken(n_line = %d, n_col = %d, n_type = %s, p_s_value = \'%s\')\n",
			n_line, n_col, p_type_name_list[n_type], (t_value.p_s_value)?
			t_value.p_s_value : "(null)");
	} else if(n_type == token_GLenum) {
		printf("TToken(n_line = %d, n_col = %d, n_type = %s, n_value = 0x%04x)\n",
			n_line, n_col, p_type_name_list[n_type], t_value.n_value);
	} else {
		printf("TToken(n_line = %d, n_col = %d, n_type = %s)\n",
			n_line, n_col, p_type_name_list[n_type]);
	}
}

#endif // MATERIAL_LEXER_TOKEN_DUMP

/*
 *								=== ~CMaterialLexer::TToken ===
 */

/*
 *								=== CMaterialLexer::CFileReader ===
 */

class CMaterialLexer::CFileReader {
protected:
	FILE *m_p_fr;

public:
	CFileReader(const char *p_s_filename)
	{
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		if(fopen_s(&m_p_fr, p_s_filename, "r"))
			m_p_fr = 0;
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		m_p_fr = fopen(p_s_filename, "r");
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	}

	~CFileReader()
	{
		if(m_p_fr)
			fclose(m_p_fr);
	}

	bool b_Status() const
	{
		return m_p_fr != 0;
	}

	inline bool ReadChar(int &r_n_char)
	{
		return m_p_fr && (r_n_char = fgetc(m_p_fr)) != EOF;
	}

	inline bool UnreadChar(int n_char)
	{
		return m_p_fr && ungetc(n_char, m_p_fr);
	}
};

/*
 *								=== ~CMaterialLexer::CFileReader ===
 */

/*
 *								=== CMaterialLexer::CTokenEmit ===
 */

class CMaterialLexer::CTokenEmit {
protected:
	CMaterialLexer::TToken &m_r_t_out_token;

public:
	CTokenEmit(TToken &r_t_out_token)
		:m_r_t_out_token(r_t_out_token)
	{}

	virtual bool operator ()(const char *p_s_buffer,
		int n_regexp_id, int n_line, int n_column) const = 0;
};

class CMaterialLexer::CEmitSkip : public CTokenEmit {
public:
	CEmitSkip(TToken &r_t_out_token)
		:CTokenEmit(r_t_out_token)
	{}

	virtual bool operator ()(const char *p_s_buffer,
		int n_regexp_id, int n_line, int n_column) const
	{
		_ASSERTE(n_regexp_id == 0);

		m_r_t_out_token = TToken(n_line, n_column, token_White);
		return true;
	}
};

class CMaterialLexer::CEmitNode : public CTokenEmit {
public:
	CEmitNode(TToken &r_t_out_token)
		:CTokenEmit(r_t_out_token)
	{}

	virtual bool operator ()(const char *p_s_buffer,
		int n_regexp_id, int n_line, int n_column) const
	{
		_ASSERTE(n_regexp_id == 1);

		const struct {
			const char *p_s_name;
			int n_type;
		} p_keyword[] = {
			{"false", token_Bool},
			{"true", token_Bool},
			// hack! bool value equals to index in this array

			{"uri", token_URI},
			{"tex-2d", token_Texture2D},
			{"tex-1d", token_Texture1D},
			{"tex-3d", token_Texture3D},
			{"tex-cube", token_TextureCube},
			{"pass", token_Pass},
			{"reflect", token_Reflect},
			{"refract", token_Refract},
			{"custom", token_Custom},
			{"depth-map", token_DepthMap}
			// otherwise int value insignificant, sorted by occurence likehood
		};
		for(int i = 0; i < sizeof(p_keyword) / sizeof(p_keyword[0]); ++ i) {
			if(!strcmp(p_s_buffer, p_keyword[i].p_s_name)) {
				m_r_t_out_token = TToken(n_line, n_column,
					 p_keyword[i].n_type, i);
				return true;
			}
		}

		m_r_t_out_token = TToken(n_line, n_column, token_Node, p_s_buffer);
		return m_r_t_out_token.p_s_Value() != 0;
	}
};

class CMaterialLexer::CEmitOperator : public CTokenEmit {
public:
	CEmitOperator(TToken &r_t_out_token)
		:CTokenEmit(r_t_out_token)
	{}

	virtual bool operator ()(const char *p_s_buffer,
		int n_regexp_id, int n_line, int n_column) const
	{
		_ASSERTE(n_regexp_id == 2);

		const char *p_s_ops = ":;(){}";
		const char *p_s_thisop = strchr(p_s_ops, *p_s_buffer);
		_ASSERTE(p_s_thisop); // operators given by FSM, not necessary to double-check
		m_r_t_out_token = TToken(n_line, n_column, token_Colon + (p_s_thisop - p_s_ops));
		return true;
	}
};

class CMaterialLexer::CEmitString : public CTokenEmit {
public:
	CEmitString(TToken &r_t_out_token)
		:CTokenEmit(r_t_out_token)
	{}

	virtual bool operator ()(const char *p_s_buffer,
		int n_regexp_id, int n_line, int n_column) const
	{
		_ASSERTE(n_regexp_id == 3);

		int n_length = strlen(p_s_buffer);
		char *p_s_string;
		if(!(p_s_string = new(std::nothrow) char[n_length - 1]))
			return false;
		// alloc buffer

		if(*p_s_buffer == '\'') {
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
			strncpy_s(p_s_string, (n_length - 1) * sizeof(char), p_s_buffer + 1, n_length - 2);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			strncpy(p_s_string, p_s_buffer + 1, n_length - 2);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			p_s_string[n_length - 2] = 0;
			// copy 'string', it won't contain any escape sequences
		} else {
			_ASSERTE(*p_s_buffer == '\"');
			++ p_s_buffer;
			for(char *p_s_out = p_s_string;; ++ p_s_buffer, ++ p_s_out) {
				if(*p_s_buffer == '\\') {
					char n_char;
					switch(n_char = *(++ p_s_buffer)) {
					case 'n':
						*p_s_out = '\n';
						break;
					case 't':
						*p_s_out = '\t';
						break;
					case 'r':
						*p_s_out = '\r';
						break;
					case '\\':
					case '\'':
					case '\"':
						*p_s_out = n_char;
						break;
					default:
						_ASSERTE(0);
						// escape sequences given by FSM, not necessary to double-check
						break;
					}
				} else if(*p_s_buffer == '\"') {
					*p_s_out = 0;
					break;
				} else
					*p_s_out = *p_s_buffer;
			}
			// copy "string", process escape sequences
		}

		m_r_t_out_token = TToken(n_line, n_column, token_String, p_s_string);
		delete[] p_s_string;
		return m_r_t_out_token.p_s_Value() != 0;
	}
};

class CMaterialLexer::CEmitInt : public CTokenEmit {
public:
	CEmitInt(TToken &r_t_out_token)
		:CTokenEmit(r_t_out_token)
	{}

	virtual bool operator ()(const char *p_s_buffer,
		int n_regexp_id, int n_line, int n_column) const
	{
		_ASSERTE(n_regexp_id == 4);

		
		m_r_t_out_token = TToken(n_line, n_column, token_Int, (int)atol(p_s_buffer));
		// (int) to avoid ambiguity
		return true;
	}
};

class CMaterialLexer::CEmitFloat : public CTokenEmit {
public:
	CEmitFloat(TToken &r_t_out_token)
		:CTokenEmit(r_t_out_token)
	{}

	virtual bool operator ()(const char *p_s_buffer,
		int n_regexp_id, int n_line, int n_column) const
	{
		_ASSERTE(n_regexp_id == 5);

		
		m_r_t_out_token = TToken(n_line, n_column, token_Float, (float)atof(p_s_buffer));
		return true;
	}
};

class CMaterialLexer::CEmitGLenum : public CTokenEmit {
public:
	CEmitGLenum(TToken &r_t_out_token)
		:CTokenEmit(r_t_out_token)
	{}

	virtual bool operator ()(const char *p_s_buffer,
		int n_regexp_id, int n_line, int n_column) const
	{
		_ASSERTE(n_regexp_id == 6);

		
		int n_enum;
		if((n_enum = CGLenumTable::n_Value(p_s_buffer)) < 0)
			return false;
		m_r_t_out_token = TToken(n_line, n_column, token_GLenum, n_enum);
		return true;
	}
};

class CMaterialLexer::CEmitCData : public CTokenEmit {
public:
	CEmitCData(TToken &r_t_out_token)
		:CTokenEmit(r_t_out_token)
	{}

	virtual bool operator ()(const char *p_s_buffer,
		int n_regexp_id, int n_line, int n_column) const
	{
		_ASSERTE(n_regexp_id == 7);
		_ASSERTE(strstr(p_s_buffer, "<![CDATA[") == p_s_buffer);

		
		p_s_buffer += 9; // strlen("<![CDATA[");

		int n_length = strlen(p_s_buffer);
		char *p_s_string;
		if(!(p_s_string = new(std::nothrow) char[n_length - 2]))
			return false;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		strncpy_s(p_s_string, (n_length - 2) * sizeof(char), p_s_buffer, n_length - 3);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		strncpy(p_s_string, p_s_buffer, n_length - 3);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		p_s_string[n_length - 3] = 0;
		// copy <![CDATA[string]]>

		m_r_t_out_token = TToken(n_line, n_column, token_CData, p_s_string);
		delete[] p_s_string;
		return m_r_t_out_token.p_s_Value() != 0;
	}
};

/*
 *								=== ~CMaterialLexer::CTokenEmit ===
 */

/*
 *								=== CMaterialLexer::CTokenEmitAdapter ===
 */

inline CMaterialLexer::CTokenEmitAdapter::CTokenEmitAdapter(CTokenEmit **p_emit_obj_list)
	:m_p_emit_obj_list(p_emit_obj_list)
{}

inline bool CMaterialLexer::CTokenEmitAdapter::operator ()(const char *p_s_buffer,
	int n_regexp_id, int n_line, int n_column) const
{
	return (*m_p_emit_obj_list[n_regexp_id])(p_s_buffer, n_regexp_id, n_line, n_column);
}

/*
 *								=== ~CMaterialLexer::CTokenEmitAdapter ===
 */

/*
 *								=== CMaterialLexer ===
 */

const CMaterialLexer::TTransition CMaterialLexer::m_p_transition[78] = {
    {0x2b, 0x2b, 5}, {0x2d, 0x2d, 5}, {0xa, 0x9, 1}, {0xd, 0xd, 1}, {0x20, 0x20, 1},
    {0x27, 0x27, 3}, {0x2e, 0x2e, 6}, {0x29, 0x28, 4}, {0x3b, 0x3a, 4}, {0x7b, 0x7b, 4},
    {0x7d, 0x7d, 4}, {0x39, 0x30, 8}, {0x2f, 0x2f, 7}, {0x22, 0x22, 2}, {0x7a, 0x61, 11},
    {0x3c, 0x3c, 9}, {0x5a, 0x41, 10}, {0xa, 0x9, 1}, {0xd, 0xd, 1}, {0x20, 0x20, 1},
    {0x21, 0x0, 2}, {0x5b, 0x23, 2}, {0x10ffff, 0x5d, 2}, {0x22, 0x22, 31}, {0x5c, 0x5c, 32},
    {0x26, 0x0, 3}, {0x10ffff, 0x28, 3}, {0x27, 0x27, 31}, {0x2e, 0x2e, 6}, {0x39, 0x30, 8},
    {0x39, 0x30, 30}, {0x2a, 0x2a, 27}, {0x2e, 0x2e, 6}, {0x39, 0x30, 8}, {0x65, 0x65, 23},
    {0x21, 0x21, 12}, {0x5a, 0x41, 10}, {0x5f, 0x5f, 10}, {0x2d, 0x2d, 11}, {0x39, 0x30, 11},
    {0x7a, 0x61, 11}, {0x5b, 0x5b, 13}, {0x43, 0x43, 14}, {0x44, 0x44, 15}, {0x41, 0x41, 16},
    {0x54, 0x54, 17}, {0x41, 0x41, 18}, {0x5b, 0x5b, 19}, {0x5d, 0x5d, 20}, {0x5c, 0x0, 19},
    {0x10ffff, 0x5e, 19}, {0x5c, 0x0, 19}, {0x10ffff, 0x5e, 19}, {0x5d, 0x5d, 21}, {0x3d, 0x0, 19},
    {0x10ffff, 0x3f, 19}, {0x3e, 0x3e, 22}, {0x2b, 0x2b, 24}, {0x2d, 0x2d, 24}, {0x39, 0x30, 25},
    {0x39, 0x30, 25}, {0x39, 0x30, 25}, {0x66, 0x66, 26}, {0x29, 0x0, 27}, {0x10ffff, 0x2b, 27},
    {0x2a, 0x2a, 28}, {0x2e, 0x0, 27}, {0x10ffff, 0x30, 27}, {0x2f, 0x2f, 29}, {0x66, 0x66, 26},
    {0x65, 0x65, 23}, {0x39, 0x30, 30}, {0x22, 0x22, 2}, {0x27, 0x27, 2}, {0x5c, 0x5c, 2},
    {0x6e, 0x6e, 2}, {0x72, 0x72, 2}, {0x74, 0x74, 2}
};

CMaterialLexer::TState CMaterialLexer::m_p_state[33] = {
    {m_p_transition + 0, -1, 17}, {m_p_transition + 17, 0, 3}, {m_p_transition + 20, -1, 5},
    {m_p_transition + 25, -1, 3}, {m_p_transition + 28, 2, 0}, {m_p_transition + 28, -1, 2},
    {m_p_transition + 30, -1, 1}, {m_p_transition + 31, -1, 1}, {m_p_transition + 32, 4, 3},
    {m_p_transition + 35, -1, 1}, {m_p_transition + 36, 6, 2}, {m_p_transition + 38, 1, 3},
    {m_p_transition + 41, -1, 1}, {m_p_transition + 42, -1, 1}, {m_p_transition + 43, -1, 1},
    {m_p_transition + 44, -1, 1}, {m_p_transition + 45, -1, 1}, {m_p_transition + 46, -1, 1},
    {m_p_transition + 47, -1, 1}, {m_p_transition + 48, -1, 3}, {m_p_transition + 51, -1, 3},
    {m_p_transition + 54, -1, 3}, {m_p_transition + 57, 7, 0}, {m_p_transition + 57, -1, 3},
    {m_p_transition + 60, -1, 1}, {m_p_transition + 61, 5, 2}, {m_p_transition + 63, 5, 0},
    {m_p_transition + 63, -1, 3}, {m_p_transition + 66, -1, 3}, {m_p_transition + 69, 0, 0},
    {m_p_transition + 69, 5, 3}, {m_p_transition + 72, 3, 0}, {m_p_transition + 72, -1, 6}
};
// new tables with fixed float exponent notation

/*
 *	CMaterialLexer::CMaterialLexer(const char *p_s_filename)
 *		- default constructor; creates a new parser, bound to p_s_filename
 *		- call b_Status() to see wheter file was opened successfully
 *		  (no parsing occurs at this moment)
 *		- note all parsing occurs in us-ascii encoding
 */
CMaterialLexer::CMaterialLexer(const char *p_s_filename)
	:m_t_token(0, 0, token_EOF), m_scanner(m_p_state, CTokenEmitAdapter(m_p_emit)) // t_odo - write adapter and fix this
{
	if((m_p_emit[0] = new(std::nothrow) CEmitSkip(m_t_token)) &&
	   (m_p_emit[1] = new(std::nothrow) CEmitNode(m_t_token)) &&
	   (m_p_emit[2] = new(std::nothrow) CEmitOperator(m_t_token)) &&
	   (m_p_emit[3] = new(std::nothrow) CEmitString(m_t_token)) &&
	   (m_p_emit[4] = new(std::nothrow) CEmitInt(m_t_token)) &&
	   (m_p_emit[5] = new(std::nothrow) CEmitFloat(m_t_token)) &&
	   (m_p_emit[6] = new(std::nothrow) CEmitGLenum(m_t_token)) &&
	   (m_p_emit[7] = new(std::nothrow) CEmitCData(m_t_token))) {
		if((m_p_file = new(std::nothrow) CFileReader(p_s_filename)) && !m_p_file->b_Status()) {
			delete m_p_file;
			m_p_file = 0;
		}
	} else
		m_p_file = 0;
	// init token emit objects, create file reader and open given file
}

/*
 *	CMaterialLexer::~CMaterialLexer()
 *		- default destructor
 */
CMaterialLexer::~CMaterialLexer()
{
	for(int i = 0; i < 8; ++ i) {
		if(m_p_emit[i])
			delete m_p_emit[i];
	}
	if(m_p_file)
		delete m_p_file;
}

/*
 *	bool CMaterialLexer::b_Status() const
 *		- returns status of parser
 *		- returns true if no errors occured, returns false if
 *		  constructor or parsing failed
 */
bool CMaterialLexer::b_Status() const
{
	return m_p_file != 0;
}

/*
 *	bool CMaterialLexer::Get_NextToken()
 *		- parses next token from file
 *		- returns true on success, false on parsing / memory failure
 *		- note reading past the end of the file is not an error
 *		- always fails in case b_Status() returns false
 */
bool CMaterialLexer::Get_NextToken()
{
	if(!m_p_file)
		return false;

	for(;;) {
		m_t_token = TToken(m_scanner.n_Cur_Line(),
			m_scanner.n_Cur_Column(), token_EOF);
		// assume EOF

		if(!m_scanner.GetToken(*m_p_file))
			return false;
		// read

		if(m_t_token.n_Type() != token_White)
			return true;
		// return anything but whitespace
	}
	// read token which is not whitespace
}

/*
 *	int CMaterialLexer::n_Cur_Line() const
 *		- returns zero-based index of line where the parser last stopped
 *		- this is useful for printing error messages on where parsing failed
 */
int CMaterialLexer::n_Cur_Line() const
{
	return m_scanner.n_Cur_Line();
}

/*
 *	int CMaterialLexer::n_Cur_Column() const
 *		- returns zero-based index of column where the parser last stopped
 *		- this is useful for printing error messages on where parsing failed
 */
int CMaterialLexer::n_Cur_Column() const
{
	return m_scanner.n_Cur_Column();
}

/*
 *	const CMaterialLexer::TToken &CMaterialLexer::t_Cur_Token() const
 *		- returns constant reference to current token
 *		- note in case no token was read, this returns token_EOF at line 0, col 0
 *		- return value is undefined in case b_Status() returns false
 */
const CMaterialLexer::TToken &CMaterialLexer::t_Cur_Token() const
{
	return m_t_token;
}

/*
 *								=== ~CMaterialLexer ===
 */

/*
 *								=== CGLEnumTable ===
 */

const CGLenumTable::TTable CGLenumTable::m_p_table[] = {
	{"GL_POINT", GL_POINT},
	{"GL_LINE", GL_LINE},
	{"GL_FILL", GL_FILL},

	{"GL_FRONT", GL_FRONT},
	{"GL_BACK", GL_BACK},
	{"GL_FRONT_AND_BACK", GL_FRONT_AND_BACK},

	{"GL_CW", GL_CW},
	{"GL_CCW", GL_CCW},

	{"GL_ZERO", GL_ZERO},
	{"GL_ONE", GL_ONE},
	{"GL_SRC_COLOR", GL_SRC_COLOR},
	{"GL_ONE_MINUS_SRC_COLOR", GL_ONE_MINUS_SRC_COLOR},
	{"GL_DST_COLOR", GL_DST_COLOR},
	{"GL_ONE_MINUS_DST_COLOR", GL_ONE_MINUS_DST_COLOR},
	{"GL_SRC_ALPHA", GL_SRC_ALPHA},
	{"GL_ONE_MINUS_SRC_ALPHA", GL_ONE_MINUS_SRC_ALPHA},
	{"GL_DST_ALPHA", GL_DST_ALPHA},
	{"GL_ONE_MINUS_DST_ALPHA", GL_ONE_MINUS_DST_ALPHA},
	{"GL_CONSTANT_COLOR", GL_CONSTANT_COLOR},
	{"GL_ONE_MINUS_CONSTANT_COLOR", GL_ONE_MINUS_CONSTANT_COLOR},
	{"GL_CONSTANT_ALPHA", GL_CONSTANT_ALPHA},
	{"GL_ONE_MINUS_CONSTANT_ALPHA", GL_ONE_MINUS_CONSTANT_ALPHA},
	{"GL_SRC_ALPHA_SATURATE", GL_SRC_ALPHA_SATURATE}, // only valid for src color / alpha

	{"GL_FUNC_ADD", GL_FUNC_ADD},
	{"GL_FUNC_SUBTRACT", GL_FUNC_SUBTRACT},
	{"GL_FUNC_REVERSE_SUBTRACT", GL_FUNC_REVERSE_SUBTRACT},
	{"GL_MIN", GL_MIN},
	{"GL_MAX", GL_MAX},

	{"GL_NEVER", GL_NEVER},
	{"GL_LESS", GL_LESS},
	{"GL_EQUAL", GL_EQUAL},
	{"GL_LEQUAL", GL_LEQUAL},
	{"GL_GREATER", GL_GREATER},
	{"GL_NOTEQUAL", GL_NOTEQUAL},
	{"GL_GEQUAL", GL_GEQUAL},
	{"GL_ALWAYS", GL_ALWAYS},

	{"GL_KEEP", GL_KEEP},
	{"GL_ZERO", GL_ZERO},
	{"GL_REPLACE", GL_REPLACE},
	{"GL_INCR", GL_INCR},
	{"GL_DECR", GL_DECR},
	{"GL_INVERT", GL_INVERT},

	{"GL_RGBA", GL_RGBA},
	{"GL_RGB", GL_RGB},
	{"GL_ALPHA", GL_ALPHA},
	{"GL_INTENSITY", GL_INTENSITY},
	{"GL_LUMINANCE", GL_LUMINANCE},
	{"GL_LUMINANCE_ALPHA", GL_LUMINANCE_ALPHA},
	{"GL_RGBA32F", GL_RGBA32F_ARB},
	{"GL_RGB32F", GL_RGB32F_ARB},
	{"GL_ALPHA32F", GL_ALPHA32F_ARB},
	{"GL_INTENSITY32F", GL_INTENSITY32F_ARB},
	{"GL_LUMINANCE32F", GL_LUMINANCE32F_ARB},
	{"GL_LUMINANCE_ALPHA32F", GL_LUMINANCE_ALPHA32F_ARB},
	{"GL_RGBA16F", GL_RGBA16F_ARB},
	{"GL_RGB16F", GL_RGB16F_ARB},
	{"GL_ALPHA16F", GL_ALPHA16F_ARB},
	{"GL_INTENSITY16F", GL_INTENSITY16F_ARB},
	{"GL_LUMINANCE16F", GL_LUMINANCE16F_ARB},
	{"GL_LUMINANCE_ALPHA16F", GL_LUMINANCE_ALPHA16F_ARB},
	{"GL_DEPTH_COMPONENT16", GL_DEPTH_COMPONENT16},
	{"GL_DEPTH_COMPONENT24", GL_DEPTH_COMPONENT24},
	{"GL_DEPTH_COMPONENT32", GL_DEPTH_COMPONENT32},
	// stencil component not allowed as texture format

	{"GL_CLAMP", GL_CLAMP},
	{"GL_CLAMP_TO_EDGE", GL_CLAMP_TO_EDGE},
	{"GL_REPEAT", GL_REPEAT},
	{"GL_CLAMP_TO_BORDER", GL_CLAMP_TO_BORDER},
	{"GL_MIRRORED_REPEAT", GL_MIRRORED_REPEAT},

	{"GL_NEAREST", GL_NEAREST},
	{"GL_LINEAR", GL_LINEAR},
	{"GL_NEAREST_MIPMAP_NEAREST", GL_NEAREST_MIPMAP_NEAREST},
	{"GL_NEAREST_MIPMAP_LINEAR", GL_NEAREST_MIPMAP_LINEAR},
	{"GL_LINEAR_MIPMAP_NEAREST", GL_LINEAR_MIPMAP_NEAREST},
	{"GL_LINEAR_MIPMAP_LINEAR", GL_LINEAR_MIPMAP_LINEAR},

	{"GL_NONE", GL_NONE},
	{"GL_COMPARE_R_TO_TEXTURE", GL_COMPARE_R_TO_TEXTURE},

	{"GL_POINTS", GL_POINTS},
	{"GL_LINES", GL_LINES},
	{"GL_LINES_ADJACENCY_EXT", GL_LINES_ADJACENCY_EXT},
	{"GL_TRIANGLES", GL_TRIANGLES},
	{"GL_TRIANGLES_ADJACENCY_EXT", GL_TRIANGLES_ADJACENCY_EXT},
	{"GL_LINE_STRIP", GL_LINE_STRIP},
	{"GL_TRIANGLE_STRIP", GL_TRIANGLE_STRIP}
	// GL_EXT_geometry_shader4
};

/*
 *	static int CGLenumTable::n_Value(const char *p_s_name)
 *		- returns value of OpenGL enum p_s_name
 *		- returns -1 in case p_s_name is not found in the table
 */
int CGLenumTable::n_Value(const char *p_s_name)
{
	/*if(strncmp(p_s_name, "GL_", 2))
		return -1;
	p_s_name += 3;*/
	for(unsigned int i = 0; i < sizeof(m_p_table) / sizeof(m_p_table[0]); ++ i) {
		if(!strcmp(p_s_name, m_p_table[i].p_s_name))
			return m_p_table[i].n_value;
	}
	return -1;
}

/*
 *								=== ~CGLEnumTable ===
 */

/*
 *								=== CTextureConfig ===
 */

const CTextureConfig::TNodeHandler CTextureConfig::m_p_handler_table[] = {
	{"source", &CTextureConfig::Read_Source, 0},
	{"alpha-source", &CTextureConfig::Read_Source, 1},
	{"internal-format", &CTextureConfig::Read_Format, 0},
	{"generate-mipmap", &CTextureConfig::Read_MipMap, 0},
	{"wrap", &CTextureConfig::Read_Wrap, 0},
	{"wrap_s", &CTextureConfig::Read_Wrap, 1},
	{"wrap_t", &CTextureConfig::Read_Wrap, 2},
	{"wrap_r", &CTextureConfig::Read_Wrap, 3},
	{"filter", &CTextureConfig::Read_Filter, 0},
	{"min-filter", &CTextureConfig::Read_Filter, 1},
	{"mag-filter", &CTextureConfig::Read_Filter, 2},
	{"lod", &CTextureConfig::Read_LOD, 0},
	{"lod-min", &CTextureConfig::Read_LOD, 1},
	{"lod-max", &CTextureConfig::Read_LOD, 2},
	{"lod-bias", &CTextureConfig::Read_LOD, 3},
	{"mip-level", &CTextureConfig::Read_MipLevel, 0},
	{"mip-level-base", &CTextureConfig::Read_MipLevel, 1},
	{"mip-level-max", &CTextureConfig::Read_MipLevel, 2},
	{"depth-tex-mode", &CTextureConfig::Read_DepthTexMode, 0},
	{"tex-compare", &CTextureConfig::Read_DepthTexCompare, 0},
	{"tex-compare-mode", &CTextureConfig::Read_DepthTexCompare, 1},
	{"tex-compare-func", &CTextureConfig::Read_DepthTexCompare, 2}
};

/*
 *	CTextureConfig::CTextureConfig(CMaterialLexer &r_scanner)
 *		- default destructor; parses texture data from r_scanner
 *		- call b_Status() to see wheter parsing was successful
 */
CTextureConfig::CTextureConfig(CMaterialLexer &r_scanner)
	:m_n_target(0), m_n_texture_unit(-1), m_p_s_source(0), m_p_s_source_alpha(0),
	m_n_source_type(tex_Static), m_n_internal_format(GL_RGBA), m_b_mipmaps(true),
	m_n_min_filter(GL_LINEAR_MIPMAP_LINEAR), m_n_mag_filter(GL_LINEAR),
	m_f_min_lod(-1000.0f), m_f_max_lod(1000.0f), m_f_lod_bias(.0f),
	m_n_base_level(0), m_n_max_level(1000), m_n_depth_tex_mode(GL_LUMINANCE),
	m_n_depth_tex_compare_mode(GL_NONE), m_n_depth_tex_compare_func(GL_LEQUAL)
{
	for(unsigned int i = 0; i < sizeof(m_p_wrap_mode) / sizeof(m_p_wrap_mode[0]); ++ i)
		m_p_wrap_mode[i] = GL_REPEAT;
	// set default values to everything

	if(!Parse(r_scanner))
		m_n_target = 0; // mark the error by invalid target value
}

/*
 *	CTextureConfig::~CTextureConfig()
 *		- default destructor
 */
CTextureConfig::~CTextureConfig()
{
	if(m_p_s_source)
		delete[] m_p_s_source;
	if(m_p_s_source_alpha)
		delete[] m_p_s_source_alpha;
}

/*
 *	bool CTextureConfig::b_Status() const
 *		- if constructor was successful, returns true. otherwise returns false
 */
bool CTextureConfig::b_Status() const
{
	return m_n_target != 0;
}

/*
 *	void CTextureConfig::ApplyTextureParameters(CGLTextureParams &r_texture_params)
 *		- sets all but mipmap generation texture parameters to r_texture_params
 *		- note the texture is supposed to be bound and enabled
 */
void CTextureConfig::ApplyTextureParameters(CGLTextureParams &r_texture_params)
{
	r_texture_params.Set_Depth_Texture_Mode(m_n_depth_tex_mode);
	r_texture_params.Set_Texture_Compare_Func(m_n_depth_tex_compare_func);
	r_texture_params.Set_Texture_Compare_Mode(m_n_depth_tex_compare_mode);

	r_texture_params.Set_Texture_LOD_Bias(m_f_lod_bias);
	r_texture_params.Set_Texture_Min_LOD(m_f_min_lod);
	r_texture_params.Set_Texture_Max_LOD(m_f_max_lod);

	r_texture_params.Set_Texture_Base_Level(m_n_base_level);
	r_texture_params.Set_Texture_Max_Level(m_n_max_level);

	r_texture_params.Set_Texture_Min_Filter(m_n_min_filter);
	r_texture_params.Set_Texture_Mag_Filter(m_n_mag_filter);

	r_texture_params.Set_Texture_Wrap_S(m_p_wrap_mode[0]);
	if(m_n_target != GL_TEXTURE_1D)
		r_texture_params.Set_Texture_Wrap_T(m_p_wrap_mode[1]);
	if(m_n_target == GL_TEXTURE_3D || m_n_target == GL_TEXTURE_CUBE_MAP)
		r_texture_params.Set_Texture_Wrap_R(m_p_wrap_mode[2]);
}

bool CTextureConfig::Parse(CMaterialLexer &r_scanner)
{
	_ASSERTE(r_scanner.n_Cur_Token() >= CMaterialLexer::token_Texture1D &&
		r_scanner.n_Cur_Token() <= CMaterialLexer::token_TextureCube);
	// otherwise this wouldn't be called

	int n_type = r_scanner.n_Cur_Token();
	if(n_type < CMaterialLexer::token_TextureCube)
		m_n_target = GL_TEXTURE_1D + (n_type - CMaterialLexer::token_Texture1D);
	else /*if(n_type == CMaterialLexer::token_TextureCube)*/
		m_n_target = GL_TEXTURE_CUBE_MAP;
	// determine texture type

	if(!r_scanner.Get_NextToken(CMaterialLexer::token_Int))
		return false;
	m_n_texture_unit = r_scanner.t_Cur_Token().n_Value();
	// read texture unit

	if(!r_scanner.Get_NextToken(CMaterialLexer::token_Begin))
		return false;
	// read { parenthesis

	for(;;) {
		if(!r_scanner.Get_NextToken())
			return false;
		if(r_scanner.n_Cur_Token() == CMaterialLexer::token_End)
			break;
		// read token

		if(r_scanner.n_Cur_Token() != CMaterialLexer::token_Node)
			return false;
		const char *p_s_node_name = r_scanner.t_Cur_Token().p_s_Value();
		const TNodeHandler *p_handler = m_p_handler_table;
		const TNodeHandler *p_end = m_p_handler_table +
			(sizeof(m_p_handler_table) / sizeof(m_p_handler_table[0]));
		for(; p_handler != p_end; ++ p_handler) {
			if(!strcmp(p_s_node_name, p_handler->p_s_node_name))
				break;
		}
		// recognize node type

		if(p_handler == p_end ||
		   !r_scanner.Get_NextToken(CMaterialLexer::token_Colon) ||
		   !r_scanner.Get_NextToken())
			return false;
		// skip past colon and get first token containing value

		if(!((this->*(p_handler->p_handler_func))(r_scanner, p_handler->n_flag)) ||
		   r_scanner.n_Cur_Token() != CMaterialLexer::token_Semicolon)
			return false;
		// call handler function to parse values, must leave semicolon behind
	}
	// read items

	return true;
}

/*
 *	static char *CTextureConfig::p_s_Parse_URI(CMaterialLexer &r_scanner)
 *		- parses URI sequence uri('string')
 *		- r_scanner is scanner, current token must be token_URI
 *		- returns string on success, 0 on failure (which might
 *		  involve insufficient memory for the string)
 *		- note returned string must be deleted when no longer needed
 */
char *CTextureConfig::p_s_Parse_URI(CMaterialLexer &r_scanner)
{
	_ASSERTE(r_scanner.n_Cur_Token() == CMaterialLexer::token_URI);
	// uri token must be current

	if(!r_scanner.Get_NextToken(CMaterialLexer::token_LeftPar) ||
	   !r_scanner.Get_NextToken(CMaterialLexer::token_String))
		return 0;
	// read left parenthesis and string token

	char *p_s_uri;
	if(!(p_s_uri = new(std::nothrow) char[strlen(r_scanner.t_Cur_Token().p_s_Value()) + 1]))
		return 0;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
	strcpy_s(p_s_uri, (strlen(r_scanner.t_Cur_Token().p_s_Value()) + 1) *
		sizeof(char), r_scanner.t_Cur_Token().p_s_Value());
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	strcpy(p_s_uri, r_scanner.t_Cur_Token().p_s_Value());
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	// copy the string token

	if(!r_scanner.Get_NextToken(CMaterialLexer::token_RightPar) ||
	   !r_scanner.Get_NextToken()) {
		delete[] p_s_uri;
		return 0;
	}
	// read right parenthesis and the next token for further parsing

	return p_s_uri;
}

/*
 *	bool CTextureConfig::Read_Source(CMaterialLexer &r_scanner, int n_flag)
 *		- reads source (flag 0) and alpha-source (flag 1)
 *		- returns true on success, false on failure
 */
bool CTextureConfig::Read_Source(CMaterialLexer &r_scanner, int n_flag)
{
	switch(r_scanner.n_Cur_Token()) {
	case CMaterialLexer::token_URI:
		m_n_source_type = tex_Static;
		// uri specifies static texture

		{
			char *&r_p_s_string = (n_flag)? m_p_s_source_alpha : m_p_s_source;
			if(r_p_s_string) {
				delete[] r_p_s_string;
				r_p_s_string = 0;
			}
			if(!(r_p_s_string = p_s_Parse_URI(r_scanner)))
				return false;
		}
		// parse uri

		return true/*r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon*/;

	case CMaterialLexer::token_Reflect:
	case CMaterialLexer::token_Refract:
	case CMaterialLexer::token_Custom:
	case CMaterialLexer::token_DepthMap:
		if(n_flag)
			return false;
		// alpha source can't be specified this way

		m_n_source_type = tex_Reflect +
			r_scanner.n_Cur_Token() - CMaterialLexer::token_Reflect;
		// note tex_* must be kept in the same order as token types

		return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);

	default:
		return false;
	}
}

/*
 *	bool CTextureConfig::Read_Format(CMaterialLexer &r_scanner, int n_flag)
 *		- reads internal-format
 *		- returns true on success, false on failure
 */
bool CTextureConfig::Read_Format(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	m_n_internal_format = r_scanner.t_Cur_Token().n_Value();
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CTextureConfig::Read_MipMap(CMaterialLexer &r_scanner, int n_flag)
 *		- reads generate-mipmap boolean flag
 *		- returns true on success, false on failure
 */
bool CTextureConfig::Read_MipMap(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_Bool)
		return false;
	m_b_mipmaps = r_scanner.t_Cur_Token().n_Value() != 0;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CTextureConfig::Read_Wrap(CMaterialLexer &r_scanner, int n_flag)
 *		- reads texture wrap modes; reads wrap (n_flag = 0), it can be followed
 *		  either by single enum value to set wrap mode on all texture's coords
 *		  or by multiple values (up to three) to set wrap modes for each
 *		  corresponding coord separately (starting with S)
 *		- reads wrap_s (n_flag = 1), wrap_t (n_flag = 2), wrap_r (n_flag = 3)
 *		- returns true on success, false on failure
 */
bool CTextureConfig::Read_Wrap(CMaterialLexer &r_scanner, int n_flag)
{
	if(n_flag) {
		_ASSERTE(n_flag >= 1 && n_flag <= 3);
		if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
			return false;
		m_p_wrap_mode[-- n_flag] = r_scanner.t_Cur_Token().n_Value();
		// set wrap mode with index n_flag - 1

		return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
	} else {
		int n_wrap_num = 0;
		for(; n_wrap_num < 3;) {
			if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
				return false;
			m_p_wrap_mode[n_wrap_num ++] = r_scanner.t_Cur_Token().n_Value();
			if(!r_scanner.Get_NextToken())
				return false;
			if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon)
				break;
		}
		// read up to three GLEnums to wrap modes

		if(n_wrap_num == 1)
			m_p_wrap_mode[1] = m_p_wrap_mode[2] = m_p_wrap_mode[0];
		// in case there was just one, set the two others to the same value

		return true/*r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon*/;
	}
}

/*
 *	bool CTextureConfig::Read_Filter(CMaterialLexer &r_scanner, int n_flag)
 *		- reads minification and magnification texture filter (n_flag = 0; in case
 *		  single value is supplied in material file, both filters are set to that
 *		  value. otherwise two values are required), minification filter (n_flag = 1)
 *		  or magnification filter (n_flag = 2)
 *		- returns true on success, false on failure
 */
bool CTextureConfig::Read_Filter(CMaterialLexer &r_scanner, int n_flag)
{
	if(!n_flag || n_flag == 1) {
		if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
			return false;
		m_n_min_filter = r_scanner.t_Cur_Token().n_Value();
		if(!r_scanner.Get_NextToken())
			return false;
	}
	if(!n_flag || n_flag == 2) {
		if(!n_flag && r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon) {
			m_n_mag_filter = m_n_min_filter;
			return true;
		} else {
			if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
				return false;
			m_n_mag_filter = r_scanner.t_Cur_Token().n_Value();
			if(!r_scanner.Get_NextToken())
				return false;
		}
	}
	return true/*r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon*/;
}

/*
 *	bool CTextureConfig::Read_LOD(CMaterialLexer &r_scanner, int n_flag)
 *		- reads all texture LOD parameters in order 'min max bias' (n_flag = 0),
 *		  lod-min (n_flag = 1), lod-max (n_flag = 2) or lod-bias (n_flag = 3)
 *		- returns true on success, false on failure
 */
bool CTextureConfig::Read_LOD(CMaterialLexer &r_scanner, int n_flag)
{
	if(!n_flag || n_flag == 1) {
		if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Float)
			m_f_min_lod = r_scanner.t_Cur_Token().f_Value();
		else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
			m_f_min_lod = float(r_scanner.t_Cur_Token().n_Value());
		else
			return false;
		if(!r_scanner.Get_NextToken())
			return false;
	}
	if(!n_flag || n_flag == 2) {
		if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Float)
			m_f_max_lod = r_scanner.t_Cur_Token().f_Value();
		else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
			m_f_max_lod = float(r_scanner.t_Cur_Token().n_Value());
		else
			return false;
		if(!r_scanner.Get_NextToken())
			return false;
	}
	if(!n_flag || n_flag == 3) {
		if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Float)
			m_f_lod_bias = r_scanner.t_Cur_Token().f_Value();
		else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
			m_f_lod_bias = float(r_scanner.t_Cur_Token().n_Value());
		else
			return false;
		if(!r_scanner.Get_NextToken())
			return false;
	}
	return true/*r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon*/;
}

/*
 *	bool CTextureConfig::Read_MipLevel(CMaterialLexer &r_scanner, int n_flag)
 *		- reads base and max mip-map level (n_flag = 0),
 *		  base mip-level (n_flag = 1) or max mip-level (n_flag = 2)
 *		- returns true on success, false on failure
 */
bool CTextureConfig::Read_MipLevel(CMaterialLexer &r_scanner, int n_flag)
{
	if(!n_flag || n_flag == 1) {
		if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
			m_n_base_level = r_scanner.t_Cur_Token().n_Value();
		else
			return false;
		if(!r_scanner.Get_NextToken())
			return false;
	}
	if(!n_flag || n_flag == 2) {
		if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
			m_n_max_level = r_scanner.t_Cur_Token().n_Value();
		else
			return false;
		if(!r_scanner.Get_NextToken())
			return false;
	}
	return true/*r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon*/;
}

/*
 *	bool CTextureConfig::Read_DepthTexMode(CMaterialLexer &r_scanner, int n_flag)
 *		- reads depth texture mode
 *		- returns true on success, false on failure
 */
bool CTextureConfig::Read_DepthTexMode(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	m_n_depth_tex_mode = r_scanner.t_Cur_Token().n_Value();
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CTextureConfig::Read_DepthTexCompare(CMaterialLexer &r_scanner, int n_flag)
 *		- reads depth texture comparison mode and function (n_flag = 0)
 *		  or comparison mode (n_flag = 1) or comparison function (n_flag = 2)
 *		- returns true on success, false on failure
 */
bool CTextureConfig::Read_DepthTexCompare(CMaterialLexer &r_scanner, int n_flag)
{
	if(!n_flag || n_flag == 1) {
		if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
			return false;
		m_n_depth_tex_compare_mode = r_scanner.t_Cur_Token().n_Value();
		if(!r_scanner.Get_NextToken())
			return false;
	}
	if(!n_flag || n_flag == 2) {
		if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
			return false;
		m_n_depth_tex_compare_func = r_scanner.t_Cur_Token().n_Value();
		if(!r_scanner.Get_NextToken())
			return false;
	}
	return true/*r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon*/;
}

/*
 *								=== ~CTextureConfig ===
 */

/*
 *								=== CMaterialPass ===
 */

class CMaterialPass::CFindConflict {
protected:
	int m_n_prev_tex_unit;

public:
	inline CFindConflict(const CTextureConfig *p_tex_data)
		:m_n_prev_tex_unit(p_tex_data->n_Texture_Unit())
	{}

	inline bool operator ()(const CTextureConfig *p_tex_data)
	{
		if(m_n_prev_tex_unit == p_tex_data->n_Texture_Unit())
			return true;
		m_n_prev_tex_unit = p_tex_data->n_Texture_Unit();
		return false;
	}
};

const CMaterialPass::TNodeHandler CMaterialPass::m_p_handler_table[] = {
	{"point-size", &CMaterialPass::Read_PointSize, 0},
	{"line-width", &CMaterialPass::Read_LineWidth, 0},
	{"polygon-mode", &CMaterialPass::Read_PolygonMode, 0},
	{"polygon-back-mode", &CMaterialPass::Read_PolygonMode, 1},
	{"polygon-front-mode", &CMaterialPass::Read_PolygonMode, 2},
	{"cull-face", &CMaterialPass::Read_CullFace, 0},
	{"front-face", &CMaterialPass::Read_FrontFace, 0},
	{"polygon-offset", &CMaterialPass::Read_PolygonOffset, 0},
	{"polygon-offset-mode", &CMaterialPass::Read_PolygonOffset_Mode, 0},
	{"blend-func", &CMaterialPass::Read_BlendFunc, 0},
	{"blend-equation", &CMaterialPass::Read_BlendEquation, 0},
	{"blend-color", &CMaterialPass::Read_BlendColor, 0},
	{"depth-test", &CMaterialPass::Read_DepthTest, 0},
	{"depth-func", &CMaterialPass::Read_DepthFunc, 0},
	{"stencil-test", &CMaterialPass::Read_StencilTest, 0},
	{"stencil-func", &CMaterialPass::Read_StencilFunc, 0},
	{"stencil-op", &CMaterialPass::Read_StencilOp, 0},
	{"alpha-test", &CMaterialPass::Read_AlphaTest, 0},
	{"alpha-func", &CMaterialPass::Read_AlphaFunc, 0},
	{"writemask", &CMaterialPass::Read_WriteMask, 0},
	{"stencil-mask", &CMaterialPass::Read_StencilWriteMask, 0},
	{"vertex-shader", &CMaterialPass::Read_Shader, 0},
	{"fragment-shader", &CMaterialPass::Read_Shader, 1}
};

/*
 *	CMaterialPass::CMaterialPass(CMaterialLexer &r_scanner)
 *		- default constructor; loads material pass from scanner r_scanner
 *		- precondition is current token is token_Pass
 *		- call b_Status() to see wheter parsing was successful
 */
CMaterialPass::CMaterialPass(CMaterialLexer &r_scanner)
	:m_p_s_name(0), m_f_point_size(1), m_f_line_width(1), m_b_cull_face(true),
	m_n_cull_face(GL_BACK), m_n_front_face(GL_CW), m_f_polygon_offset_factor(-1),
	m_f_polygon_offset_units(-2), m_n_blend_src(GL_ONE), m_n_blend_dst(GL_ONE),
	m_n_blend_equation(GL_FUNC_ADD), m_b_blend_func(false), m_b_blend_equation(false),
	m_b_depth_test(true), m_n_depth_func(GL_LESS),
	m_b_alpha_test(false), m_n_alpha_func(GL_ALWAYS), m_f_alpha_ref(0),
	m_b_stencil_test(false), m_n_stencil_func(GL_ALWAYS), m_n_stencil_ref(0),
	m_n_stencil_mask(~0), m_b_depth_write(true), m_n_stencil_write_mask(~0),
	m_p_s_vertex_shader(0), m_n_vertex_shader_type(shader_None),
	m_p_s_fragment_shader(0), m_n_fragment_shader_type(shader_None)
{
	for(int i = 0; i < 2; ++ i)
		m_p_polygon_mode[i] = GL_FILL;
	for(int i = 0; i < 3; ++ i)
		m_p_polygon_offset_enabled[i] = false;
	for(int i = 0; i < 3; ++ i)
		m_p_stencil_op[i] = GL_KEEP;
	for(int i = 0; i < 4; ++ i)
		m_p_color_write[i] = true;
	for(int i = 0; i < 4; ++ i)
		m_p_blend_color[i] = (i == 3);
	// set default values for everything

	if(r_scanner.b_Status() && !Parse(r_scanner) && m_p_s_name) {
		delete[] m_p_s_name;
		m_p_s_name = 0; // set zero name to mark the error
	}
	// parse from file
}

/*
 *	CMaterialPass::~CMaterialPass()
 *		- default destructor
 */
CMaterialPass::~CMaterialPass()
{
	if(m_p_s_name)
		delete[] m_p_s_name;
	if(m_p_s_vertex_shader)
		delete[] m_p_s_vertex_shader;
	if(m_p_s_fragment_shader)
		delete[] m_p_s_fragment_shader;
	std::for_each(m_texture_list.begin(), m_texture_list.end(),
		DeleteTextureData);
	// cleanup
}

/*
 *	bool CMaterialPass::b_Status() const
 *		- if constructor was successful, returns true. otherwise returns false
 */
bool CMaterialPass::b_Status() const
{
	return m_p_s_name != 0;
}

/*
 *	const char *CMaterialPass::p_s_Name() const
 *		- returns material name
 *		- note it returns 0 in case constructor failed
 */
const char *CMaterialPass::p_s_Name() const
{
	return m_p_s_name;
}

/*
 *	static inline void CMaterialPass::DeleteTextureData(CTextureConfig *p_tex_data)
 *		- cleanup-assist function for std::for_each
 */
inline void CMaterialPass::DeleteTextureData(CTextureConfig *p_tex_data)
{
	delete p_tex_data;
}

/*
 *	bool CMaterialPass::Parse(CMaterialLexer &r_scanner)
 *		- parses material pass using r_scanner, it supposes current token is token_Pass
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Parse(CMaterialLexer &r_scanner)
{
	_ASSERTE(r_scanner.n_Cur_Token() == CMaterialLexer::token_Pass);
	// this is how we get called, right?

	if(!r_scanner.Get_NextToken(CMaterialLexer::token_Node))
		return false;
	const char *p_s_name = r_scanner.t_Cur_Token().p_s_Value();
	if(!(m_p_s_name = new(std::nothrow) char[strlen(p_s_name) + 1]))
		return false;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
	strcpy_s(m_p_s_name, (strlen(p_s_name) + 1) * sizeof(char), p_s_name);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	strcpy(m_p_s_name, p_s_name);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	// read pass name

	if(!r_scanner.Get_NextToken(CMaterialLexer::token_Begin))
		return false;
	// read { parenthesis

	for(;;) {
		if(!r_scanner.Get_NextToken())
			return false;
		if(r_scanner.n_Cur_Token() == CMaterialLexer::token_End)
			break;
		// read token

		if(r_scanner.n_Cur_Token() >= CMaterialLexer::token_Texture1D &&
		   r_scanner.n_Cur_Token() <= CMaterialLexer::token_TextureCube) {
			if(!Read_TextureSection(r_scanner) ||
			   r_scanner.n_Cur_Token() != CMaterialLexer::token_End)
				return false;
			// parse texture section
		} else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Node) {
			const char *p_s_node_name = r_scanner.t_Cur_Token().p_s_Value();
			const TNodeHandler *p_handler = m_p_handler_table;
			const TNodeHandler *p_end = m_p_handler_table +
				(sizeof(m_p_handler_table) / sizeof(m_p_handler_table[0]));
			for(; p_handler != p_end; ++ p_handler) {
				if(!strcmp(p_s_node_name, p_handler->p_s_node_name))
					break;
			}
			// recognize node type

			if(p_handler == p_end ||
			   !r_scanner.Get_NextToken(CMaterialLexer::token_Colon) ||
			   !r_scanner.Get_NextToken())
				return false;
			// skip past colon and get first token containing value

			if(!((this->*(p_handler->p_handler_func))(r_scanner, p_handler->n_flag)) ||
			   r_scanner.n_Cur_Token() != CMaterialLexer::token_Semicolon)
				return false;
			// call handler function to parse values, must leave semicolon behind
		} else
			return false;
	}
	// read items

	std::sort(m_texture_list.begin(), m_texture_list.end(), CompareTextures);
	// sort by texture unit and in the same time 

	if(m_texture_list.size() && std::find_if(m_texture_list.begin() + 1,
	   m_texture_list.end(), CFindConflict(m_texture_list.front())) != m_texture_list.end())
		return false;
	// determine wheter two textures want the same texture unit as their target

	return true;
}

/*
 *	bool CMaterialPass::Read_PointSize(CMaterialLexer &r_scanner, int n_flag)
 *		- reads point-size
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_PointSize(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Float)
		m_f_point_size = r_scanner.t_Cur_Token().f_Value();
	else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
		m_f_point_size = GLfloat(r_scanner.t_Cur_Token().n_Value());
	else
		return false;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_LineWidth(CMaterialLexer &r_scanner, int n_flag)
 *		- reads line-width
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_LineWidth(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Float)
		m_f_line_width = r_scanner.t_Cur_Token().f_Value();
	else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
		m_f_line_width = GLfloat(r_scanner.t_Cur_Token().n_Value());
	else
		return false;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_PolygonMode(CMaterialLexer &r_scanner, int n_flag)
 *		- reads polygon-mode (n_flag = 0) which can contain either one
 *		  value (both sides) or two values (for each side, back goes first),
 *		  polygon-back-mode (n_flag = 1) or polygon-front-mode (n_flag = 2)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_PolygonMode(CMaterialLexer &r_scanner, int n_flag)
{
	for(int i = 0; i < 2; ++ i) {
		if(!n_flag || n_flag == i + 1) {
			if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum) {
				if(i && !n_flag && r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon) {
					m_p_polygon_mode[1] = m_p_polygon_mode[0];
					return true;
				}
				return false;
			}
			m_p_polygon_mode[i] = r_scanner.t_Cur_Token().n_Value();
			if(!r_scanner.Get_NextToken())
				return false;
		}
	}
	return true/*r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon*/;
}

/*
 *	bool CMaterialPass::Read_CullFace(CMaterialLexer &r_scanner, int n_flag)
 *		- reads cull-face
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_CullFace(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	m_n_cull_face = r_scanner.t_Cur_Token().n_Value();
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_FrontFace(CMaterialLexer &r_scanner, int n_flag)
 *		- reads front-face
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_FrontFace(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	m_n_front_face = r_scanner.t_Cur_Token().n_Value();
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_PolygonOffset(CMaterialLexer &r_scanner, int n_flag)
 *		- reads polygon offset (two float values: factor and units)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_PolygonOffset(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Float)
		m_f_polygon_offset_factor = r_scanner.t_Cur_Token().f_Value();
	else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
		m_f_polygon_offset_factor = GLfloat(r_scanner.t_Cur_Token().n_Value());
	else
		return false;
	if(!r_scanner.Get_NextToken())
		return false;
	if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Float)
		m_f_polygon_offset_units = r_scanner.t_Cur_Token().f_Value();
	else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
		m_f_polygon_offset_units = GLfloat(r_scanner.t_Cur_Token().n_Value());
	else
		return false;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_PolygonOffset_Mode(CMaterialLexer &r_scanner, int n_flag)
 *		- reads polygon-offset mode, consisting of one to three GLenums
 *		  GL_POINT GL_LINE GL_FILL which toggles polygon offset for the
 *		  specified primitive(s)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_PolygonOffset_Mode(CMaterialLexer &r_scanner, int n_flag)
{
	for(int i = 0; i < 3; ++ i)
		m_p_polygon_offset_enabled[i] = false;
	// clear polygon offset toggles

	do {
		if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
			return false;
		int n_primitive = r_scanner.t_Cur_Token().n_Value();
		// get primitive name

		switch(n_primitive) {
		case GL_POINT:
			if(m_p_polygon_offset_enabled[0])
				return false; // do not allow repeating one multiple times
			m_p_polygon_offset_enabled[0] = true;
			break;
		case GL_LINE:
			if(m_p_polygon_offset_enabled[1])
				return false; // do not allow repeating one multiple times
			m_p_polygon_offset_enabled[1] = true;
			break;
		case GL_FILL:
			if(m_p_polygon_offset_enabled[2])
				return false; // do not allow repeating one multiple times
			m_p_polygon_offset_enabled[2] = true;
			break;
		default:
			return false;
		}
		// enable polygon offset for specified prim

		if(!r_scanner.Get_NextToken())
			return false;
	} while(r_scanner.n_Cur_Token() != CMaterialLexer::token_Semicolon);

	return true;
}

/*
 *	bool CMaterialPass::Read_BlendFunc(CMaterialLexer &r_scanner, int n_flag)
 *		- reads blend-func (two GLenums)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_BlendFunc(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	m_n_blend_src = r_scanner.t_Cur_Token().n_Value();
	if(!r_scanner.Get_NextToken(CMaterialLexer::token_GLenum))
		return false;
	m_n_blend_dst = r_scanner.t_Cur_Token().n_Value();
	m_b_blend_equation = false;
	m_b_blend_func = true;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_BlendEquation(CMaterialLexer &r_scanner, int n_flag)
 *		- reads blend-equation (a single GLenum)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_BlendEquation(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	m_n_blend_equation = r_scanner.t_Cur_Token().n_Value();
	m_b_blend_equation = true;
	m_b_blend_func = false;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_BlendColor(CMaterialLexer &r_scanner, int n_flag)
 *		- reads blend-color, which is three or four floating-point values (rgb / rgba)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_BlendColor(CMaterialLexer &r_scanner, int n_flag)
{
	for(int i = 0; i < 4; ++ i) {
		if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Float)
			m_p_blend_color[i] = r_scanner.t_Cur_Token().f_Value();
		else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
			m_p_blend_color[i] = GLfloat(r_scanner.t_Cur_Token().n_Value());
		else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon && i == 3) {
			m_p_blend_color[3] = 1.0f;
			return true;
		} else
			return false;
		if(!r_scanner.Get_NextToken())
			return false;
	}
	return true/*r_scanner.n_Cur_Token() == CMaterialLexer::token_Semicolon*/;
}

/*
 *	bool CMaterialPass::Read_DepthTest(CMaterialLexer &r_scanner, int n_flag)
 *		- reads depth test enable flag (a single bool value)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_DepthTest(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_Bool)
		return false;
	m_b_depth_test = r_scanner.t_Cur_Token().n_Value() != 0;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_DepthFunc(CMaterialLexer &r_scanner, int n_flag)
 *		- reads depth func which happens to be a single GLenum
 *		- enables depth testing (as it makes sense it's necessary
 *		  to use it when setting up depth test function)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_DepthFunc(CMaterialLexer &r_scanner, int n_flag)
{
	m_b_depth_test = true;
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	m_n_depth_func = r_scanner.t_Cur_Token().n_Value();
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_StencilTest(CMaterialLexer &r_scanner, int n_flag)
 *		- reads stencil test enable flag (a single bool value)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_StencilTest(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_Bool)
		return false;
	m_b_stencil_test = r_scanner.t_Cur_Token().n_Value() != 0;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_StencilFunc(CMaterialLexer &r_scanner, int n_flag)
 *		- reads stencil test function which is GLenum comparison
 *		  function followed by two integers, reference and mask
 *		- enables stencil test (as it makes sense it's necessary
 *		  to use it when setting up stencil test function)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_StencilFunc(CMaterialLexer &r_scanner, int n_flag)
{
	m_b_stencil_test = true;
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	m_n_stencil_func = r_scanner.t_Cur_Token().n_Value();
	if(!r_scanner.Get_NextToken(CMaterialLexer::token_Int))
		return false;
	m_n_stencil_ref = r_scanner.t_Cur_Token().n_Value();
	if(!r_scanner.Get_NextToken(CMaterialLexer::token_Int))
		return false;
	m_n_stencil_mask = (r_scanner.t_Cur_Token().n_Value() > 0)?
		r_scanner.t_Cur_Token().n_Value() : ~0; // entering negative value = full mask
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_StencilOp(CMaterialLexer &r_scanner, int n_flag)
 *		- reads stencil op which happens to be three GLenums (sfail zfail zpass)
 *		- enables stencil test (as it makes sense it's necessary
 *		  to use it when setting up stencil op function)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_StencilOp(CMaterialLexer &r_scanner, int n_flag)
{
	m_b_stencil_test = true;
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	for(int i = 0; i < 3; ++ i) {
		m_p_stencil_op[i] = r_scanner.t_Cur_Token().n_Value();
		if(!r_scanner.Get_NextToken((i < 2)?
		   CMaterialLexer::token_GLenum : CMaterialLexer::token_Semicolon))
			return false;
	}
	return true;
}

/*
 *	bool CMaterialPass::Read_AlphaTest(CMaterialLexer &r_scanner, int n_flag)
 *		- reads alpha test enable flag (a single bool value)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_AlphaTest(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_Bool)
		return false;
	m_b_alpha_test = r_scanner.t_Cur_Token().n_Value() != 0;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_AlphaFunc(CMaterialLexer &r_scanner, int n_flag)
 *		- reads alpha function which consists of comparison op GLenum and reference value
 *		- enables alpha test (as it makes sense it's necessary
 *		  to use it when setting up alpha test function)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_AlphaFunc(CMaterialLexer &r_scanner, int n_flag)
{
	m_b_alpha_test = true;
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum)
		return false;
	m_n_alpha_func = r_scanner.t_Cur_Token().n_Value();
	if(!r_scanner.Get_NextToken())
		return false;
	if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Float)
		m_f_alpha_ref = r_scanner.t_Cur_Token().f_Value();
	else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Int)
		m_f_alpha_ref = GLfloat(r_scanner.t_Cur_Token().n_Value());
	else
		return false;
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_WriteMask(CMaterialLexer &r_scanner, int n_flag)
 *		- reads write-mask, a simple string containing letters of channels to be written
 *		- default is "dsrgba" (depth stencil RGBA)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_WriteMask(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_String)
		return false;
	const char *p_s_mask = r_scanner.t_Cur_Token().p_s_Value();
	int n_count = 0;
	for(int i = 0; i < 6; ++ i) {
		const char *p_s_tmp;
		bool b_write;
		if(b_write = ((p_s_tmp = strchr(p_s_mask, "rgbads"[i])) != 0)) {
			if(strchr(p_s_tmp + 1, "rgbads"[i]))
				return false; // deny duplicate letters
			++ n_count;
		}
		if(i < 4)
			m_p_color_write[i] = b_write;
		else if(i == 4)
			m_b_depth_write = b_write;
		else
			m_n_stencil_write_mask = (b_write)? ~0 : 0;
	}
	if(n_count != strlen(p_s_mask))
		return false; // deny additional letters
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_StencilWriteMask(CMaterialLexer &r_scanner, int n_flag)
 *		- reads stencil write-mask, a 32 bit integer mask
 *		- default is 0xffffffff (full 32 bits of 1's)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_StencilWriteMask(CMaterialLexer &r_scanner, int n_flag)
{
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_Int)
		return false;
	m_n_stencil_write_mask = r_scanner.t_Cur_Token().n_Value();
	return r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/);
}

/*
 *	bool CMaterialPass::Read_Shader(CMaterialLexer &r_scanner, int n_flag)
 *		- reads vertex shader (n_flag = 0) or fragment shader (n_flag = 1)
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_Shader(CMaterialLexer &r_scanner, int n_flag)
{
	int n_shader_type;
	char *p_s_shader;
	if(r_scanner.n_Cur_Token() == CMaterialLexer::token_URI) {
		n_shader_type = shader_File;
		if(!(p_s_shader = CTextureConfig::p_s_Parse_URI(r_scanner)))
			return false;
	} else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_String) {
		n_shader_type = shader_Inline;
		const char *p_s_src = r_scanner.t_Cur_Token().p_s_Value();
		if(!(p_s_shader = new(std::nothrow) char[strlen(p_s_src) + 1]))
			return false;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		strcpy_s(p_s_shader, (strlen(p_s_src) + 1) * sizeof(char), p_s_src);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		strcpy(p_s_shader, p_s_src);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		if(!r_scanner.Get_NextToken(/*CMaterialLexer::token_Semicolon*/))
			return false;
	} else
		return false;
	if(n_flag) {
		m_n_fragment_shader_type = n_shader_type;
		m_p_s_fragment_shader = p_s_shader;
	} else {
		m_n_vertex_shader_type = n_shader_type;
		m_p_s_vertex_shader = p_s_shader;
	}
	return true;
}

/*
 *	bool CMaterialPass::Read_TextureSection(CMaterialLexer &r_scanner)
 *		- reads texture section, adds a new CTextureConfig to m_texture_list
 *		- returns true on success, false on failure
 */
bool CMaterialPass::Read_TextureSection(CMaterialLexer &r_scanner)
{
	if(!stl_ut::Reserve_1More(m_texture_list))
		return false;
	CTextureConfig *p_texture;
	if(!(p_texture = new(std::nothrow) CTextureConfig(r_scanner)))
		return false;
	if(!p_texture->b_Status()) {
		delete p_texture;
		return false;
	}
	m_texture_list.push_back(p_texture);
	return true;
}

/*
 *	inline bool CMaterialPass::CompareTextures(const CTextureConfig *p_tex0, const CTextureConfig *p_tex1)
 *		- less-comparison adaptor for std::sort
 */
inline bool CMaterialPass::CompareTextures(const CTextureConfig *p_tex0, const CTextureConfig *p_tex1)
{
	return *p_tex0 < *p_tex1;
}

/*
 *								=== ~CMaterialPass ===
 */

/*
 *								=== CMaterial ===
 */

class CMaterial::CFindPass {
protected:
	const char *m_p_s_name;

public:
	CFindPass(const char *p_s_name)
		:m_p_s_name(p_s_name)
	{}

	inline bool operator ()(const CMaterialPass *p_pass) const
	{
		return *p_pass == m_p_s_name;
	}
};

/*
 *	CMaterial::CMaterial(CMaterialLexer &r_scanner)
 *		- default constructor
 *		- r_scanner is scanner with opened material file
 *		- call b_Status() to see wheter parsing was successful
 */
CMaterial::CMaterial(CMaterialLexer &r_scanner)
	:m_p_s_name(0)
{
	if(r_scanner.b_Status() && !Parse(r_scanner) && m_p_s_name) {
		delete[] m_p_s_name;
		m_p_s_name = 0; // delete name to mark error
	}
}

/*
 *	CMaterial::~CMaterial()
 *		- default destructor
 */
CMaterial::~CMaterial()
{
	std::for_each(m_pass_list.begin(), m_pass_list.end(), DeleteMaterialPass);
	if(m_p_s_name)
		delete[] m_p_s_name;
}

/*
 *	bool CMaterial::b_Status() const
 *		- if constructor succeeded in parsing
 *		  material returns true, otherwise returns false
 */
bool CMaterial::b_Status() const
{
	return m_p_s_name != 0;
}

/*
 *	const char *CMaterial::p_s_Name() const
 *		- returns material name
 *		- note if constructor failed this returns 0
 */
const char *CMaterial::p_s_Name() const
{
	return m_p_s_name;
}

/*
 *	int CMaterial::n_Pass_Num() const
 *		- returns number of material passes
 */
int CMaterial::n_Pass_Num() const
{
	return m_pass_list.size();
}

/*
 *	int CMaterial::n_Find_Pass(const char *p_s_name) const
 *		- finds pass by name p_s_name
 *		- returns pass index or in case no pass called p_s_name was found, returns -1
 *		- note this isn't optimised and shouldn't be called on pass-bind basis
 */
int CMaterial::n_Find_Pass(const char *p_s_name) const
{
	std::vector<CMaterialPass*>::const_iterator p_pass_iter;
	if((p_pass_iter = std::find_if(m_pass_list.begin(),
	   m_pass_list.end(), CFindPass(p_s_name))) != m_pass_list.end())
		return p_pass_iter - m_pass_list.begin();
	return -1;
}

/*
 *	static inline void CMaterial::DeleteMaterialPass(CMaterialPass *p_pass)
 *		- deletes p_pass; to be used in std::for_each in cleanup
 */
inline void CMaterial::DeleteMaterialPass(CMaterialPass *p_pass)
{
	delete p_pass;
}

/*
 *	bool CMaterial::Parse(CMaterialLexer &r_scanner)
 *		- parses material file
 *		- r_scanner is scanner with opened material file
 *		- returns true on success, false on failure
 */
bool CMaterial::Parse(CMaterialLexer &r_scanner)
{
	if(r_scanner.n_Cur_Token() == CMaterialLexer::token_EOF && !r_scanner.Get_NextToken())
		return false;
	// start up scanner

	while(r_scanner.n_Cur_Token() != CMaterialLexer::token_EOF) {
		if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Pass) {
			if(!Read_PassSection(r_scanner) ||
			   r_scanner.n_Cur_Token() != CMaterialLexer::token_End ||
			   !r_scanner.Get_NextToken())
				return false;
			// read pass
		} else if(r_scanner.n_Cur_Token() == CMaterialLexer::token_Node) {
			if(strcmp(r_scanner.t_Cur_Token().p_s_Value(), "material-name"))
				return false; // does not handle any other nodes for now
			if(!r_scanner.Get_NextToken(CMaterialLexer::token_Colon) ||
			   !r_scanner.Get_NextToken(CMaterialLexer::token_String))
				return false;
			if(m_p_s_name) {
				delete[] m_p_s_name;
				m_p_s_name = 0; // can re-specify as many times as desired
			}
			const char *p_s_src = r_scanner.t_Cur_Token().p_s_Value();
			if(!(m_p_s_name = new(std::nothrow) char[strlen(p_s_src) + 1]))
				return false;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
			strcpy_s(m_p_s_name, (strlen(p_s_src) + 1) * sizeof(char), p_s_src);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			strcpy(m_p_s_name, p_s_src);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
			if(!r_scanner.Get_NextToken(CMaterialLexer::token_Semicolon) ||
			   !r_scanner.Get_NextToken())
				return false;
			// read material name
		} else
			return false;
	}
	// parse material passes and name

	if(!m_p_s_name) {
		const char *p_s_src = "<unnamed>";
		if(!(m_p_s_name = new(std::nothrow) char[strlen(p_s_src) + 1]))
			return false;
#if defined(_MSC_VER) && !defined(__MWERKS__) && _MSC_VER >= 1400
		strcpy_s(m_p_s_name, (strlen(p_s_src) + 1) * sizeof(char), p_s_src);
#else // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
		strcpy(m_p_s_name, p_s_src);
#endif // _MSC_VER && !__MWERKS__ && _MSC_VER >= 1400
	}
	// assign default name

	return true;
}

/*
 *	bool CMaterial::Read_PassSection(CMaterialLexer &r_scanner)
 *		- read section with material pass
 *		- returns true on success, false on failure
 */
bool CMaterial::Read_PassSection(CMaterialLexer &r_scanner)
{
	if(!stl_ut::Reserve_1More(m_pass_list))
		return false;
	CMaterialPass *p_pass;
	if(!(p_pass = new(std::nothrow) CMaterialPass(r_scanner)))
		return false;
	if(!p_pass->b_Status()) {
		delete p_pass;
		return false;
	}
	m_pass_list.push_back(p_pass);
	// read rendering pass

	return true;
}

/*
 *								=== ~CMaterial ===
 */

/*
 *								=== CShaderInfo ===
 */

/*
 *	CShaderInfo::CShaderInfo(CMaterialLexer &r_scanner)
 *		- default constructor
 *		- r_scanner is scanner with opened shader file
 *		- call b_Status() to see wheter parsing was successful
 */
CShaderInfo::CShaderInfo(CMaterialLexer &r_scanner)
	:m_p_info(0)
{
	if(!Parse(r_scanner) && m_p_info) {
		delete m_p_info;
		m_p_info = 0;
	}
}

/*
 *	CShaderInfo::~CShaderInfo()
 *		- default destructor
 */
CShaderInfo::~CShaderInfo()
{
	if(m_p_info)
		delete m_p_info;
}

/*
 *	bool CShaderInfo::b_Status() const
 *		- if constructor succeeded in parsing
 *		  shader returns true, otherwise returns false
 */
bool CShaderInfo::b_Status() const
{
	return m_p_info != 0;
}

/*
 *	const char *CShaderInfo::p_s_Name() const
 *		- returns shader name
 *		- note if constructor failed this returns 0
 */
const char *CShaderInfo::p_s_Name() const
{
	return (m_p_info)? m_p_info->p_s_name : 0;
}

/*
 *	const TShaderInfo *CShaderInfo::p_ShaderInfo() const
 *		- returns const pointer to TShaderInfo (for constructing shader object)
 */
const TShaderInfo *CShaderInfo::p_ShaderInfo() const
{
	return m_p_info;
}

/*
 *	bool CShaderInfo::Parse(CMaterialLexer &r_scanner)
 *		- parses shader info from r_scanner (assuming newly opened
 *		  file that contains no other data than shader)
 *		- returns true on success, false on failure
 */
bool CShaderInfo::Parse(CMaterialLexer &r_scanner)
{
	_ASSERTE(!m_p_info); // called in constructor only

	if(r_scanner.n_Cur_Token() == CMaterialLexer::token_EOF && !r_scanner.Get_NextToken())
		return false;
	// start up scanner

	int n_target = -1; // 0 = program, 1 = shader
	int n_processor = -1; // vertex / geometry / fragment
	std::string s_name, s_source;
	std::string s_parameters; // parameter names scopes and locations. separated by '|' and ':' (like 'n:s:l|n:s:l')
	std::string s_texunits; // texture units separated by '|' (required for programs, explicit assignment for shaders)

	int n_prim_type_in = GL_TRIANGLES;
	int n_prim_type_out = GL_TRIANGLE_STRIP;
	int n_vertex_buffer_size = 0; // geometry shader parameters

	bool b_geom_props = false; // geometry shader parameters specification flag

	while(r_scanner.n_Cur_Token() == CMaterialLexer::token_Node) {
		const char *p_name_list[] = {
			"target", "name", "source", "parameter-list", "sampler-list",
			"primitive-type-in", "primitive-type-out", "vertex-buffer-size"
		};
		const char *p_s_node = r_scanner.t_Cur_Token().p_s_Value();
		int n_index = -1;
		for(int i = 0; i < sizeof(p_name_list) / sizeof(p_name_list[0]); ++ i) {
			if(!strcmp(p_s_node, p_name_list[i])) {
				n_index = i;
				break;
			}
		}
		// find node type (don't need handler functions here, it's very simple)

		if(!r_scanner.Get_NextToken(CMaterialLexer::token_Colon) ||
		   !r_scanner.Get_NextToken())
			return false;
		// followed by colon and value

		if((n_index == 0 && r_scanner.n_Cur_Token() != CMaterialLexer::token_Node) ||
		   ((n_index == 1 || n_index == 3 || n_index == 4) &&
		   r_scanner.n_Cur_Token() != CMaterialLexer::token_String) ||
		   (n_index == 2 && r_scanner.n_Cur_Token() != CMaterialLexer::token_CData &&
		   r_scanner.n_Cur_Token() != CMaterialLexer::token_String) ||
		   ((n_index == 5 || n_index == 6) &&
		   r_scanner.n_Cur_Token() != CMaterialLexer::token_GLenum &&
		   r_scanner.n_Cur_Token() != CMaterialLexer::token_Int) ||
		   (n_index == 7 && r_scanner.n_Cur_Token() != CMaterialLexer::token_Int))
			return false;
		// target is followed by node, source can be cdata / string, parameter / sampler lists
		// are strings, primitive types are glenums / ints and vertex buffer size is int

		switch(n_index) {
		case 0: // target
			{
				const char *p_s_node = r_scanner.t_Cur_Token().p_s_Value();

				const char *p_processor_list[] = {"vertex-", "geometry-", "fragment-"};
				const int p_processor_id_list[] = {TShaderInfo::proc_Vertex,
					TShaderInfo::proc_Geometry, TShaderInfo::proc_Fragment}; // id's != indices
				for(int i = 0;; ++ i) {
					unsigned int n_len = strlen(p_processor_list[i]);
					if(!strncmp(p_s_node, p_processor_list[i], n_len)) {
						p_s_node += n_len;
						n_processor = p_processor_id_list[i];
						break;
					}
					if(i == 2)
						return false;
				}
				// identifies processor type

				const char *p_target_list[] = {"program", "shader"};
				for(int i = 0;; ++ i) {
					if(!strcmp(p_s_node, p_target_list[i])) {
						n_target = i;
						break;
					}
					if(i == 1)
						return false;
				}
				// identifies target
			}
			break;
		case 1: // name
			{
				const char *p_s_src_string = r_scanner.t_Cur_Token().p_s_Value();
				if(!stl_ut::AssignCStr(s_name, p_s_src_string))
					return false;
				// copy name string
			}
			break;
		case 2: // source
			{
				const char *p_s_src_string = r_scanner.t_Cur_Token().p_s_Value();
				if(!stl_ut::AssignCStr(s_source, p_s_src_string))
					return false;
				// copy source string
			}
			break;
		case 3: // param-list
			{
				const char *p_s_src_string = r_scanner.t_Cur_Token().p_s_Value();
				if(!stl_ut::AssignCStr(s_parameters, p_s_src_string))
					return false;
				// copy param-list string
			}
			break;
		case 4: // sampler-list
			{
				const char *p_s_src_string = r_scanner.t_Cur_Token().p_s_Value();
				if(!stl_ut::AssignCStr(s_texunits, p_s_src_string))
					return false;
				// copy sampler-list string
			}
			break;
		case 5: // primitive-type-in
			b_geom_props = true;
			n_prim_type_in = r_scanner.t_Cur_Token().n_Value();
			break;
		case 6: // primitive-type-out
			b_geom_props = true;
			n_prim_type_out = r_scanner.t_Cur_Token().n_Value();
			break;
		case 7: // vertex-buffer-size
			b_geom_props = true;
			if(r_scanner.t_Cur_Token().n_Type() != CMaterialLexer::token_Int)
				return false;
			n_vertex_buffer_size = r_scanner.t_Cur_Token().n_Value();
			break;
		default:
			return false;
		}

		if(!r_scanner.Get_NextToken(CMaterialLexer::token_Semicolon) ||
		   !r_scanner.Get_NextToken())
			return false;
		// followed by semicolon and something next (node / EOF)
	}
	if(r_scanner.n_Cur_Token() != CMaterialLexer::token_EOF)
		return false;
	// parse shader file

	if(n_target < 0 || n_processor < 0 || s_name.empty() || s_source.empty() ||
	   (n_target == 0 && (s_parameters.empty() || s_texunits.empty())))
		return false;
	// check if no value is missing

	if(n_processor == TShaderInfo::proc_Geometry && (n_target != 1 || n_prim_type_in < 0 ||
	   n_prim_type_out < 0 || n_vertex_buffer_size <= 0))
		return false;
	// check wheter geometry shader has all it's properties and is compiled as shader
	// (ÜberLame supports geometry shaders, but not geometry programs)

	if(n_processor != TShaderInfo::proc_Geometry && b_geom_props)
		return false;
	// other than geometry shader can't use these values

	if(!n_target) {
		if(!(m_p_info = new(std::nothrow) TShaderInfo(s_name.c_str(), n_processor, s_source.c_str(),
		   s_parameters.c_str(), s_texunits.c_str())) || !m_p_info->p_low)
			return false;
	} else {
		if(!(m_p_info = new(std::nothrow) TShaderInfo(s_name.c_str(), n_processor, s_source.c_str(),
		   (s_texunits.empty())? 0 : s_texunits.c_str())) || !m_p_info->p_high)
			return false;
	}
	// construct shader info struct

	if(n_processor == TShaderInfo::proc_Geometry) {
		m_p_info->n_geometry_in_type = n_prim_type_in;
		m_p_info->n_geometry_out_type = n_prim_type_out;
		m_p_info->n_max_vertex_num = n_vertex_buffer_size;
	}
	// set geometry shader properties

	return true;
}

/*
 *								=== ~CShaderInfo ===
 */
