basic architecture for game configuration at runtime. writes out a .game, no sanity...
[xonotic/netradiant.git] / radiant / qe3.cpp
index e3f9cec65142cf8413afaec9a53f78377666c8a2..a658456407672f7222613fc6c4345e3d8cd08793 100644 (file)
-/*\r
-Copyright (C) 1999-2007 id Software, Inc. and contributors.\r
-For a list of contributors, see the accompanying CONTRIBUTORS file.\r
-\r
-This file is part of GtkRadiant.\r
-\r
-GtkRadiant is free software; you can redistribute it and/or modify\r
-it under the terms of the GNU General Public License as published by\r
-the Free Software Foundation; either version 2 of the License, or\r
-(at your option) any later version.\r
-\r
-GtkRadiant is distributed in the hope that it will be useful,\r
-but WITHOUT ANY WARRANTY; without even the implied warranty of\r
-MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the\r
-GNU General Public License for more details.\r
-\r
-You should have received a copy of the GNU General Public License\r
-along with GtkRadiant; if not, write to the Free Software\r
-Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA\r
-*/\r
-\r
-//\r
-// Linux stuff\r
-//\r
-// Leonardo Zide (leo@lokigames.com)\r
-//\r
-\r
-#include "stdafx.h"\r
-#include <gtk/gtk.h>\r
-#include <sys/stat.h> \r
-#include "gtkmisc.h"\r
-#if defined (__linux__) || defined (__APPLE__)\r
-#include <unistd.h>\r
-#include <X11/keysym.h>\r
-#include <gdk/gdkx.h>\r
-#include <gdk/gdkprivate.h>\r
-#endif\r
-// for the logging part\r
-#include <fcntl.h>\r
-#include <sys/types.h>\r
-\r
-QEGlobals_t  g_qeglobals;\r
-QEGlobals_GUI_t g_qeglobals_gui;\r
-\r
-// leo: Track memory allocations for debugging\r
-// NOTE TTimo this was never used and probably not relevant\r
-//   there are tools to do that\r
-#ifdef MEM_DEBUG\r
-\r
-static GList *memblocks;\r
-\r
-void* debug_malloc (size_t size, const char* file, int line)\r
-{\r
-  void *buf = g_malloc (size + 8);\r
-\r
-  *((const char**)buf) = file;\r
-  buf = (char*)buf + 4;\r
-  *((int*)buf) = line;\r
-  buf = (char*)buf + 4;\r
-\r
-  memblocks = g_list_append (memblocks, buf);\r
-\r
-  return buf;\r
-}\r
-\r
-void debug_free (void *buf, const char* file, int line)\r
-{\r
-  const char *f;\r
-  int l;\r
-\r
-  if (g_list_find (memblocks, buf))\r
-  {\r
-    memblocks = g_list_remove (memblocks, buf);\r
-\r
-    buf = (char*)buf - 4;\r
-    l = *((int*)buf);\r
-    buf = (char*)buf - 4;\r
-    f = *((const char**)buf);\r
-\r
-    Sys_FPrintf (SYS_DBG, "free: %s %d", file, line);\r
-    Sys_FPrintf (SYS_DBG, " allocated: %s %d\n", f, l);\r
-\r
-    g_free (buf);\r
-  }\r
-//  else\r
-//    free (buf); // from qmalloc, will leak unless we add this same hack to cmdlib\r
-}\r
-\r
-#endif\r
-\r
-vec_t Rad_rint (vec_t in)\r
-{\r
-  if (g_PrefsDlg.m_bNoClamp)\r
-    return in;\r
-  else\r
-    return (float)floor (in + 0.5);\r
-}\r
-\r
-void WINAPI QE_CheckOpenGLForErrors(void)\r
-{\r
-  char strMsg[1024];\r
-  int i = qglGetError();\r
-  if (i != GL_NO_ERROR)\r
-  {\r
-    if (i == GL_OUT_OF_MEMORY)\r
-    {      \r
-      sprintf(strMsg, "OpenGL out of memory error %s\nDo you wish to save before exiting?", qgluErrorString((GLenum)i));\r
-      if (gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg, "Radiant Error", MB_YESNO) == IDYES)\r
-      {\r
-        Map_SaveFile(NULL, false);\r
-      }\r
-      _exit(1);\r
-    }\r
-    else\r
-    {\r
-      Sys_Printf ("Warning: OpenGL Error %s\n", qgluErrorString((GLenum)i));\r
-    }\r
-  }\r
-}\r
-\r
-// NOTE: don't this function, use VFS instead\r
-char *ExpandReletivePath (char *p)\r
-{\r
-       static char     temp[1024];\r
-       const char      *base;\r
-\r
-       if (!p || !p[0])\r
-               return NULL;\r
-       if (p[0] == '/' || p[0] == '\\')\r
-               return p;\r
-\r
-       base = ValueForKey(g_qeglobals.d_project_entity, "basepath");\r
-       sprintf (temp, "%s/%s", base, p);\r
-       return temp;\r
-}\r
-\r
-char *copystring (char *s)\r
-{\r
-       char    *b;\r
-       b = (char*)malloc(strlen(s)+1);\r
-       strcpy (b,s);\r
-       return b;\r
-}\r
-\r
-\r
-bool DoesFileExist(const char* pBuff, long& lSize)\r
-{\r
-  FileStream file;\r
-  if (file.Open(pBuff, "r"))\r
-  {\r
-    lSize += file.GetLength();\r
-    file.Close();\r
-    return true;\r
-  }\r
-  return false;\r
-}\r
-\r
-\r
-void Map_Snapshot()\r
-{\r
-  CString strMsg;\r
-\r
-  // I hope the modified flag is kept correctly up to date\r
-  if (!modified)\r
-    return;\r
-\r
-  // we need to do the following\r
-  // 1. make sure the snapshot directory exists (create it if it doesn't)\r
-  // 2. find out what the lastest save is based on number\r
-  // 3. inc that and save the map\r
-  CString strOrgPath, strOrgFile;\r
-  ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile);\r
-  AddSlash(strOrgPath);\r
-  strOrgPath += "snapshots";\r
-  bool bGo = true;\r
-  struct stat Stat;\r
-  if (stat(strOrgPath, &Stat) == -1)\r
-  {\r
-#ifdef _WIN32\r
-    bGo = (_mkdir(strOrgPath) != -1);\r
-#endif\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
-    bGo = (mkdir(strOrgPath,0755) != -1);\r
-#endif\r
-  }\r
-  AddSlash(strOrgPath);\r
-  if (bGo)\r
-  {\r
-    int nCount = 0;\r
-    long lSize = 0;\r
-    CString strNewPath;\r
-    strNewPath = strOrgPath;\r
-    strNewPath += strOrgFile;\r
-    CString strFile;\r
-    while (bGo)\r
-    {\r
-      char buf[PATH_MAX];\r
-      sprintf( buf, "%s.%i", strNewPath.GetBuffer(), nCount );\r
-      strFile = buf;\r
-      bGo = DoesFileExist(strFile, lSize);\r
-      nCount++;\r
-    }\r
-    // strFile has the next available slot\r
-    Map_SaveFile(strFile, false);\r
-    // it is still a modified map (we enter this only if this is a modified map)\r
-    Sys_SetTitle (currentmap);\r
-    Sys_MarkMapModified();\r
-    if (lSize > 12 * 1024 * 1024) // total size of saves > 4 mb\r
-    {\r
-      Sys_Printf("The snapshot files in %s total more than 4 megabytes. You might consider cleaning up.", strOrgPath.GetBuffer());\r
-    }\r
-  }\r
-  else\r
-  {\r
-    strMsg.Format("Snapshot save failed.. unabled to create directory\n%s", strOrgPath.GetBuffer());\r
-    gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg);\r
-  }\r
-  strOrgPath = "";\r
-  strOrgFile = "";\r
-}\r
-/*\r
-===============\r
-QE_CheckAutoSave\r
-\r
-If five minutes have passed since making a change\r
-and the map hasn't been saved, save it out.\r
-===============\r
-*/\r
-\r
-\r
-void QE_CheckAutoSave( void )\r
-{\r
-  static time_t s_start;\r
-  time_t now;\r
-  time (&now);\r
-\r
-  if (modified != 1 || !s_start)\r
-  {\r
-    s_start = now;\r
-    return;\r
-  }\r
-\r
-  if ((now - s_start) > (60 * g_PrefsDlg.m_nAutoSave))\r
-  {\r
-    if (g_PrefsDlg.m_bAutoSave)\r
-    {\r
-      CString strMsg;\r
-      strMsg = g_PrefsDlg.m_bSnapShots ? "Autosaving snapshot..." : "Autosaving...";\r
-      Sys_Printf(strMsg);\r
-      Sys_Printf("\n");\r
-      Sys_Status (strMsg,0);\r
-\r
-      // only snapshot if not working on a default map\r
-      if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0)\r
-      {\r
-        Map_Snapshot();\r
-      }\r
-      else\r
-      {\r
-        Map_SaveFile (ValueForKey(g_qeglobals.d_project_entity, "autosave"), false);\r
-      }\r
-\r
-      Sys_Status ("Autosaving...Saved.", 0 );\r
-      modified = 2;\r
-    }\r
-    else\r
-    {\r
-      Sys_Printf ("Autosave skipped...\n");\r
-      Sys_Status ("Autosave skipped...", 0 );\r
-    }\r
-    s_start = now;\r
-  }\r
-}\r
-\r
-\r
-// NOTE TTimo we don't like that BuildShortPathName too much\r
-//   the VFS provides a vfsCleanFileName which should perform the cleanup tasks\r
-//   in the long run I'd like to completely get rid of this\r
-\r
-// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=144\r
-// used to be disabled, but caused problems\r
-\r
-// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=291\r
-// can't work with long win32 names until the BSP commands are not working differently\r
-#ifdef _WIN32\r
-int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen)\r
-{\r
-  char *pFile = NULL;\r
-  int nResult = GetFullPathName(pPath, nBufferLen, pBuffer, &pFile);\r
-  nResult = GetShortPathName(pPath, pBuffer, nBufferLen);\r
-  if (nResult == 0)\r
-    strcpy(pBuffer, pPath);                     // Use long filename\r
-  return nResult;\r
-}\r
-#endif\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
-int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen)\r
-{\r
-  // remove /../ from directories\r
-  const char *scr = pPath; char *dst = pBuffer;\r
-  for (int i = 0; (i < nBufferLen) && (*scr != 0); i++)\r
-  {\r
-    if (*scr == '/' && *(scr+1) == '.' && *(scr+2) == '.')\r
-    {\r
-      scr += 3;\r
-      while (dst != pBuffer && *(--dst) != '/')\r
-      {\r
-        i--;\r
-      }\r
-    }\r
-\r
-    *dst = *scr;\r
-\r
-    scr++; dst++;\r
-  }\r
-  *dst = 0;\r
-\r
-  return strlen (pBuffer);\r
-}\r
-#endif\r
-\r
-/*\r
-const char *g_pPathFixups[]=\r
-{\r
-  "basepath",\r
-  "autosave",\r
-};\r
-\r
-const int g_nPathFixupCount = sizeof(g_pPathFixups) / sizeof(const char*);\r
-\r
-void QE_CheckProjectEntity()\r
-{\r
-  char *pFile;\r
-  char pBuff[PATH_MAX];\r
-  char pNewPath[PATH_MAX];\r
-  for (int i = 0; i < g_nPathFixupCount; i++)\r
-  {\r
-    char *pPath = ValueForKey (g_qeglobals.d_project_entity, g_pPathFixups[i]);\r
-\r
-    strcpy (pNewPath, pPath);\r
-    if (pPath[0] != '\\' && pPath[0] != '/')\r
-      if (GetFullPathName(pPath, PATH_MAX, pBuff, &pFile))\r
-             strcpy (pNewPath, pBuff);\r
-\r
-    BuildShortPathName (pNewPath, pBuff, PATH_MAX);\r
-\r
-    // check it's not ending with a filename seperator\r
-    if (pBuff[strlen(pBuff)-1] == '/' || pBuff[strlen(pBuff)-1] == '\\')\r
-    {\r
-      Sys_FPrintf(SYS_WRN, "WARNING: \"%s\" path in the project file has an ending file seperator, fixing.\n", g_pPathFixups[i]);\r
-      pBuff[strlen(pBuff)-1]=0;\r
-    }\r
-\r
-    SetKeyValue(g_qeglobals.d_project_entity, g_pPathFixups[i], pBuff);\r
-  }\r
-}\r
-*/\r
-\r
-void HandleXMLError( void* ctxt, const char* text, ... )\r
-{\r
-  va_list argptr;\r
-  static char buf[32768];\r
-\r
-  va_start (argptr,text);\r
-  vsprintf (buf, text, argptr);\r
-  Sys_FPrintf (SYS_ERR, "XML %s\n", buf);\r
-  va_end (argptr);\r
-}\r
-\r
-#define DTD_BUFFER_LENGTH 1024\r
-xmlDocPtr ParseXMLStream(IDataStream *stream, bool validate = false)\r
-{\r
-  xmlDocPtr doc = NULL;\r
-  bool wellFormed = false, valid = false;\r
-  int res, size = 1024;\r
-  char chars[1024];\r
-  xmlParserCtxtPtr ctxt;\r
-\r
-  // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=433\r
-  //if(validate)\r
-  //  xmlDoValidityCheckingDefaultValue = 1;\r
-  //else\r
-    xmlDoValidityCheckingDefaultValue = 0;\r
-  xmlSetGenericErrorFunc(NULL, HandleXMLError);\r
-\r
-  // SPoG\r
-  // HACK: use AppPath to resolve DTD location\r
-  // do a buffer-safe string copy and concatenate\r
-  int i;\r
-  char* w;\r
-  const char* r;\r
-  char buf[DTD_BUFFER_LENGTH];\r
-\r
-  w = buf;\r
-  i = 0;\r
-  // copy\r
-  //assert(g_strAppPath.GetBuffer() != NULL);\r
-  for(r = g_strAppPath.GetBuffer(); i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++)  w[i] = *r;\r
-  // concatenate\r
-  for(r = "dtds/"; i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++)  w[i] = *r;\r
-  // terminate\r
-  w[i] = '\0';\r
-\r
-  if(i == DTD_BUFFER_LENGTH)\r
-  {\r
-    HandleXMLError(NULL, "ERROR: buffer overflow: DTD path length too large\n");\r
-    return NULL;\r
-  }\r
-\r
-  res = stream->Read(chars, 4);\r
-  if (res > 0)\r
-  {\r
-    ctxt = xmlCreatePushParserCtxt(NULL, NULL, chars, res, buf);\r
-\r
-    while ((res = stream->Read(chars, size)) > 0)\r
-    {\r
-      xmlParseChunk(ctxt, chars, res, 0);\r
-    }\r
-    xmlParseChunk(ctxt, chars, 0, 1);\r
-    doc = ctxt->myDoc;\r
-\r
-    wellFormed = (ctxt->wellFormed == 1);\r
-    valid = (ctxt->valid == 1);\r
-\r
-    xmlFreeParserCtxt(ctxt);\r
-  }\r
-\r
-  if(wellFormed && (!validate || (validate && valid)))\r
-    return doc;\r
-\r
-  if(doc != NULL)\r
-    xmlFreeDoc(doc);\r
-\r
-  return NULL;\r
-}\r
-\r
-xmlDocPtr ParseXMLFile(const char* filename, bool validate = false)\r
-{\r
-  FileStream stream;\r
-  if (stream.Open(filename, "r"))\r
-    return ParseXMLStream(&stream, validate);\r
-  \r
-  Sys_FPrintf(SYS_ERR, "Failed to open file: %s\n",filename);\r
-  return NULL;\r
-}\r
-\r
-// copy a string r to a buffer w\r
-// replace $string as appropriate\r
-void ReplaceTemplates(char* w, const char* r)\r
-{\r
-  const char *p;\r
-  const char *__ENGINEPATH = "TEMPLATEenginepath";\r
-  const char *__USERHOMEPATH = "TEMPLATEuserhomepath";\r
-  const char *__TOOLSPATH = "TEMPLATEtoolspath";\r
-  const char *__BASEDIR = "TEMPLATEbasedir";\r
-  const char *__APPPATH = "TEMPLATEapppath";\r
-\r
-  // iterate through string r\r
-  while(*r!='\0')\r
-  {\r
-    // check for special character\r
-    if(*r=='$')\r
-    {\r
-      if(strncmp(r+1, __ENGINEPATH, strlen(__ENGINEPATH)) == 0)\r
-      {\r
-        r+=strlen(__ENGINEPATH)+1;\r
-        p = g_pGameDescription->mEnginePath.GetBuffer();\r
-      }\r
-      else if(strncmp(r+1, __USERHOMEPATH, strlen(__USERHOMEPATH)) == 0)\r
-      {\r
-        r+=strlen(__USERHOMEPATH)+1;\r
-        p = g_qeglobals.m_strHomeGame.GetBuffer();\r
-      }\r
-      else if(strncmp(r+1, __BASEDIR, strlen(__BASEDIR)) == 0)\r
-      {\r
-        r+=strlen(__BASEDIR)+1;\r
-        p = g_pGameDescription->mBaseGame;\r
-      }\r
-      else if(strncmp(r+1, __TOOLSPATH, strlen(__TOOLSPATH)) == 0)\r
-      {\r
-        r+=strlen(__TOOLSPATH)+1;\r
-        p = g_strGameToolsPath.GetBuffer();\r
-      }\r
-      else if(strncmp(r+1, __APPPATH, strlen(__APPPATH)) == 0)\r
-      {\r
-        r+=strlen(__APPPATH)+1;\r
-        p = g_strAppPath.GetBuffer();\r
-      }\r
-      else\r
-      {\r
-        r++;\r
-        p = "$";\r
-      }\r
-          \r
-      while(*p!='\0') *w++ = *p++;\r
-    }\r
-    else *w++ = *r++;\r
-  }\r
-  *w = '\0';\r
-}\r
-\r
-/*\r
-===========\r
-QE_LoadProject\r
-TODO TODO TODO (don't think this got fully merged in)\r
-TTimo: added project file "version", version 2 adds '#' chars to the BSP command strings\r
-version 3 was .. I don't remember .. version 4 adds q3map2 commands\r
-TTimo: when QE_LoadProject is called, the prefs are updated with path to the latest project and saved on disk\r
-===========\r
-*/\r
-/*\todo decide on a sensible location/name for project files.*/\r
-bool QE_LoadProject (const char *projectfile)\r
-{\r
-  char buf[1024];\r
-  xmlDocPtr doc;\r
-  xmlNodePtr node, project;\r
-\r
-  Sys_Printf("Loading project file: \"%s\"\n", projectfile);\r
-  doc = ParseXMLFile(projectfile, true);\r
-\r
-  if(doc == NULL) return false;\r
-\r
-  node=doc->children;\r
-  while(node != NULL && node->type != XML_DTD_NODE) node=node->next;\r
-  if(node == NULL || strcmp((char*)node->name, "project") != 0)\r
-  {\r
-    Sys_FPrintf(SYS_ERR, "ERROR: invalid file type\n");\r
-    return false;\r
-  }\r
-\r
-  while(node->type != XML_ELEMENT_NODE) node=node->next;\r
-  // <project>\r
-  project = node;\r
-\r
-  if(g_qeglobals.d_project_entity != NULL) Entity_Free(g_qeglobals.d_project_entity);\r
-  g_qeglobals.d_project_entity = Entity_Alloc();\r
-\r
-  for(node = project->children; node != NULL; node=node->next)\r
-  {\r
-    if(node->type != XML_ELEMENT_NODE) continue;\r
-\r
-    // <key>\r
-    ReplaceTemplates(buf, (char*)node->properties->next->children->content);\r
-\r
-    SetKeyValue(g_qeglobals.d_project_entity, (char*)node->properties->children->content, buf);\r
-  }\r
-\r
-  xmlFreeDoc(doc);\r
-  \r
-  // project file version checking\r
-  // add a version checking to avoid people loading later versions of the project file and bitching\r
-  int ver = IntForKey( g_qeglobals.d_project_entity, "version" );\r
-  if (ver > PROJECT_VERSION)\r
-  {\r
-    char strMsg[1024];\r
-    sprintf (strMsg, "This is a version %d project file. This build only supports <=%d project files.\n"\r
-      "Please choose another project file or upgrade your version of Radiant.", ver, PROJECT_VERSION);\r
-    gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK);\r
-               // set the project file to nothing so we are sure we'll ask next time?\r
-               g_PrefsDlg.m_strLastProject = "";\r
-               g_PrefsDlg.SavePrefs();\r
-    return false;\r
-  }\r
-  \r
-  // set here some default project settings you need\r
-  if ( strlen( ValueForKey( g_qeglobals.d_project_entity, "brush_primit" ) ) == 0 )\r
-  {\r
-    SetKeyValue( g_qeglobals.d_project_entity, "brush_primit", "0" );\r
-  }\r
-\r
-  g_qeglobals.m_bBrushPrimitMode = IntForKey( g_qeglobals.d_project_entity, "brush_primit" );\r
-\r
-  g_qeglobals.m_strHomeMaps = g_qeglobals.m_strHomeGame;\r
-  const char* str = ValueForKey(g_qeglobals.d_project_entity, "gamename");\r
-  if(str[0] == '\0') str = g_pGameDescription->mBaseGame.GetBuffer();\r
-  g_qeglobals.m_strHomeMaps += str;\r
-  g_qeglobals.m_strHomeMaps += '/';\r
-\r
-  // don't forget to create the dirs\r
-  Q_mkdir(g_qeglobals.m_strHomeGame.GetBuffer(), 0775);\r
-  Q_mkdir(g_qeglobals.m_strHomeMaps.GetBuffer(), 0775);\r
-  \r
-  // usefull for the log file and debuggin fucked up configurations from users:\r
-  // output the basic information of the .qe4 project file\r
-  // SPoG\r
-  // all these paths should be unix format, with a trailing slash at the end\r
-  // if not.. to debug, check that the project file paths are set up correctly\r
-  Sys_Printf("basepath    : %s\n", ValueForKey( g_qeglobals.d_project_entity, "basepath") );\r
-  Sys_Printf("entitypath  : %s\n", ValueForKey( g_qeglobals.d_project_entity, "entitypath" ) );\r
-\r
-\r
-  // check whether user_project key exists..\r
-  // if not, save the current project under a new name\r
-  if (ValueForKey(g_qeglobals.d_project_entity, "user_project")[0] == '\0')\r
-  {\r
-    Sys_Printf("Loaded a template project file\n");\r
-    \r
-    // create the user_project key\r
-    SetKeyValue( g_qeglobals.d_project_entity, "user_project", "1" );\r
-    \r
-    // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672\r
-    if (IntForKey( g_qeglobals.d_project_entity, "version" ) != PROJECT_VERSION)\r
-    {\r
-      char strMsg[2048];\r
-      sprintf(strMsg, \r
-        "The template project '%s' has version %d. The editor binary is configured for version %d.\n"\r
-        "This indicates a problem in your setup. See http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672\n"\r
-        "I will keep going with this project till you fix this",\r
-        projectfile, IntForKey( g_qeglobals.d_project_entity, "version" ), PROJECT_VERSION);\r
-      gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK);\r
-    }\r
-    \r
-    // create the writable project file path\r
-    strcpy(buf, g_qeglobals.m_strHomeGame.GetBuffer());\r
-    strcat(buf, g_pGameDescription->mBaseGame.GetBuffer());\r
-    strcat(buf, "/scripts/");\r
-    // while the filename is already in use, increment the number we add to the end\r
-    int counter = 0;\r
-    char pUser[PATH_MAX];\r
-    while (1)\r
-    {      \r
-      sprintf( pUser, "%suser%d." PROJECT_FILETYPE, buf, counter );\r
-      counter++;\r
-      if (access( pUser, R_OK) != 0)\r
-      {\r
-        // this is the one\r
-        strcpy( buf, pUser );\r
-        break;\r
-      }\r
-    }\r
-    // saving project will cause a save prefs\r
-    g_PrefsDlg.m_strLastProject = buf;\r
-    g_PrefsDlg.m_nLastProjectVer = IntForKey( g_qeglobals.d_project_entity, "version" );    \r
-    QE_SaveProject(buf);\r
-  }\r
-  else\r
-  {\r
-    // update preferences::LastProject with path of this successfully-loaded project\r
-    // save preferences\r
-    Sys_Printf("Setting current project in prefs to \"%s\"\n", g_PrefsDlg.m_strLastProject.GetBuffer() );\r
-    g_PrefsDlg.m_strLastProject = projectfile;\r
-    g_PrefsDlg.SavePrefs();\r
-  }\r
-\r
-  return true;\r
-}\r
-\r
-/*\r
-===========\r
-QE_SaveProject\r
-TTimo: whenever QE_SaveProject is called, prefs are updated and saved with the path to the project\r
-===========\r
-*/\r
-qboolean QE_SaveProject (const char* filename)\r
-{\r
-  Sys_Printf("Save project file '%s'\n", filename);\r
-\r
-  xmlNodePtr node;\r
-  xmlDocPtr doc = xmlNewDoc((xmlChar *)"1.0");\r
-  // create DTD node\r
-  xmlCreateIntSubset(doc, (xmlChar *)"project", NULL, (xmlChar *)"project.dtd");\r
-  // create project node\r
-  doc->children->next = xmlNewDocNode(doc, NULL, (xmlChar *)"project", NULL);\r
-\r
-  for(epair_t* epair = g_qeglobals.d_project_entity->epairs; epair != NULL; epair = epair->next)\r
-  {\r
-    node = xmlNewChild(doc->children->next, NULL, (xmlChar *)"key", NULL);\r
-    xmlSetProp(node, (xmlChar*)"name", (xmlChar*)epair->key);\r
-    xmlSetProp(node, (xmlChar*)"value", (xmlChar*)epair->value);\r
-  }\r
-\r
-  CreateDirectoryPath(filename);\r
-  if (xmlSaveFormatFile(filename, doc, 1) != -1)\r
-  {\r
-    xmlFreeDoc(doc);\r
-    Sys_Printf("Setting current project in prefs to \"%s\"\n", filename );\r
-    g_PrefsDlg.m_strLastProject = filename;\r
-    g_PrefsDlg.SavePrefs();\r
-    return TRUE;\r
-  }\r
-  else\r
-  {\r
-    xmlFreeDoc(doc);\r
-    Sys_FPrintf(SYS_ERR, "failed to save project file: \"%s\"\n", filename);\r
-    return FALSE;\r
-  }\r
-}\r
-\r
-\r
-\r
-/*\r
-===========\r
-QE_KeyDown\r
-===========\r
-*/\r
-#define        SPEED_MOVE      32\r
-#define        SPEED_TURN      22.5\r
-\r
-\r
-/*\r
-===============\r
-ConnectEntities\r
-\r
-Sets target / targetname on the two entities selected\r
-from the first selected to the secon\r
-===============\r
-*/\r
-void ConnectEntities (void)\r
-{\r
-       entity_t        *e1, *e2;\r
-       const char              *target;\r
-       char            *newtarg = NULL;\r
-\r
-       if (g_qeglobals.d_select_count != 2)\r
-       {\r
-               Sys_Status ("Must have two brushes selected", 0);\r
-               Sys_Beep ();\r
-               return;\r
-       }\r
-\r
-       e1 = g_qeglobals.d_select_order[0]->owner;\r
-       e2 = g_qeglobals.d_select_order[1]->owner;\r
-\r
-       if (e1 == world_entity || e2 == world_entity)\r
-       {\r
-               Sys_Status ("Can't connect to the world", 0);\r
-               Sys_Beep ();\r
-               return;\r
-       }\r
-\r
-       if (e1 == e2)\r
-       {\r
-               Sys_Status ("Brushes are from same entity", 0);\r
-               Sys_Beep ();\r
-               return;\r
-       }\r
-    \r
-  target = ValueForKey (e1, "target");\r
-  if (target && target[0])\r
-    newtarg = g_strdup(target);\r
-  else\r
-  {\r
-    target = ValueForKey(e2, "targetname");\r
-    if(target && target[0])\r
-      newtarg = g_strdup(target);\r
-    else\r
-      Entity_Connect(e1, e2);\r
-  }\r
-\r
-  if(newtarg != NULL)\r
-  {\r
-    SetKeyValue(e1, "target", newtarg);\r
-    SetKeyValue(e2, "targetname", newtarg);\r
-    g_free(newtarg);\r
-  }\r
-  \r
-       Sys_UpdateWindows (W_XY | W_CAMERA);\r
-\r
-       Select_Deselect();\r
-       Select_Brush (g_qeglobals.d_select_order[1]);\r
-}\r
-\r
-qboolean QE_SingleBrush (bool bQuiet)\r
-{\r
-       if ( (selected_brushes.next == &selected_brushes)\r
-               || (selected_brushes.next->next != &selected_brushes) )\r
-       {\r
-         if (!bQuiet)\r
-    {\r
-               Sys_Printf ("Error: you must have a single brush selected\n");\r
-         }\r
-               return false;\r
-       }\r
-       if (selected_brushes.next->owner->eclass->fixedsize)\r
-       {\r
-         if (!bQuiet)\r
-         {\r
-                 Sys_Printf ("Error: you cannot manipulate fixed size entities\n");\r
-         }\r
-               return false;\r
-       }\r
-\r
-       return true;\r
-}\r
-\r
-void QE_InitVFS (void)\r
-{\r
-  // VFS initialization -----------------------\r
-  // we will call vfsInitDirectory, giving the directories to look in (for files in pk3's and for standalone files)\r
-  // we need to call in order, the mod ones first, then the base ones .. they will be searched in this order\r
-  // *nix systems have a dual filesystem in ~/.q3a, which is searched first .. so we need to add that too\r
-  Str directory,prefabs;\r
-\r
-  // TTimo: let's leave this to HL mode for now\r
-  if (g_pGameDescription->mGameFile == "hl.game")\r
-  {\r
-    // Hydra: we search the "gametools" path first so that we can provide editor\r
-    // specific pk3's wads and misc files for use by the editor.\r
-    // the relevant map compiler tools will NOT use this directory, so this helps\r
-    // to ensure that editor files are not used/required in release versions of maps\r
-    // it also helps keep your editor files all in once place, with the editor modules,\r
-    // plugins, scripts and config files.\r
-    // it also helps when testing maps, as you'll know your files won't/can't be used\r
-    // by the game engine itself.\r
-  \r
-    // <gametools>\r
-    directory = g_pGameDescription->mGameToolsPath;\r
-    vfsInitDirectory(directory.GetBuffer());\r
-  }\r
-\r
-  // NOTE TTimo about the mymkdir calls .. this is a bit dirty, but a safe thing on *nix\r
-\r
-  // if we have a mod dir\r
-  if (*ValueForKey(g_qeglobals.d_project_entity, "gamename") != '\0')\r
-  {\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
-    // ~/.<gameprefix>/<fs_game>\r
-    directory = g_qeglobals.m_strHomeGame.GetBuffer();\r
-    Q_mkdir (directory.GetBuffer (), 0775);\r
-    directory += ValueForKey(g_qeglobals.d_project_entity, "gamename");\r
-    Q_mkdir (directory.GetBuffer (), 0775);\r
-    vfsInitDirectory(directory.GetBuffer());\r
-    AddSlash (directory);\r
-    prefabs = directory;\r
-    // also create the maps dir, it will be used as prompt for load/save\r
-    directory += "/maps";\r
-    Q_mkdir (directory, 0775);\r
-    // and the prefabs dir\r
-    prefabs += "/prefabs";\r
-    Q_mkdir (prefabs, 0775);\r
-\r
-#endif\r
-\r
-    // <fs_basepath>/<fs_game>\r
-    directory = g_pGameDescription->mEnginePath;\r
-    directory += ValueForKey(g_qeglobals.d_project_entity, "gamename");\r
-    Q_mkdir (directory.GetBuffer (), 0775);\r
-    vfsInitDirectory(directory.GetBuffer());\r
-    AddSlash(directory);\r
-    prefabs = directory;\r
-    // also create the maps dir, it will be used as prompt for load/save\r
-    directory += "/maps";\r
-    Q_mkdir (directory.GetBuffer (), 0775);\r
-    // and the prefabs dir\r
-    prefabs += "/prefabs";\r
-    Q_mkdir (prefabs, 0775);\r
-  }\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
-  // ~/.<gameprefix>/<fs_main>\r
-  directory = g_qeglobals.m_strHomeGame.GetBuffer();\r
-  directory += g_pGameDescription->mBaseGame;\r
-  vfsInitDirectory (directory.GetBuffer ());\r
-#endif\r
-\r
-  // <fs_basepath>/<fs_main>\r
-  directory = g_pGameDescription->mEnginePath;\r
-  directory += g_pGameDescription->mBaseGame;\r
-  vfsInitDirectory(directory.GetBuffer());\r
-}\r
-\r
-void QE_Init (void)\r
-{\r
-       /*\r
-       ** initialize variables\r
-       */\r
-       g_qeglobals.d_gridsize = 8;\r
-       g_qeglobals.d_showgrid = true;\r
-\r
-  QE_InitVFS();\r
-\r
-  Eclass_Init();\r
-  FillClassList();             // list in entity window\r
-  Map_Init();\r
-\r
-  FillTextureMenu();\r
-  FillBSPMenu();\r
-\r
-       /*\r
-       ** other stuff\r
-       */\r
-       Z_Init ();\r
-}\r
-\r
-void WINAPI QE_ConvertDOSToUnixName( char *dst, const char *src )\r
-{\r
-       while ( *src )\r
-       {\r
-               if ( *src == '\\' )\r
-                       *dst = '/';\r
-               else\r
-                       *dst = *src;\r
-               dst++; src++;\r
-       }\r
-       *dst = 0;\r
-}\r
-\r
-int g_numbrushes, g_numentities;\r
-\r
-void QE_CountBrushesAndUpdateStatusBar( void )\r
-{\r
-       static int      s_lastbrushcount, s_lastentitycount;\r
-       static qboolean s_didonce;\r
-       \r
-       //entity_t   *e;\r
-       brush_t    *b, *next;\r
-\r
-       g_numbrushes = 0;\r
-       g_numentities = 0;\r
-       \r
-       if ( active_brushes.next != NULL )\r
-       {\r
-               for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next)\r
-               {\r
-                       next = b->next;\r
-                       if (b->brush_faces )\r
-                       {\r
-                               if ( !b->owner->eclass->fixedsize)\r
-                                       g_numbrushes++;\r
-                               else\r
-                                       g_numentities++;\r
-                       }\r
-               }\r
-       }\r
-/*\r
-       if ( entities.next != NULL )\r
-       {\r
-               for ( e = entities.next ; e != &entities && g_numentities != MAX_MAP_ENTITIES ; e = e->next)\r
-               {\r
-                       g_numentities++;\r
-               }\r
-       }\r
-*/\r
-       if ( ( ( g_numbrushes != s_lastbrushcount ) || ( g_numentities != s_lastentitycount ) ) || ( !s_didonce ) )\r
-       {\r
-               Sys_UpdateStatusBar();\r
-\r
-               s_lastbrushcount = g_numbrushes;\r
-               s_lastentitycount = g_numentities;\r
-               s_didonce = true;\r
-       }\r
-}\r
-\r
-char           com_token[1024];\r
-qboolean       com_eof;\r
-\r
-/*\r
-================\r
-I_FloatTime\r
-================\r
-*/\r
-double I_FloatTime (void)\r
-{\r
-       time_t  t;\r
-       \r
-       time (&t);\r
-       \r
-       return t;\r
-#if 0\r
-// more precise, less portable\r
-       struct timeval tp;\r
-       struct timezone tzp;\r
-       static int              secbase;\r
-\r
-       gettimeofday(&tp, &tzp);\r
-       \r
-       if (!secbase)\r
-       {\r
-               secbase = tp.tv_sec;\r
-               return tp.tv_usec/1000000.0;\r
-       }\r
-       \r
-       return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;\r
-#endif\r
-}\r
-\r
-\r
-/*\r
-==============\r
-COM_Parse\r
-\r
-Parse a token out of a string\r
-==============\r
-*/\r
-char *COM_Parse (char *data)\r
-{\r
-       int             c;\r
-       int             len;\r
-\r
-       len = 0;\r
-       com_token[0] = 0;\r
-       \r
-       if (!data)\r
-               return NULL;\r
-               \r
-// skip whitespace\r
-skipwhite:\r
-       while ( (c = *data) <= ' ')\r
-       {\r
-               if (c == 0)\r
-               {\r
-                       com_eof = true;\r
-                       return NULL;                    // end of file;\r
-               }\r
-               data++;\r
-       }\r
-       \r
-// skip // comments\r
-       if (c=='/' && data[1] == '/')\r
-       {\r
-               while (*data && *data != '\n')\r
-                       data++;\r
-               goto skipwhite;\r
-       }\r
-       \r
-\r
-// handle quoted strings specially\r
-       if (c == '\"')\r
-       {\r
-               data++;\r
-               do\r
-               {\r
-                       c = *data++;\r
-                       if (c=='\"')\r
-                       {\r
-                               com_token[len] = 0;\r
-                               return data;\r
-                       }\r
-                       com_token[len] = c;\r
-                       len++;\r
-               } while (1);\r
-       }\r
-\r
-// parse single characters\r
-       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')\r
-       {\r
-               com_token[len] = c;\r
-               len++;\r
-               com_token[len] = 0;\r
-               return data+1;\r
-       }\r
-\r
-// parse a regular word\r
-       do\r
-       {\r
-               com_token[len] = c;\r
-               data++;\r
-               len++;\r
-               c = *data;\r
-       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')\r
-                       break;\r
-       } while (c>32);\r
-       \r
-       com_token[len] = 0;\r
-       return data;\r
-}\r
-\r
-char* Get_COM_Token()\r
-{\r
-  return com_token;\r
-}\r
-\r
-/*\r
-=============================================================================\r
-\r
-                                               MISC FUNCTIONS\r
-\r
-=============================================================================\r
-*/\r
-\r
-\r
-int            argc;\r
-char   *argv[MAX_NUM_ARGVS];\r
-\r
-/*\r
-============\r
-ParseCommandLine\r
-============\r
-*/\r
-void ParseCommandLine (char *lpCmdLine)\r
-{\r
-       argc = 1;\r
-       argv[0] = "programname";\r
-\r
-       while (*lpCmdLine && (argc < MAX_NUM_ARGVS))\r
-       {\r
-               while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))\r
-                       lpCmdLine++;\r
-\r
-               if (*lpCmdLine)\r
-               {\r
-                       argv[argc] = lpCmdLine;\r
-                       argc++;\r
-\r
-                       while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))\r
-                               lpCmdLine++;\r
-\r
-                       if (*lpCmdLine)\r
-                       {\r
-                               *lpCmdLine = 0;\r
-                               lpCmdLine++;\r
-                       }\r
-                       \r
-               }\r
-       }\r
-}\r
-\r
-\r
-\r
-/*\r
-=================\r
-CheckParm\r
-\r
-Checks for the given parameter in the program's command line arguments\r
-Returns the argument number (1 to argc-1) or 0 if not present\r
-=================\r
-*/\r
-int CheckParm (char *check)\r
-{\r
-       int             i;\r
-\r
-       for (i = 1;i<argc;i++)\r
-       {\r
-               if ( stricmp(check, argv[i]) )\r
-                       return i;\r
-       }\r
-\r
-       return 0;\r
-}\r
-\r
-\r
-\r
-\r
-/*\r
-==============\r
-ParseNum / ParseHex\r
-==============\r
-*/\r
-int ParseHex (char *hex)\r
-{\r
-       char    *str;\r
-       int    num;\r
-\r
-       num = 0;\r
-       str = hex;\r
-\r
-       while (*str)\r
-       {\r
-               num <<= 4;\r
-               if (*str >= '0' && *str <= '9')\r
-                       num += *str-'0';\r
-               else if (*str >= 'a' && *str <= 'f')\r
-                       num += 10 + *str-'a';\r
-               else if (*str >= 'A' && *str <= 'F')\r
-                       num += 10 + *str-'A';\r
-               else\r
-                       Error ("Bad hex number: %s",hex);\r
-               str++;\r
-       }\r
-\r
-       return num;\r
-}\r
-\r
-\r
-int ParseNum (char *str)\r
-{\r
-       if (str[0] == '$')\r
-               return ParseHex (str+1);\r
-       if (str[0] == '0' && str[1] == 'x')\r
-               return ParseHex (str+2);\r
-       return atol (str);\r
-}\r
-\r
-// BSP frontend plugin\r
-// global flag for BSP frontend plugin is g_qeglobals.bBSPFrontendPlugin\r
-_QERPlugBSPFrontendTable g_BSPFrontendTable;\r
-\r
-// =============================================================================\r
-// Sys_ functions\r
-\r
-bool Sys_AltDown ()\r
-{\r
-#ifdef _WIN32\r
-  return (GetKeyState(VK_MENU) & 0x8000) != 0;\r
-#endif\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
-  char keys[32];\r
-  int x;\r
-\r
-  XQueryKeymap(GDK_DISPLAY(), keys);\r
-\r
-  x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_L);\r
-  if (keys[x/8] & (1 << (x % 8)))\r
-    return true;\r
-\r
-  x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_R);\r
-  if (keys[x/8] & (1 << (x % 8)))\r
-    return true;\r
-\r
-  return false;\r
-#endif\r
-}\r
-\r
-bool Sys_ShiftDown ()\r
-{\r
-#ifdef _WIN32\r
-  return (GetKeyState(VK_SHIFT) & 0x8000) != 0;\r
-#endif\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
-  char keys[32];\r
-  int x;\r
-\r
-  XQueryKeymap(GDK_DISPLAY(), keys);\r
-\r
-  x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_L);\r
-  if (keys[x/8] & (1 << (x % 8)))\r
-    return true;\r
-\r
-  x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_R);\r
-  if (keys[x/8] & (1 << (x % 8)))\r
-    return true;\r
-\r
-  return false;\r
-#endif\r
-}\r
-\r
-void Sys_MarkMapModified (void)\r
-{\r
-  char title[PATH_MAX];\r
-\r
-  if (modified != 1)\r
-  {\r
-    modified = true;   // mark the map as changed\r
-    sprintf (title, "%s *", currentmap);\r
-\r
-    QE_ConvertDOSToUnixName( title, title );\r
-    Sys_SetTitle (title);\r
-  }\r
-}\r
-\r
-void Sys_SetTitle (const char *text)\r
-{\r
-  gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_main_window), text);\r
-}\r
-\r
-bool g_bWaitCursor = false;\r
-\r
-void WINAPI Sys_BeginWait (void)\r
-{\r
-  GdkCursor *cursor = gdk_cursor_new (GDK_WATCH);\r
-  gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor);\r
-  gdk_cursor_unref (cursor);\r
-  g_bWaitCursor = true;\r
-}\r
-\r
-void WINAPI Sys_EndWait (void)\r
-{\r
-  GdkCursor *cursor = gdk_cursor_new (GDK_LEFT_PTR);\r
-  gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor);\r
-  gdk_cursor_unref (cursor);\r
-  g_bWaitCursor = false;\r
-}\r
-\r
-void Sys_GetCursorPos (int *x, int *y)\r
-{\r
-  // FIXME: not multihead safe\r
-  gdk_window_get_pointer (NULL, x, y, NULL);\r
-}\r
-\r
-void Sys_SetCursorPos (int x, int y)\r
-{\r
-  // NOTE: coordinates are in GDK space, not OS space\r
-#ifdef _WIN32\r
-  int sys_x = x - g_pParentWnd->GetGDKOffsetX();\r
-  int sys_y = y - g_pParentWnd->GetGDKOffsetY();\r
-\r
-  SetCursorPos (sys_x, sys_y);\r
-#endif\r
-\r
-#if defined (__linux__) || defined (__APPLE__)\r
-  XWarpPointer (GDK_DISPLAY(), None, GDK_ROOT_WINDOW(), 0, 0, 0, 0, x, y);\r
-#endif\r
-}\r
-\r
-void Sys_Beep (void)\r
-{\r
-#if defined (__linux__) || defined (__APPLE__)\r
-  gdk_beep ();\r
-#else\r
-       MessageBeep (MB_ICONASTERISK);  \r
-#endif\r
-}\r
-\r
-double Sys_DoubleTime (void)\r
-{\r
-  return clock()/ 1000.0;\r
-}\r
-\r
-/*\r
-===============================================================\r
-\r
-  STATUS WINDOW\r
-\r
-===============================================================\r
-*/\r
-\r
-void Sys_UpdateStatusBar( void )\r
-{\r
-  extern int   g_numbrushes, g_numentities;\r
-\r
-  char numbrushbuffer[100]="";\r
-\r
-  sprintf( numbrushbuffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities );\r
-  g_pParentWnd->SetStatusText(2, numbrushbuffer);\r
-  //Sys_Status( numbrushbuffer, 2 );\r
-}\r
-\r
-void Sys_Status(const char *psz, int part )\r
-{\r
-  g_pParentWnd->SetStatusText (part, psz);\r
-}\r
-\r
-// =============================================================================\r
-// MRU\r
-\r
-#define MRU_MAX 4\r
-static GtkWidget *MRU_items[MRU_MAX];\r
-static int MRU_used;\r
-typedef char MRU_filename_t[PATH_MAX];\r
-MRU_filename_t MRU_filenames[MRU_MAX];\r
-\r
-static char* MRU_GetText (int index)\r
-{\r
-  return MRU_filenames[index];\r
-}\r
-\r
-void buffer_write_escaped_mnemonic(char* buffer, const char* string)\r
-{\r
-  while(*string != '\0')\r
-  {\r
-    if(*string == '_')\r
-    {\r
-      *buffer++ = '_';\r
-    }\r
-\r
-    *buffer++ = *string++;\r
-  }\r
-  *buffer = '\0';\r
-}\r
-\r
-static void MRU_SetText (int index, const char *filename)\r
-{\r
-  strcpy(MRU_filenames[index], filename);\r
-\r
-  char mnemonic[PATH_MAX * 2 + 4];\r
-  mnemonic[0] = '_';\r
-  sprintf(mnemonic+1, "%d", index+1);\r
-  mnemonic[2] = '-';\r
-  mnemonic[3] = ' ';\r
-  buffer_write_escaped_mnemonic(mnemonic+4, filename);\r
-  gtk_label_set_text_with_mnemonic(GTK_LABEL (GTK_BIN (MRU_items[index])->child), mnemonic);\r
-}\r
-\r
-void MRU_Load ()\r
-{\r
-  int i = g_PrefsDlg.m_nMRUCount;\r
-\r
-  if(i > 4)\r
-    i = 4; //FIXME: make this a define\r
-\r
-  for (; i > 0; i--)\r
-    MRU_AddFile (g_PrefsDlg.m_strMRUFiles[i-1].GetBuffer());\r
-}\r
-\r
-void MRU_Save ()\r
-{\r
-  g_PrefsDlg.m_nMRUCount = MRU_used;\r
-\r
-  for (int i = 0; i < MRU_used; i++)\r
-    g_PrefsDlg.m_strMRUFiles[i] = MRU_GetText (i);\r
-}\r
-\r
-void MRU_AddWidget (GtkWidget *widget, int pos)\r
-{\r
-  if (pos < MRU_MAX)\r
-    MRU_items[pos] = widget;\r
-}\r
-\r
-void MRU_AddFile (const char *str)\r
-{\r
-  int i;\r
-  char* text;\r
-\r
-  // check if file is already in our list\r
-  for (i = 0; i < MRU_used; i++)\r
-  {\r
-    text = MRU_GetText (i);\r
-\r
-    if (strcmp (text, str) == 0)\r
-    {\r
-      // reorder menu\r
-      for (; i > 0; i--)\r
-       MRU_SetText (i, MRU_GetText (i-1));\r
-\r
-      MRU_SetText (0, str);\r
-\r
-      return;\r
-    }\r
-  }\r
-\r
-  if (MRU_used < MRU_MAX)\r
-    MRU_used++;\r
-\r
-  // move items down\r
-  for (i = MRU_used-1; i > 0; i--)\r
-    MRU_SetText (i, MRU_GetText (i-1));\r
-\r
-  MRU_SetText (0, str);\r
-  gtk_widget_set_sensitive (MRU_items[0], TRUE);\r
-  gtk_widget_show (MRU_items[MRU_used-1]);\r
-}\r
-\r
-void MRU_Activate (int index)\r
-{\r
-  char *text = MRU_GetText (index);\r
-\r
-  if (access (text, R_OK) == 0)\r
-  {\r
-    text = strdup (text);\r
-    MRU_AddFile (text);\r
-    Map_LoadFile (text);\r
-    free (text);\r
-  }\r
-  else\r
-  {\r
-    MRU_used--;\r
-\r
-    for (int i = index; i < MRU_used; i++)\r
-      MRU_SetText (i, MRU_GetText (i+1));\r
-\r
-    if (MRU_used == 0)\r
-    {\r
-      gtk_label_set_text (GTK_LABEL (GTK_BIN (MRU_items[0])->child), "Recent Files");\r
-      gtk_widget_set_sensitive (MRU_items[0], FALSE);\r
-    }\r
-    else\r
-    {\r
-      gtk_widget_hide (MRU_items[MRU_used]);\r
-    }\r
-  }\r
-}\r
-\r
-/*\r
-======================================================================\r
-\r
-FILE DIALOGS\r
-\r
-======================================================================\r
-*/\r
\r
-qboolean ConfirmModified ()\r
-{\r
-  if (!modified)\r
-    return true;\r
-\r
-  if (gtk_MessageBox (g_pParentWnd->m_pWidget, "This will lose changes to the map", "warning", MB_OKCANCEL) == IDCANCEL)\r
-    return false;\r
-  return true;\r
-}\r
-\r
-void ProjectDialog ()\r
-{\r
-  const char *filename;\r
-  char buffer[NAME_MAX];\r
-\r
-  /* \r
-   * Obtain the system directory name and \r
-   * store it in buffer. \r
-   */ \r
-\r
-  strcpy(buffer, g_qeglobals.m_strHomeGame.GetBuffer());\r
-  strcat(buffer, g_pGameDescription->mBaseGame.GetBuffer());\r
-  strcat (buffer, "/scripts/");\r
-\r
-  // Display the Open dialog box\r
-  filename = file_dialog (NULL, TRUE, "Open File", buffer, "project");\r
-\r
-  if (filename == NULL)\r
-    return;    // canceled\r
-\r
-  // Open the file.\r
-  // NOTE: QE_LoadProject takes care of saving prefs with new path to the project file\r
-  if (!QE_LoadProject(filename))\r
-    Sys_Printf ("Failed to load project from file: %s\n", filename);\r
-  else\r
-    // FIXME TTimo QE_Init is probably broken if you don't call it during startup right now .. \r
-    QE_Init();\r
-}\r
-\r
-/*\r
-=======================================================\r
-\r
-Menu modifications\r
-\r
-=======================================================\r
-*/\r
-\r
-/*\r
-==================\r
-FillBSPMenu\r
-\r
-==================\r
-*/\r
-char *bsp_commands[256];\r
-\r
-void FillBSPMenu ()\r
-{\r
-  GtkWidget *item, *menu; // menu points to a GtkMenu (not an item)\r
-  epair_t *ep;\r
-  GList *lst;\r
-  int i;\r
-\r
-  menu = GTK_WIDGET (g_object_get_data (G_OBJECT (g_qeglobals_gui.d_main_window), "menu_bsp"));\r
-\r
-  while ((lst = gtk_container_children (GTK_CONTAINER (menu))) != NULL)\r
-    gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (lst->data));\r
-\r
-  if (g_PrefsDlg.m_bDetachableMenus) {\r
-    item = gtk_tearoff_menu_item_new ();\r
-    gtk_menu_append (GTK_MENU (menu), item);\r
-    gtk_widget_set_sensitive (item, TRUE);\r
-    gtk_widget_show (item);\r
-  }\r
-\r
-  if (g_qeglobals.bBSPFrontendPlugin)\r
-  {\r
-    CString str = g_BSPFrontendTable.m_pfnGetBSPMenu();\r
-    char cTemp[1024];\r
-    strcpy(cTemp, str);\r
-    char* token = strtok(cTemp, ",;");\r
-    if (token && *token == ' ')\r
-    {\r
-      while (*token == ' ')\r
-       token++;\r
-    }\r
-    i = 0;\r
-\r
-    // first token is menu name\r
-    item = gtk_menu_get_attach_widget (GTK_MENU (menu));\r
-    gtk_label_set_text (GTK_LABEL (GTK_BIN (item)->child), token);\r
-\r
-    token = strtok(NULL, ",;");\r
-    while (token != NULL)\r
-    {\r
-      g_BSPFrontendCommands = g_slist_append (g_BSPFrontendCommands, g_strdup (token));\r
-      item = gtk_menu_item_new_with_label (token);\r
-      gtk_widget_show (item);\r
-      gtk_container_add (GTK_CONTAINER (menu), item);\r
-      gtk_signal_connect (GTK_OBJECT (item), "activate",\r
-                         GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i));\r
-      token = strtok(NULL, ",;");\r
-      i++;\r
-    }\r
-  }\r
-  else\r
-  {\r
-    i = 0;\r
-    for (ep = g_qeglobals.d_project_entity->epairs; ep; ep = ep->next)\r
-    {\r
-      if (strncmp(ep->key, "bsp_", 4)==0)\r
-      {\r
-        bsp_commands[i] = ep->key;     \r
-        item = gtk_menu_item_new_with_label (ep->key+4);\r
-        gtk_widget_show (item);\r
-        gtk_container_add (GTK_CONTAINER (menu), item);\r
-        gtk_signal_connect (GTK_OBJECT (item), "activate",\r
-          GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i));\r
-        i++;\r
-      }\r
-    }\r
-  }\r
-}\r
-\r
-//==============================================\r
-\r
-void AddSlash(CString& strPath)\r
-{\r
-  if (strPath.GetLength() > 0)\r
-  {\r
-    if ((strPath.GetAt(strPath.GetLength()-1) != '/') &&\r
-       (strPath.GetAt(strPath.GetLength()-1) != '\\'))\r
-      strPath += '/';\r
-  }\r
-}\r
-\r
-bool ExtractPath_and_Filename(const char* pPath, CString& strPath, CString& strFilename)\r
-{\r
-  CString strPathName;\r
-  strPathName = pPath;\r
-  int nSlash = strPathName.ReverseFind('\\');\r
-  if (nSlash == -1)\r
-    // TTimo: try forward slash, some are using forward\r
-    nSlash = strPathName.ReverseFind('/');\r
-  if (nSlash >= 0)\r
-  {\r
-    strPath = strPathName.Left(nSlash+1);\r
-    strFilename = strPathName.Right(strPathName.GetLength() - nSlash - 1);\r
-  }\r
-  // TTimo: try forward slash, some are using forward\r
-  else\r
-    strFilename = pPath;\r
-  return true;\r
-}\r
-\r
-//===========================================\r
-\r
-//++timo FIXME: no longer used .. remove!\r
-char *TranslateString (char *buf)\r
-{\r
-  static char  buf2[32768];\r
-  int          i, l;\r
-  char         *out;\r
-\r
-  l = strlen(buf);\r
-  out = buf2;\r
-  for (i=0 ; i<l ; i++)\r
-  {\r
-    if (buf[i] == '\n')\r
-    {\r
-      *out++ = '\r';\r
-      *out++ = '\n';\r
-    }\r
-    else\r
-      *out++ = buf[i];\r
-  }\r
-  *out++ = 0;\r
-\r
-  return buf2;\r
-}\r
-\r
-// called whenever we need to open/close/check the console log file\r
-void Sys_LogFile (void)\r
-{\r
-  if (g_PrefsDlg.mGamesDialog.m_bLogConsole && !g_qeglobals.hLogFile)\r
-  {\r
-       // settings say we should be logging and we don't have a log file .. so create it\r
-    // open a file to log the console (if user prefs say so)\r
-    // the file handle is g_qeglobals.hLogFile\r
-    // the log file is erased\r
-    Str name;\r
-    name = g_strTempPath;    \r
-    name += "radiant.log";\r
-#if defined (__linux__) || defined (__APPLE__)\r
-    g_qeglobals.hLogFile = open( name.GetBuffer(), O_TRUNC | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE );\r
-#endif\r
-#ifdef _WIN32\r
-    g_qeglobals.hLogFile = _open( name.GetBuffer(), _O_TRUNC | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE );\r
-#endif\r
-    if (g_qeglobals.hLogFile)\r
-    {\r
-      Sys_Printf("Started logging to %s\n", name.GetBuffer());\r
-      time_t localtime;\r
-      time(&localtime);\r
-      Sys_Printf("Today is: %s", ctime(&localtime));\r
-      Sys_Printf("This is GtkRadiant '" RADIANT_VERSION "' compiled " __DATE__ "\n");\r
-      Sys_Printf(RADIANT_ABOUTMSG "\n");\r
-    }\r
-    else\r
-      gtk_MessageBox (NULL, "Failed to create log file, check write permissions in Radiant directory.\n",\r
-                     "Console logging", MB_OK );\r
-  }\r
-  else if (!g_PrefsDlg.mGamesDialog.m_bLogConsole && g_qeglobals.hLogFile)\r
-  {\r
-       // settings say we should not be logging but still we have an active logfile .. close it\r
-    time_t localtime;\r
-    time(&localtime);\r
-    Sys_Printf("Closing log file at %s\n", ctime(&localtime));\r
-               #ifdef _WIN32\r
-    _close( g_qeglobals.hLogFile );\r
-               #endif\r
-               #if defined (__linux__) || defined (__APPLE__)\r
-               close( g_qeglobals.hLogFile );\r
-               #endif\r
-    g_qeglobals.hLogFile = 0;\r
-  }\r
-}\r
-\r
-void Sys_ClearPrintf (void)\r
-{\r
-  GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(g_qeglobals_gui.d_edit));\r
-  gtk_text_buffer_set_text(buffer, "", -1);\r
-}\r
-\r
-// used to be around 32000, that should be way enough already\r
-#define BUFFER_SIZE 4096\r
-\r
-extern "C" void Sys_FPrintf_VA (int level, const char *text, va_list args)\r
-{\r
-  char buf[BUFFER_SIZE];\r
-  \r
-  buf[0] = 0;\r
-  vsnprintf(buf, BUFFER_SIZE, text, args);\r
-  buf[BUFFER_SIZE-1] = 0;\r
-  const unsigned int length = strlen(buf);\r
-\r
-  if (g_qeglobals.hLogFile)\r
-  {\r
-#ifdef _WIN32\r
-    _write(g_qeglobals.hLogFile, buf,length);\r
-    _commit(g_qeglobals.hLogFile);\r
-#endif\r
-#if defined (__linux__) || defined (__APPLE__)\r
-    write(g_qeglobals.hLogFile, buf, length);\r
-#endif\r
-  }\r
-\r
-  if (level != SYS_NOCON)\r
-  {\r
-    // TTimo: FIXME: killed the console to avoid GDI leak fuckup\r
-    if (g_qeglobals_gui.d_edit != NULL)\r
-    {\r
-      GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(g_qeglobals_gui.d_edit));\r
-      \r
-      GtkTextIter iter;\r
-      gtk_text_buffer_get_end_iter(buffer, &iter);\r
-\r
-      static GtkTextMark* end = gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);\r
-\r
-      const GdkColor yellow = { 0, 0xb0ff, 0xb0ff, 0x0000 };\r
-      const GdkColor red = { 0, 0xffff, 0x0000, 0x0000 };\r
-      const GdkColor black = { 0, 0x0000, 0x0000, 0x0000 };\r
-\r
-      static GtkTextTag* error_tag = gtk_text_buffer_create_tag (buffer, "red_foreground", "foreground-gdk", &red, NULL);\r
-      static GtkTextTag* warning_tag = gtk_text_buffer_create_tag (buffer, "yellow_foreground", "foreground-gdk", &yellow, NULL);\r
-      static GtkTextTag* standard_tag = gtk_text_buffer_create_tag (buffer, "black_foreground", "foreground-gdk", &black, NULL);\r
-      GtkTextTag* tag;\r
-      switch (level)\r
-      {\r
-      case SYS_WRN:\r
-        tag = warning_tag;\r
-        break;\r
-      case SYS_ERR:\r
-        tag = error_tag;\r
-        break;\r
-      case SYS_STD:\r
-      case SYS_VRB:\r
-      default:\r
-        tag = standard_tag;\r
-        break;\r
-      }\r
-      gtk_text_buffer_insert_with_tags(buffer, &iter, buf, length, tag, NULL);\r
-\r
-      gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(g_qeglobals_gui.d_edit), end);\r
-\r
-      // update console widget immediatly if we're doing something time-consuming\r
-      if( !g_bScreenUpdates && GTK_WIDGET_REALIZED( g_qeglobals_gui.d_edit ) )\r
-      {\r
-        gtk_grab_add(g_qeglobals_gui.d_edit);\r
-\r
-        while(gtk_events_pending())\r
-          gtk_main_iteration();\r
-\r
-        gtk_grab_remove(g_qeglobals_gui.d_edit);\r
-      }\r
-    }\r
-  }  \r
-}\r
-\r
-// NOTE: this is the handler sent to synapse\r
-// must match PFN_SYN_PRINTF_VA\r
-extern "C" void Sys_Printf_VA (const char *text, va_list args)\r
-{\r
-  Sys_FPrintf_VA (SYS_STD, text, args);\r
-}\r
-\r
-extern "C" void Sys_Printf (const char *text, ...)\r
-{\r
-  va_list args;\r
-\r
-  va_start (args, text);\r
-  Sys_FPrintf_VA (SYS_STD, text, args);\r
-  va_end (args);\r
-}\r
-\r
-extern "C" void Sys_FPrintf (int level, const char *text, ...)\r
-{\r
-  va_list args;\r
-\r
-  va_start (args, text);\r
-  Sys_FPrintf_VA (level, text, args);\r
-  va_end (args);\r
-}\r
+/*
+Copyright (C) 1999-2007 id Software, Inc. and contributors.
+For a list of contributors, see the accompanying CONTRIBUTORS file.
+
+This file is part of GtkRadiant.
+
+GtkRadiant is free software; you can redistribute it and/or modify
+it under the terms of the GNU General Public License as published by
+the Free Software Foundation; either version 2 of the License, or
+(at your option) any later version.
+
+GtkRadiant is distributed in the hope that it will be useful,
+but WITHOUT ANY WARRANTY; without even the implied warranty of
+MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+GNU General Public License for more details.
+
+You should have received a copy of the GNU General Public License
+along with GtkRadiant; if not, write to the Free Software
+Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
+*/
+
+//
+// Linux stuff
+//
+// Leonardo Zide (leo@lokigames.com)
+//
+
+#include "stdafx.h"
+#include <gtk/gtk.h>
+#include <sys/stat.h> 
+#include "gtkmisc.h"
+#if defined (__linux__) || defined (__APPLE__)
+#include <unistd.h>
+#include <X11/keysym.h>
+#include <gdk/gdkx.h>
+#include <gdk/gdkprivate.h>
+#endif
+// for the logging part
+#include <fcntl.h>
+#include <sys/types.h>
+
+QEGlobals_t  g_qeglobals;
+QEGlobals_GUI_t g_qeglobals_gui;
+
+// leo: Track memory allocations for debugging
+// NOTE TTimo this was never used and probably not relevant
+//   there are tools to do that
+#ifdef MEM_DEBUG
+
+static GList *memblocks;
+
+void* debug_malloc (size_t size, const char* file, int line)
+{
+  void *buf = g_malloc (size + 8);
+
+  *((const char**)buf) = file;
+  buf = (char*)buf + 4;
+  *((int*)buf) = line;
+  buf = (char*)buf + 4;
+
+  memblocks = g_list_append (memblocks, buf);
+
+  return buf;
+}
+
+void debug_free (void *buf, const char* file, int line)
+{
+  const char *f;
+  int l;
+
+  if (g_list_find (memblocks, buf))
+  {
+    memblocks = g_list_remove (memblocks, buf);
+
+    buf = (char*)buf - 4;
+    l = *((int*)buf);
+    buf = (char*)buf - 4;
+    f = *((const char**)buf);
+
+    Sys_FPrintf (SYS_DBG, "free: %s %d", file, line);
+    Sys_FPrintf (SYS_DBG, " allocated: %s %d\n", f, l);
+
+    g_free (buf);
+  }
+//  else
+//    free (buf); // from qmalloc, will leak unless we add this same hack to cmdlib
+}
+
+#endif
+
+vec_t Rad_rint (vec_t in)
+{
+  if (g_PrefsDlg.m_bNoClamp)
+    return in;
+  else
+    return (float)floor (in + 0.5);
+}
+
+void WINAPI QE_CheckOpenGLForErrors(void)
+{
+  char strMsg[1024];
+  int i = qglGetError();
+  if (i != GL_NO_ERROR)
+  {
+    if (i == GL_OUT_OF_MEMORY)
+    {      
+      sprintf(strMsg, "OpenGL out of memory error %s\nDo you wish to save before exiting?", qgluErrorString((GLenum)i));
+      if (gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg, "Radiant Error", MB_YESNO) == IDYES)
+      {
+        Map_SaveFile(NULL, false);
+      }
+      _exit(1);
+    }
+    else
+    {
+      Sys_Printf ("Warning: OpenGL Error %s\n", qgluErrorString((GLenum)i));
+    }
+  }
+}
+
+// NOTE: don't this function, use VFS instead
+char *ExpandReletivePath (char *p)
+{
+       static char     temp[1024];
+       const char      *base;
+
+       if (!p || !p[0])
+               return NULL;
+       if (p[0] == '/' || p[0] == '\\')
+               return p;
+
+       base = ValueForKey(g_qeglobals.d_project_entity, "basepath");
+       sprintf (temp, "%s/%s", base, p);
+       return temp;
+}
+
+char *copystring (char *s)
+{
+       char    *b;
+       b = (char*)malloc(strlen(s)+1);
+       strcpy (b,s);
+       return b;
+}
+
+
+bool DoesFileExist(const char* pBuff, long& lSize)
+{
+  FileStream file;
+  if (file.Open(pBuff, "r"))
+  {
+    lSize += file.GetLength();
+    file.Close();
+    return true;
+  }
+  return false;
+}
+
+
+void Map_Snapshot()
+{
+  CString strMsg;
+
+  // I hope the modified flag is kept correctly up to date
+  if (!modified)
+    return;
+
+  // we need to do the following
+  // 1. make sure the snapshot directory exists (create it if it doesn't)
+  // 2. find out what the lastest save is based on number
+  // 3. inc that and save the map
+  CString strOrgPath, strOrgFile;
+  ExtractPath_and_Filename(currentmap, strOrgPath, strOrgFile);
+  AddSlash(strOrgPath);
+  strOrgPath += "snapshots";
+  bool bGo = true;
+  struct stat Stat;
+  if (stat(strOrgPath, &Stat) == -1)
+  {
+#ifdef _WIN32
+    bGo = (_mkdir(strOrgPath) != -1);
+#endif
+
+#if defined (__linux__) || defined (__APPLE__)
+    bGo = (mkdir(strOrgPath,0755) != -1);
+#endif
+  }
+  AddSlash(strOrgPath);
+  if (bGo)
+  {
+    int nCount = 0;
+    long lSize = 0;
+    CString strNewPath;
+    strNewPath = strOrgPath;
+    strNewPath += strOrgFile;
+    CString strFile;
+    while (bGo)
+    {
+      char buf[PATH_MAX];
+      sprintf( buf, "%s.%i", strNewPath.GetBuffer(), nCount );
+      strFile = buf;
+      bGo = DoesFileExist(strFile, lSize);
+      nCount++;
+    }
+    // strFile has the next available slot
+    Map_SaveFile(strFile, false);
+    // it is still a modified map (we enter this only if this is a modified map)
+    Sys_SetTitle (currentmap);
+    Sys_MarkMapModified();
+    if (lSize > 12 * 1024 * 1024) // total size of saves > 4 mb
+    {
+      Sys_Printf("The snapshot files in %s total more than 4 megabytes. You might consider cleaning up.", strOrgPath.GetBuffer());
+    }
+  }
+  else
+  {
+    strMsg.Format("Snapshot save failed.. unabled to create directory\n%s", strOrgPath.GetBuffer());
+    gtk_MessageBox(g_pParentWnd->m_pWidget, strMsg);
+  }
+  strOrgPath = "";
+  strOrgFile = "";
+}
+/*
+===============
+QE_CheckAutoSave
+
+If five minutes have passed since making a change
+and the map hasn't been saved, save it out.
+===============
+*/
+
+
+void QE_CheckAutoSave( void )
+{
+  static time_t s_start;
+  time_t now;
+  time (&now);
+
+  if (modified != 1 || !s_start)
+  {
+    s_start = now;
+    return;
+  }
+
+  if ((now - s_start) > (60 * g_PrefsDlg.m_nAutoSave))
+  {
+    if (g_PrefsDlg.m_bAutoSave)
+    {
+      CString strMsg;
+      strMsg = g_PrefsDlg.m_bSnapShots ? "Autosaving snapshot..." : "Autosaving...";
+      Sys_Printf(strMsg);
+      Sys_Printf("\n");
+      Sys_Status (strMsg,0);
+
+      // only snapshot if not working on a default map
+      if (g_PrefsDlg.m_bSnapShots && stricmp(currentmap, "unnamed.map") != 0)
+      {
+        Map_Snapshot();
+      }
+      else
+      {
+        Map_SaveFile (ValueForKey(g_qeglobals.d_project_entity, "autosave"), false);
+      }
+
+      Sys_Status ("Autosaving...Saved.", 0 );
+      modified = 2;
+    }
+    else
+    {
+      Sys_Printf ("Autosave skipped...\n");
+      Sys_Status ("Autosave skipped...", 0 );
+    }
+    s_start = now;
+  }
+}
+
+
+// NOTE TTimo we don't like that BuildShortPathName too much
+//   the VFS provides a vfsCleanFileName which should perform the cleanup tasks
+//   in the long run I'd like to completely get rid of this
+
+// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=144
+// used to be disabled, but caused problems
+
+// http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=291
+// can't work with long win32 names until the BSP commands are not working differently
+#ifdef _WIN32
+int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen)
+{
+  char *pFile = NULL;
+  int nResult = GetFullPathName(pPath, nBufferLen, pBuffer, &pFile);
+  nResult = GetShortPathName(pPath, pBuffer, nBufferLen);
+  if (nResult == 0)
+    strcpy(pBuffer, pPath);                     // Use long filename
+  return nResult;
+}
+#endif
+
+#if defined (__linux__) || defined (__APPLE__)
+int BuildShortPathName(const char* pPath, char* pBuffer, int nBufferLen)
+{
+  // remove /../ from directories
+  const char *scr = pPath; char *dst = pBuffer;
+  for (int i = 0; (i < nBufferLen) && (*scr != 0); i++)
+  {
+    if (*scr == '/' && *(scr+1) == '.' && *(scr+2) == '.')
+    {
+      scr += 3;
+      while (dst != pBuffer && *(--dst) != '/')
+      {
+        i--;
+      }
+    }
+
+    *dst = *scr;
+
+    scr++; dst++;
+  }
+  *dst = 0;
+
+  return strlen (pBuffer);
+}
+#endif
+
+/*
+const char *g_pPathFixups[]=
+{
+  "basepath",
+  "autosave",
+};
+
+const int g_nPathFixupCount = sizeof(g_pPathFixups) / sizeof(const char*);
+
+void QE_CheckProjectEntity()
+{
+  char *pFile;
+  char pBuff[PATH_MAX];
+  char pNewPath[PATH_MAX];
+  for (int i = 0; i < g_nPathFixupCount; i++)
+  {
+    char *pPath = ValueForKey (g_qeglobals.d_project_entity, g_pPathFixups[i]);
+
+    strcpy (pNewPath, pPath);
+    if (pPath[0] != '\\' && pPath[0] != '/')
+      if (GetFullPathName(pPath, PATH_MAX, pBuff, &pFile))
+             strcpy (pNewPath, pBuff);
+
+    BuildShortPathName (pNewPath, pBuff, PATH_MAX);
+
+    // check it's not ending with a filename seperator
+    if (pBuff[strlen(pBuff)-1] == '/' || pBuff[strlen(pBuff)-1] == '\\')
+    {
+      Sys_FPrintf(SYS_WRN, "WARNING: \"%s\" path in the project file has an ending file seperator, fixing.\n", g_pPathFixups[i]);
+      pBuff[strlen(pBuff)-1]=0;
+    }
+
+    SetKeyValue(g_qeglobals.d_project_entity, g_pPathFixups[i], pBuff);
+  }
+}
+*/
+
+void HandleXMLError( void* ctxt, const char* text, ... )
+{
+  va_list argptr;
+  static char buf[32768];
+
+  va_start (argptr,text);
+  vsprintf (buf, text, argptr);
+  Sys_FPrintf (SYS_ERR, "XML %s\n", buf);
+  va_end (argptr);
+}
+
+#define DTD_BUFFER_LENGTH 1024
+xmlDocPtr ParseXMLStream(IDataStream *stream, bool validate = false)
+{
+  xmlDocPtr doc = NULL;
+  bool wellFormed = false, valid = false;
+  int res, size = 1024;
+  char chars[1024];
+  xmlParserCtxtPtr ctxt;
+
+  // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=433
+  //if(validate)
+  //  xmlDoValidityCheckingDefaultValue = 1;
+  //else
+    xmlDoValidityCheckingDefaultValue = 0;
+  xmlSetGenericErrorFunc(NULL, HandleXMLError);
+
+  // SPoG
+  // HACK: use AppPath to resolve DTD location
+  // do a buffer-safe string copy and concatenate
+  int i;
+  char* w;
+  const char* r;
+  char buf[DTD_BUFFER_LENGTH];
+
+  w = buf;
+  i = 0;
+  // copy
+  //assert(g_strAppPath.GetBuffer() != NULL);
+  for(r = g_strAppPath.GetBuffer(); i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++)  w[i] = *r;
+  // concatenate
+  for(r = "dtds/"; i<DTD_BUFFER_LENGTH && *r != '\0'; i++, r++)  w[i] = *r;
+  // terminate
+  w[i] = '\0';
+
+  if(i == DTD_BUFFER_LENGTH)
+  {
+    HandleXMLError(NULL, "ERROR: buffer overflow: DTD path length too large\n");
+    return NULL;
+  }
+
+  res = stream->Read(chars, 4);
+  if (res > 0)
+  {
+    ctxt = xmlCreatePushParserCtxt(NULL, NULL, chars, res, buf);
+
+    while ((res = stream->Read(chars, size)) > 0)
+    {
+      xmlParseChunk(ctxt, chars, res, 0);
+    }
+    xmlParseChunk(ctxt, chars, 0, 1);
+    doc = ctxt->myDoc;
+
+    wellFormed = (ctxt->wellFormed == 1);
+    valid = (ctxt->valid == 1);
+
+    xmlFreeParserCtxt(ctxt);
+  }
+
+  if(wellFormed && (!validate || (validate && valid)))
+    return doc;
+
+  if(doc != NULL)
+    xmlFreeDoc(doc);
+
+  return NULL;
+}
+
+xmlDocPtr ParseXMLFile(const char* filename, bool validate = false)
+{
+  FileStream stream;
+  if (stream.Open(filename, "r"))
+    return ParseXMLStream(&stream, validate);
+  
+  Sys_FPrintf(SYS_ERR, "Failed to open file: %s\n",filename);
+  return NULL;
+}
+
+// copy a string r to a buffer w
+// replace $string as appropriate
+void ReplaceTemplates(char* w, const char* r)
+{
+  const char *p;
+  const char *__ENGINEPATH = "TEMPLATEenginepath";
+  const char *__USERHOMEPATH = "TEMPLATEuserhomepath";
+  const char *__TOOLSPATH = "TEMPLATEtoolspath";
+  const char *__BASEDIR = "TEMPLATEbasedir";
+  const char *__APPPATH = "TEMPLATEapppath";
+
+  // iterate through string r
+  while(*r!='\0')
+  {
+    // check for special character
+    if(*r=='$')
+    {
+      if(strncmp(r+1, __ENGINEPATH, strlen(__ENGINEPATH)) == 0)
+      {
+        r+=strlen(__ENGINEPATH)+1;
+        p = g_pGameDescription->mEnginePath.GetBuffer();
+      }
+      else if(strncmp(r+1, __USERHOMEPATH, strlen(__USERHOMEPATH)) == 0)
+      {
+        r+=strlen(__USERHOMEPATH)+1;
+        p = g_qeglobals.m_strHomeGame.GetBuffer();
+      }
+      else if(strncmp(r+1, __BASEDIR, strlen(__BASEDIR)) == 0)
+      {
+        r+=strlen(__BASEDIR)+1;
+        p = g_pGameDescription->mBaseGame;
+      }
+      else if(strncmp(r+1, __TOOLSPATH, strlen(__TOOLSPATH)) == 0)
+      {
+        r+=strlen(__TOOLSPATH)+1;
+        p = g_strGameToolsPath.GetBuffer();
+      }
+      else if(strncmp(r+1, __APPPATH, strlen(__APPPATH)) == 0)
+      {
+        r+=strlen(__APPPATH)+1;
+        p = g_strAppPath.GetBuffer();
+      }
+      else
+      {
+        r++;
+        p = "$";
+      }
+          
+      while(*p!='\0') *w++ = *p++;
+    }
+    else *w++ = *r++;
+  }
+  *w = '\0';
+}
+
+/*
+===========
+QE_LoadProject
+TODO TODO TODO (don't think this got fully merged in)
+TTimo: added project file "version", version 2 adds '#' chars to the BSP command strings
+version 3 was .. I don't remember .. version 4 adds q3map2 commands
+TTimo: when QE_LoadProject is called, the prefs are updated with path to the latest project and saved on disk
+===========
+*/
+/*\todo decide on a sensible location/name for project files.*/
+bool QE_LoadProject (const char *projectfile)
+{
+  char buf[1024];
+  xmlDocPtr doc;
+  xmlNodePtr node, project;
+
+  Sys_Printf("Loading project file: \"%s\"\n", projectfile);
+  doc = ParseXMLFile(projectfile, true);
+
+  if(doc == NULL) return false;
+
+  node=doc->children;
+  while(node != NULL && node->type != XML_DTD_NODE) node=node->next;
+  if(node == NULL || strcmp((char*)node->name, "project") != 0)
+  {
+    Sys_FPrintf(SYS_ERR, "ERROR: invalid file type\n");
+    return false;
+  }
+
+  while(node->type != XML_ELEMENT_NODE) node=node->next;
+  // <project>
+  project = node;
+
+  if(g_qeglobals.d_project_entity != NULL) Entity_Free(g_qeglobals.d_project_entity);
+  g_qeglobals.d_project_entity = Entity_Alloc();
+
+  for(node = project->children; node != NULL; node=node->next)
+  {
+    if(node->type != XML_ELEMENT_NODE) continue;
+
+    // <key>
+    ReplaceTemplates(buf, (char*)node->properties->next->children->content);
+
+    SetKeyValue(g_qeglobals.d_project_entity, (char*)node->properties->children->content, buf);
+  }
+
+  xmlFreeDoc(doc);
+  
+  // project file version checking
+  // add a version checking to avoid people loading later versions of the project file and bitching
+  int ver = IntForKey( g_qeglobals.d_project_entity, "version" );
+  if (ver > PROJECT_VERSION)
+  {
+    char strMsg[1024];
+    sprintf (strMsg, "This is a version %d project file. This build only supports <=%d project files.\n"
+      "Please choose another project file or upgrade your version of Radiant.", ver, PROJECT_VERSION);
+    gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK);
+               // set the project file to nothing so we are sure we'll ask next time?
+               g_PrefsDlg.m_strLastProject = "";
+               g_PrefsDlg.SavePrefs();
+    return false;
+  }
+  
+  // set here some default project settings you need
+  if ( strlen( ValueForKey( g_qeglobals.d_project_entity, "brush_primit" ) ) == 0 )
+  {
+    SetKeyValue( g_qeglobals.d_project_entity, "brush_primit", "0" );
+  }
+
+  g_qeglobals.m_bBrushPrimitMode = IntForKey( g_qeglobals.d_project_entity, "brush_primit" );
+
+  g_qeglobals.m_strHomeMaps = g_qeglobals.m_strHomeGame;
+  const char* str = ValueForKey(g_qeglobals.d_project_entity, "gamename");
+  if(str[0] == '\0') str = g_pGameDescription->mBaseGame.GetBuffer();
+  g_qeglobals.m_strHomeMaps += str;
+  g_qeglobals.m_strHomeMaps += '/';
+
+  // don't forget to create the dirs
+  Q_mkdir(g_qeglobals.m_strHomeGame.GetBuffer(), 0775);
+  Q_mkdir(g_qeglobals.m_strHomeMaps.GetBuffer(), 0775);
+  
+  // usefull for the log file and debuggin fucked up configurations from users:
+  // output the basic information of the .qe4 project file
+  // SPoG
+  // all these paths should be unix format, with a trailing slash at the end
+  // if not.. to debug, check that the project file paths are set up correctly
+  Sys_Printf("basepath    : %s\n", ValueForKey( g_qeglobals.d_project_entity, "basepath") );
+  Sys_Printf("entitypath  : %s\n", ValueForKey( g_qeglobals.d_project_entity, "entitypath" ) );
+
+
+  // check whether user_project key exists..
+  // if not, save the current project under a new name
+  if (ValueForKey(g_qeglobals.d_project_entity, "user_project")[0] == '\0')
+  {
+    Sys_Printf("Loaded a template project file\n");
+    
+    // create the user_project key
+    SetKeyValue( g_qeglobals.d_project_entity, "user_project", "1" );
+    
+    // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672
+    if (IntForKey( g_qeglobals.d_project_entity, "version" ) != PROJECT_VERSION)
+    {
+      char strMsg[2048];
+      sprintf(strMsg, 
+        "The template project '%s' has version %d. The editor binary is configured for version %d.\n"
+        "This indicates a problem in your setup. See http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=672\n"
+        "I will keep going with this project till you fix this",
+        projectfile, IntForKey( g_qeglobals.d_project_entity, "version" ), PROJECT_VERSION);
+      gtk_MessageBox (g_pParentWnd->m_pWidget, strMsg, "Can't load project file", MB_ICONERROR | MB_OK);
+    }
+    
+    // create the writable project file path
+    strcpy(buf, g_qeglobals.m_strHomeGame.GetBuffer());
+    strcat(buf, g_pGameDescription->mBaseGame.GetBuffer());
+    strcat(buf, "/scripts/");
+    // while the filename is already in use, increment the number we add to the end
+    int counter = 0;
+    char pUser[PATH_MAX];
+    while (1)
+    {      
+      sprintf( pUser, "%suser%d." PROJECT_FILETYPE, buf, counter );
+      counter++;
+      if (access( pUser, R_OK) != 0)
+      {
+        // this is the one
+        strcpy( buf, pUser );
+        break;
+      }
+    }
+    // saving project will cause a save prefs
+    g_PrefsDlg.m_strLastProject = buf;
+    g_PrefsDlg.m_nLastProjectVer = IntForKey( g_qeglobals.d_project_entity, "version" );    
+    QE_SaveProject(buf);
+  }
+  else
+  {
+    // update preferences::LastProject with path of this successfully-loaded project
+    // save preferences
+    Sys_Printf("Setting current project in prefs to \"%s\"\n", g_PrefsDlg.m_strLastProject.GetBuffer() );
+    g_PrefsDlg.m_strLastProject = projectfile;
+    g_PrefsDlg.SavePrefs();
+  }
+
+  return true;
+}
+
+/*
+===========
+QE_SaveProject
+TTimo: whenever QE_SaveProject is called, prefs are updated and saved with the path to the project
+===========
+*/
+qboolean QE_SaveProject (const char* filename)
+{
+  Sys_Printf("Save project file '%s'\n", filename);
+
+  xmlNodePtr node;
+  xmlDocPtr doc = xmlNewDoc((xmlChar *)"1.0");
+  // create DTD node
+  xmlCreateIntSubset(doc, (xmlChar *)"project", NULL, (xmlChar *)"project.dtd");
+  // create project node
+  doc->children->next = xmlNewDocNode(doc, NULL, (xmlChar *)"project", NULL);
+
+  for(epair_t* epair = g_qeglobals.d_project_entity->epairs; epair != NULL; epair = epair->next)
+  {
+    node = xmlNewChild(doc->children->next, NULL, (xmlChar *)"key", NULL);
+    xmlSetProp(node, (xmlChar*)"name", (xmlChar*)epair->key);
+    xmlSetProp(node, (xmlChar*)"value", (xmlChar*)epair->value);
+  }
+
+  CreateDirectoryPath(filename);
+  if (xmlSaveFormatFile(filename, doc, 1) != -1)
+  {
+    xmlFreeDoc(doc);
+    Sys_Printf("Setting current project in prefs to \"%s\"\n", filename );
+    g_PrefsDlg.m_strLastProject = filename;
+    g_PrefsDlg.SavePrefs();
+    return TRUE;
+  }
+  else
+  {
+    xmlFreeDoc(doc);
+    Sys_FPrintf(SYS_ERR, "failed to save project file: \"%s\"\n", filename);
+    return FALSE;
+  }
+}
+
+
+
+/*
+===========
+QE_KeyDown
+===========
+*/
+#define        SPEED_MOVE      32
+#define        SPEED_TURN      22.5
+
+
+/*
+===============
+ConnectEntities
+
+Sets target / targetname on the two entities selected
+from the first selected to the secon
+===============
+*/
+void ConnectEntities (void)
+{
+       entity_t        *e1, *e2;
+       const char              *target;
+       char            *newtarg = NULL;
+
+       if (g_qeglobals.d_select_count != 2)
+       {
+               Sys_Status ("Must have two brushes selected", 0);
+               Sys_Beep ();
+               return;
+       }
+
+       e1 = g_qeglobals.d_select_order[0]->owner;
+       e2 = g_qeglobals.d_select_order[1]->owner;
+
+       if (e1 == world_entity || e2 == world_entity)
+       {
+               Sys_Status ("Can't connect to the world", 0);
+               Sys_Beep ();
+               return;
+       }
+
+       if (e1 == e2)
+       {
+               Sys_Status ("Brushes are from same entity", 0);
+               Sys_Beep ();
+               return;
+       }
+    
+  target = ValueForKey (e1, "target");
+  if (target && target[0])
+    newtarg = g_strdup(target);
+  else
+  {
+    target = ValueForKey(e2, "targetname");
+    if(target && target[0])
+      newtarg = g_strdup(target);
+    else
+      Entity_Connect(e1, e2);
+  }
+
+  if(newtarg != NULL)
+  {
+    SetKeyValue(e1, "target", newtarg);
+    SetKeyValue(e2, "targetname", newtarg);
+    g_free(newtarg);
+  }
+  
+       Sys_UpdateWindows (W_XY | W_CAMERA);
+
+       Select_Deselect();
+       Select_Brush (g_qeglobals.d_select_order[1]);
+}
+
+qboolean QE_SingleBrush (bool bQuiet)
+{
+       if ( (selected_brushes.next == &selected_brushes)
+               || (selected_brushes.next->next != &selected_brushes) )
+       {
+         if (!bQuiet)
+    {
+               Sys_Printf ("Error: you must have a single brush selected\n");
+         }
+               return false;
+       }
+       if (selected_brushes.next->owner->eclass->fixedsize)
+       {
+         if (!bQuiet)
+         {
+                 Sys_Printf ("Error: you cannot manipulate fixed size entities\n");
+         }
+               return false;
+       }
+
+       return true;
+}
+
+void QE_InitVFS (void)
+{
+  // VFS initialization -----------------------
+  // we will call vfsInitDirectory, giving the directories to look in (for files in pk3's and for standalone files)
+  // we need to call in order, the mod ones first, then the base ones .. they will be searched in this order
+  // *nix systems have a dual filesystem in ~/.q3a, which is searched first .. so we need to add that too
+  Str directory,prefabs;
+
+  // TTimo: let's leave this to HL mode for now
+  if (g_pGameDescription->mGameFile == "hl.game")
+  {
+    // Hydra: we search the "gametools" path first so that we can provide editor
+    // specific pk3's wads and misc files for use by the editor.
+    // the relevant map compiler tools will NOT use this directory, so this helps
+    // to ensure that editor files are not used/required in release versions of maps
+    // it also helps keep your editor files all in once place, with the editor modules,
+    // plugins, scripts and config files.
+    // it also helps when testing maps, as you'll know your files won't/can't be used
+    // by the game engine itself.
+  
+    // <gametools>
+    directory = g_pGameDescription->mGameToolsPath;
+    vfsInitDirectory(directory.GetBuffer());
+  }
+
+  // NOTE TTimo about the mymkdir calls .. this is a bit dirty, but a safe thing on *nix
+
+  // if we have a mod dir
+  if (*ValueForKey(g_qeglobals.d_project_entity, "gamename") != '\0')
+  {
+
+#if defined (__linux__) || defined (__APPLE__)
+    // ~/.<gameprefix>/<fs_game>
+    directory = g_qeglobals.m_strHomeGame.GetBuffer();
+    Q_mkdir (directory.GetBuffer (), 0775);
+    directory += ValueForKey(g_qeglobals.d_project_entity, "gamename");
+    Q_mkdir (directory.GetBuffer (), 0775);
+    vfsInitDirectory(directory.GetBuffer());
+    AddSlash (directory);
+    prefabs = directory;
+    // also create the maps dir, it will be used as prompt for load/save
+    directory += "/maps";
+    Q_mkdir (directory, 0775);
+    // and the prefabs dir
+    prefabs += "/prefabs";
+    Q_mkdir (prefabs, 0775);
+
+#endif
+
+    // <fs_basepath>/<fs_game>
+    directory = g_pGameDescription->mEnginePath;
+    directory += ValueForKey(g_qeglobals.d_project_entity, "gamename");
+    Q_mkdir (directory.GetBuffer (), 0775);
+    vfsInitDirectory(directory.GetBuffer());
+    AddSlash(directory);
+    prefabs = directory;
+    // also create the maps dir, it will be used as prompt for load/save
+    directory += "/maps";
+    Q_mkdir (directory.GetBuffer (), 0775);
+    // and the prefabs dir
+    prefabs += "/prefabs";
+    Q_mkdir (prefabs, 0775);
+  }
+
+#if defined (__linux__) || defined (__APPLE__)
+  // ~/.<gameprefix>/<fs_main>
+  directory = g_qeglobals.m_strHomeGame.GetBuffer();
+  directory += g_pGameDescription->mBaseGame;
+  vfsInitDirectory (directory.GetBuffer ());
+#endif
+
+  // <fs_basepath>/<fs_main>
+  directory = g_pGameDescription->mEnginePath;
+  directory += g_pGameDescription->mBaseGame;
+  vfsInitDirectory(directory.GetBuffer());
+}
+
+void QE_Init (void)
+{
+       /*
+       ** initialize variables
+       */
+       g_qeglobals.d_gridsize = 8;
+       g_qeglobals.d_showgrid = true;
+
+  QE_InitVFS();
+
+  Eclass_Init();
+  FillClassList();             // list in entity window
+  Map_Init();
+
+  FillTextureMenu();
+  FillBSPMenu();
+
+       /*
+       ** other stuff
+       */
+       Z_Init ();
+}
+
+void WINAPI QE_ConvertDOSToUnixName( char *dst, const char *src )
+{
+       while ( *src )
+       {
+               if ( *src == '\\' )
+                       *dst = '/';
+               else
+                       *dst = *src;
+               dst++; src++;
+       }
+       *dst = 0;
+}
+
+int g_numbrushes, g_numentities;
+
+void QE_CountBrushesAndUpdateStatusBar( void )
+{
+       static int      s_lastbrushcount, s_lastentitycount;
+       static qboolean s_didonce;
+       
+       //entity_t   *e;
+       brush_t    *b, *next;
+
+       g_numbrushes = 0;
+       g_numentities = 0;
+       
+       if ( active_brushes.next != NULL )
+       {
+               for ( b = active_brushes.next ; b != NULL && b != &active_brushes ; b=next)
+               {
+                       next = b->next;
+                       if (b->brush_faces )
+                       {
+                               if ( !b->owner->eclass->fixedsize)
+                                       g_numbrushes++;
+                               else
+                                       g_numentities++;
+                       }
+               }
+       }
+/*
+       if ( entities.next != NULL )
+       {
+               for ( e = entities.next ; e != &entities && g_numentities != MAX_MAP_ENTITIES ; e = e->next)
+               {
+                       g_numentities++;
+               }
+       }
+*/
+       if ( ( ( g_numbrushes != s_lastbrushcount ) || ( g_numentities != s_lastentitycount ) ) || ( !s_didonce ) )
+       {
+               Sys_UpdateStatusBar();
+
+               s_lastbrushcount = g_numbrushes;
+               s_lastentitycount = g_numentities;
+               s_didonce = true;
+       }
+}
+
+char           com_token[1024];
+qboolean       com_eof;
+
+/*
+================
+I_FloatTime
+================
+*/
+double I_FloatTime (void)
+{
+       time_t  t;
+       
+       time (&t);
+       
+       return t;
+#if 0
+// more precise, less portable
+       struct timeval tp;
+       struct timezone tzp;
+       static int              secbase;
+
+       gettimeofday(&tp, &tzp);
+       
+       if (!secbase)
+       {
+               secbase = tp.tv_sec;
+               return tp.tv_usec/1000000.0;
+       }
+       
+       return (tp.tv_sec - secbase) + tp.tv_usec/1000000.0;
+#endif
+}
+
+
+/*
+==============
+COM_Parse
+
+Parse a token out of a string
+==============
+*/
+char *COM_Parse (char *data)
+{
+       int             c;
+       int             len;
+
+       len = 0;
+       com_token[0] = 0;
+       
+       if (!data)
+               return NULL;
+               
+// skip whitespace
+skipwhite:
+       while ( (c = *data) <= ' ')
+       {
+               if (c == 0)
+               {
+                       com_eof = true;
+                       return NULL;                    // end of file;
+               }
+               data++;
+       }
+       
+// skip // comments
+       if (c=='/' && data[1] == '/')
+       {
+               while (*data && *data != '\n')
+                       data++;
+               goto skipwhite;
+       }
+       
+
+// handle quoted strings specially
+       if (c == '\"')
+       {
+               data++;
+               do
+               {
+                       c = *data++;
+                       if (c=='\"')
+                       {
+                               com_token[len] = 0;
+                               return data;
+                       }
+                       com_token[len] = c;
+                       len++;
+               } while (1);
+       }
+
+// parse single characters
+       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+       {
+               com_token[len] = c;
+               len++;
+               com_token[len] = 0;
+               return data+1;
+       }
+
+// parse a regular word
+       do
+       {
+               com_token[len] = c;
+               data++;
+               len++;
+               c = *data;
+       if (c=='{' || c=='}'|| c==')'|| c=='(' || c=='\'' || c==':')
+                       break;
+       } while (c>32);
+       
+       com_token[len] = 0;
+       return data;
+}
+
+char* Get_COM_Token()
+{
+  return com_token;
+}
+
+/*
+=============================================================================
+
+                                               MISC FUNCTIONS
+
+=============================================================================
+*/
+
+
+int            argc;
+char   *argv[MAX_NUM_ARGVS];
+
+/*
+============
+ParseCommandLine
+============
+*/
+void ParseCommandLine (char *lpCmdLine)
+{
+       argc = 1;
+       argv[0] = "programname";
+
+       while (*lpCmdLine && (argc < MAX_NUM_ARGVS))
+       {
+               while (*lpCmdLine && ((*lpCmdLine <= 32) || (*lpCmdLine > 126)))
+                       lpCmdLine++;
+
+               if (*lpCmdLine)
+               {
+                       argv[argc] = lpCmdLine;
+                       argc++;
+
+                       while (*lpCmdLine && ((*lpCmdLine > 32) && (*lpCmdLine <= 126)))
+                               lpCmdLine++;
+
+                       if (*lpCmdLine)
+                       {
+                               *lpCmdLine = 0;
+                               lpCmdLine++;
+                       }
+                       
+               }
+       }
+}
+
+
+
+/*
+=================
+CheckParm
+
+Checks for the given parameter in the program's command line arguments
+Returns the argument number (1 to argc-1) or 0 if not present
+=================
+*/
+int CheckParm (char *check)
+{
+       int             i;
+
+       for (i = 1;i<argc;i++)
+       {
+               if ( stricmp(check, argv[i]) )
+                       return i;
+       }
+
+       return 0;
+}
+
+
+
+
+/*
+==============
+ParseNum / ParseHex
+==============
+*/
+int ParseHex (char *hex)
+{
+       char    *str;
+       int    num;
+
+       num = 0;
+       str = hex;
+
+       while (*str)
+       {
+               num <<= 4;
+               if (*str >= '0' && *str <= '9')
+                       num += *str-'0';
+               else if (*str >= 'a' && *str <= 'f')
+                       num += 10 + *str-'a';
+               else if (*str >= 'A' && *str <= 'F')
+                       num += 10 + *str-'A';
+               else
+                       Error ("Bad hex number: %s",hex);
+               str++;
+       }
+
+       return num;
+}
+
+
+int ParseNum (char *str)
+{
+       if (str[0] == '$')
+               return ParseHex (str+1);
+       if (str[0] == '0' && str[1] == 'x')
+               return ParseHex (str+2);
+       return atol (str);
+}
+
+// BSP frontend plugin
+// global flag for BSP frontend plugin is g_qeglobals.bBSPFrontendPlugin
+_QERPlugBSPFrontendTable g_BSPFrontendTable;
+
+// =============================================================================
+// Sys_ functions
+
+bool Sys_AltDown ()
+{
+#ifdef _WIN32
+  return (GetKeyState(VK_MENU) & 0x8000) != 0;
+#endif
+
+#if defined (__linux__) || defined (__APPLE__)
+  char keys[32];
+  int x;
+
+  XQueryKeymap(GDK_DISPLAY(), keys);
+
+  x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_L);
+  if (keys[x/8] & (1 << (x % 8)))
+    return true;
+
+  x = XKeysymToKeycode (GDK_DISPLAY(), XK_Alt_R);
+  if (keys[x/8] & (1 << (x % 8)))
+    return true;
+
+  return false;
+#endif
+}
+
+bool Sys_ShiftDown ()
+{
+#ifdef _WIN32
+  return (GetKeyState(VK_SHIFT) & 0x8000) != 0;
+#endif
+
+#if defined (__linux__) || defined (__APPLE__)
+  char keys[32];
+  int x;
+
+  XQueryKeymap(GDK_DISPLAY(), keys);
+
+  x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_L);
+  if (keys[x/8] & (1 << (x % 8)))
+    return true;
+
+  x = XKeysymToKeycode (GDK_DISPLAY(), XK_Shift_R);
+  if (keys[x/8] & (1 << (x % 8)))
+    return true;
+
+  return false;
+#endif
+}
+
+void Sys_MarkMapModified (void)
+{
+  char title[PATH_MAX];
+
+  if (modified != 1)
+  {
+    modified = true;   // mark the map as changed
+    sprintf (title, "%s *", currentmap);
+
+    QE_ConvertDOSToUnixName( title, title );
+    Sys_SetTitle (title);
+  }
+}
+
+void Sys_SetTitle (const char *text)
+{
+  gtk_window_set_title (GTK_WINDOW (g_qeglobals_gui.d_main_window), text);
+}
+
+bool g_bWaitCursor = false;
+
+void WINAPI Sys_BeginWait (void)
+{
+  GdkCursor *cursor = gdk_cursor_new (GDK_WATCH);
+  gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor);
+  gdk_cursor_unref (cursor);
+  g_bWaitCursor = true;
+}
+
+void WINAPI Sys_EndWait (void)
+{
+  GdkCursor *cursor = gdk_cursor_new (GDK_LEFT_PTR);
+  gdk_window_set_cursor (g_pParentWnd->m_pWidget->window, cursor);
+  gdk_cursor_unref (cursor);
+  g_bWaitCursor = false;
+}
+
+void Sys_GetCursorPos (int *x, int *y)
+{
+  // FIXME: not multihead safe
+  gdk_window_get_pointer (NULL, x, y, NULL);
+}
+
+void Sys_SetCursorPos (int x, int y)
+{
+  // NOTE: coordinates are in GDK space, not OS space
+#ifdef _WIN32
+  int sys_x = x - g_pParentWnd->GetGDKOffsetX();
+  int sys_y = y - g_pParentWnd->GetGDKOffsetY();
+
+  SetCursorPos (sys_x, sys_y);
+#endif
+
+#if defined (__linux__) || defined (__APPLE__)
+  XWarpPointer (GDK_DISPLAY(), None, GDK_ROOT_WINDOW(), 0, 0, 0, 0, x, y);
+#endif
+}
+
+void Sys_Beep (void)
+{
+#if defined (__linux__) || defined (__APPLE__)
+  gdk_beep ();
+#else
+       MessageBeep (MB_ICONASTERISK);  
+#endif
+}
+
+double Sys_DoubleTime (void)
+{
+  return clock()/ 1000.0;
+}
+
+/*
+===============================================================
+
+  STATUS WINDOW
+
+===============================================================
+*/
+
+void Sys_UpdateStatusBar( void )
+{
+  extern int   g_numbrushes, g_numentities;
+
+  char numbrushbuffer[100]="";
+
+  sprintf( numbrushbuffer, "Brushes: %d Entities: %d", g_numbrushes, g_numentities );
+  g_pParentWnd->SetStatusText(2, numbrushbuffer);
+  //Sys_Status( numbrushbuffer, 2 );
+}
+
+void Sys_Status(const char *psz, int part )
+{
+  g_pParentWnd->SetStatusText (part, psz);
+}
+
+// =============================================================================
+// MRU
+
+#define MRU_MAX 4
+static GtkWidget *MRU_items[MRU_MAX];
+static int MRU_used;
+typedef char MRU_filename_t[PATH_MAX];
+MRU_filename_t MRU_filenames[MRU_MAX];
+
+static char* MRU_GetText (int index)
+{
+  return MRU_filenames[index];
+}
+
+void buffer_write_escaped_mnemonic(char* buffer, const char* string)
+{
+  while(*string != '\0')
+  {
+    if(*string == '_')
+    {
+      *buffer++ = '_';
+    }
+
+    *buffer++ = *string++;
+  }
+  *buffer = '\0';
+}
+
+static void MRU_SetText (int index, const char *filename)
+{
+  strcpy(MRU_filenames[index], filename);
+
+  char mnemonic[PATH_MAX * 2 + 4];
+  mnemonic[0] = '_';
+  sprintf(mnemonic+1, "%d", index+1);
+  mnemonic[2] = '-';
+  mnemonic[3] = ' ';
+  buffer_write_escaped_mnemonic(mnemonic+4, filename);
+  gtk_label_set_text_with_mnemonic(GTK_LABEL (GTK_BIN (MRU_items[index])->child), mnemonic);
+}
+
+void MRU_Load ()
+{
+  int i = g_PrefsDlg.m_nMRUCount;
+
+  if(i > 4)
+    i = 4; //FIXME: make this a define
+
+  for (; i > 0; i--)
+    MRU_AddFile (g_PrefsDlg.m_strMRUFiles[i-1].GetBuffer());
+}
+
+void MRU_Save ()
+{
+  g_PrefsDlg.m_nMRUCount = MRU_used;
+
+  for (int i = 0; i < MRU_used; i++)
+    g_PrefsDlg.m_strMRUFiles[i] = MRU_GetText (i);
+}
+
+void MRU_AddWidget (GtkWidget *widget, int pos)
+{
+  if (pos < MRU_MAX)
+    MRU_items[pos] = widget;
+}
+
+void MRU_AddFile (const char *str)
+{
+  int i;
+  char* text;
+
+  // check if file is already in our list
+  for (i = 0; i < MRU_used; i++)
+  {
+    text = MRU_GetText (i);
+
+    if (strcmp (text, str) == 0)
+    {
+      // reorder menu
+      for (; i > 0; i--)
+       MRU_SetText (i, MRU_GetText (i-1));
+
+      MRU_SetText (0, str);
+
+      return;
+    }
+  }
+
+  if (MRU_used < MRU_MAX)
+    MRU_used++;
+
+  // move items down
+  for (i = MRU_used-1; i > 0; i--)
+    MRU_SetText (i, MRU_GetText (i-1));
+
+  MRU_SetText (0, str);
+  gtk_widget_set_sensitive (MRU_items[0], TRUE);
+  gtk_widget_show (MRU_items[MRU_used-1]);
+}
+
+void MRU_Activate (int index)
+{
+  char *text = MRU_GetText (index);
+
+  if (access (text, R_OK) == 0)
+  {
+    text = strdup (text);
+    MRU_AddFile (text);
+    Map_LoadFile (text);
+    free (text);
+  }
+  else
+  {
+    MRU_used--;
+
+    for (int i = index; i < MRU_used; i++)
+      MRU_SetText (i, MRU_GetText (i+1));
+
+    if (MRU_used == 0)
+    {
+      gtk_label_set_text (GTK_LABEL (GTK_BIN (MRU_items[0])->child), "Recent Files");
+      gtk_widget_set_sensitive (MRU_items[0], FALSE);
+    }
+    else
+    {
+      gtk_widget_hide (MRU_items[MRU_used]);
+    }
+  }
+}
+
+/*
+======================================================================
+
+FILE DIALOGS
+
+======================================================================
+*/
+qboolean ConfirmModified ()
+{
+  if (!modified)
+    return true;
+
+  if (gtk_MessageBox (g_pParentWnd->m_pWidget, "This will lose changes to the map", "warning", MB_OKCANCEL) == IDCANCEL)
+    return false;
+  return true;
+}
+
+void ProjectDialog ()
+{
+  const char *filename;
+  char buffer[NAME_MAX];
+
+  /* 
+   * Obtain the system directory name and 
+   * store it in buffer. 
+   */ 
+
+  strcpy(buffer, g_qeglobals.m_strHomeGame.GetBuffer());
+  strcat(buffer, g_pGameDescription->mBaseGame.GetBuffer());
+  strcat (buffer, "/scripts/");
+
+  // Display the Open dialog box
+  filename = file_dialog (NULL, TRUE, "Open File", buffer, "project");
+
+  if (filename == NULL)
+    return;    // canceled
+
+  // Open the file.
+  // NOTE: QE_LoadProject takes care of saving prefs with new path to the project file
+  if (!QE_LoadProject(filename))
+    Sys_Printf ("Failed to load project from file: %s\n", filename);
+  else
+    // FIXME TTimo QE_Init is probably broken if you don't call it during startup right now .. 
+    QE_Init();
+}
+
+/*
+=======================================================
+
+Menu modifications
+
+=======================================================
+*/
+
+/*
+==================
+FillBSPMenu
+
+==================
+*/
+char *bsp_commands[256];
+
+void FillBSPMenu ()
+{
+  GtkWidget *item, *menu; // menu points to a GtkMenu (not an item)
+  epair_t *ep;
+  GList *lst;
+  int i;
+
+  menu = GTK_WIDGET (g_object_get_data (G_OBJECT (g_qeglobals_gui.d_main_window), "menu_bsp"));
+
+  while ((lst = gtk_container_children (GTK_CONTAINER (menu))) != NULL)
+    gtk_container_remove (GTK_CONTAINER (menu), GTK_WIDGET (lst->data));
+
+  if (g_PrefsDlg.m_bDetachableMenus) {
+    item = gtk_tearoff_menu_item_new ();
+    gtk_menu_append (GTK_MENU (menu), item);
+    gtk_widget_set_sensitive (item, TRUE);
+    gtk_widget_show (item);
+  }
+
+  if (g_qeglobals.bBSPFrontendPlugin)
+  {
+    CString str = g_BSPFrontendTable.m_pfnGetBSPMenu();
+    char cTemp[1024];
+    strcpy(cTemp, str);
+    char* token = strtok(cTemp, ",;");
+    if (token && *token == ' ')
+    {
+      while (*token == ' ')
+       token++;
+    }
+    i = 0;
+
+    // first token is menu name
+    item = gtk_menu_get_attach_widget (GTK_MENU (menu));
+    gtk_label_set_text (GTK_LABEL (GTK_BIN (item)->child), token);
+
+    token = strtok(NULL, ",;");
+    while (token != NULL)
+    {
+      g_BSPFrontendCommands = g_slist_append (g_BSPFrontendCommands, g_strdup (token));
+      item = gtk_menu_item_new_with_label (token);
+      gtk_widget_show (item);
+      gtk_container_add (GTK_CONTAINER (menu), item);
+      gtk_signal_connect (GTK_OBJECT (item), "activate",
+                         GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i));
+      token = strtok(NULL, ",;");
+      i++;
+    }
+  }
+  else
+  {
+    i = 0;
+    for (ep = g_qeglobals.d_project_entity->epairs; ep; ep = ep->next)
+    {
+      if (strncmp(ep->key, "bsp_", 4)==0)
+      {
+        bsp_commands[i] = ep->key;     
+        item = gtk_menu_item_new_with_label (ep->key+4);
+        gtk_widget_show (item);
+        gtk_container_add (GTK_CONTAINER (menu), item);
+        gtk_signal_connect (GTK_OBJECT (item), "activate",
+          GTK_SIGNAL_FUNC (HandleCommand), GINT_TO_POINTER (CMD_BSPCOMMAND+i));
+        i++;
+      }
+    }
+  }
+}
+
+//==============================================
+
+void AddSlash(CString& strPath)
+{
+  if (strPath.GetLength() > 0)
+  {
+    if ((strPath.GetAt(strPath.GetLength()-1) != '/') &&
+       (strPath.GetAt(strPath.GetLength()-1) != '\\'))
+      strPath += '/';
+  }
+}
+
+bool ExtractPath_and_Filename(const char* pPath, CString& strPath, CString& strFilename)
+{
+  CString strPathName;
+  strPathName = pPath;
+  int nSlash = strPathName.ReverseFind('\\');
+  if (nSlash == -1)
+    // TTimo: try forward slash, some are using forward
+    nSlash = strPathName.ReverseFind('/');
+  if (nSlash >= 0)
+  {
+    strPath = strPathName.Left(nSlash+1);
+    strFilename = strPathName.Right(strPathName.GetLength() - nSlash - 1);
+  }
+  // TTimo: try forward slash, some are using forward
+  else
+    strFilename = pPath;
+  return true;
+}
+
+//===========================================
+
+//++timo FIXME: no longer used .. remove!
+char *TranslateString (char *buf)
+{
+  static char  buf2[32768];
+  int          i, l;
+  char         *out;
+
+  l = strlen(buf);
+  out = buf2;
+  for (i=0 ; i<l ; i++)
+  {
+    if (buf[i] == '\n')
+    {
+      *out++ = '\r';
+      *out++ = '\n';
+    }
+    else
+      *out++ = buf[i];
+  }
+  *out++ = 0;
+
+  return buf2;
+}
+
+// called whenever we need to open/close/check the console log file
+void Sys_LogFile (void)
+{
+  if (g_PrefsDlg.mGamesDialog.m_bLogConsole && !g_qeglobals.hLogFile)
+  {
+       // settings say we should be logging and we don't have a log file .. so create it
+    // open a file to log the console (if user prefs say so)
+    // the file handle is g_qeglobals.hLogFile
+    // the log file is erased
+    Str name;
+    name = g_strTempPath;    
+    name += "radiant.log";
+#if defined (__linux__) || defined (__APPLE__)
+    g_qeglobals.hLogFile = open( name.GetBuffer(), O_TRUNC | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE );
+#endif
+#ifdef _WIN32
+    g_qeglobals.hLogFile = _open( name.GetBuffer(), _O_TRUNC | _O_CREAT | _O_WRONLY, _S_IREAD | _S_IWRITE );
+#endif
+    if (g_qeglobals.hLogFile)
+    {
+      Sys_Printf("Started logging to %s\n", name.GetBuffer());
+      time_t localtime;
+      time(&localtime);
+      Sys_Printf("Today is: %s", ctime(&localtime));
+      Sys_Printf("This is GtkRadiant '" RADIANT_VERSION "' compiled " __DATE__ "\n");
+      Sys_Printf(RADIANT_ABOUTMSG "\n");
+    }
+    else
+      gtk_MessageBox (NULL, "Failed to create log file, check write permissions in Radiant directory.\n",
+                     "Console logging", MB_OK );
+  }
+  else if (!g_PrefsDlg.mGamesDialog.m_bLogConsole && g_qeglobals.hLogFile)
+  {
+       // settings say we should not be logging but still we have an active logfile .. close it
+    time_t localtime;
+    time(&localtime);
+    Sys_Printf("Closing log file at %s\n", ctime(&localtime));
+               #ifdef _WIN32
+    _close( g_qeglobals.hLogFile );
+               #endif
+               #if defined (__linux__) || defined (__APPLE__)
+               close( g_qeglobals.hLogFile );
+               #endif
+    g_qeglobals.hLogFile = 0;
+  }
+}
+
+void Sys_ClearPrintf (void)
+{
+  GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(g_qeglobals_gui.d_edit));
+  gtk_text_buffer_set_text(buffer, "", -1);
+}
+
+// used to be around 32000, that should be way enough already
+#define BUFFER_SIZE 4096
+
+extern "C" void Sys_FPrintf_VA( int level, const char *text, va_list args ) {
+  char buf[BUFFER_SIZE];
+  
+  buf[0] = 0;
+  vsnprintf( buf, BUFFER_SIZE, text, args );
+  buf[BUFFER_SIZE-1] = 0;
+  const unsigned int length = strlen(buf);
+
+  if ( g_qeglobals.hLogFile ) {
+#ifdef _WIN32
+    _write( g_qeglobals.hLogFile, buf, length );
+    _commit( g_qeglobals.hLogFile );
+#endif
+#if defined (__linux__) || defined (__APPLE__)
+    write( g_qeglobals.hLogFile, buf, length );
+#endif
+  }
+
+  if ( level != SYS_NOCON ) {
+    // TTimo: FIXME: killed the console to avoid GDI leak fuckup
+    if ( g_qeglobals_gui.d_edit != NULL ) {
+      GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(g_qeglobals_gui.d_edit));
+      
+      GtkTextIter iter;
+      gtk_text_buffer_get_end_iter(buffer, &iter);
+
+      static GtkTextMark* end = gtk_text_buffer_create_mark(buffer, "end", &iter, FALSE);
+
+      const GdkColor yellow = { 0, 0xb0ff, 0xb0ff, 0x0000 };
+      const GdkColor red = { 0, 0xffff, 0x0000, 0x0000 };
+      const GdkColor black = { 0, 0x0000, 0x0000, 0x0000 };
+
+      static GtkTextTag* error_tag = gtk_text_buffer_create_tag (buffer, "red_foreground", "foreground-gdk", &red, NULL);
+      static GtkTextTag* warning_tag = gtk_text_buffer_create_tag (buffer, "yellow_foreground", "foreground-gdk", &yellow, NULL);
+      static GtkTextTag* standard_tag = gtk_text_buffer_create_tag (buffer, "black_foreground", "foreground-gdk", &black, NULL);
+      GtkTextTag* tag;
+      switch (level)
+      {
+      case SYS_WRN:
+        tag = warning_tag;
+        break;
+      case SYS_ERR:
+        tag = error_tag;
+        break;
+      case SYS_STD:
+      case SYS_VRB:
+      default:
+        tag = standard_tag;
+        break;
+      }
+      gtk_text_buffer_insert_with_tags(buffer, &iter, buf, length, tag, NULL);
+
+      gtk_text_view_scroll_mark_onscreen(GTK_TEXT_VIEW(g_qeglobals_gui.d_edit), end);
+
+      // update console widget immediatly if we're doing something time-consuming
+      if( !g_bScreenUpdates && GTK_WIDGET_REALIZED( g_qeglobals_gui.d_edit ) )
+      {
+        gtk_grab_add(g_qeglobals_gui.d_edit);
+
+        while(gtk_events_pending())
+          gtk_main_iteration();
+
+        gtk_grab_remove(g_qeglobals_gui.d_edit);
+      }
+    }
+  }  
+}
+
+// NOTE: this is the handler sent to synapse
+// must match PFN_SYN_PRINTF_VA
+extern "C" void Sys_Printf_VA (const char *text, va_list args)
+{
+  Sys_FPrintf_VA (SYS_STD, text, args);
+}
+
+extern "C" void Sys_Printf( const char *text, ... ) {
+  va_list args;
+
+  va_start( args, text );
+  Sys_FPrintf_VA( SYS_STD, text, args );
+  va_end( args );
+}
+
+extern "C" void Sys_FPrintf (int level, const char *text, ...)
+{
+  va_list args;
+
+  va_start (args, text);
+  Sys_FPrintf_VA (level, text, args);
+  va_end (args);
+}