]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - plugins/shaders/shaders.cpp
more eol-style
[xonotic/netradiant.git] / plugins / shaders / shaders.cpp
index b151c505e5f3ee10c8fa9a063825b4272e69128f..c4213b870c90a0ebc12c7388ebb29555d0ca6c5f 100644 (file)
-/*\r
-Copyright (c) 2001, Loki software, inc.\r
-All rights reserved.\r
-\r
-Redistribution and use in source and binary forms, with or without modification, \r
-are permitted provided that the following conditions are met:\r
-\r
-Redistributions of source code must retain the above copyright notice, this list \r
-of conditions and the following disclaimer.\r
-\r
-Redistributions in binary form must reproduce the above copyright notice, this\r
-list of conditions and the following disclaimer in the documentation and/or\r
-other materials provided with the distribution.\r
-\r
-Neither the name of Loki software nor the names of its contributors may be used \r
-to endorse or promote products derived from this software without specific prior \r
-written permission. \r
-\r
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' \r
-AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE \r
-IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE \r
-DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY \r
-DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES \r
-(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; \r
-LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON \r
-ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT \r
-(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS \r
-SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. \r
-*/\r
-\r
-//\r
-// Shaders Manager Plugin\r
-//\r
-// Leonardo Zide (leo@lokigames.com)\r
-//\r
-\r
-// standard headers\r
-#include <stdio.h>\r
-#include <stdlib.h>\r
-#include "plugin.h"\r
-#include "mathlib.h"\r
-#include "missing.h" //++timo FIXME: this one is intended to go away some day, it's MFC compatibility classes\r
-#include "shaders.h"\r
-\r
-// some forward declarations\r
-IShader *WINAPI QERApp_Shader_ForName (const char *name);\r
-qtexture_t *WINAPI QERApp_Try_Texture_ForName (const char *name);\r
-qtexture_t *WINAPI QERApp_Texture_ForName2 (const char *filename);\r
-IShader *WINAPI QERApp_ColorShader_ForName (const char *name);\r
-void WINAPI QERApp_LoadShaderFile (const char *filename);\r
-\r
-//++timo TODO: use stl::map !! (I tried having a look to CMap but it obviously sucks)\r
-CShaderArray g_Shaders;\r
-// whenever a shader gets activated / deactivated this list is updated\r
-// NOTE: make sure you don't add a shader that's already in\r
-// NOTE: all shaders in this array are in the main g_Shaders\r
-CShaderArray g_ActiveShaders;\r
-\r
-// clean a texture name to the qtexture_t name format we use internally\r
-// NOTE: there are so many cases .. this may need to get updated to cover all of them\r
-// we expect a "textures/" path on top, except if bAddTexture is set to true .. in case we add in needed\r
-// NOTE: case sensitivity: the engine is case sensitive. we store the shader name with case information and save with case\r
-// information as well. but we assume there won't be any case conflict and so when doing lookups based on shader name,\r
-// we compare as case insensitive. That is Radiant is case insensitive, but knows that the engine is case sensitive.\r
-//++timo FIXME: we need to put code somewhere to detect when two shaders that are case insensitive equal are present\r
-const char *WINAPI QERApp_CleanTextureName (const char *name, bool bAddTexture = false)\r
-{\r
-  static char stdName[QER_MAX_NAMELEN];\r
-#ifdef _DEBUG\r
-  if (strlen(name)>QER_MAX_NAMELEN)\r
-    g_FuncTable.m_pfnSysFPrintf(SYS_WRN, "WARNING: name exceeds QER_MAX_NAMELEN in CleanTextureName\n");\r
-#endif\r
-\r
-  strcpy (stdName, name);\r
-  g_FuncTable.m_pfnQE_ConvertDOSToUnixName (stdName, stdName);\r
-  if (stdName[strlen (name) - 4] == '.')\r
-    // strip extension\r
-    stdName[strlen (stdName) - 4] = '\0';\r
-\r
-       if (bAddTexture)\r
-  {\r
-    char aux[QER_MAX_NAMELEN];\r
-    sprintf (aux, "textures/%s", stdName);\r
-    strcpy (stdName, aux);\r
-       }\r
-  return stdName;\r
-}\r
-\r
-int WINAPI QERApp_GetActiveShaderCount ()\r
-{\r
-  return g_ActiveShaders.GetSize ();\r
-}\r
-\r
-IShader *WINAPI QERApp_ActiveShader_ForIndex (int i)\r
-{\r
-  return static_cast < CShader * >(g_ActiveShaders.GetAt (i));\r
-}\r
-\r
-void CShaderArray::SortShaders ()\r
-{\r
-  CPtrArray aux;\r
-  int i, icount;\r
-  int j, jcount;\r
-  CShader *pSort;\r
-  const char *sSort;\r
-  // dumb sort .. would it ever grow big enough so we would have to do something clever? noooo\r
-  icount = CPtrArray::GetSize ();\r
-  for (i = 0; i < icount; i++)\r
-  {\r
-    pSort = static_cast < CShader * >(GetAt (i));\r
-    sSort = pSort->getName ();\r
-    jcount = aux.GetSize ();\r
-    for (j = 0; j < jcount; j++)\r
-    {\r
-      if (strcmp (sSort, static_cast < CShader * >(aux.GetAt (j))->getName ()) < 0)\r
-        break;\r
-    }\r
-    aux.InsertAt (j, pSort);\r
-  }\r
-  CPtrArray::RemoveAll ();\r
-  CPtrArray::InsertAt (0, &aux);\r
-}\r
-\r
-// will sort the active shaders list by name\r
-// NOTE: it would be easier if the thing would stay sorted by using a map<name,CShader> thing\r
-//++timo FIXME: would need to export that to allow external override?\r
-void WINAPI QERApp_SortActiveShaders ()\r
-{\r
-  g_ActiveShaders.SortShaders ();\r
-}\r
-\r
-// NOTE: case sensitivity\r
-// although we store shader names with case information, Radiant does case insensitive searches\r
-// (we assume there's no case conflict with the names)\r
-CShader *CShaderArray::Shader_ForName (const char *name) const\r
-{\r
-  int i;\r
-  for (i = 0; i < CPtrArray::GetSize (); i++)\r
-  {\r
-    CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));\r
-    if (stricmp (pShader->getName (), name) == 0)\r
-      return pShader;\r
-  }\r
-  return NULL;\r
-}\r
-\r
-void CShader::CreateDefault (const char *name)\r
-{\r
-  const char *stdName = QERApp_CleanTextureName (name);\r
-  m_strTextureName = stdName;\r
-  setName (name);\r
-}\r
-\r
-CShader *CShaderArray::Shader_ForTextureName (const char *name) const\r
-{\r
-#ifdef _DEBUG\r
-  // check we were given a texture name that fits the qtexture_t naming conventions\r
-  if (strcmp (name, QERApp_CleanTextureName (name)) != 0)\r
-    Sys_Printf\r
-      ("WARNING: texture name %s doesn't fit qtexture_t conventions in CShaderArray::Shader_ForTextureName\n",\r
-       name);\r
-#endif\r
-  int i;\r
-  for (i = 0; i < CPtrArray::GetSize (); i++)\r
-  {\r
-    CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));\r
-    if (strcmp (name, QERApp_CleanTextureName (pShader->getTextureName ())) == 0)\r
-      return pShader;\r
-  }\r
-  return NULL;\r
-}\r
-\r
-IShader *WINAPI QERApp_ActiveShader_ForTextureName (char *name)\r
-{\r
-  return g_ActiveShaders.Shader_ForTextureName (name);\r
-}\r
-\r
-void CShaderArray::AddSingle (void *lp)\r
-{\r
-  int i;\r
-  for (i = 0; i < CPtrArray::GetSize (); i++)\r
-  {\r
-    if (CPtrArray::GetAt (i) == lp)\r
-      return;\r
-  }\r
-  CPtrArray::Add (lp);\r
-  static_cast < CShader * >(CPtrArray::GetAt (i))->IncRef();\r
-}\r
-\r
-void CShaderArray::operator = (const class CShaderArray & src)\r
-{\r
-  int i;\r
-\r
-#ifdef _DEBUG\r
-  if (CPtrArray::GetSize () != 0)\r
-    Sys_Printf ("WARNING: CShaderArray::operator = expects an empty array\n");\r
-#endif\r
-  Copy (src);\r
-  // now go through and IncRef\r
-  for (i = 0; i < CPtrArray::GetSize (); i++)\r
-    static_cast < IShader * >(CPtrArray::GetAt (i))->IncRef ();\r
-}\r
-\r
-//++timo NOTE: for debugging we may need to keep track and tell wether everything has been properly unloaded\r
-void CShaderArray::ReleaseAll ()\r
-{\r
-  int i;\r
-  int count = CPtrArray::GetSize ();\r
-  // decref\r
-  for (i = 0; i < count; i++)\r
-    static_cast < IShader * >(CPtrArray::GetAt (i))->DecRef ();\r
-  // get rid\r
-  CPtrArray::RemoveAll ();\r
-}\r
-\r
-// NOTE TTimo:\r
-// this was hacked to work a long time ago\r
-// in Loki's fenris tracker as bug #104655\r
-// since that info is no longer available, and the hack has been there for so long, it's part of the code now\r
-// don't remember the details, but basically across a flush and reload for the shaders\r
-// we have to keep track of the patches texture names in a seperate entry\r
-// not sure why anymore, but I know that doesn't happen with brushes\r
-typedef struct patchEntry_s\r
-{\r
-  char name[QER_MAX_NAMELEN];\r
-  patchMesh_t *p;\r
-} patchEntry_t;\r
-\r
-CPtrArray PatchShaders;\r
-\r
-void PushPatch (patchMesh_t * patch)\r
-{\r
-  patchEntry_t *pEntry = new patchEntry_s;\r
-  pEntry->p = patch;\r
-  strcpy (pEntry->name, patch->pShader->getName ());\r
-  PatchShaders.Add (pEntry);\r
-}\r
-\r
-char *ShaderNameLookup (patchMesh_t * patch)\r
-{\r
-  int i;\r
-  int count = PatchShaders.GetSize ();\r
-  for (i = 0; i < count; i++)\r
-  {\r
-    if (static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->p == patch)\r
-      return static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->name;\r
-  }\r
-  Sys_Printf ("ERROR: failed to lookup name in ShaderNameLookup??\n");\r
-  return SHADER_NOT_FOUND;\r
-}\r
-//++timo end clean\r
-\r
-// will free all GL binded qtextures and shaders\r
-// NOTE: doesn't make much sense out of Radiant exit or called during a reload\r
-void WINAPI QERApp_FreeShaders ()\r
-{\r
-  int i;\r
-  brush_t *b;\r
-  brush_t *active_brushes;\r
-  brush_t *selected_brushes;\r
-  brush_t *filtered_brushes;\r
-  qtexture_t **d_qtextures;\r
-\r
-  active_brushes = g_DataTable.m_pfnActiveBrushes ();\r
-  selected_brushes = g_DataTable.m_pfnSelectedBrushes ();\r
-  filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();\r
-  d_qtextures = g_ShadersTable.m_pfnQTextures ();\r
-\r
-  // store the shader names used by the patches\r
-  for (i = 0; i < PatchShaders.GetSize (); i++)\r
-    delete static_cast < patchMesh_t * >(PatchShaders.GetAt (i));\r
-  PatchShaders.RemoveAll ();\r
-\r
-  for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)\r
-  {\r
-    if (b->patchBrush)\r
-      PushPatch (b->pPatch);\r
-  }\r
-  for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)\r
-  {\r
-    if (b->patchBrush)\r
-      PushPatch (b->pPatch);\r
-  }\r
-  for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)\r
-  {\r
-    if (b->patchBrush)\r
-      PushPatch (b->pPatch);\r
-  }\r
-\r
-  // reload shaders\r
-  // empty the actives shaders list\r
-  g_ActiveShaders.ReleaseAll ();\r
-  g_Shaders.ReleaseAll ();\r
-  // empty the main g_qeglobals.d_qtextures list\r
-  // FIXME: when we reload later on, we need to have the shader names\r
-  // for brushes it's stored in the texdef\r
-  // but patches don't have texdef\r
-  // see bug 104655 for details\r
-  // so the solution, build an array of patchMesh_t* and their shader names\r
-#ifdef _DEBUG\r
-  Sys_Printf ("FIXME: patch shader reload workaround (old fenris? bug 104655)\n");\r
-#endif\r
-\r
-  //GtkWidget *widget = g_QglTable.m_pfn_GetQeglobalsGLWidget ();\r
-  GHashTable *texmap = g_ShadersTable.m_pfnQTexmap ();\r
-\r
-  // NOTE: maybe before we'd like to set all qtexture_t in the shaders list to notex?\r
-  // NOTE: maybe there are some qtexture_t we don't want to erase? For plain color faces maybe?\r
-  while (*d_qtextures)\r
-  {\r
-    qtexture_t *pTex = *d_qtextures;\r
-    qtexture_t *pNextTex = pTex->next;\r
-\r
-    //if (widget != NULL)\r
-    g_QglTable.m_pfn_qglDeleteTextures (1, &pTex->texture_number);\r
-\r
-    g_hash_table_remove (texmap, pTex->name);\r
-      \r
-    // all qtexture_t should be manipulated with the glib alloc handlers for now\r
-    g_free (pTex);\r
-    *d_qtextures = pNextTex;\r
-  }\r
-\r
-  g_QglTable.m_pfn_QE_CheckOpenGLForErrors ();\r
-}\r
-\r
-// those functions are only used during a shader reload phase\r
-// the patch one relies on ShaderNameLookup, a table that is being built only when a flush is performed\r
-// so it's not something we want to expose publicly\r
-\r
-void SetShader (patchMesh_t * patch)\r
-{\r
-  // unhook current shader\r
-  patch->pShader->DecRef();\r
-  // don't access this one! it has been deleted .. it's DEAD\r
-  patch->d_texture = NULL;\r
-  // hook the new one, increment the refcount\r
-  // NOTE TTimo this function increments the refcount, don't incref ourselves\r
-  patch->pShader = QERApp_Shader_ForName (ShaderNameLookup (patch));\r
-  patch->d_texture = patch->pShader->getTexture ();\r
-}\r
-\r
-void SetShader (face_t * f)\r
-{\r
-  // unhook current shader\r
-  f->pShader->DecRef();\r
-  // don't access the texdef! it's DEAD\r
-  f->d_texture = NULL;\r
-  // hook\r
-  // NOTE TTimo this function increments the refcount, don't incref ourselves\r
-  f->pShader = QERApp_Shader_ForName (f->texdef.GetName());\r
-  f->d_texture = f->pShader->getTexture ();\r
-}\r
-\r
-void Brush_RefreshShader(brush_t *b)\r
-{\r
-  if (b->patchBrush)\r
-    SetShader(b->pPatch);\r
-  else if (b->owner->eclass->fixedsize)\r
-  {\r
-    /*eclass_t *eclass = HasModel(b);\r
-    if (eclass)\r
-    {\r
-      for(entitymodel *model = eclass->model; model!=NULL; model=model->pNext)\r
-        if(model && model->strSkin)\r
-          model->nTextureBind = g_FuncTable.m_pfnTexture_LoadSkin(((GString *)model->strSkin)->str, &model->nSkinWidth, &model->nSkinHeight);\r
-    }*/\r
-  }\r
-  else\r
-               for (face_t *f=b->brush_faces ; f ; f=f->next)\r
-                       SetShader(f);\r
-}\r
-\r
-void WINAPI QERApp_ReloadShaders ()\r
-{\r
-  brush_t *b;\r
-  brush_t *active_brushes;\r
-  brush_t *selected_brushes;\r
-  brush_t *filtered_brushes;\r
-\r
-  QERApp_FreeShaders ();\r
-\r
-  g_DataTable.m_pfnLstSkinCache()->RemoveAll(); //md3 skins\r
-\r
-  active_brushes = g_DataTable.m_pfnActiveBrushes ();\r
-  selected_brushes = g_DataTable.m_pfnSelectedBrushes ();\r
-  filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();\r
-\r
-  // now we must reload the shader information from shaderfiles\r
-  g_ShadersTable.m_pfnBuildShaderList();\r
-  g_ShadersTable.m_pfnPreloadShaders();\r
-\r
-  // refresh the map visuals: replace our old shader objects by the new ones\r
-  // on brush faces we have the shader name in texdef.name\r
-  // on patches we have the shader name in PatchShaders\r
-  // while we walk through the map data, we DecRef the old shaders and push the new ones in\r
-  // if all goes well, most of our old shaders will get deleted on the way\r
-\r
-  // FIXME: bug 104655, when we come accross a patch, we use the above array since the d_texture is lost\r
-  // NOTE: both face_t and patchMesh_t store pointers to the shader and qtexture_t\r
-  // in an ideal world they would only store shader and access the qtexture_t through it\r
-  // reassign all current shaders\r
-  for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)\r
-    Brush_RefreshShader(b);\r
-  for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)\r
-    Brush_RefreshShader(b);\r
-  // do that to the filtered brushes as well (we might have some region compiling going on)\r
-  for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)\r
-    Brush_RefreshShader(b);\r
-}\r
-\r
-int WINAPI QERApp_LoadShadersFromDir (const char *path)\r
-{\r
-  int count = 0;\r
-  // scan g_Shaders, and call QERApp_Shader_ForName for each in the given path\r
-  // this will load the texture if needed and will set it in use..\r
-  int nSize = g_Shaders.GetSize ();\r
-  for (int i = 0; i < nSize; i++)\r
-  {\r
-    CShader *pShader = reinterpret_cast < CShader * >(g_Shaders[i]);\r
-    if (strstr (pShader->getShaderFileName (), path) || strstr (pShader->getName (), path))\r
-    {\r
-      count++;\r
-      // request the shader, this will load the texture if needed and set "inuse"\r
-      //++timo FIXME: should we put an Activate member on CShader?\r
-      // this QERApp_Shader_ForName call is a kind of hack\r
-      IShader *pFoo = QERApp_Shader_ForName (pShader->getName ());\r
-#ifdef _DEBUG\r
-      // check we activated the right shader\r
-      // NOTE: if there was something else loaded, the size of g_Shaders may have changed and strange behaviours are to be expected\r
-      if (pFoo != pShader)\r
-       Sys_Printf ("WARNING: unexpected pFoo != pShader in QERApp_LoadShadersFromDir\n");\r
-#else\r
-      pFoo = NULL;             // leo: shut up the compiler\r
-#endif\r
-    }\r
-  }\r
-  return count;\r
-}\r
-\r
-bool CShader::Parse ()\r
-{\r
-  char *token = g_ScripLibTable.m_pfnToken ();\r
-\r
-  // the parsing needs to be taken out in another module\r
-//  Sys_Printf("TODO: CShader::Parse\n");\r
-\r
-  // token is shader name (full path with a "textures\")\r
-  // we remove the "textures\" part\r
-  //setName ((char *) &token[9]));\r
-       // no we don't\r
-       setName (token);\r
-  // name of the qtexture_t we'll use to represent this shader (this one has the "textures\" before)\r
-  const char *stdName = QERApp_CleanTextureName (token);\r
-  m_strTextureName = stdName; // FIXME: BC reports stdName is uninitialised?\r
-  g_ScripLibTable.m_pfnGetToken (true);\r
-  if (strcmp (token, "{"))\r
-    return false;\r
-  else\r
-  {\r
-    // we need to read until we hit a balanced }\r
-    int nMatch = 1;\r
-    while (nMatch > 0 && g_ScripLibTable.m_pfnGetToken (true))\r
-    {\r
-      if (strcmp (token, "{") == 0)\r
-      {\r
-       nMatch++;\r
-        continue;\r
-      }\r
-      else if (strcmp (token, "}") == 0)\r
-      {\r
-       nMatch--;\r
-        continue;\r
-      }\r
-      if (nMatch > 1) continue; // ignore layers for now\r
-      if (strcmpi (token, "qer_nocarve") == 0)\r
-      {\r
-       m_nFlags |= QER_NOCARVE;\r
-      }\r
-      else if (strcmpi (token, "qer_trans") == 0)\r
-      {\r
-             if (g_ScripLibTable.m_pfnGetToken (true))\r
-             {\r
-               m_fTrans = (float) atof (token);\r
-             }\r
-             m_nFlags |= QER_TRANS;\r
-      }\r
-      else if (strcmpi (token, "qer_editorimage") == 0)\r
-      {\r
-             if (g_ScripLibTable.m_pfnGetToken (true))\r
-             {\r
-          // bAddTexture changed to false to allow editorimages in other locations than "textures/"\r
-               m_strTextureName = QERApp_CleanTextureName (token, false);\r
-             }\r
-      }\r
-      else if (strcmpi (token, "qer_alphafunc") == 0)\r
-      {\r
-             if (g_ScripLibTable.m_pfnGetToken (true))\r
-             {\r
-          \r
-          if(stricmp( token, "greater" ) == 0 )\r
-          {\r
-                 m_nAlphaFunc = GL_GREATER;\r
-          }\r
-          else if(stricmp( token, "less" ) == 0 )\r
-          {\r
-            m_nAlphaFunc = GL_LESS;\r
-          }\r
-          else if(stricmp( token, "gequal" ) == 0 )\r
-          {\r
-            m_nAlphaFunc = GL_GEQUAL;\r
-          }\r
-\r
-          if( m_nAlphaFunc )\r
-            m_nFlags |= QER_ALPHAFUNC;\r
-             }\r
-        if (g_ScripLibTable.m_pfnGetToken (true))\r
-             {\r
-               m_fAlphaRef = (float) atof (token);\r
-             }\r
-      }\r
-      else if (strcmpi (token, "cull") == 0)\r
-      {\r
-        if (g_ScripLibTable.m_pfnGetToken (true))\r
-             {\r
-          if( stricmp( token, "none" ) == 0 || stricmp( token, "twosided" ) == 0 || stricmp( token, "disable" ) == 0 )\r
-          {\r
-                 m_nCull = 2;\r
-          }\r
-          else if( stricmp( token, "back" ) == 0 || stricmp( token, "backside" ) == 0 || stricmp( token, "backsided" ) == 0 )\r
-          {\r
-                 m_nCull = 1;\r
-          }\r
-\r
-          if( m_nCull )\r
-             m_nFlags |= QER_CULL;\r
-        }\r
-      }\r
-      else if (strcmpi (token, "surfaceparm") == 0)\r
-      {\r
-             if (g_ScripLibTable.m_pfnGetToken (true))\r
-             {\r
-               if (strcmpi (token, "fog") == 0)\r
-               {\r
-            m_nFlags |= QER_FOG;\r
-                 if (m_fTrans == 1.0f) // has not been explicitly set by qer_trans\r
-                 {\r
-                   m_fTrans = 0.35f;\r
-                 }\r
-               }\r
-          else if (strcmpi (token, "nodraw") == 0)\r
-          {\r
-            m_nFlags |= QER_NODRAW;\r
-          }\r
-          else if (strcmpi (token, "nonsolid") == 0)\r
-          {\r
-            m_nFlags |= QER_NONSOLID;\r
-          }\r
-          else if (strcmpi (token, "water") == 0)\r
-          {\r
-            m_nFlags |= QER_WATER;\r
-          }\r
-          else if (strcmpi (token, "lava") == 0)\r
-          {\r
-            m_nFlags |= QER_LAVA;\r
-          }\r
-             }\r
-      }\r
-    }\r
-    if (nMatch != 0)\r
-      return false;\r
-  }\r
-  return true;\r
-}\r
-\r
-void CShader::RegisterActivate ()\r
-{\r
-  // fill the qtexture_t with shader information\r
-  //++timo FIXME: a lot of that won't be necessary, will be stored at IShader* level\r
-//  strcpy (m_pTexture->shadername, m_Name);\r
-  // this flag is set only if we have a shaderfile name\r
-//  if (m_ShaderFileName[0] != '\0')\r
-//    m_pTexture->bFromShader = true;\r
-//  else\r
-//    m_pTexture->bFromShader = false;\r
-  //++timo FIXME: what do we do with that?\r
-  //m_pTexture->fTrans = pInfo->m_fTransValue;\r
-//  m_pTexture->fTrans = 1.0f; // if != 1.0 it's ot getting drawn in Cam_Draw\r
-//  m_pTexture->nShaderFlags = m_nFlags;\r
-  // store in the active shaders list (if necessary)\r
-  g_ActiveShaders.AddSingle (this);\r
-  // when you activate a shader, it gets displayed in the texture browser\r
-  m_bDisplayed = true;\r
-  IncRef ();\r
-}\r
-\r
-void CShader::Try_Activate ()\r
-{\r
-  m_pTexture = QERApp_Try_Texture_ForName (m_strTextureName.GetBuffer());\r
-  if (m_pTexture)\r
-    RegisterActivate ();\r
-}\r
-\r
-// Hydra: now returns false if the ORIGINAL shader could not be activated\r
-// (missing texture, or incorrect shader script), true otherwise\r
-// the shader is still activated in all cases.\r
-bool CShader::Activate ()\r
-{\r
-  Try_Activate ();\r
-  if (!m_pTexture)\r
-  {\r
-    m_pTexture = QERApp_Texture_ForName2 (SHADER_NOTEX);\r
-    RegisterActivate ();\r
-    return false;\r
-  }\r
-  return true;\r
-}\r
-\r
-void WINAPI QERApp_LoadShaderFile (const char *filename)\r
-{\r
-  char *pBuff;\r
-  int nSize = vfsLoadFile (filename, reinterpret_cast < void **>(&pBuff), 0);\r
-  if (nSize > 0)\r
-  {\r
-    Sys_Printf ("Parsing shaderfile %s\n", filename);\r
-    g_ScripLibTable.m_pfnStartTokenParsing (pBuff);\r
-    while (g_ScripLibTable.m_pfnGetToken (true))\r
-    {\r
-      // first token should be the path + name.. (from base)\r
-      CShader *pShader = new CShader ();\r
-      // we want the relative filename only, it's easier for later lookup .. see QERApp_ReloadShaderFile\r
-      char cTmp[1024];\r
-      g_FuncTable.m_pfnQE_ConvertDOSToUnixName (cTmp, filename);\r
-      // given the vfs, we should not store the full path\r
-      //pShader->setShaderFileName( filename + strlen(ValueForKey(g_qeglobals.d_project_entity, "basepath")));\r
-      pShader->setShaderFileName (filename);\r
-      if (pShader->Parse ())\r
-      {\r
-             // do we already have this shader?\r
-             //++timo NOTE: this may a bit slow, we may need to use a map instead of a dumb list\r
-             if (g_Shaders.Shader_ForName (pShader->getName ()) != NULL)\r
-             {\r
-#ifdef _DEBUG\r
-               Sys_Printf ("WARNING: shader %s is already in memory, definition in %s ignored.\n",\r
-                           pShader->getName (), filename);\r
-#endif\r
-               delete pShader;\r
-             }\r
-             else\r
-             {\r
-               pShader->IncRef ();\r
-\r
-               g_Shaders.Add ((void *) pShader);\r
-             }\r
-      }\r
-      else\r
-      {\r
-             Sys_Printf ("Error parsing shader %s\n", pShader->getName ());\r
-             delete pShader;\r
-      }\r
-    }\r
-    vfsFreeFile (pBuff);\r
-  }\r
-  else\r
-  {\r
-    Sys_Printf ("Unable to read shaderfile %s\n", filename);\r
-  }\r
-}\r
-\r
-IShader *WINAPI QERApp_Try_Shader_ForName (const char *name)\r
-{\r
-  // look for the shader\r
-  CShader *pShader = g_Shaders.Shader_ForName (name);\r
-  if (!pShader)\r
-    // not found\r
-    return NULL;\r
-  // we may need to load the texture or use the "shader without texture" one\r
-  pShader->Activate ();\r
-  pShader->SetDisplayed (true);\r
-  return pShader;\r
-}\r
-\r
-IShader *WINAPI QERApp_CreateShader_ForTextureName (const char *name)\r
-{\r
-  CShader *pShader;\r
-  pShader = new CShader;\r
-  // CreateDefault expects a texture / shader name relative to the "textures" directory\r
-  // (cause shader names are reletive to "textures/")\r
-  pShader->CreateDefault (name);\r
-  // hook it into the shader list\r
-  g_Shaders.Add ((void *) pShader);\r
-  pShader->IncRef ();\r
-  // if it can't find the texture, SHADER_NOT_FOUND will be used\r
-  // Hydra: display an error message, so the user can quickly find a list of missing\r
-  // textures by looking at the console.\r
-  if (!pShader->Activate ())\r
-  {\r
-    Sys_Printf ("WARNING: Activate shader failed for %s\n",pShader->getName());\r
-  }\r
-  pShader->SetDisplayed (true);\r
-  \r
-  return pShader;\r
-}\r
-\r
-IShader *WINAPI QERApp_Shader_ForName (const char *name)\r
-{\r
-  if (name == NULL || strlen (name) == 0)\r
-  {\r
-    // Hydra: This error can occur if the user loaded a map with/dropped an entity that\r
-    // did not set a texture name "(r g b)" - check the entity definition loader\r
-\r
-    g_FuncTable.m_pfnSysFPrintf (SYS_ERR, "FIXME: name == NULL || strlen(name) == 0 in QERApp_Shader_ForName\n");\r
-    return QERApp_Shader_ForName (SHADER_NOT_FOUND);\r
-  }\r
-  // entities that should be represented with plain colors instead of textures\r
-  // request a texture name with (r g b) (it's stored in their class_t)\r
-  if (name[0] == '(')\r
-  {\r
-    return QERApp_ColorShader_ForName (name);\r
-  }\r
-\r
-  CShader *pShader = static_cast < CShader * >(QERApp_Try_Shader_ForName (name));\r
-  if (pShader)\r
-  {\r
-    pShader->SetDisplayed (true);\r
-    return pShader;\r
-  }\r
-  return QERApp_CreateShader_ForTextureName (name);\r
-}\r
-\r
-qtexture_t *WINAPI QERApp_Try_Texture_ForName (const char *name)\r
-{\r
-  qtexture_t *q;\r
-//  char f1[1024], f2[1024];\r
-  unsigned char *pPixels = NULL;\r
-  int nWidth, nHeight;\r
-\r
-  // convert the texture name to the standard format we use in qtexture_t\r
-  const char *stdName = QERApp_CleanTextureName (name);\r
-\r
-  // use the hash table\r
-  q = (qtexture_t*)g_hash_table_lookup (g_ShadersTable.m_pfnQTexmap (), stdName);\r
-  if (q)\r
-    return q;\r
-\r
-#ifdef QTEXMAP_DEBUG\r
-  for (q = g_qeglobals.d_qtextures; q; q = q->next)\r
-  {\r
-    if (!strcmp (stdName, q->name))\r
-    {\r
-      Sys_Printf ("ERROR: %s is not in texture map, but was found in texture list\n");\r
-      return q;\r
-    }\r
-  }\r
-#endif\r
-\r
-  g_FuncTable.m_pfnLoadImage (name, &pPixels, &nWidth, &nHeight);\r
-  \r
-  if (!pPixels)\r
-    return NULL; // we failed\r
-  else\r
-    Sys_Printf ("LOADED: %s\n", name);\r
-\r
-  // instanciate a new qtexture_t\r
-  // NOTE: when called by a plugin we must make sure we have set Radiant's GL context before binding the texture\r
-\r
-  // we'll be binding the GL texture now\r
-  // need to check we are using a right GL context\r
-  // with GL plugins that have their own window, the GL context may be the plugin's, in which case loading textures will bug\r
-  //  g_QglTable.m_pfn_glwidget_make_current (g_QglTable.m_pfn_GetQeglobalsGLWidget ());\r
-  q = g_FuncTable.m_pfnLoadTextureRGBA (pPixels, nWidth, nHeight);\r
-  if (!q)\r
-    return NULL;\r
-  g_free (pPixels);\r
-\r
-  strcpy (q->name, name);\r
-  // only strip extension if extension there is!\r
-  if (q->name[strlen (q->name) - 4] == '.')\r
-    q->name[strlen (q->name) - 4] = '\0';\r
-  // hook into the main qtexture_t list\r
-  qtexture_t **d_qtextures = g_ShadersTable.m_pfnQTextures ();\r
-  q->next = *d_qtextures;\r
-  *d_qtextures = q;\r
-  // push it in the map\r
-  g_hash_table_insert (g_ShadersTable.m_pfnQTexmap (), q->name, q);\r
-  return q;\r
-}\r
-\r
-int WINAPI QERApp_HasShader (const char *pName)\r
-{\r
-  //  mickey check the global shader array for existense of pName\r
-  CShader *pShader = g_Shaders.Shader_ForName (pName);\r
-  if (pShader)\r
-    return 1;\r
-  return 0;\r
-}\r
-\r
-IShader *WINAPI QERApp_Shader_ForName_NoLoad (const char *pName)\r
-{\r
-  CShader *pShader = g_Shaders.Shader_ForName (pName);\r
-  return pShader;\r
-}\r
-\r
-/*!\r
-This should NEVER return NULL, it is the last-chance call in the load cascade\r
-*/\r
-qtexture_t *WINAPI QERApp_Texture_ForName2 (const char *filename)\r
-{\r
-  qtexture_t *q;\r
-  q = QERApp_Try_Texture_ForName (filename);\r
-  if (q)\r
-    return q;\r
-  // not found? use "texture not found"\r
-  q = QERApp_Try_Texture_ForName (SHADER_NOT_FOUND);\r
-  if (q)\r
-    return q;\r
-\r
-  // still not found? this is a fatal error\r
-  g_FuncTable.m_pfnError("Failed to load " SHADER_NOT_FOUND ". Looks like your installation is broken / missing some essential elements.");\r
-  return NULL;\r
-}\r
-\r
-void CShader::CreateColor (const char *name)\r
-{\r
-  // parse\r
-  sscanf (name, "(%g %g %g)", m_vColor, m_vColor + 1, m_vColor + 2);\r
-  m_strTextureName = name;\r
-  setName ("color");\r
-  // create the qtexture_t\r
-  qtexture_t *q1 = QERApp_Texture_ForName2 (SHADER_NOT_FOUND);\r
-  // copy this one\r
-  qtexture_t *q2 = new qtexture_t;\r
-  memcpy (q2, q1, sizeof (qtexture_t));\r
-  strcpy (q2->name, m_strTextureName.GetBuffer ());\r
-  VectorCopy (m_vColor, q2->color);\r
-  m_pTexture = q2;\r
-}\r
-\r
-IShader *WINAPI QERApp_ColorShader_ForName (const char *name)\r
-{\r
-  CShader *pShader = new CShader ();\r
-  pShader->CreateColor (name);\r
-  // hook it into the shader list\r
-  pShader->IncRef ();\r
-  g_Shaders.Add ((void *) pShader);\r
-  return pShader;\r
-}\r
-\r
-void CShaderArray::ReleaseForShaderFile (const char *name)\r
-{\r
-  int i;\r
-  // decref\r
-  for (i = 0; i < CPtrArray::GetSize (); i++)\r
-  {\r
-    IShader *pShader = static_cast < IShader * >(CPtrArray::GetAt (i));\r
-    if (!strcmp (name, pShader->getShaderFileName ()))\r
-    {\r
-      pShader->DecRef ();\r
-      CPtrArray::RemoveAt (i);\r
-      i--;                     // get ready for next loop\r
-    }\r
-  }\r
-}\r
-\r
-void WINAPI QERApp_ReloadShaderFile (const char *name)\r
-{\r
-  brush_t *b;\r
-  face_t *f;\r
-  brush_t *active_brushes;\r
-  brush_t *selected_brushes;\r
-  brush_t *filtered_brushes;\r
-\r
-//  Sys_Printf("TODO: QERApp_ReloadShaderFile\n");\r
-\r
-  active_brushes = g_DataTable.m_pfnActiveBrushes ();\r
-  selected_brushes = g_DataTable.m_pfnSelectedBrushes ();\r
-  filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();\r
-\r
-#ifdef _DEBUG\r
-  // check the shader name is a reletive path\r
-  // I hacked together a few quick tests to make sure :-)\r
-  if (strstr (name, ":\\") || !strstr (name, "scripts"))\r
-    Sys_Printf ("WARNING: is %s a reletive path to a shader file? (QERApp_ReloadShaderFile\n");\r
-#endif\r
-\r
-  // in the actives and global shaders lists, decref and unhook the shaders\r
-  //++timo NOTE: maybe we'd like to keep track of the shaders we are unhooking?\r
-  g_ActiveShaders.ReleaseForShaderFile (name);\r
-  g_Shaders.ReleaseForShaderFile (name);\r
-  // go through a reload of the shader file\r
-  QERApp_LoadShaderFile (name);\r
-  // scan all the brushes, replace all the old ones by refs to their new equivalents\r
-  for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)\r
-  {\r
-    if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))\r
-      SetShader (b->pPatch);\r
-    else\r
-      for (f = b->brush_faces; f; f = f->next)\r
-       if (!strcmp (f->pShader->getShaderFileName (), name))\r
-         SetShader (f);\r
-  }\r
-  for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)\r
-  {\r
-    if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))\r
-      SetShader (b->pPatch);\r
-    else\r
-      for (f = b->brush_faces; f; f = f->next)\r
-       if (!strcmp (f->pShader->getShaderFileName (), name))\r
-         SetShader (f);\r
-  }\r
-  // do that to the filtered brushes as well (we might have some region compiling going on)\r
-  for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)\r
-  {\r
-    if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))\r
-      SetShader (b->pPatch);\r
-    else\r
-      for (f = b->brush_faces; f; f = f->next)\r
-       if (!strcmp (f->pShader->getShaderFileName (), name))\r
-         SetShader (f);\r
-  }\r
-  // call Texture_ShowInUse to clean and display only what's required\r
-  g_ShadersTable.m_pfnTexture_ShowInuse ();\r
-  QERApp_SortActiveShaders ();\r
-  g_FuncTable.m_pfnSysUpdateWindows (W_TEXTURE);\r
-}\r
-\r
-void CShaderArray::SetDisplayed (bool b)\r
-{\r
-  int i, count;\r
-  count = CPtrArray::GetSize ();\r
-  for (i = 0; i < count; i++)\r
-    static_cast < IShader * >(CPtrArray::GetAt (i))->SetDisplayed (b);\r
-}\r
-\r
-void CShaderArray::SetInUse (bool b)\r
-{\r
-  int i, count;\r
-  count = CPtrArray::GetSize ();\r
-  for (i = 0; i < count; i++)\r
-    static_cast < IShader * >(CPtrArray::GetAt (i))->SetInUse (b);\r
-}\r
-\r
-// Set the IsDisplayed flag on all active shaders\r
-void WINAPI QERApp_ActiveShaders_SetDisplayed (bool b)\r
-{\r
-  g_ActiveShaders.SetDisplayed (b);\r
-}\r
-\r
-void WINAPI QERApp_ActiveShaders_SetInUse (bool b)\r
-{\r
-  g_ActiveShaders.SetInUse (b);\r
-}\r
-\r
-// =============================================================================\r
-// SYNAPSE\r
-\r
-bool CSynapseClientShaders::RequestAPI(APIDescriptor_t *pAPI)\r
-{\r
-  if (!strcmp(pAPI->major_name, SHADERS_MAJOR))\r
-  {\r
-    _QERShadersTable* pTable= static_cast<_QERShadersTable*>(pAPI->mpTable);\r
-    \r
-    pTable->m_pfnFreeShaders = QERApp_FreeShaders;\r
-    pTable->m_pfnReloadShaders = QERApp_ReloadShaders;\r
-    pTable->m_pfnLoadShadersFromDir = QERApp_LoadShadersFromDir;\r
-    pTable->m_pfnReloadShaderFile = QERApp_ReloadShaderFile;\r
-    pTable->m_pfnLoadShaderFile = QERApp_LoadShaderFile;\r
-    pTable->m_pfnHasShader = QERApp_HasShader;\r
-    pTable->m_pfnTry_Shader_ForName = QERApp_Try_Shader_ForName;\r
-    pTable->m_pfnShader_ForName = QERApp_Shader_ForName;\r
-    pTable->m_pfnTry_Texture_ForName = QERApp_Try_Texture_ForName;\r
-    pTable->m_pfnTexture_ForName = QERApp_Texture_ForName2;\r
-    pTable->m_pfnGetActiveShaderCount = QERApp_GetActiveShaderCount;\r
-    pTable->m_pfnColorShader_ForName = QERApp_ColorShader_ForName;\r
-    pTable->m_pfnShader_ForName_NoLoad = QERApp_Shader_ForName_NoLoad;\r
-    pTable->m_pfnActiveShaders_SetInUse = QERApp_ActiveShaders_SetInUse;\r
-    pTable->m_pfnSortActiveShaders = QERApp_SortActiveShaders;\r
-    pTable->m_pfnActiveShader_ForTextureName = QERApp_ActiveShader_ForTextureName;\r
-    pTable->m_pfnCreateShader_ForTextureName = QERApp_CreateShader_ForTextureName;\r
-    pTable->m_pfnActiveShaders_SetDisplayed = QERApp_ActiveShaders_SetDisplayed;\r
-    pTable->m_pfnActiveShader_ForIndex = QERApp_ActiveShader_ForIndex;\r
-    pTable->m_pfnCleanTextureName = QERApp_CleanTextureName;    \r
-    \r
-    return true;\r
-  }\r
-\r
-  Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());\r
-  return false;\r
-}\r
+/*
+Copyright (c) 2001, Loki software, inc.
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without modification, 
+are permitted provided that the following conditions are met:
+
+Redistributions of source code must retain the above copyright notice, this list 
+of conditions and the following disclaimer.
+
+Redistributions in binary form must reproduce the above copyright notice, this
+list of conditions and the following disclaimer in the documentation and/or
+other materials provided with the distribution.
+
+Neither the name of Loki software nor the names of its contributors may be used 
+to endorse or promote products derived from this software without specific prior 
+written permission. 
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ``AS IS'' 
+AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 
+IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE FOR ANY 
+DIRECT,INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+(INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON 
+ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 
+*/
+
+//
+// Shaders Manager Plugin
+//
+// Leonardo Zide (leo@lokigames.com)
+//
+
+// standard headers
+#include <stdio.h>
+#include <stdlib.h>
+#include "plugin.h"
+#include "mathlib.h"
+#include "missing.h" //++timo FIXME: this one is intended to go away some day, it's MFC compatibility classes
+#include "shaders.h"
+
+// some forward declarations
+IShader *WINAPI QERApp_Shader_ForName (const char *name);
+qtexture_t *WINAPI QERApp_Try_Texture_ForName (const char *name);
+qtexture_t *WINAPI QERApp_Texture_ForName2 (const char *filename);
+IShader *WINAPI QERApp_ColorShader_ForName (const char *name);
+void WINAPI QERApp_LoadShaderFile (const char *filename);
+
+//++timo TODO: use stl::map !! (I tried having a look to CMap but it obviously sucks)
+CShaderArray g_Shaders;
+// whenever a shader gets activated / deactivated this list is updated
+// NOTE: make sure you don't add a shader that's already in
+// NOTE: all shaders in this array are in the main g_Shaders
+CShaderArray g_ActiveShaders;
+
+// clean a texture name to the qtexture_t name format we use internally
+// NOTE: there are so many cases .. this may need to get updated to cover all of them
+// we expect a "textures/" path on top, except if bAddTexture is set to true .. in case we add in needed
+// NOTE: case sensitivity: the engine is case sensitive. we store the shader name with case information and save with case
+// information as well. but we assume there won't be any case conflict and so when doing lookups based on shader name,
+// we compare as case insensitive. That is Radiant is case insensitive, but knows that the engine is case sensitive.
+//++timo FIXME: we need to put code somewhere to detect when two shaders that are case insensitive equal are present
+const char *WINAPI QERApp_CleanTextureName (const char *name, bool bAddTexture = false)
+{
+  static char stdName[QER_MAX_NAMELEN];
+#ifdef _DEBUG
+  if (strlen(name)>QER_MAX_NAMELEN)
+    g_FuncTable.m_pfnSysFPrintf(SYS_WRN, "WARNING: name exceeds QER_MAX_NAMELEN in CleanTextureName\n");
+#endif
+
+  strcpy (stdName, name);
+  g_FuncTable.m_pfnQE_ConvertDOSToUnixName (stdName, stdName);
+  if (stdName[strlen (name) - 4] == '.')
+    // strip extension
+    stdName[strlen (stdName) - 4] = '\0';
+
+       if (bAddTexture)
+  {
+    char aux[QER_MAX_NAMELEN];
+    sprintf (aux, "textures/%s", stdName);
+    strcpy (stdName, aux);
+       }
+  return stdName;
+}
+
+int WINAPI QERApp_GetActiveShaderCount ()
+{
+  return g_ActiveShaders.GetSize ();
+}
+
+IShader *WINAPI QERApp_ActiveShader_ForIndex (int i)
+{
+  return static_cast < CShader * >(g_ActiveShaders.GetAt (i));
+}
+
+void CShaderArray::SortShaders ()
+{
+  CPtrArray aux;
+  int i, icount;
+  int j, jcount;
+  CShader *pSort;
+  const char *sSort;
+  // dumb sort .. would it ever grow big enough so we would have to do something clever? noooo
+  icount = CPtrArray::GetSize ();
+  for (i = 0; i < icount; i++)
+  {
+    pSort = static_cast < CShader * >(GetAt (i));
+    sSort = pSort->getName ();
+    jcount = aux.GetSize ();
+    for (j = 0; j < jcount; j++)
+    {
+      if (strcmp (sSort, static_cast < CShader * >(aux.GetAt (j))->getName ()) < 0)
+        break;
+    }
+    aux.InsertAt (j, pSort);
+  }
+  CPtrArray::RemoveAll ();
+  CPtrArray::InsertAt (0, &aux);
+}
+
+// will sort the active shaders list by name
+// NOTE: it would be easier if the thing would stay sorted by using a map<name,CShader> thing
+//++timo FIXME: would need to export that to allow external override?
+void WINAPI QERApp_SortActiveShaders ()
+{
+  g_ActiveShaders.SortShaders ();
+}
+
+// NOTE: case sensitivity
+// although we store shader names with case information, Radiant does case insensitive searches
+// (we assume there's no case conflict with the names)
+CShader *CShaderArray::Shader_ForName (const char *name) const
+{
+  int i;
+  for (i = 0; i < CPtrArray::GetSize (); i++)
+  {
+    CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));
+    if (stricmp (pShader->getName (), name) == 0)
+      return pShader;
+  }
+  return NULL;
+}
+
+void CShader::CreateDefault (const char *name)
+{
+  const char *stdName = QERApp_CleanTextureName (name);
+  m_strTextureName = stdName;
+  setName (name);
+}
+
+CShader *CShaderArray::Shader_ForTextureName (const char *name) const
+{
+#ifdef _DEBUG
+  // check we were given a texture name that fits the qtexture_t naming conventions
+  if (strcmp (name, QERApp_CleanTextureName (name)) != 0)
+    Sys_Printf
+      ("WARNING: texture name %s doesn't fit qtexture_t conventions in CShaderArray::Shader_ForTextureName\n",
+       name);
+#endif
+  int i;
+  for (i = 0; i < CPtrArray::GetSize (); i++)
+  {
+    CShader *pShader = static_cast < CShader * >(CPtrArray::GetAt (i));
+    if (strcmp (name, QERApp_CleanTextureName (pShader->getTextureName ())) == 0)
+      return pShader;
+  }
+  return NULL;
+}
+
+IShader *WINAPI QERApp_ActiveShader_ForTextureName (char *name)
+{
+  return g_ActiveShaders.Shader_ForTextureName (name);
+}
+
+void CShaderArray::AddSingle (void *lp)
+{
+  int i;
+  for (i = 0; i < CPtrArray::GetSize (); i++)
+  {
+    if (CPtrArray::GetAt (i) == lp)
+      return;
+  }
+  CPtrArray::Add (lp);
+  static_cast < CShader * >(CPtrArray::GetAt (i))->IncRef();
+}
+
+void CShaderArray::operator = (const class CShaderArray & src)
+{
+  int i;
+
+#ifdef _DEBUG
+  if (CPtrArray::GetSize () != 0)
+    Sys_Printf ("WARNING: CShaderArray::operator = expects an empty array\n");
+#endif
+  Copy (src);
+  // now go through and IncRef
+  for (i = 0; i < CPtrArray::GetSize (); i++)
+    static_cast < IShader * >(CPtrArray::GetAt (i))->IncRef ();
+}
+
+//++timo NOTE: for debugging we may need to keep track and tell wether everything has been properly unloaded
+void CShaderArray::ReleaseAll ()
+{
+  int i;
+  int count = CPtrArray::GetSize ();
+  // decref
+  for (i = 0; i < count; i++)
+    static_cast < IShader * >(CPtrArray::GetAt (i))->DecRef ();
+  // get rid
+  CPtrArray::RemoveAll ();
+}
+
+// NOTE TTimo:
+// this was hacked to work a long time ago
+// in Loki's fenris tracker as bug #104655
+// since that info is no longer available, and the hack has been there for so long, it's part of the code now
+// don't remember the details, but basically across a flush and reload for the shaders
+// we have to keep track of the patches texture names in a seperate entry
+// not sure why anymore, but I know that doesn't happen with brushes
+typedef struct patchEntry_s
+{
+  char name[QER_MAX_NAMELEN];
+  patchMesh_t *p;
+} patchEntry_t;
+
+CPtrArray PatchShaders;
+
+void PushPatch (patchMesh_t * patch)
+{
+  patchEntry_t *pEntry = new patchEntry_s;
+  pEntry->p = patch;
+  strcpy (pEntry->name, patch->pShader->getName ());
+  PatchShaders.Add (pEntry);
+}
+
+char *ShaderNameLookup (patchMesh_t * patch)
+{
+  int i;
+  int count = PatchShaders.GetSize ();
+  for (i = 0; i < count; i++)
+  {
+    if (static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->p == patch)
+      return static_cast < patchEntry_t * >(PatchShaders.GetAt (i))->name;
+  }
+  Sys_Printf ("ERROR: failed to lookup name in ShaderNameLookup??\n");
+  return SHADER_NOT_FOUND;
+}
+//++timo end clean
+
+// will free all GL binded qtextures and shaders
+// NOTE: doesn't make much sense out of Radiant exit or called during a reload
+void WINAPI QERApp_FreeShaders ()
+{
+  int i;
+  brush_t *b;
+  brush_t *active_brushes;
+  brush_t *selected_brushes;
+  brush_t *filtered_brushes;
+  qtexture_t **d_qtextures;
+
+  active_brushes = g_DataTable.m_pfnActiveBrushes ();
+  selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
+  filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
+  d_qtextures = g_ShadersTable.m_pfnQTextures ();
+
+  // store the shader names used by the patches
+  for (i = 0; i < PatchShaders.GetSize (); i++)
+    delete static_cast < patchMesh_t * >(PatchShaders.GetAt (i));
+  PatchShaders.RemoveAll ();
+
+  for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
+  {
+    if (b->patchBrush)
+      PushPatch (b->pPatch);
+  }
+  for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
+  {
+    if (b->patchBrush)
+      PushPatch (b->pPatch);
+  }
+  for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
+  {
+    if (b->patchBrush)
+      PushPatch (b->pPatch);
+  }
+
+  // reload shaders
+  // empty the actives shaders list
+  g_ActiveShaders.ReleaseAll ();
+  g_Shaders.ReleaseAll ();
+  // empty the main g_qeglobals.d_qtextures list
+  // FIXME: when we reload later on, we need to have the shader names
+  // for brushes it's stored in the texdef
+  // but patches don't have texdef
+  // see bug 104655 for details
+  // so the solution, build an array of patchMesh_t* and their shader names
+#ifdef _DEBUG
+  Sys_Printf ("FIXME: patch shader reload workaround (old fenris? bug 104655)\n");
+#endif
+
+  //GtkWidget *widget = g_QglTable.m_pfn_GetQeglobalsGLWidget ();
+  GHashTable *texmap = g_ShadersTable.m_pfnQTexmap ();
+
+  // NOTE: maybe before we'd like to set all qtexture_t in the shaders list to notex?
+  // NOTE: maybe there are some qtexture_t we don't want to erase? For plain color faces maybe?
+  while (*d_qtextures)
+  {
+    qtexture_t *pTex = *d_qtextures;
+    qtexture_t *pNextTex = pTex->next;
+
+    //if (widget != NULL)
+    g_QglTable.m_pfn_qglDeleteTextures (1, &pTex->texture_number);
+
+    g_hash_table_remove (texmap, pTex->name);
+      
+    // all qtexture_t should be manipulated with the glib alloc handlers for now
+    g_free (pTex);
+    *d_qtextures = pNextTex;
+  }
+
+  g_QglTable.m_pfn_QE_CheckOpenGLForErrors ();
+}
+
+// those functions are only used during a shader reload phase
+// the patch one relies on ShaderNameLookup, a table that is being built only when a flush is performed
+// so it's not something we want to expose publicly
+
+void SetShader (patchMesh_t * patch)
+{
+  // unhook current shader
+  patch->pShader->DecRef();
+  // don't access this one! it has been deleted .. it's DEAD
+  patch->d_texture = NULL;
+  // hook the new one, increment the refcount
+  // NOTE TTimo this function increments the refcount, don't incref ourselves
+  patch->pShader = QERApp_Shader_ForName (ShaderNameLookup (patch));
+  patch->d_texture = patch->pShader->getTexture ();
+}
+
+void SetShader (face_t * f)
+{
+  // unhook current shader
+  f->pShader->DecRef();
+  // don't access the texdef! it's DEAD
+  f->d_texture = NULL;
+  // hook
+  // NOTE TTimo this function increments the refcount, don't incref ourselves
+  f->pShader = QERApp_Shader_ForName (f->texdef.GetName());
+  f->d_texture = f->pShader->getTexture ();
+}
+
+void Brush_RefreshShader(brush_t *b)
+{
+  if (b->patchBrush)
+    SetShader(b->pPatch);
+  else if (b->owner->eclass->fixedsize)
+  {
+    /*eclass_t *eclass = HasModel(b);
+    if (eclass)
+    {
+      for(entitymodel *model = eclass->model; model!=NULL; model=model->pNext)
+        if(model && model->strSkin)
+          model->nTextureBind = g_FuncTable.m_pfnTexture_LoadSkin(((GString *)model->strSkin)->str, &model->nSkinWidth, &model->nSkinHeight);
+    }*/
+  }
+  else
+               for (face_t *f=b->brush_faces ; f ; f=f->next)
+                       SetShader(f);
+}
+
+void WINAPI QERApp_ReloadShaders ()
+{
+  brush_t *b;
+  brush_t *active_brushes;
+  brush_t *selected_brushes;
+  brush_t *filtered_brushes;
+
+  QERApp_FreeShaders ();
+
+  g_DataTable.m_pfnLstSkinCache()->RemoveAll(); //md3 skins
+
+  active_brushes = g_DataTable.m_pfnActiveBrushes ();
+  selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
+  filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
+
+  // now we must reload the shader information from shaderfiles
+  g_ShadersTable.m_pfnBuildShaderList();
+  g_ShadersTable.m_pfnPreloadShaders();
+
+  // refresh the map visuals: replace our old shader objects by the new ones
+  // on brush faces we have the shader name in texdef.name
+  // on patches we have the shader name in PatchShaders
+  // while we walk through the map data, we DecRef the old shaders and push the new ones in
+  // if all goes well, most of our old shaders will get deleted on the way
+
+  // FIXME: bug 104655, when we come accross a patch, we use the above array since the d_texture is lost
+  // NOTE: both face_t and patchMesh_t store pointers to the shader and qtexture_t
+  // in an ideal world they would only store shader and access the qtexture_t through it
+  // reassign all current shaders
+  for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
+    Brush_RefreshShader(b);
+  for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
+    Brush_RefreshShader(b);
+  // do that to the filtered brushes as well (we might have some region compiling going on)
+  for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
+    Brush_RefreshShader(b);
+}
+
+int WINAPI QERApp_LoadShadersFromDir (const char *path)
+{
+  int count = 0;
+  // scan g_Shaders, and call QERApp_Shader_ForName for each in the given path
+  // this will load the texture if needed and will set it in use..
+  int nSize = g_Shaders.GetSize ();
+  for (int i = 0; i < nSize; i++)
+  {
+    CShader *pShader = reinterpret_cast < CShader * >(g_Shaders[i]);
+    if (strstr (pShader->getShaderFileName (), path) || strstr (pShader->getName (), path))
+    {
+      count++;
+      // request the shader, this will load the texture if needed and set "inuse"
+      //++timo FIXME: should we put an Activate member on CShader?
+      // this QERApp_Shader_ForName call is a kind of hack
+      IShader *pFoo = QERApp_Shader_ForName (pShader->getName ());
+#ifdef _DEBUG
+      // check we activated the right shader
+      // NOTE: if there was something else loaded, the size of g_Shaders may have changed and strange behaviours are to be expected
+      if (pFoo != pShader)
+       Sys_Printf ("WARNING: unexpected pFoo != pShader in QERApp_LoadShadersFromDir\n");
+#else
+      pFoo = NULL;             // leo: shut up the compiler
+#endif
+    }
+  }
+  return count;
+}
+
+bool CShader::Parse ()
+{
+  char *token = g_ScripLibTable.m_pfnToken ();
+
+  // the parsing needs to be taken out in another module
+//  Sys_Printf("TODO: CShader::Parse\n");
+
+  // token is shader name (full path with a "textures\")
+  // we remove the "textures\" part
+  //setName ((char *) &token[9]));
+       // no we don't
+       setName (token);
+  // name of the qtexture_t we'll use to represent this shader (this one has the "textures\" before)
+  const char *stdName = QERApp_CleanTextureName (token);
+  m_strTextureName = stdName; // FIXME: BC reports stdName is uninitialised?
+  g_ScripLibTable.m_pfnGetToken (true);
+  if (strcmp (token, "{"))
+    return false;
+  else
+  {
+    // we need to read until we hit a balanced }
+    int nMatch = 1;
+    while (nMatch > 0 && g_ScripLibTable.m_pfnGetToken (true))
+    {
+      if (strcmp (token, "{") == 0)
+      {
+       nMatch++;
+        continue;
+      }
+      else if (strcmp (token, "}") == 0)
+      {
+       nMatch--;
+        continue;
+      }
+      if (nMatch > 1) continue; // ignore layers for now
+      if (strcmpi (token, "qer_nocarve") == 0)
+      {
+       m_nFlags |= QER_NOCARVE;
+      }
+      else if (strcmpi (token, "qer_trans") == 0)
+      {
+             if (g_ScripLibTable.m_pfnGetToken (true))
+             {
+               m_fTrans = (float) atof (token);
+             }
+             m_nFlags |= QER_TRANS;
+      }
+      else if (strcmpi (token, "qer_editorimage") == 0)
+      {
+             if (g_ScripLibTable.m_pfnGetToken (true))
+             {
+          // bAddTexture changed to false to allow editorimages in other locations than "textures/"
+               m_strTextureName = QERApp_CleanTextureName (token, false);
+             }
+      }
+      else if (strcmpi (token, "qer_alphafunc") == 0)
+      {
+             if (g_ScripLibTable.m_pfnGetToken (true))
+             {
+          
+          if(stricmp( token, "greater" ) == 0 )
+          {
+                 m_nAlphaFunc = GL_GREATER;
+          }
+          else if(stricmp( token, "less" ) == 0 )
+          {
+            m_nAlphaFunc = GL_LESS;
+          }
+          else if(stricmp( token, "gequal" ) == 0 )
+          {
+            m_nAlphaFunc = GL_GEQUAL;
+          }
+
+          if( m_nAlphaFunc )
+            m_nFlags |= QER_ALPHAFUNC;
+             }
+        if (g_ScripLibTable.m_pfnGetToken (true))
+             {
+               m_fAlphaRef = (float) atof (token);
+             }
+      }
+      else if (strcmpi (token, "cull") == 0)
+      {
+        if (g_ScripLibTable.m_pfnGetToken (true))
+             {
+          if( stricmp( token, "none" ) == 0 || stricmp( token, "twosided" ) == 0 || stricmp( token, "disable" ) == 0 )
+          {
+                 m_nCull = 2;
+          }
+          else if( stricmp( token, "back" ) == 0 || stricmp( token, "backside" ) == 0 || stricmp( token, "backsided" ) == 0 )
+          {
+                 m_nCull = 1;
+          }
+
+          if( m_nCull )
+             m_nFlags |= QER_CULL;
+        }
+      }
+      else if (strcmpi (token, "surfaceparm") == 0)
+      {
+             if (g_ScripLibTable.m_pfnGetToken (true))
+             {
+               if (strcmpi (token, "fog") == 0)
+               {
+            m_nFlags |= QER_FOG;
+                 if (m_fTrans == 1.0f) // has not been explicitly set by qer_trans
+                 {
+                   m_fTrans = 0.35f;
+                 }
+               }
+          else if (strcmpi (token, "nodraw") == 0)
+          {
+            m_nFlags |= QER_NODRAW;
+          }
+          else if (strcmpi (token, "nonsolid") == 0)
+          {
+            m_nFlags |= QER_NONSOLID;
+          }
+          else if (strcmpi (token, "water") == 0)
+          {
+            m_nFlags |= QER_WATER;
+          }
+          else if (strcmpi (token, "lava") == 0)
+          {
+            m_nFlags |= QER_LAVA;
+          }
+             }
+      }
+    }
+    if (nMatch != 0)
+      return false;
+  }
+  return true;
+}
+
+void CShader::RegisterActivate ()
+{
+  // fill the qtexture_t with shader information
+  //++timo FIXME: a lot of that won't be necessary, will be stored at IShader* level
+//  strcpy (m_pTexture->shadername, m_Name);
+  // this flag is set only if we have a shaderfile name
+//  if (m_ShaderFileName[0] != '\0')
+//    m_pTexture->bFromShader = true;
+//  else
+//    m_pTexture->bFromShader = false;
+  //++timo FIXME: what do we do with that?
+  //m_pTexture->fTrans = pInfo->m_fTransValue;
+//  m_pTexture->fTrans = 1.0f; // if != 1.0 it's ot getting drawn in Cam_Draw
+//  m_pTexture->nShaderFlags = m_nFlags;
+  // store in the active shaders list (if necessary)
+  g_ActiveShaders.AddSingle (this);
+  // when you activate a shader, it gets displayed in the texture browser
+  m_bDisplayed = true;
+  IncRef ();
+}
+
+void CShader::Try_Activate ()
+{
+  m_pTexture = QERApp_Try_Texture_ForName (m_strTextureName.GetBuffer());
+  if (m_pTexture)
+    RegisterActivate ();
+}
+
+// Hydra: now returns false if the ORIGINAL shader could not be activated
+// (missing texture, or incorrect shader script), true otherwise
+// the shader is still activated in all cases.
+bool CShader::Activate ()
+{
+  Try_Activate ();
+  if (!m_pTexture)
+  {
+    m_pTexture = QERApp_Texture_ForName2 (SHADER_NOTEX);
+    RegisterActivate ();
+    return false;
+  }
+  return true;
+}
+
+void WINAPI QERApp_LoadShaderFile (const char *filename)
+{
+  char *pBuff;
+  int nSize = vfsLoadFile (filename, reinterpret_cast < void **>(&pBuff), 0);
+  if (nSize > 0)
+  {
+    Sys_Printf ("Parsing shaderfile %s\n", filename);
+    g_ScripLibTable.m_pfnStartTokenParsing (pBuff);
+    while (g_ScripLibTable.m_pfnGetToken (true))
+    {
+      // first token should be the path + name.. (from base)
+      CShader *pShader = new CShader ();
+      // we want the relative filename only, it's easier for later lookup .. see QERApp_ReloadShaderFile
+      char cTmp[1024];
+      g_FuncTable.m_pfnQE_ConvertDOSToUnixName (cTmp, filename);
+      // given the vfs, we should not store the full path
+      //pShader->setShaderFileName( filename + strlen(ValueForKey(g_qeglobals.d_project_entity, "basepath")));
+      pShader->setShaderFileName (filename);
+      if (pShader->Parse ())
+      {
+             // do we already have this shader?
+             //++timo NOTE: this may a bit slow, we may need to use a map instead of a dumb list
+             if (g_Shaders.Shader_ForName (pShader->getName ()) != NULL)
+             {
+#ifdef _DEBUG
+               Sys_Printf ("WARNING: shader %s is already in memory, definition in %s ignored.\n",
+                           pShader->getName (), filename);
+#endif
+               delete pShader;
+             }
+             else
+             {
+               pShader->IncRef ();
+
+               g_Shaders.Add ((void *) pShader);
+             }
+      }
+      else
+      {
+             Sys_Printf ("Error parsing shader %s\n", pShader->getName ());
+             delete pShader;
+      }
+    }
+    vfsFreeFile (pBuff);
+  }
+  else
+  {
+    Sys_Printf ("Unable to read shaderfile %s\n", filename);
+  }
+}
+
+IShader *WINAPI QERApp_Try_Shader_ForName (const char *name)
+{
+  // look for the shader
+  CShader *pShader = g_Shaders.Shader_ForName (name);
+  if (!pShader)
+    // not found
+    return NULL;
+  // we may need to load the texture or use the "shader without texture" one
+  pShader->Activate ();
+  pShader->SetDisplayed (true);
+  return pShader;
+}
+
+IShader *WINAPI QERApp_CreateShader_ForTextureName (const char *name)
+{
+  CShader *pShader;
+  pShader = new CShader;
+  // CreateDefault expects a texture / shader name relative to the "textures" directory
+  // (cause shader names are reletive to "textures/")
+  pShader->CreateDefault (name);
+  // hook it into the shader list
+  g_Shaders.Add ((void *) pShader);
+  pShader->IncRef ();
+  // if it can't find the texture, SHADER_NOT_FOUND will be used
+  // Hydra: display an error message, so the user can quickly find a list of missing
+  // textures by looking at the console.
+  if (!pShader->Activate ())
+  {
+    Sys_Printf ("WARNING: Activate shader failed for %s\n",pShader->getName());
+  }
+  pShader->SetDisplayed (true);
+  
+  return pShader;
+}
+
+IShader *WINAPI QERApp_Shader_ForName (const char *name)
+{
+  if (name == NULL || strlen (name) == 0)
+  {
+    // Hydra: This error can occur if the user loaded a map with/dropped an entity that
+    // did not set a texture name "(r g b)" - check the entity definition loader
+
+    g_FuncTable.m_pfnSysFPrintf (SYS_ERR, "FIXME: name == NULL || strlen(name) == 0 in QERApp_Shader_ForName\n");
+    return QERApp_Shader_ForName (SHADER_NOT_FOUND);
+  }
+  // entities that should be represented with plain colors instead of textures
+  // request a texture name with (r g b) (it's stored in their class_t)
+  if (name[0] == '(')
+  {
+    return QERApp_ColorShader_ForName (name);
+  }
+
+  CShader *pShader = static_cast < CShader * >(QERApp_Try_Shader_ForName (name));
+  if (pShader)
+  {
+    pShader->SetDisplayed (true);
+    return pShader;
+  }
+  return QERApp_CreateShader_ForTextureName (name);
+}
+
+qtexture_t *WINAPI QERApp_Try_Texture_ForName (const char *name)
+{
+  qtexture_t *q;
+//  char f1[1024], f2[1024];
+  unsigned char *pPixels = NULL;
+  int nWidth, nHeight;
+
+  // convert the texture name to the standard format we use in qtexture_t
+  const char *stdName = QERApp_CleanTextureName (name);
+
+  // use the hash table
+  q = (qtexture_t*)g_hash_table_lookup (g_ShadersTable.m_pfnQTexmap (), stdName);
+  if (q)
+    return q;
+
+#ifdef QTEXMAP_DEBUG
+  for (q = g_qeglobals.d_qtextures; q; q = q->next)
+  {
+    if (!strcmp (stdName, q->name))
+    {
+      Sys_Printf ("ERROR: %s is not in texture map, but was found in texture list\n");
+      return q;
+    }
+  }
+#endif
+
+  g_FuncTable.m_pfnLoadImage (name, &pPixels, &nWidth, &nHeight);
+  
+  if (!pPixels)
+    return NULL; // we failed
+  else
+    Sys_Printf ("LOADED: %s\n", name);
+
+  // instanciate a new qtexture_t
+  // NOTE: when called by a plugin we must make sure we have set Radiant's GL context before binding the texture
+
+  // we'll be binding the GL texture now
+  // need to check we are using a right GL context
+  // with GL plugins that have their own window, the GL context may be the plugin's, in which case loading textures will bug
+  //  g_QglTable.m_pfn_glwidget_make_current (g_QglTable.m_pfn_GetQeglobalsGLWidget ());
+  q = g_FuncTable.m_pfnLoadTextureRGBA (pPixels, nWidth, nHeight);
+  if (!q)
+    return NULL;
+  g_free (pPixels);
+
+  strcpy (q->name, name);
+  // only strip extension if extension there is!
+  if (q->name[strlen (q->name) - 4] == '.')
+    q->name[strlen (q->name) - 4] = '\0';
+  // hook into the main qtexture_t list
+  qtexture_t **d_qtextures = g_ShadersTable.m_pfnQTextures ();
+  q->next = *d_qtextures;
+  *d_qtextures = q;
+  // push it in the map
+  g_hash_table_insert (g_ShadersTable.m_pfnQTexmap (), q->name, q);
+  return q;
+}
+
+int WINAPI QERApp_HasShader (const char *pName)
+{
+  //  mickey check the global shader array for existense of pName
+  CShader *pShader = g_Shaders.Shader_ForName (pName);
+  if (pShader)
+    return 1;
+  return 0;
+}
+
+IShader *WINAPI QERApp_Shader_ForName_NoLoad (const char *pName)
+{
+  CShader *pShader = g_Shaders.Shader_ForName (pName);
+  return pShader;
+}
+
+/*!
+This should NEVER return NULL, it is the last-chance call in the load cascade
+*/
+qtexture_t *WINAPI QERApp_Texture_ForName2 (const char *filename)
+{
+  qtexture_t *q;
+  q = QERApp_Try_Texture_ForName (filename);
+  if (q)
+    return q;
+  // not found? use "texture not found"
+  q = QERApp_Try_Texture_ForName (SHADER_NOT_FOUND);
+  if (q)
+    return q;
+
+  // still not found? this is a fatal error
+  g_FuncTable.m_pfnError("Failed to load " SHADER_NOT_FOUND ". Looks like your installation is broken / missing some essential elements.");
+  return NULL;
+}
+
+void CShader::CreateColor (const char *name)
+{
+  // parse
+  sscanf (name, "(%g %g %g)", m_vColor, m_vColor + 1, m_vColor + 2);
+  m_strTextureName = name;
+  setName ("color");
+  // create the qtexture_t
+  qtexture_t *q1 = QERApp_Texture_ForName2 (SHADER_NOT_FOUND);
+  // copy this one
+  qtexture_t *q2 = new qtexture_t;
+  memcpy (q2, q1, sizeof (qtexture_t));
+  strcpy (q2->name, m_strTextureName.GetBuffer ());
+  VectorCopy (m_vColor, q2->color);
+  m_pTexture = q2;
+}
+
+IShader *WINAPI QERApp_ColorShader_ForName (const char *name)
+{
+  CShader *pShader = new CShader ();
+  pShader->CreateColor (name);
+  // hook it into the shader list
+  pShader->IncRef ();
+  g_Shaders.Add ((void *) pShader);
+  return pShader;
+}
+
+void CShaderArray::ReleaseForShaderFile (const char *name)
+{
+  int i;
+  // decref
+  for (i = 0; i < CPtrArray::GetSize (); i++)
+  {
+    IShader *pShader = static_cast < IShader * >(CPtrArray::GetAt (i));
+    if (!strcmp (name, pShader->getShaderFileName ()))
+    {
+      pShader->DecRef ();
+      CPtrArray::RemoveAt (i);
+      i--;                     // get ready for next loop
+    }
+  }
+}
+
+void WINAPI QERApp_ReloadShaderFile (const char *name)
+{
+  brush_t *b;
+  face_t *f;
+  brush_t *active_brushes;
+  brush_t *selected_brushes;
+  brush_t *filtered_brushes;
+
+//  Sys_Printf("TODO: QERApp_ReloadShaderFile\n");
+
+  active_brushes = g_DataTable.m_pfnActiveBrushes ();
+  selected_brushes = g_DataTable.m_pfnSelectedBrushes ();
+  filtered_brushes = g_DataTable.m_pfnFilteredBrushes ();
+
+#ifdef _DEBUG
+  // check the shader name is a reletive path
+  // I hacked together a few quick tests to make sure :-)
+  if (strstr (name, ":\\") || !strstr (name, "scripts"))
+    Sys_Printf ("WARNING: is %s a reletive path to a shader file? (QERApp_ReloadShaderFile\n");
+#endif
+
+  // in the actives and global shaders lists, decref and unhook the shaders
+  //++timo NOTE: maybe we'd like to keep track of the shaders we are unhooking?
+  g_ActiveShaders.ReleaseForShaderFile (name);
+  g_Shaders.ReleaseForShaderFile (name);
+  // go through a reload of the shader file
+  QERApp_LoadShaderFile (name);
+  // scan all the brushes, replace all the old ones by refs to their new equivalents
+  for (b = active_brushes->next; b != NULL && b != active_brushes; b = b->next)
+  {
+    if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
+      SetShader (b->pPatch);
+    else
+      for (f = b->brush_faces; f; f = f->next)
+       if (!strcmp (f->pShader->getShaderFileName (), name))
+         SetShader (f);
+  }
+  for (b = selected_brushes->next; b != NULL && b != selected_brushes; b = b->next)
+  {
+    if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
+      SetShader (b->pPatch);
+    else
+      for (f = b->brush_faces; f; f = f->next)
+       if (!strcmp (f->pShader->getShaderFileName (), name))
+         SetShader (f);
+  }
+  // do that to the filtered brushes as well (we might have some region compiling going on)
+  for (b = filtered_brushes->next; b != NULL && b != filtered_brushes; b = b->next)
+  {
+    if (b->patchBrush && !strcmp (b->pPatch->pShader->getShaderFileName (), name))
+      SetShader (b->pPatch);
+    else
+      for (f = b->brush_faces; f; f = f->next)
+       if (!strcmp (f->pShader->getShaderFileName (), name))
+         SetShader (f);
+  }
+  // call Texture_ShowInUse to clean and display only what's required
+  g_ShadersTable.m_pfnTexture_ShowInuse ();
+  QERApp_SortActiveShaders ();
+  g_FuncTable.m_pfnSysUpdateWindows (W_TEXTURE);
+}
+
+void CShaderArray::SetDisplayed (bool b)
+{
+  int i, count;
+  count = CPtrArray::GetSize ();
+  for (i = 0; i < count; i++)
+    static_cast < IShader * >(CPtrArray::GetAt (i))->SetDisplayed (b);
+}
+
+void CShaderArray::SetInUse (bool b)
+{
+  int i, count;
+  count = CPtrArray::GetSize ();
+  for (i = 0; i < count; i++)
+    static_cast < IShader * >(CPtrArray::GetAt (i))->SetInUse (b);
+}
+
+// Set the IsDisplayed flag on all active shaders
+void WINAPI QERApp_ActiveShaders_SetDisplayed (bool b)
+{
+  g_ActiveShaders.SetDisplayed (b);
+}
+
+void WINAPI QERApp_ActiveShaders_SetInUse (bool b)
+{
+  g_ActiveShaders.SetInUse (b);
+}
+
+// =============================================================================
+// SYNAPSE
+
+bool CSynapseClientShaders::RequestAPI(APIDescriptor_t *pAPI)
+{
+  if (!strcmp(pAPI->major_name, SHADERS_MAJOR))
+  {
+    _QERShadersTable* pTable= static_cast<_QERShadersTable*>(pAPI->mpTable);
+    
+    pTable->m_pfnFreeShaders = QERApp_FreeShaders;
+    pTable->m_pfnReloadShaders = QERApp_ReloadShaders;
+    pTable->m_pfnLoadShadersFromDir = QERApp_LoadShadersFromDir;
+    pTable->m_pfnReloadShaderFile = QERApp_ReloadShaderFile;
+    pTable->m_pfnLoadShaderFile = QERApp_LoadShaderFile;
+    pTable->m_pfnHasShader = QERApp_HasShader;
+    pTable->m_pfnTry_Shader_ForName = QERApp_Try_Shader_ForName;
+    pTable->m_pfnShader_ForName = QERApp_Shader_ForName;
+    pTable->m_pfnTry_Texture_ForName = QERApp_Try_Texture_ForName;
+    pTable->m_pfnTexture_ForName = QERApp_Texture_ForName2;
+    pTable->m_pfnGetActiveShaderCount = QERApp_GetActiveShaderCount;
+    pTable->m_pfnColorShader_ForName = QERApp_ColorShader_ForName;
+    pTable->m_pfnShader_ForName_NoLoad = QERApp_Shader_ForName_NoLoad;
+    pTable->m_pfnActiveShaders_SetInUse = QERApp_ActiveShaders_SetInUse;
+    pTable->m_pfnSortActiveShaders = QERApp_SortActiveShaders;
+    pTable->m_pfnActiveShader_ForTextureName = QERApp_ActiveShader_ForTextureName;
+    pTable->m_pfnCreateShader_ForTextureName = QERApp_CreateShader_ForTextureName;
+    pTable->m_pfnActiveShaders_SetDisplayed = QERApp_ActiveShaders_SetDisplayed;
+    pTable->m_pfnActiveShader_ForIndex = QERApp_ActiveShader_ForIndex;
+    pTable->m_pfnCleanTextureName = QERApp_CleanTextureName;    
+    
+    return true;
+  }
+
+  Syn_Printf("ERROR: RequestAPI( '%s' ) not found in '%s'\n", pAPI->major_name, GetInfo());
+  return false;
+}