text: #include <string>
  #include <vector>
  #include <time.h>
  #include "../src/glsl/glsl_optimizer.h"
  
  #if __linux__
  #define GOT_GFX 0
  #else
  #define GOT_GFX 1
  #endif
  
  #if GOT_GFX
  
  #ifdef _MSC_VER
  #include <windows.h>
  #include <gl/GL.h>
  extern "C" {
  typedef char GLcharARB;		/* native character */
  typedef unsigned int GLhandleARB;	/* shader object handle */
  #define GL_VERTEX_SHADER_ARB              0x8B31
  #define GL_FRAGMENT_SHADER_ARB            0x8B30
  #define GL_OBJECT_COMPILE_STATUS_ARB      0x8B81
  typedef void (WINAPI * PFNGLDELETEOBJECTARBPROC) (GLhandleARB obj);
  typedef GLhandleARB (WINAPI * PFNGLCREATESHADEROBJECTARBPROC) (GLenum shaderType);
  typedef void (WINAPI * PFNGLSHADERSOURCEARBPROC) (GLhandleARB shaderObj, GLsizei count, const GLcharARB* *string, const GLint *length);
  typedef void (WINAPI * PFNGLCOMPILESHADERARBPROC) (GLhandleARB shaderObj);
  typedef void (WINAPI * PFNGLGETINFOLOGARBPROC) (GLhandleARB obj, GLsizei maxLength, GLsizei *length, GLcharARB *infoLog);
  typedef void (WINAPI * PFNGLGETOBJECTPARAMETERIVARBPROC) (GLhandleARB obj, GLenum pname, GLint *params);
  static PFNGLDELETEOBJECTARBPROC glDeleteObjectARB;
  static PFNGLCREATESHADEROBJECTARBPROC glCreateShaderObjectARB;
  static PFNGLSHADERSOURCEARBPROC glShaderSourceARB;
  static PFNGLCOMPILESHADERARBPROC glCompileShaderARB;
  static PFNGLGETINFOLOGARBPROC glGetInfoLogARB;
  static PFNGLGETOBJECTPARAMETERIVARBPROC glGetObjectParameterivARB;
  }
  #else
  #include <OpenGL/OpenGL.h>
  #include <AGL/agl.h>
  #include <dirent.h>
  #endif
  
  #else // GOT_GFX
  #include <cstdio>
  #include <cstring>
  #include "dirent.h"
  #include "GL/gl.h"
  #include "GL/glext.h"
  #endif
  
  static bool InitializeOpenGL ()
  {
  	bool hasGLSL = false;
  
  #if GOT_GFX
  
  #ifdef _MSC_VER
  	// setup minimal required GL
  	HWND wnd = CreateWindowA(
  		"STATIC",
  		"GL",
  		WS_OVERLAPPEDWINDOW | WS_CLIPSIBLINGS |	WS_CLIPCHILDREN,
  		0, 0, 16, 16,
  		NULL, NULL,
  		GetModuleHandle(NULL), NULL );
  	HDC dc = GetDC( wnd );
  
  	PIXELFORMATDESCRIPTOR pfd = {
  		sizeof(PIXELFORMATDESCRIPTOR), 1,
  		PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL,
  		PFD_TYPE_RGBA, 32,
  		0, 0, 0, 0, 0, 0,
  		0, 0, 0, 0, 0, 0, 0,
  		16, 0,
  		0, PFD_MAIN_PLANE, 0, 0, 0, 0
  	};
  
  	int fmt = ChoosePixelFormat( dc, &pfd );
  	SetPixelFormat( dc, fmt, &pfd );
  
  	HGLRC rc = wglCreateContext( dc );
  	wglMakeCurrent( dc, rc );
  
  #else
  	GLint attributes[16];
  	int i = 0;
  	attributes[i++]=AGL_RGBA;
  	attributes[i++]=AGL_PIXEL_SIZE;
  	attributes[i++]=32;
  	attributes[i++]=AGL_NO_RECOVERY;
  	attributes[i++]=AGL_NONE;
  	
  	AGLPixelFormat pixelFormat = aglChoosePixelFormat(NULL,0,attributes);
  	AGLContext agl = aglCreateContext(pixelFormat, NULL);
  	aglSetCurrentContext (agl);
  
  #endif
  
  	// check if we have GLSL
  	const char* extensions = (const char*)glGetString(GL_EXTENSIONS);
  	hasGLSL = strstr(extensions, "GL_ARB_shader_objects") && strstr(extensions, "GL_ARB_vertex_shader") && strstr(extensions, "GL_ARB_fragment_shader");
  	
  #ifdef _MSC_VER
  	if (hasGLSL)
  	{
  		glDeleteObjectARB = (PFNGLDELETEOBJECTARBPROC)wglGetProcAddress("glDeleteObjectARB");
  		glCreateShaderObjectARB = (PFNGLCREATESHADEROBJECTARBPROC)wglGetProcAddress("glCreateShaderObjectARB");
  		glShaderSourceARB = (PFNGLSHADERSOURCEARBPROC)wglGetProcAddress("glShaderSourceARB");
  		glCompileShaderARB = (PFNGLCOMPILESHADERARBPROC)wglGetProcAddress("glCompileShaderARB");
  		glGetInfoLogARB = (PFNGLGETINFOLOGARBPROC)wglGetProcAddress("glGetInfoLogARB");
  		glGetObjectParameterivARB = (PFNGLGETOBJECTPARAMETERIVARBPROC)wglGetProcAddress("glGetObjectParameterivARB");
  	}
  #endif
  
  #endif
  	return hasGLSL;
  }
  
  static void replace_string (std::string& target, const std::string& search, const std::string& replace, size_t startPos)
  {
  	if (search.empty())
  		return;
  	
  	std::string::size_type p = startPos;
  	while ((p = target.find (search, p)) != std::string::npos)
  	{
  		target.replace (p, search.size (), replace);
  		p += replace.size ();
  	}
  }
  
  static bool CheckGLSL (bool vertex, bool gles, const char* prefix, const std::string& source)
  {
  	std::string src;
  	if (gles)
  	{
  		src += "#define lowp\n";
  		src += "#define mediump\n";
  		src += "#define highp\n";
  		src += "#define texture2DLodEXT texture2DLod\n";
  	}
  	src += source;
  	if (gles)
  	{
  		replace_string (src, "GL_EXT_shader_texture_lod", "GL_ARB_shader_texture_lod", 0);
  	}
  	const char* sourcePtr = src.c_str();
  
  #if GOT_GFX
  	
  	GLhandleARB shader = glCreateShaderObjectARB (vertex ? GL_VERTEX_SHADER_ARB : GL_FRAGMENT_SHADER_ARB);
  	glShaderSourceARB (shader, 1, &sourcePtr, NULL);
  	glCompileShaderARB (shader);
  	GLint status;
  	glGetObjectParameterivARB (shader, GL_OBJECT_COMPILE_STATUS_ARB, &status);
  	bool res = true;
  	if (status == 0)
  	{
  		char log[4096];
  		GLsizei logLength;
  		glGetInfoLogARB (shader, sizeof(log), &logLength, log);
  		printf ("  glsl compile error on %s:\n%s\n", prefix, log);
  		res = false;
  	}
  	glDeleteObjectARB (shader);
  	return res;
  #else
  	// just assume it's ok...
  	return true;
  #endif
  }
  
  
  static bool ReadStringFromFile (const char* pathName, std::string& output)
  {
  	FILE* file = fopen( pathName, "rb" );
  	if (file == NULL)
  		return false;
  	fseek(file, 0, SEEK_END);
  	int length = ftell(file);
  	fseek(file, 0, SEEK_SET);
  	if (length < 0)
  	{
  		fclose( file );
  		return false;
  	}
  	output.resize(length);
  	int readLength = fread(&*output.begin(), 1, length, file);
  	fclose(file);
  	if (readLength != length)
  	{
  		output.clear();
  		return false;
  	}
  	return true;
  }
  
  bool EndsWith (const std::string& str, const std::string& sub)
  {
  	return (str.size() >= sub.size()) && (strncmp (str.c_str()+str.size()-sub.size(), sub.c_str(), sub.size())==0);
  }
  
  typedef std::vector<std::string> StringVector;
  
  static StringVector GetFiles (const std::string& folder, const std::string& endsWith)
  {
  	StringVector res;
  
  	#ifdef _MSC_VER
  	WIN32_FIND_DATAA FindFileData;
  	HANDLE hFind = FindFirstFileA ((folder+"/*"+endsWith).c_str(), &FindFileData);
  	if (hFind == INVALID_HANDLE_VALUE)
  		return res;
  
  	do {
  		res.push_back (FindFileData.cFileName);
  	} while (FindNextFileA (hFind, &FindFileData));
  
  	FindClose (hFind);
  	
  	#else
  	
  	DIR *dirp;
  	struct dirent *dp;
  
  	if ((dirp = opendir(folder.c_str())) == NULL)
  		return res;
  
  	while ( (dp = readdir(dirp)) )
  	{
  		std::string fname = dp->d_name;
  		if (fname == "." || fname == "..")
  			continue;
  		if (!EndsWith (fname, endsWith))
  			continue;
  		res.push_back (fname);
  	}
  	closedir(dirp);
  	
  	#endif
  
  	return res;
  }
  
  static void DeleteFile (const std::string& path)
  {
  	#ifdef _MSC_VER
  	DeleteFileA (path.c_str());
  	#else
  	unlink (path.c_str());
  	#endif
  }
  
  static void MassageVertexForGLES (std::string& s)
  {
  	std::string pre;
  	pre += "#define gl_Vertex _glesVertex\nattribute highp vec4 _glesVertex;\n";
  	pre += "#define gl_Normal _glesNormal\nattribute mediump vec3 _glesNormal;\n";
  	pre += "#define gl_MultiTexCoord0 _glesMultiTexCoord0\nattribute highp vec4 _glesMultiTexCoord0;\n";
  	pre += "#define gl_MultiTexCoord1 _glesMultiTexCoord1\nattribute highp vec4 _glesMultiTexCoord1;\n";
  	pre += "#define gl_Color _glesColor\nattribute lowp vec4 _glesColor;\n";
  	s = pre + s;
  }
  
  static void MassageFragmentForGLES (std::string& s)
  {
  	std::string pre;
  	s = pre + s;
  }
  
  static bool TestFile (glslopt_ctx* ctx, bool vertex,
  	const std::string& inputPath,
  	const std::string& hirPath,
  	const std::string& outputPath,
  	bool gles,
  	bool doCheckGLSL)
  {
  	std::string input;
  	if (!ReadStringFromFile (inputPath.c_str(), input))
  	{
  		printf ("  failed to read input file\n");
  		return false;
  	}
  	if (doCheckGLSL)
  	{
  		if (!CheckGLSL (vertex, gles, "input", input.c_str()))
  			return false;
  	}
  
  	if (gles)
  	{
  		if (vertex)
  			MassageVertexForGLES (input);
  		else
  			MassageFragmentForGLES (input);
  	}
  
  	bool res = true;
  
  	glslopt_shader_type type = vertex ? kGlslOptShaderVertex : kGlslOptShaderFragment;
  	glslopt_shader* shader = glslopt_optimize (ctx, type, input.c_str(), 0);
  
  	bool optimizeOk = glslopt_get_status(shader);
  	if (optimizeOk)
  	{
  		std::string textHir = glslopt_get_raw_output (shader);
  		std::string textOpt = glslopt_get_output (shader);
  		std::string outputHir;
  		ReadStringFromFile (hirPath.c_str(), outputHir);
  		std::string outputOpt;
  		ReadStringFromFile (outputPath.c_str(), outputOpt);
  
  		if (textHir != outputHir)
  		{
  			// write output
  			FILE* f = fopen (hirPath.c_str(), "wb");
  			if (!f)
  			{
  				printf ("  can't write to IR file!\n");
  			}
  			else
  			{
  				fwrite (textHir.c_str(), 1, textHir.size(), f);
  				fclose (f);
  			}
  			printf ("  does not match raw output\n");
  			res = false;
  		}
  
  		if (textOpt != outputOpt)
  		{
  			// write output
  			FILE* f = fopen (outputPath.c_str(), "wb");
  			if (!f)
  			{
  				printf ("  can't write to optimized file!\n");
  			}
  			else
  			{
  				fwrite (textOpt.c_str(), 1, textOpt.size(), f);
  				fclose (f);
  			}
  			printf ("  does not match optimized output\n");
  			res = false;
  		}
  		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, "raw", textHir.c_str()))
  			res = false;
  		if (res && doCheckGLSL && !CheckGLSL (vertex, gles, "optimized", textOpt.c_str()))
  			res = false;
  	}
  	else
  	{
  		printf ("  optimize error: %s\n", glslopt_get_log(shader));
  		res = false;
  	}
  
  	glslopt_shader_delete (shader);
  
  	return res;
  }
  
  
  int main (int argc, const char** argv)
  {
  	if (argc < 2)
  	{
  		printf ("USAGE: glsloptimizer testfolder\n");
  		return 1;
  	}
  
  	bool hasOpenGL = InitializeOpenGL ();
  	glslopt_ctx* ctx[2] = {
  		glslopt_initialize(true),
  		glslopt_initialize(false),
  	};
  
  	std::string baseFolder = argv[1];
  
  	clock_t time0 = clock();
  
  	static const char* kTypeName[2] = { "vertex", "fragment" };
  	size_t tests = 0;
  	size_t errors = 0;
  	for (int type = 0; type < 2; ++type)
  	{
  		std::string testFolder = baseFolder + "/" + kTypeName[type];
  
  		static const char* kAPIName[2] = { "OpenGL ES 2.0", "OpenGL" };
  		static const char* kApiIn [2] = {"-inES.txt", "-in.txt"};
  		static const char* kApiIR [2] = {"-irES.txt", "-ir.txt"};
  		static const char* kApiOut[2] = {"-outES.txt", "-out.txt"};
  		for (int api = 0; api < 2; ++api)
  		{
  			printf ("** running %s tests for %s...\n", kTypeName[type], kAPIName[api]);
  			StringVector inputFiles = GetFiles (testFolder, kApiIn[api]);
  
  			size_t n = inputFiles.size();
  			for (size_t i = 0; i < n; ++i)
  			{
  				std::string inname = inputFiles[i];
  				printf ("test %s\n", inname.c_str());
  				std::string hirname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiIR[api];
  				std::string outname = inname.substr (0,inname.size()-strlen(kApiIn[api])) + kApiOut[api];
  				bool ok = TestFile (ctx[api], type==0, testFolder + "/" + inname, testFolder + "/" + hirname, testFolder + "/" + outname, api==0, hasOpenGL);
  				if (!ok)
  				{
  					++errors;
  				}
  				++tests;
  			}
  		}
  	}
  	clock_t time1 = clock();
  	float timeDelta = float(time1-time0)/CLOCKS_PER_SEC;
  
  	if (errors != 0)
  		printf ("**** %i tests (%.2fsec), %i !!!FAILED!!!\n", tests, timeDelta, errors);
  	else
  		printf ("**** %i tests (%.2fsec) succeeded\n", tests, timeDelta);
  
  	for (int i = 0; i < 2; ++i)
  		glslopt_cleanup (ctx[i]);
  
  	return errors ? 1 : 0;
  }
  