#include "../plugin.h"
#include "shader.h"
static plugfsfuncs_t *fsfuncs;

typedef struct shaderparsestate_s parsestate_t;

typedef struct
{
	char **savefile;
	char *sourcefile;
	char type[MAX_QPATH];
	char normalmap[MAX_QPATH];
	struct
	{
		char name[MAX_QPATH];
	} tex[5];
	char envmap[MAX_QPATH];
	char envmapmask[MAX_QPATH];
	char envfrombase;
	char halflambert;

	float alphatestref;
	char *blendfunc;
	qboolean alphatest;
	qboolean culldisable;
	qboolean ignorez;
	char *replaceblock;
} vmtstate_t;


static void VARGS Q_strlcatfz (char *dest, size_t *offset, size_t size, const char *fmt, ...) LIKEPRINTF(4);
static void VARGS Q_strlcatfz (char *dest, size_t *offset, size_t size, const char *fmt, ...)
{
	va_list		argptr;

	dest += *offset;
	size -= *offset;

	va_start (argptr, fmt);
	Q_vsnprintfz(dest, size, fmt, argptr);
	va_end (argptr);
	*offset += strlen(dest);
}
static void Q_StrCat(char **ptr, const char *append)
{
	size_t oldlen = *ptr?strlen(*ptr):0;
	size_t newlen = strlen(append);
	char *newptr = plugfuncs->Malloc(oldlen+newlen+1);
	memcpy(newptr, *ptr, oldlen);
	memcpy(newptr+oldlen, append, newlen);
	newptr[oldlen+newlen] = 0;
	plugfuncs->Free(*ptr);
	*ptr = newptr;
}

//case comparisons are specific to ascii only, so this should be 'safe' for utf-8 strings too.
int Q_strncasecmp (const char *s1, const char *s2, int n)
{
	int		c1, c2;

	while (1)
	{
		c1 = *s1++;
		c2 = *s2++;

		if (!n--)
			return 0;		// strings are equal until end point

		if (c1 != c2)
		{
			if (c1 >= 'a' && c1 <= 'z')
				c1 -= ('a' - 'A');
			if (c2 >= 'a' && c2 <= 'z')
				c2 -= ('a' - 'A');
			if (c1 != c2)
			{	// strings not equal
				if (c1 > c2)
					return 1;		// strings not equal
				return -1;
			}
		}
		if (!c1)
			return 0;		// strings are equal
//		s1++;
//		s2++;
	}

	return -1;
}
int Q_strcasecmp (const char *s1, const char *s2)
{
	return Q_strncasecmp (s1, s2, 0x7fffffff);
}

static qboolean VMT_ReadVMT(const char *materialname, vmtstate_t *st);	//this is made more complicated on account of includes allowing recursion
static char *VMT_ParseBlock(const char *fname, vmtstate_t *st, char *line)
{	//assumes the open { was already parsed, but will parse the close.
	char *replace = NULL;
	com_tokentype_t ttype;
	char key[MAX_OSPATH];
	char value[MAX_OSPATH];
	char *qmark;
	qboolean cond;
	for(;line;)
	{
		line = cmdfuncs->ParseToken(line, key, sizeof(key), &ttype);
		if (ttype == TTP_RAWTOKEN && !strcmp(key, "}"))
			break;	//end-of-block
		line = cmdfuncs->ParseToken(line, value, sizeof(value), &ttype);
		if (ttype == TTP_RAWTOKEN && !strcmp(value, "{"))
		{	//sub block. we don't go into details here.
			if (!Q_strcasecmp(key, "replace"))
				replace = line;
			else
				Con_DPrintf("%s: Unknown block \"%s\"\n", fname, key);
			line = VMT_ParseBlock(fname, NULL, line);
			continue;
		}

		while ((qmark = strchr(key, '?')))
		{
			*qmark++ = 0;
			if (!Q_strcasecmp(key, "srgb"))
				cond = false;//!!(vid.flags & VID_SRGBAWARE);
			else
			{
				Con_DPrintf("%s: Unknown vmt conditional \"%s\"\n", fname, key);
				cond = false;
			}
			if (!cond)
			{
				*key = 0;
				break;
			}
			else
				memmove(key, qmark, strlen(qmark)+1);
		}

		if (!*key || !st)
			;
		else if (!Q_strcasecmp(key, "include"))
		{
			if (!VMT_ReadVMT(value, st))
				return NULL;
		}
		else if (!Q_strcasecmp(key, "$basetexture") || !Q_strcasecmp(key, "$hdrbasetexture"))	//fixme: hdr version should probably override the other. order matters.
			Q_strlcpy(st->tex[0].name, value, sizeof(st->tex[0].name));
		else if (!Q_strcasecmp(key, "$hdrcompressedtexture"))	//named texture is R8G8B8E8 and needs to be decompressed manually... should probably just use e5bgr9 but we don't have a way to transcode it here.
			;
		else if (!Q_strcasecmp(key, "$basetexturetransform"))
			;
		else if (!Q_strcasecmp(key, "$bumpmap")) // same as normalmap ~eukara
		{
			Q_strlcpy(st->normalmap, value, sizeof(st->normalmap));
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		}
		else if (!Q_strcasecmp(key, "$ssbump"))
			;
		else if (!Q_strcasecmp(key, "$ssbumpmathfix"))
			;
		else if (!Q_strcasecmp(key, "$basetexture2") || !strcmp(key, "$texture2"))
			Q_strlcpy(st->tex[1].name, value, sizeof(st->tex[1].name));
		else if (!Q_strcasecmp(key, "$basetexturetransform2"))
			;
		else if (!Q_strcasecmp(key, "$surfaceprop"))
			;

		else if (!Q_strcasecmp(key, "$ignorez"))
			st->ignorez = !!atoi(value);
		else if (!Q_strcasecmp(key, "$nocull") && (!strcmp(value, "1")||!strcmp(value, "0")))
			st->culldisable = atoi(value);
		else if (!Q_strcasecmp(key, "$alphatest") && (!strcmp(value, "1")||!strcmp(value, "0")))
			st->alphatest = atoi(value);
		else if (!Q_strcasecmp(key, "$alphatestreference"))
			st->alphatestref = atof(value);
		else if (!Q_strcasecmp(key, "$alphafunc"))
		{
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		}
		else if (!Q_strcasecmp(key, "$alpha"))
		{
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		}
		else if (!Q_strcasecmp(key, "$translucent"))
		{
			if (atoi(value))
				st->blendfunc = "src_alpha one_minus_src_alpha\n";
		}
		else if (!Q_strcasecmp(key, "$additive"))
		{
			if (atoi(value))
				st->blendfunc = "src_one one_minus_src_alpha\n";
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		}
		else if (!Q_strcasecmp(key, "$halflambert"))
			st->halflambert = 1;
		else if (!Q_strcasecmp(key, "$color"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$vertexcolor"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$vertexalpha"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$decal"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$decalscale"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$decalsize"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$envmap"))
			Q_strlcpy(st->envmap, value, sizeof(st->envmap));
		else if (!Q_strcasecmp(key, "$envmapmask"))
			Q_strlcpy(st->envmapmask, value, sizeof(st->envmapmask));
		else if (!Q_strcasecmp(key, "$envmapcontrast"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$envmaptint"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$envmapsaturation"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$basealphaenvmapmask"))
			st->envfrombase=1;
		else if (!Q_strcasecmp(key, "$normalmapalphaenvmapmask"))
			st->envfrombase=0;
		else if (!Q_strcasecmp(key, "$crackmaterial"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$selfillum"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$selfillummask"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$selfillumtint"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$nofog"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$nomip"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$nodecal"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$detail"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$detailscale"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$detailtint"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$detailblendfactor"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$detailblendmode"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);

		else if (!Q_strcasecmp(key, "$surfaceprop2"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$AllowAlphaToCoverage"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$blendmodulatetexture"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);

		//water/reflection stuff
		else if (!Q_strcasecmp(key, "$refracttinttexture"))
			Q_strlcpy(st->tex[0].name, value, sizeof(st->tex[0].name));
		else if (!Q_strcasecmp(key, "$refracttexture"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$refractamount"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$refracttint"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$reflecttexture"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$reflectamount"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$reflecttint"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$fresnelpower"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$minreflectivity"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$maxreflectivity"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$normalmap"))
		{
			Q_strlcpy(st->normalmap, value, sizeof(st->normalmap));
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		}
		else if (!Q_strcasecmp(key, "$bumpframe"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$fogenable"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$fogcolor"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$fogstart"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$fogend"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$abovewater"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$underwateroverlay"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$reflectentities"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$scale"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$bottommaterial"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$scroll1"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);
		else if (!Q_strcasecmp(key, "$scroll2"))
			Con_DPrintf("%s: %s \"%s\"\n", fname, key, value);

		else if (*key == '%')
			;	//editor lines
		else
			Con_DPrintf("%s: Unknown field \"%s\"\n", fname, key);
	}
	if (replace)
		VMT_ParseBlock(fname, st, replace);
	return line;
}
static void Shader_GenerateFromVMT(parsestate_t *ps, vmtstate_t *st, const char *shortname, void (*LoadMaterialString)(parsestate_t *ps, const char *script))
{
	size_t offset = 0;
	char script[8192];
	char *progargs = "";

	if (!*st->tex[0].name)	//fill in a default...
		Q_strlcpy(st->tex[0].name, shortname, sizeof(st->tex[0].name));

	if (st->alphatest)
		progargs = "#MASK=0.5#MASKLT";	//alphamask has to be handled by glsl (when glsl is used)

	Q_strlcatfz(script, &offset, sizeof(script), "\n");
	if (!Q_strcasecmp(st->type, "WorldVertexTransition"))
	{	//attempt to do terrain blending
		Q_strlcpy(st->type, "vmt/transition#TWOWAY", sizeof(st->type));
		Q_strlcatfz(script, &offset, sizeof(script),	"\tprogram \"%s%s\"\n", st->type, progargs);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tdiffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tuppermap \"%s%s.vtf\"\n", strcmp(st->tex[1].name, "materials/")?"materials/":"", st->tex[1].name);
	}
	else if (!Q_strcasecmp(st->type, "Decal"))
	{
		Q_strlcpy(st->type, "vmt/vertexlit", sizeof(st->type));
		Q_strlcatfz(script, &offset, sizeof(script),	"\tprogram \"%s%s\"\n", st->type, progargs);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tdiffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tpolygonOffset 1\n");
	}
	else if (!Q_strcasecmp(st->type, "DecalModulate"))
	{
		Q_strlcatfz(script, &offset, sizeof(script),
			"\t{\n"
				"\t\tprogram \"vmt/vertexlit%s\"\n"
				"\t\tblendFunc gl_dst_color gl_one_minus_src_alpha\n"
			"\t}\n", progargs);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tdiffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tpolygonOffset 1\n");
	}
	else if (!Q_strcasecmp(st->type, "Water"))
	{
		Q_strlcatfz(script, &offset, sizeof(script),
			"\t{\n"
				"\t\tprogram \"vmt/water%s\"\n"
				"\t\tmap $refraction\n"
				"\t\tmap $reflection\n"
			"\t}\n", progargs);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tdiffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tnormalmap \"%s%s.vtf\"\n", strcmp(st->normalmap, "materials/")?"materials/":"", st->normalmap);
	}
	else if (!Q_strcasecmp(st->type, "Refract"))
	{
		Q_strlcatfz(script, &offset, sizeof(script),
			"\t{\n"
				"\t\tprogram \"vmt/refract%s\"\n"
				"\t\tmap $refraction\n"
			"\t}\n", progargs);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tdiffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tnormalmap \"%s%s.vtf\"\n", strcmp(st->normalmap, "materials/")?"materials/":"", st->normalmap);
	}
	else if (!Q_strcasecmp(st->type, "VertexlitGeneric"))
	{
		if (*st->envmap && st->envfrombase)
		{
			if (st->halflambert)
				Q_strlcpy(st->type, "vmt/vertexlit#ENVFROMBASE#HALFLAMBERT", sizeof(st->type));
			else
				Q_strlcpy(st->type, "vmt/vertexlit#ENVFROMBASE", sizeof(st->type));
		}
		else
		{
			if (st->halflambert)
				Q_strlcpy(st->type, "vmt/vertexlit#HALFLAMBERT", sizeof(st->type));
			else
				Q_strlcpy(st->type, "vmt/vertexlit", sizeof(st->type));
		}

		Q_strlcatfz(script, &offset, sizeof(script),	"\tprogram \"%s%s\"\n", st->type, progargs);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tdiffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name);

		if (*st->normalmap)
			Q_strlcatfz(script, &offset, sizeof(script),	"\tnormalmap \"%s%s.vtf\"\n", strcmp(st->normalmap, "materials/")?"materials/":"", st->normalmap);
	}
	else if (!Q_strcasecmp(st->type, "LightmappedGeneric"))
	{
		/* reflectmask from diffuse map alpha */
		if (*st->envmap && st->envfrombase)
			Q_strlcpy(st->type, "vmt/lightmapped#ENVFROMBASE", sizeof(st->type));
		else if (*st->envmap && *st->envmapmask) /* dedicated reflectmask */
			Q_strlcpy(st->type, "vmt/lightmapped#ENVFROMMASK", sizeof(st->type));
		else /* take from normalmap */
			Q_strlcpy(st->type, "vmt/lightmapped", sizeof(st->type));

		Q_strlcatfz(script, &offset, sizeof(script),	"\tprogram \"%s%s\"\n", st->type, progargs);
		Q_strlcatfz(script, &offset, sizeof(script),	"\tdiffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name);

		if (*st->normalmap)
			Q_strlcatfz(script, &offset, sizeof(script),	"\tnormalmap \"%s%s.vtf\"\n", strcmp(st->normalmap, "materials/")?"materials/":"", st->normalmap);
	}
	else
	{
		/* render-target camera/monitor - eukara*/
		if (!Q_strcasecmp(st->tex[0].name, "_rt_Camera"))
			Q_strlcatfz(script, &offset, sizeof(script),
			"\t{\n"
				"\t\tmap $rt:base\n"
			"\t}\n"/*, progargs*/);
		else
		{
			/* the default should just be unlit, let's not make any assumptions - eukara*/
			Q_strlcpy(st->type, "vmt/unlit", sizeof(st->type));
			Q_strlcatfz(script, &offset, sizeof(script),	"\tprogram \"%s%s\"\n", st->type, progargs);
			Q_strlcatfz(script, &offset, sizeof(script),	"\tdiffusemap \"%s%s.vtf\"\n", strcmp(st->tex[0].name, "materials/")?"materials/":"", st->tex[0].name);
		}
	}

	if (*st->envmapmask)
		Q_strlcatfz(script, &offset, sizeof(script),	"\treflectmask \"%s%s.vtf\"\n", strcmp(st->envmapmask, "materials/")?"materials/":"", st->envmapmask);
	if (*st->envmap && strcmp(st->envmap, "env_cubemap"))
		Q_strlcatfz(script, &offset, sizeof(script),	"\treflectcube \"%s%s.vtf\"\n", strcmp(st->envmap, "materials/")?"materials/":"", st->envmap);
	if (st->alphatest)
		Q_strlcatfz(script, &offset, sizeof(script), "\talphatest ge128\n");
	if (st->culldisable)
		Q_strlcatfz(script, &offset, sizeof(script), "\tcull disable\n");
	if (st->ignorez)
		Q_strlcatfz(script, &offset, sizeof(script), "\tnodepth\n");
	if (st->blendfunc)
		Q_strlcatfz(script, &offset, sizeof(script), "\tprogblendfunc %s\n", st->blendfunc);

	Q_strlcatfz(script, &offset, sizeof(script), "}\n");

	LoadMaterialString(ps, script);

	if (st->sourcefile)
	{	//cat the original file on there...
		if (st->savefile)
		{
			char *winsucks;	//strip any '\r' chars in there that like to show as ugly glyphs.
			for (winsucks = st->sourcefile; *winsucks; winsucks++)
				if (*winsucks=='\r')
					*winsucks = ' ';

			Q_StrCat(st->savefile, "\n/*\n");
			Q_StrCat(st->savefile, st->sourcefile);
			Q_StrCat(st->savefile, "*/");
		}
		plugfuncs->Free(st->sourcefile);
	}
}
static qboolean VMT_ReadVMT(const char *fname, vmtstate_t *st)
{
	char *line, *file = NULL;
	com_tokentype_t ttype;
	char token[MAX_QPATH];
	char *prefix="", *postfix="";

	//don't dupe the mandatory materials/ prefix
	if (strncmp(fname, "materials/", 10))
		prefix = "materials/";
	if (strcmp(fsfuncs->GetExtension(fname, NULL), ".vmt"))
		postfix = ".vmt";
	Q_snprintfz(token, sizeof(token), "%s%s%s", prefix, fname, postfix);

	file = fsfuncs->LoadFile(token, NULL);
	if (file)
	{
		if (st->savefile)
		{
			if (st->sourcefile)
			{
				Q_StrCat(&st->sourcefile, fname);
				Q_StrCat(&st->sourcefile, ":\n");
				Q_StrCat(&st->sourcefile, file);
			}
			else
				Q_StrCat(&st->sourcefile, file);
		}

		line = file;
		line = cmdfuncs->ParseToken(line, st->type, sizeof(st->type), &ttype);
		line = cmdfuncs->ParseToken(line, token, sizeof(token), &ttype);
		if (!strcmp(token, "{"))
		{
			line = VMT_ParseBlock(fname, st, line);
		}

		plugfuncs->Free(file);
		return !!line;
	}
	return false;
}
static qboolean Shader_LoadVMT(parsestate_t *ps, const char *filename, void (*LoadMaterialString)(parsestate_t *ps, const char *script))
{
	vmtstate_t st;
	memset(&st, 0, sizeof(st));
	st.savefile = NULL;//ps->saveshaderbody;
	if (!VMT_ReadVMT(filename, &st))
	{
		if (st.sourcefile)
			plugfuncs->Free(st.sourcefile);
		return false;
	}

	Shader_GenerateFromVMT(ps, &st, filename, LoadMaterialString);
	return true;
}

static struct sbuiltin_s vmtprograms[] =
{
//we don't know what renderer the engine will need...
#ifdef FTEPLUGIN
	#ifndef GLQUAKE
		#define GLQUAKE
	#endif
	#ifndef VKQUAKE
		#define VKQUAKE
	#endif
	#ifndef D3DQUAKE
		#define D3DQUAKE
	#endif
#endif
#include "mat_vmt_progs.h"
	{QR_NONE}
};
static plugmaterialloaderfuncs_t vmtfuncs =
{
	"HL2 VMT",
	Shader_LoadVMT,

	vmtprograms,
};

qboolean VMT_Init(void)
{
	fsfuncs = plugfuncs->GetEngineInterface(plugfsfuncs_name, sizeof(*fsfuncs));
	if (!fsfuncs)
		return false;
	return plugfuncs->ExportInterface(plugmaterialloaderfuncs_name, &vmtfuncs, sizeof(vmtfuncs));
}