]> de.git.xonotic.org Git - xonotic/netradiant.git/blobdiff - radiant/main.cpp
apply misc fixes from Markus Fischer and Rambetter
[xonotic/netradiant.git] / radiant / main.cpp
index 329d3e90701cb36ca4bc92b845184150db636795..02369ac84a7e9dd5c51ae725fbd0b5f90f917a4f 100644 (file)
@@ -1,5 +1,5 @@
 /*
-Copyright (C) 1999-2006 Id Software, Inc. and contributors.
+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.
@@ -19,78 +19,311 @@ along with GtkRadiant; if not, write to the Free Software
 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
 */
 
-/*! \mainpage GtkRadiant Documentation Index
+#if defined (__linux__) || defined (__APPLE__)
+  #include <gdk/gdkx.h>
+  #include <pwd.h>
+  #include <unistd.h>
+  #ifdef __linux__
+    #include <mntent.h>
+  #endif
+  #include <dirent.h>
+  #include <pthread.h>
+  #include <sys/wait.h>
+  #include <signal.h>
+  #include <sys/stat.h>
+#endif
+
+#include <gtk/gtk.h>
+#include <glib/gi18n.h>
+#include "stdafx.h"
+#include <assert.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include <stdlib.h>
+
+#include "watchbsp.h"
+#include "filters.h"
+
+bool g_bBuildList = false;
+int g_argc;
+char** g_argv;
+
+// =============================================================================
+// Splash screen
+
+// get rid of it when debugging
+#if defined (_DEBUG)
+  #define SKIP_SPLASH
+#endif
+
+static GtkWidget *splash_screen;
+
+// called based on a timer, or in particular cases when we don't want to keep it around
+gint try_destroy_splash (gpointer data)
+{
+  if (splash_screen)
+  {
+    gtk_widget_destroy (splash_screen);
+    splash_screen = NULL;
+  }
+  return FALSE;
+}
 
-\section intro_sec Introduction
+static void create_splash ()
+{
+  GtkWidget *alert_frame, *alert_frame1, *pixmap;
 
-This documentation is generated from comments in the source code.
+  splash_screen = gtk_window_new (GTK_WINDOW_POPUP);
+  gtk_window_position (GTK_WINDOW (splash_screen), GTK_WIN_POS_CENTER);
+  gtk_widget_realize (splash_screen);
 
-\section links_sec Useful Links
+  alert_frame1 = gtk_frame_new (NULL);
+  gtk_widget_show (alert_frame1);
+  gtk_container_add (GTK_CONTAINER (splash_screen), alert_frame1);
+  gtk_frame_set_shadow_type (GTK_FRAME (alert_frame1), GTK_SHADOW_OUT);
 
-\link include/itextstream.h include/itextstream.h \endlink - Global output and error message streams, similar to std::cout and std::cerr. \n
+  alert_frame = gtk_frame_new (NULL);
+  gtk_widget_show (alert_frame);
 
-FileInputStream - similar to std::ifstream (binary mode) \n
-FileOutputStream - similar to std::ofstream (binary mode) \n
-TextFileInputStream - similar to std::ifstream (text mode) \n
-TextFileOutputStream - similar to std::ofstream (text mode) \n
-StringOutputStream - similar to std::stringstream \n
+  gtk_container_add (GTK_CONTAINER (alert_frame1), alert_frame);
+  gtk_frame_set_shadow_type (GTK_FRAME (alert_frame), GTK_SHADOW_IN);
+  gtk_container_border_width (GTK_CONTAINER (alert_frame), 3);
 
-\link string/string.h string/string.h \endlink - C-style string comparison and memory management. \n
-\link os/path.h os/path.h \endlink - Path manipulation for radiant's standard path format \n
-\link os/file.h os/file.h \endlink - OS file-system access. \n
+  pixmap = gtk_preview_new (GTK_PREVIEW_COLOR);
+  gtk_widget_show (pixmap);
+  gtk_container_add (GTK_CONTAINER (alert_frame), pixmap);
 
-::CopiedString - automatic string memory management \n
-Array - automatic array memory management \n
-HashTable - generic hashtable, similar to std::hash_map \n
+  CString str;
+  guint16 width, height;
+  unsigned char *buf;
 
-\link math/vector.h math/vector.h \endlink - Vectors \n
-\link math/matrix.h math/matrix.h \endlink - Matrices \n
-\link math/quaternion.h math/quaternion.h \endlink - Quaternions \n
-\link math/plane.h math/plane.h \endlink - Planes \n
-\link math/aabb.h math/aabb.h \endlink - AABBs \n
+  str = g_strGameToolsPath;
+  str += "bitmaps/splash.bmp";
 
-Callback MemberCaller FunctionCaller - callbacks similar to using boost::function with boost::bind \n
-SmartPointer SmartReference - smart-pointer and smart-reference similar to Loki's SmartPtr \n
+  unsigned char* load_bitmap_file (const char* filename, guint16* width, guint16* height);
+  buf = load_bitmap_file (str.GetBuffer (), &width, &height);
 
-\link generic/bitfield.h generic/bitfield.h \endlink - Type-safe bitfield \n
-\link generic/enumeration.h generic/enumeration.h \endlink - Type-safe enumeration \n
+  if (!buf)
+  {
+    str = g_strBitmapsPath;
+    str += "splash.bmp";
 
-DefaultAllocator - Memory allocation using new/delete, compliant with std::allocator interface \n
+    buf = load_bitmap_file (str.GetBuffer (), &width, &height);
+  }
 
-\link debugging/debugging.h debugging/debugging.h \endlink - Debugging macros \n
+  if (buf)
+  {
+    GtkPreview *preview = GTK_PREVIEW (pixmap);
+    gtk_preview_size (preview, width, height);
+    for (int y = 0; y < height; y++)
+      gtk_preview_draw_row (preview, buf+y*width*3, 0, y, width);
+  }
+
+  gtk_widget_show_all (splash_screen);
+
+  while (gtk_events_pending ())
+    gtk_main_iteration ();
+}
 
+// =============================================================================
+// Loki stuff
+
+#if defined (__linux__) || defined (__APPLE__)
+
+/* A short game name, could be used as argv[0] */
+static char game_name[100] = "";
+
+/* The directory where the data files can be found (run directory) */
+static char datapath[PATH_MAX];
+
+char *loki_gethomedir(void)
+{
+  char *home = NULL;
+
+  home = getenv("HOME");
+  if ( home == NULL )
+  {
+    uid_t id = getuid();
+    struct passwd *pwd;
+
+    setpwent();
+    while ( (pwd = getpwent()) != NULL )
+    {
+      if ( pwd->pw_uid == id )
+      {
+        home = pwd->pw_dir;
+        break;
+      }
+    }
+    endpwent();
+  }
+  return home;
+}
+
+/* Must be called BEFORE loki_initialize */
+void loki_setgamename(const char *n)
+{
+  strncpy(game_name, n, sizeof(game_name));
+}
+
+  #ifdef __linux__
+/* Code to determine the mount point of a CD-ROM */
+int loki_getmountpoint(const char *device, char *mntpt, int max_size)
+{
+  char devpath[PATH_MAX], mntdevpath[PATH_MAX];
+  FILE * mountfp;
+  struct mntent *mntent;
+  int mounted;
+
+  /* Nothing to do with no device file */
+  if ( device == NULL )
+  {
+    *mntpt = '\0';
+    return -1;
+  }
+
+  /* Get the fully qualified path of the CD-ROM device */
+  if ( realpath(device, devpath) == NULL )
+  {
+    perror("realpath() on your CD-ROM failed");
+    return(-1);
+  }
+
+  /* Get the mount point */
+  mounted = -1;
+  memset(mntpt, 0, max_size);
+  mountfp = setmntent( _PATH_MNTTAB, "r" );
+  if ( mountfp != NULL )
+  {
+    mounted = 0;
+    while ( (mntent = getmntent( mountfp )) != NULL )
+    {
+      char *tmp, mntdev[1024];
+
+      strcpy(mntdev, mntent->mnt_fsname);
+      if ( strcmp(mntent->mnt_type, "supermount") == 0 )
+      {
+        tmp = strstr(mntent->mnt_opts, "dev=");
+        if ( tmp )
+        {
+          strcpy(mntdev, tmp+strlen("dev="));
+          tmp = strchr(mntdev, ',');
+          if ( tmp )
+          {
+            *tmp = '\0';
+          }
+        }
+      }
+      if ( strncmp(mntdev, "/dev", 4) ||
+           realpath(mntdev, mntdevpath) == NULL )
+      {
+        continue;
+      }
+      if ( strcmp( mntdevpath, devpath ) == 0 )
+      {
+        mounted = 1;
+        assert((int)strlen( mntent->mnt_dir ) < max_size);
+        strncpy( mntpt, mntent->mnt_dir, max_size-1);
+        mntpt[max_size-1] = '\0';
+        break;
+      }
+    }
+    endmntent( mountfp );
+  }
+  return(mounted);
+}
+  #endif
+
+/*
+    This function gets the directory containing the running program.
+    argv0 - the 0'th argument to the program
 */
+// FIXME TTimo
+// I don't understand this function. It looks like something cut from another piece of software
+// we somehow get the g_strAppPath from it, but it's done through a weird scan across $PATH env. var.
+// even worse, it doesn't behave the same in all cases .. works well when ran through gdb and borks when ran from a shell
+void loki_initpaths(char *argv0)
+{
+  char temppath[PATH_MAX]; //, env[100];
+  char *home; //, *ptr, *data_env;
+
+  home = loki_gethomedir();
+  if ( home == NULL )
+  {
+    home = ".";
+  }
 
-#include "main.h"
+  if (*game_name == 0) /* Game name defaults to argv[0] */
+    loki_setgamename(argv0);
 
-#include "version.h"
+  strcpy(temppath, argv0);  /* If this overflows, it's your own fault :) */
+  if ( ! strrchr(temppath, '/') )
+  {
+    char *path;
+    char *last;
+    int found;
 
-#include "debugging/debugging.h"
+    found = 0;
+    path = getenv("PATH");
+    do
+    {
+      /* Initialize our filename variable */
+      temppath[0] = '\0';
 
-#include "iundo.h"
+      /* Get next entry from path variable */
+      last = strchr(path, ':');
+      if ( ! last )
+        last = path+strlen(path);
 
-#include <gtk/gtkmain.h>
+      /* Perform tilde expansion */
+      if ( *path == '~' )
+      {
+        strcpy(temppath, home);
+        ++path;
+      }
+
+      /* Fill in the rest of the filename */
+      if ( last > (path+1) )
+      {
+        strncat(temppath, path, (last-path));
+        strcat(temppath, "/");
+      }
+      strcat(temppath, "./");
+      strcat(temppath, argv0);
+
+      /* See if it exists, and update path */
+      if ( access(temppath, X_OK) == 0 )
+      {
+        ++found;
+      }
+      path = last+1;
+
+    } while ( *last && !found );
+
+  } else
+  {
+    /* Increment argv0 to the basename */
+    argv0 = strrchr(argv0, '/')+1;
+  }
+
+  /* Now canonicalize it to a full pathname for the data path */
+  if ( realpath(temppath, datapath) )
+  {
+    /* There should always be '/' in the path */
+    *(strrchr(datapath, '/')) = '\0';
+  }
+}
 
-#include "cmdlib.h"
-#include "os/file.h"
-#include "os/path.h"
-#include "stream/stringstream.h"
-#include "stream/textfilestream.h"
+char *loki_getdatapath(void)
+{
+  return(datapath);
+}
 
-#include "gtkutil/messagebox.h"
-#include "gtkutil/image.h"
-#include "console.h"
-#include "texwindow.h"
-#include "map.h"
-#include "mainframe.h"
-#include "commands.h"
-#include "preferences.h"
-#include "environment.h"
-#include "referencecache.h"
-#include "stacktrace.h"
+#endif
 
-void show_splash();
-void hide_splash();
+// end of Loki stuff
+// =============================================================================
 
 void error_redirect (const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
 {
@@ -103,7 +336,7 @@ void error_redirect (const gchar *domain, GLogLevelFlags log_level, const gchar
   log_level = (GLogLevelFlags) (log_level & G_LOG_LEVEL_MASK);
 
   if (!message)
-    message = "(0) message";
+    message = "(NULL) message";
 
   if (domain)
     strcpy (buf, domain);
@@ -182,476 +415,813 @@ void error_redirect (const gchar *domain, GLogLevelFlags log_level, const gchar
     strcat (buf, "\n");
 
   printf ("%s\n", buf);
-
-  ERROR_MESSAGE("GTK+ error: " << buf);
+  Sys_FPrintf (SYS_WRN, buf);
+  // TTimo NOTE: in some cases it may be handy to log only to the file
+//  Sys_FPrintf (SYS_NOCON, buf);
 }
 
-#if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
-#include "crtdbg.h"
+#define GETTEXT_PACKAGE "radiant"
+#define LOCALEDIR "lang"
+
+int main( int argc, char* argv[] ) {
+       char *libgl, *ptr;
+       int i, j, k;
+
+#ifdef _WIN32
+  libgl = "opengl32.dll";
 #endif
 
-void crt_init()
-{
-#if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
-  _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
+#if defined (__linux__)
+  libgl = "libGL.so.1";
 #endif
-}
 
-class Lock
-{
-  bool m_locked;
-public:
-  Lock() : m_locked(false)
-  {
-  }
-  void lock()
-  {
-    m_locked = true;
-  }
-  void unlock()
-  {
-    m_locked = false;
-  }
-  bool locked() const
-  {
-    return m_locked;
-  }
-};
+#ifdef __APPLE__
+  libgl = "/usr/X11R6/lib/libGL.1.dylib";
+#endif
 
-class ScopedLock
-{
-  Lock& m_lock;
-public:
-  ScopedLock(Lock& lock) : m_lock(lock)
-  {
-    m_lock.lock();
+#if defined (__linux__) || defined (__APPLE__)
+  // Give away unnecessary root privileges.
+  // Important: must be done before calling gtk_init().
+  char *loginname;
+  struct passwd *pw;
+  seteuid(getuid());
+  if ( geteuid() == 0 && ( loginname = getlogin() ) != NULL && ( pw = getpwnam(loginname) ) != NULL ) {
+         setuid(pw->pw_uid);
   }
-  ~ScopedLock()
-  {
-    m_lock.unlock();
-  }
-};
+#endif
 
-class LineLimitedTextOutputStream : public TextOutputStream
-{
-  TextOutputStream& outputStream;
-  std::size_t count;
-public:
-  LineLimitedTextOutputStream(TextOutputStream& outputStream, std::size_t count)
-    : outputStream(outputStream), count(count)
-  {
-  }
-  std::size_t write(const char* buffer, std::size_t length)
+
+       bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
+       bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
+       textdomain(GETTEXT_PACKAGE);
+//  gtk_disable_setlocale();
+
+  gtk_init(&argc, &argv);
+
+  if ((ptr = getenv ("Q3R_LIBGL")) != NULL)
+    libgl = ptr;
+
+  for (i = 1; i < argc; i++)
   {
-    if(count != 0)
+    char* param = argv[i];
+
+    if (param[0] == '-' && param[1] == '-')
     {
-      const char* p = buffer;
-      const char* end = buffer+length;
-      for(;;)
+      param += 2;
+
+      if ((strcmp (param, "libgl") == 0) && (i != argc))
       {
-        p = std::find(p, end, '\n');
-        if(p == end)
-        {
-          break;
-        }
-        ++p;
-        if(--count == 0)
-        {
-          length = p - buffer;
-          break;
-        }
+        libgl = argv[i+1];
+        argv[i] = argv[i+1] = NULL;
+        i++;
+      } else if (strcmp (param, "builddefs") == 0)
+      {
+        g_bBuildList = true;
+        argv[i] = NULL;
       }
-      outputStream.write(buffer, length);
     }
-    return length;
   }
-};
 
-class PopupDebugMessageHandler : public DebugMessageHandler
-{
-  StringOutputStream m_buffer;
-  Lock m_lock;
-public:
-  TextOutputStream& getOutputStream()
+  for (i = 1; i < argc; i++)
   {
-    if(!m_lock.locked())
-    {
-      return m_buffer;
-    }
-    return globalErrorStream();
-  }
-  bool handleMessage()
-  {
-    getOutputStream() << "----------------\n";
-    LineLimitedTextOutputStream outputStream(getOutputStream(), 24);
-    write_stack_trace(outputStream);
-    getOutputStream() << "----------------\n";
-    globalErrorStream() << m_buffer.c_str();
-    if(!m_lock.locked())
+    for (k = i; k < argc; k++)
+      if (argv[k] != NULL)
+        break;
+
+    if (k > i)
     {
-      ScopedLock lock(m_lock);
-#if defined _DEBUG
-      m_buffer << "Break into the debugger?\n";
-      bool handled = gtk_MessageBox(0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_YESNO, eMB_ICONERROR) == eIDNO;
-      m_buffer.clear();
-      return handled;
-#else
-      m_buffer << "Please report this error to the developers\n";
-      gtk_MessageBox(0, m_buffer.c_str(), "Radiant - Runtime Error", eMB_OK, eMB_ICONERROR);
-      m_buffer.clear();
-      return true;
-#endif
+      k -= i;
+      for (j = i + k; j < argc; j++)
+        argv[j-k] = argv[j];
+      argc -= k;
     }
-    return false;
   }
-};
 
-typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
+  g_argc = argc;
+  g_argv = argv;
 
-void streams_init()
-{
-  GlobalErrorStream::instance().setOutputStream(getSysPrintErrorStream());
-  GlobalOutputStream::instance().setOutputStream(getSysPrintOutputStream());
-}
+  g_strPluginsDir = "plugins/";
+  g_strModulesDir = "modules/";
 
-void paths_init()
-{
-  const char* home = environment_get_home_path();
-  Q_mkdir(home);
+#ifdef _WIN32
+  // get path to the editor
+  char* pBuffer = g_strAppPath.GetBufferSetLength(_MAX_PATH + 1);
+  GetModuleFileName(NULL, pBuffer, _MAX_PATH);
+  pBuffer[g_strAppPath.ReverseFind('\\') + 1] = '\0';
+  QE_ConvertDOSToUnixName(pBuffer, pBuffer);
+  g_strAppPath.ReleaseBuffer();
 
+  g_strBitmapsPath = g_strAppPath;
+  g_strBitmapsPath += "bitmaps/";
+
+  CGameDialog::UpdateNetrun(false); // read the netrun configuration
+
+  if ( CGameDialog::GetNetrun() ) {
+    // we have to find a per-user g_strTempPath
+    // this behaves the same as on Linux
+    g_strTempPath = getenv("USERPROFILE");
+    if (!g_strTempPath.GetLength())
+    {
+      CString msg;
+      msg = "Radiant is configured to run from a network installation.\n";
+      msg += "I couldn't find the environement variable USERPROFILE\n";
+      msg += "I'm going to use C:\\RadiantSettings. Please set USERPROFILE\n";
+      gtk_MessageBox (NULL, msg, "Radiant - Network mode", MB_OK);
+      g_strTempPath = "C:\\";
+    }
+    g_strTempPath += "\\RadiantSettings\\";
+    Q_mkdir(g_strTempPath.GetBuffer(), 0755);
+    g_strTempPath += RADIANT_VERSION;
+    g_strTempPath += "\\";
+    Q_mkdir(g_strTempPath.GetBuffer(), 0755);
+  }
+  else
   {
-    StringOutputStream path(256);
-    path << home << RADIANT_VERSION << '/';
-    g_strSettingsPath = path.c_str();
+    // use the core path as temp (to save commandlist.txt, and do the .pid files)
+    g_strTempPath = g_strAppPath;
   }
 
-  Q_mkdir(g_strSettingsPath.c_str());
+#endif
+
+#if defined (__linux__) || defined (__APPLE__)
+  Str home;
+  home = g_get_home_dir ();
+  AddSlash (home);
+  home += ".radiant/";
+  Q_mkdir (home.GetBuffer (), 0775);
+  home += RADIANT_VERSION;
+  Q_mkdir (home.GetBuffer (), 0775);
+  g_strTempPath = home.GetBuffer ();
+  AddSlash (g_strTempPath);
+
+  loki_initpaths(argv[0]);
+
+  // NOTE: we build g_strAppPath with a '/' (or '\' on WIN32)
+  // it's a general convention in Radiant to have the slash at the end of directories
+  char real[PATH_MAX];
+  realpath (loki_getdatapath(), real);
+  if (real[strlen(real)-1] != '/')
+    strcat(real, "/");
 
-  g_strAppPath = environment_get_app_path();
+  g_strAppPath = real;
 
   // radiant is installed in the parent dir of "tools/"
   // NOTE: this is not very easy for debugging
   // maybe add options to lookup in several places?
   // (for now I had to create symlinks)
-  {
-    StringOutputStream path(256);
-    path << g_strAppPath.c_str() << "bitmaps/";
-    BitmapsPath_set(path.c_str());
-  }
+  g_strBitmapsPath = g_strAppPath;
+  g_strBitmapsPath += "bitmaps/";
 
   // we will set this right after the game selection is done
   g_strGameToolsPath = g_strAppPath;
-}
-
-bool check_version_file(const char* filename, const char* version)
-{
-  TextFileInputStream file(filename);
-  if(!file.failed())
-  {
-    char buf[10];
-    buf[file.read(buf, 9)] = '\0';
 
-    // chomp it (the hard way)
-    int chomp = 0;
-    while(buf[chomp] >= '0' && buf[chomp] <= '9')
-      chomp++;
-    buf[chomp] = '\0';
-
-    return string_equal(buf, version);
-  }
-  return false;
-}
-
-bool check_version()
-{
-  // a safe check to avoid people running broken installations
-  // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
-  // make something idiot proof and someone will make better idiots, this may be overkill
-  // let's leave it disabled in debug mode in any case
-  // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
-#ifndef _DEBUG
-#define CHECK_VERSION
 #endif
-#ifdef CHECK_VERSION  
-  // locate and open RADIANT_MAJOR and RADIANT_MINOR
-  bool bVerIsGood = true;
-  {
-    StringOutputStream ver_file_name(256);
-    ver_file_name << AppPath_get() << "RADIANT_MAJOR";
-    bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MAJOR_VERSION);
-  }
-  {
-    StringOutputStream ver_file_name(256);
-    ver_file_name << AppPath_get() << "RADIANT_MINOR";
-    bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MINOR_VERSION);
-  }
 
-  if (!bVerIsGood)
-  {
-    StringOutputStream msg(256);
-    msg << "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n"
-    "Make sure you run the right/latest editor binary you installed\n"
-    << AppPath_get();
-    gtk_MessageBox(0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONDEFAULT);
-  }
-  return bVerIsGood;
-#else
-  return true;
-#endif
-}
+  // init the DTD path
+  g_strDTDPath = g_strAppPath;
+  g_strDTDPath += "dtds/";
 
-void create_global_pid()
-{
   /*!
   the global prefs loading / game selection dialog might fail for any reason we don't know about
   we need to catch when it happens, to cleanup the stateful prefs which might be killing it
   and to turn on console logging for lookup of the problem
   this is the first part of the two step .pid system
-  http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
   */
-  StringOutputStream g_pidFile(256); ///< the global .pid file (only for global part of the startup)
-
-  g_pidFile << SettingsPath_get() << "radiant.pid";
+  g_pidFile = g_strTempPath.GetBuffer ();
+  g_pidFile += "radiant.pid";
 
   FILE *pid;
-  pid = fopen (g_pidFile.c_str(), "r");
-  if (pid != 0)
-  {
+  pid = fopen( g_pidFile.GetBuffer(), "r" );
+  if ( pid != NULL ) {
     fclose (pid);
+    CString msg;
 
-    if (remove (g_pidFile.c_str()) == -1)
+    if (remove (g_pidFile.GetBuffer ()) == -1)
     {
-      StringOutputStream msg(256);
-      msg << "WARNING: Could not delete " << g_pidFile.c_str();
-      gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
+      msg = "WARNING: Could not delete "; msg += g_pidFile;
+      gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
     }
 
     // in debug, never prompt to clean registry, turn console logging auto after a failed start
 #if !defined(_DEBUG)
-    StringOutputStream msg(256);
-    msg << "Radiant failed to start properly the last time it was run.\n"
-           "The failure may be related to current global preferences.\n"
-           "Do you want to reset global preferences to defaults?";
+    msg = "Found the file ";
+    msg += g_pidFile;
+    msg += ".\nThis indicates that Radiant failed during the game selection startup last time it was run.\n"
+           "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"
+           "WARNING: the global prefs will be lost if you choose YES.";
 
-    if (gtk_MessageBox (0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION) == eIDYES)
+    if (gtk_MessageBox (NULL, msg, "Radiant - Reset global startup?", MB_YESNO | MB_ICONQUESTION) == IDYES)
     {
-      g_GamesDialog.Reset();
+      // remove global prefs and shutdown
+      g_PrefsDlg.mGamesDialog.Reset();
+      // remove the prefs file (like a full reset of the registry)
+      //remove (g_PrefsDlg.m_inipath->str);
+      gtk_MessageBox(NULL, "Removed global settings, choose OK to close Radiant.", "Radiant", MB_OK );
+      _exit(-1);
     }
+    msg = "Logging console output to ";
+    msg += g_strTempPath;
+    msg += "radiant.log\nRefer to the log if Radiant fails to start again.";
 
-    msg.clear();
-    msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
-
-    gtk_MessageBox (0, msg.c_str(), "Radiant - Console Log", eMB_OK);
+    gtk_MessageBox (NULL, msg, "Radiant - Console Log", MB_OK);
 #endif
 
     // set without saving, the class is not in a coherent state yet
     // just do the value change and call to start logging, CGamesDialog will pickup when relevant
-    g_GamesDialog.m_bForceLogConsole = true;
-    Sys_LogFile(true);
+    g_PrefsDlg.mGamesDialog.m_bLogConsole = true;
+    g_PrefsDlg.mGamesDialog.m_bForceLogConsole = true;
+    Sys_LogFile();
   }
 
   // create a primary .pid for global init run
-  pid = fopen (g_pidFile.c_str(), "w");
-  if (pid)
-    fclose (pid);
-}
+  pid = fopen( g_pidFile.GetBuffer(), "w" );
+  if ( pid ) {
+         fclose( pid );
+  }
 
-void remove_global_pid()
-{
-  StringOutputStream g_pidFile(256);
-  g_pidFile << SettingsPath_get() << "radiant.pid";
+  // a safe check to avoid people running broken installations
+  // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
+  // make something idiot proof and someone will make better idiots, this may be overkill
+  // let's leave it disabled in debug mode in any case
+#ifndef _DEBUG
+  //#define CHECK_VERSION
+#endif
+#ifdef CHECK_VERSION
+  // locate and open RADIANT_MAJOR and RADIANT_MINOR
+  qboolean bVerIsGood = true;
+  Str ver_file_name;
+  ver_file_name = g_strAppPath;
+  ver_file_name += "RADIANT_MAJOR";
+  FILE *ver_file = fopen (ver_file_name.GetBuffer(), "r");
+  if (ver_file)
+  {
+    char buf[10];
+    int chomp;
+    fread(buf, 1, 10, ver_file);
+    // chomp it (the hard way)
+    chomp = 0;
+    while(buf[chomp] >= '0' && buf[chomp] <= '9')
+      chomp++;
+    buf[chomp] = '\0';
+    if (strcmp(buf, RADIANT_MAJOR_VERSION))
+    {
+      Sys_Printf("ERROR: file RADIANT_MAJOR doesn't match ('%s')\n", buf);
+      bVerIsGood = false;
+    }
+  }
+  else
+  {
+    Sys_Printf("ERROR: can't find RADIANT_MAJOR in '%s'\n", ver_file_name.GetBuffer());
+    bVerIsGood = false;
+  }
+  ver_file_name = g_strAppPath;
+  ver_file_name += "RADIANT_MINOR";
+  ver_file = fopen (ver_file_name.GetBuffer(), "r");
+  if (ver_file)
+  {
+    char buf[10];
+    int chomp;
+    fread(buf, 1, 10, ver_file);
+    // chomp it (the hard way)
+    chomp = 0;
+    while(buf[chomp] >= '0' && buf[chomp] <= '9')
+      chomp++;
+    buf[chomp] = '\0';
+    if (strcmp(buf, RADIANT_MINOR_VERSION))
+    {
+      Sys_Printf("ERROR: file RADIANT_MINOR doesn't match ('%s')\n", buf);
+      bVerIsGood = false;
+    }
+  }
+  else
+  {
+    Sys_Printf("ERROR: can't find RADIANT_MINOR in '%s'\n", ver_file_name.GetBuffer());
+    bVerIsGood = false;
+  }
+  if (!bVerIsGood)
+  {
+    CString msg;
+    msg = "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n";
+    msg += "Make sure you run the right/latest editor binary you installed\n";
+    msg += g_strAppPath; msg += "\n";
+    msg += "Check http://www.qeradiant.com/faq/index.cgi?file=219 for more information";
+    gtk_MessageBox(NULL, msg.GetBuffer(), "Radiant", MB_OK, "http://www.qeradiant.com/faq/index.cgi?file=219");
+    _exit(-1);
+  }
+#endif
+
+  g_qeglobals.disable_ini = false;
+  g_PrefsDlg.Init();
 
   // close the primary
-  if (remove (g_pidFile.c_str()) == -1)
-  {
-    StringOutputStream msg(256);
-    msg << "WARNING: Could not delete " << g_pidFile.c_str();
-    gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
+  if ( remove( g_pidFile.GetBuffer () ) == -1 ) {
+    CString msg;
+    msg = "WARNING: Could not delete "; msg += g_pidGameFile;
+    gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
   }
-}
 
-/*!
-now the secondary game dependant .pid file
-http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
-*/
-void create_local_pid()
-{
-  StringOutputStream g_pidGameFile(256); ///< the game-specific .pid file
-  g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
+  /*!
+  now the secondary game dependant .pid file
+  */
+  g_pidGameFile = g_PrefsDlg.m_rc_path->str;
+  g_pidGameFile += "radiant-game.pid";
 
-  FILE *pid = fopen (g_pidGameFile.c_str(), "r");
-  if (pid != 0)
+  pid = fopen (g_pidGameFile.GetBuffer(), "r");
+  if (pid != NULL)
   {
     fclose (pid);
-    if (remove (g_pidGameFile.c_str()) == -1)
+    CString msg;
+    if (remove (g_pidGameFile.GetBuffer ()) == -1)
     {
-      StringOutputStream msg;
-      msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
-      gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
+      msg = "WARNING: Could not delete "; msg += g_pidGameFile;
+      gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
     }
 
+    msg = "Found the file ";
+    msg += g_pidGameFile;
+    msg += ".\nThis indicates that Radiant failed to load the last time it was run.\n"
+           "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"
+           "WARNING: preferences will be lost if you choose YES.";
+
     // in debug, never prompt to clean registry, turn console logging auto after a failed start
 #if !defined(_DEBUG)
-    StringOutputStream msg;
-    msg << "Radiant failed to start properly the last time it was run.\n"
-           "The failure may be caused by current preferences.\n"
-           "Do you want to reset all preferences to defaults?";
-
-    if (gtk_MessageBox (0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION) == eIDYES)
+    //bleh
+    if (gtk_MessageBox (NULL, msg, "Radiant - Clean Registry?", MB_YESNO | MB_ICONQUESTION) == IDYES)
     {
-      Preferences_Reset();
+      // remove the game prefs files
+      remove (g_PrefsDlg.m_inipath->str);
+      char buf[PATH_MAX];
+      sprintf(buf, "%sSavedInfo.bin", g_PrefsDlg.m_rc_path->str);
+      remove(buf);
+      // remove the global pref too
+      g_PrefsDlg.mGamesDialog.Reset();
+      gtk_MessageBox(NULL, "Cleaned registry settings, choose OK to close Radiant.\nThe next time Radiant runs it will use default settings.", "Radiant", MB_OK );
+      _exit(-1);
     }
+    msg = "Logging console output to ";
+    msg += g_strTempPath;
+    msg += "radiant.log\nRefer to the log if Radiant fails to start again.";
 
-    msg.clear();
-    msg << "Logging console output to " << SettingsPath_get() << "radiant.log\nRefer to the log if Radiant fails to start again.";
-
-    gtk_MessageBox (0, msg.c_str(), "Radiant - Console Log", eMB_OK);
+    gtk_MessageBox (NULL, msg, "Radiant - Console Log", MB_OK);
 #endif
 
     // force console logging on! (will go in prefs too)
-    g_GamesDialog.m_bForceLogConsole = true;
-    Sys_LogFile(true);
-  }
-  else
+    g_PrefsDlg.mGamesDialog.m_bLogConsole = true;
+    g_PrefsDlg.mGamesDialog.SavePrefs();
+    Sys_LogFile();
+
+    g_PrefsDlg.LoadPrefs();
+
+  } else
   {
     // create one, will remove right after entering message loop
-    pid = fopen (g_pidGameFile.c_str(), "w");
+    pid = fopen (g_pidGameFile.GetBuffer(), "w");
     if (pid)
       fclose (pid);
-  }
-}
 
+    g_PrefsDlg.LoadPrefs();
 
-/*!
-now the secondary game dependant .pid file
-http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
-*/
-void remove_local_pid()
-{
-  StringOutputStream g_pidGameFile(256);
-  g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
-  remove(g_pidGameFile.c_str());
-}
+#ifndef _DEBUG // I can't be arsed about that prompt in debug mode
+    // if console logging is on in the prefs, warn about performance hit
+    if (g_PrefsDlg.mGamesDialog.m_bLogConsole)
+    {
+      if (gtk_MessageBox (NULL, "Preferences indicate that console logging is on. This affects performance.\n"
+                          "Turn it off?", "Radiant", MB_YESNO | MB_ICONQUESTION) == IDYES)
+      {
+        g_PrefsDlg.mGamesDialog.m_bLogConsole = false;
+        g_PrefsDlg.mGamesDialog.SavePrefs();
+      }
+    }
+#endif
+    // toggle console logging if necessary
+    Sys_LogFile();
+  }
 
-void user_shortcuts_init()
-{
-  StringOutputStream path(256);
-  path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
-  LoadCommandMap(path.c_str());
-  SaveCommandMap(path.c_str());
-}
+  // we should search in g_strTempPath, then move over to look at g_strAppPath?
+#ifdef _WIN32
+  // fine tune the look of the app using a gtk rc file
+  // we try to load an RC file placed in the application directory
+  // build the full path
+  Str sRCFile;
+  sRCFile = g_strAppPath;
+  sRCFile += "radiantgtkrc";
+  // we load that file with g_new in gtk_rc_parse_file (gtkrc.c), change the '/' into '\\'
+  pBuffer = (char *)sRCFile.GetBuffer();
+  for (i=0; i<sRCFile.GetLength(); i++)
+  {
+    if (pBuffer[i]=='/')
+    {
+      pBuffer[i] = '\\';
+    }
+  }
+  // check the file exists
+  if (access(sRCFile.GetBuffer(), R_OK) != 0)
+    Sys_Printf("RC file %s not found\n", sRCFile.GetBuffer());
+  else
+  {
+    Sys_Printf ("Attemping to load RC file %s\n", sRCFile.GetBuffer());
+    gtk_rc_parse (sRCFile.GetBuffer());
+  }
+#endif
 
-int main (int argc, char* argv[])
-{
-  crt_init();
+#ifndef SKIP_SPLASH
+  create_splash();
+#endif
 
-  streams_init();
+  if (!QGL_Init(libgl, ""))
+  {
+    Sys_FPrintf (SYS_ERR, "Failed to load OpenGL libraries\n");
+    _exit (1);
+    return 1;
+  }
 
-  gtk_disable_setlocale();
-  gtk_init(&argc, &argv);
+#if defined (__linux__) || defined (__APPLE__)
+  if ((qglXQueryExtension == NULL) || (qglXQueryExtension(GDK_DISPLAY(),NULL,NULL) != True))
+  {
+    Sys_FPrintf (SYS_ERR, "glXQueryExtension failed\n");
+    _exit (1);
+    return 1;
+  }
+#endif
 
   // redirect Gtk warnings to the console
-  g_log_set_handler ("Gdk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
-                                             G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
-  g_log_set_handler ("Gtk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
-                                             G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
-  g_log_set_handler ("GtkGLExt", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
-                                             G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
-  g_log_set_handler ("GLib", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
-                                             G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
-  g_log_set_handler (0, (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
-                                             G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, 0);
+  g_log_set_handler( "Gdk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
+                                             G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, NULL);
+  g_log_set_handler( "Gtk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
+                                             G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, NULL);
 
-  GlobalDebugMessageHandler::instance().setHandler(GlobalPopupDebugMessageHandler::instance());
+  // spog - creates new filters list for the first time
+  g_qeglobals.d_savedinfo.filters = NULL;
+  g_qeglobals.d_savedinfo.filters = FilterUpdate(g_qeglobals.d_savedinfo.filters);
 
-  environment_init(argc, argv);
-
-  paths_init();
+  g_pParentWnd = new MainFrame();
 
-  if(!check_version())
-  {
-    return EXIT_FAILURE;
+  if ( g_PrefsDlg.m_bLoadLastMap && g_PrefsDlg.m_strLastMap.GetLength() > 0 ) {
+         Map_LoadFile(g_PrefsDlg.m_strLastMap.GetBuffer());
+  } else {
+         Map_New();
   }
 
-  show_splash();
+  // load up shaders now that we have the map loaded
+  // eviltypeguy
+  Texture_ShowStartupShaders();
 
-  create_global_pid();
+#ifndef SKIP_SPLASH
+  gdk_window_raise(splash_screen->window);
+  gtk_window_set_transient_for( GTK_WINDOW( splash_screen ), GTK_WINDOW( g_pParentWnd->m_pWidget ) );
+  gtk_timeout_add( 1000, try_destroy_splash, NULL );
+#endif
 
-  GlobalPreferences_Init();
+  g_pParentWnd->GetSynapseServer().DumpActiveClients();
 
-  g_GamesDialog.Init();
+  //++timo: temporary debug
+  g_pParentWnd->DoWatchBSP();
 
-  g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
-  
-  remove_global_pid();
+  gtk_main();
 
-  g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
+  // close the log file if any
+  // NOTE: don't save prefs past this point!
+  g_PrefsDlg.mGamesDialog.m_bLogConsole = false;
+  // set the console window to NULL to avoid Sys_Printf crashing
+  g_qeglobals_gui.d_edit = NULL;
+  Sys_LogFile();
+
+  // NOTE TTimo not sure what this _exit(0) call is worth
+  //   restricting it to linux build
+#ifdef __linux__
+  _exit( 0 );
+#endif
+  return 0;
+}
 
-  create_local_pid();
+// ydnar: quick and dirty fix, just make the buffer bigger
+#define BIG_PATH_MAX   4096
 
-  // in a very particular post-.pid startup
-  // we may have the console turned on and want to keep it that way
-  // so we use a latching system
-  if (g_GamesDialog.m_bForceLogConsole)
+// TTimo: decompose the BSP command into several steps so we can monitor them eventually
+void QE_ExpandBspString (char *bspaction, GPtrArray *out_array, char *mapname)
+{
+  const char  *in;
+  char  *out;
+  char  src[BIG_PATH_MAX];
+  char  rsh[BIG_PATH_MAX];
+  char  base[BIG_PATH_MAX];
+
+  strcpy(src, mapname);
+  strlwr(src);
+  in = strstr(src, "maps/");
+  if (!in)
   {
-    Sys_LogFile(true);
-    g_Console_enableLogging = true;
-    g_GamesDialog.m_bForceLogConsole = false;
+    in = strstr(src, "maps/");
+  }
+  if (in)
+  {
+    in += 5;
+    strcpy(base, in);
+    out = base;
+    while (*out)
+    {
+      if (*out == '\\')
+      {
+        *out = '/';
+      }
+      out++;
+    }
+  } else
+  {
+    ExtractFileName (mapname, base);
   }
 
+  // this important step alters the map name to add fs_game
+  // NOTE: it used to add fs_basepath too
+  // the fs_basepath addition moved to being in the project file during the bug fixing rush
+  // but it may not have been the right thing to do
 
-  Radiant_Initialise();
+  // HACK: halflife compiler tools don't support -fs_game
+  // HACK: neither does JKII/SoF2/ etc..
+  // do we use & have fs_game?
 
-  global_accel_init();
+  if (g_pGameDescription->mGameFile != "hl.game" &&
+      *ValueForKey(g_qeglobals.d_project_entity,"gamename") != '\0')
+    {
+      // set with fs_game
+      sprintf(src, "-fs_game %s \"%s\"", ValueForKey(g_qeglobals.d_project_entity,"gamename"), mapname);
+    }
+    else
+    {
+      sprintf(src, "\"%s\"", mapname);
+  }
 
-  user_shortcuts_init();
+  rsh[0] = 0;
 
-  g_pParentWnd = 0;
-  g_pParentWnd = new MainFrame();
+  QE_ConvertDOSToUnixName(src, src);
 
-  hide_splash();
+  // initialise the first step
+  out = new char[BIG_PATH_MAX];        //% PATH_MAX
+  g_ptr_array_add( out_array, out );
 
-  if (g_bLoadLastMap && !g_strLastMap.empty())
+  in = ValueForKey( g_qeglobals.d_project_entity, bspaction );
+  while (*in)
   {
-    Map_LoadFile(g_strLastMap.c_str());
+    if (in[0] == '!')
+    {
+      strcpy (out, rsh);
+      out += strlen(rsh);
+      in++;
+      continue;
+    }
+    if (in[0] == '#')
+    {
+      char tmp[2048];
+      // we process these only if monitoring
+      if (g_PrefsDlg.m_bWatchBSP)
+      {
+        // -connect global option (the only global option so far anyway)
+        strcpy (tmp, " -connect 127.0.0.1:39000 ");
+        strcpy (out, tmp);
+        out += strlen(tmp);
+      }
+      in++;
+      continue;
+    }
+    if (in[0] == '$')
+    {
+      // $ expansion
+      strcpy (out, src);
+      out += strlen(src);
+      in++;
+      continue;
+    }
+    if (in[0] == '@')
+    {
+      *out++ = '"';
+      in++;
+      continue;
+    }
+    if (in[0] == '&')
+      if (in[1] == '&')
+      {
+        // start a new step
+        *out = 0;
+        in = in + 2;
+        out = new char[BIG_PATH_MAX];  //% PATH_MAX
+        g_ptr_array_add( out_array, out );
+      }
+    *out++ = *in++;
   }
-  else
+  *out = 0;
+}
+
+void FindReplace(CString& strContents, const char* pTag, const char* pValue)
+{
+  if (strcmp(pTag, pValue) == 0)
+    return;
+  for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag))
   {
-    Map_New();
+    int nRightLen = strContents.GetLength() - strlen(pTag) - nPos;
+    CString strLeft = strContents.Left(nPos);
+    CString strRight = strContents.Right(nRightLen);
+    strLeft += pValue;
+    strLeft += strRight;
+    strContents = strLeft;
   }
+}
 
-  // load up shaders now that we have the map loaded
-  // eviltypeguy
-  TextureBrowser_ShowStartupShaders(GlobalTextureBrowser());
+// save the map, deals with regioning
+void SaveWithRegion(char *name)
+{
+  strcpy (name, currentmap);
+  if (region_active)
+  {
+    // temporary cut the region to save regular map
+    region_active = false;
+    Map_SaveFile (name, false);
+    region_active = true;
+    StripExtension (name);
+    strcat (name, ".reg");
+  }
 
+  Map_SaveFile (name, region_active);
+}
 
-  remove_local_pid();
+void RunBsp (char *command)
+{
+  GPtrArray *sys;
+  char  batpath[BIG_PATH_MAX]; //% PATH_MAX
+  char  temppath[BIG_PATH_MAX];        //% PATH_MAX
+  char  name[BIG_PATH_MAX];            //% PATH_MAX
+  char  cWork[BIG_PATH_MAX];   //% PATH_MAX
+  FILE  *hFile;
+  unsigned int   i;
 
-  gtk_main();
+  SetInspectorMode(W_CONSOLE);
+
+  strcpy (temppath, g_strTempPath.GetBuffer ());
+
+  SaveWithRegion(name);
+
+  const char *rsh = ValueForKey(g_qeglobals.d_project_entity, "rshcmd");
+  if (rsh == NULL)
+  {
+    CString strPath, strFile;
 
-  // avoid saving prefs when the app is minimized
-  if (g_pParentWnd->IsSleeping())
+    ExtractPath_and_Filename(name, strPath, strFile);
+    AddSlash(strPath);
+    strncpy(cWork, strPath, 1024);
+    strcat(cWork, strFile);
+  } else
   {
-    globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
-    g_preferences_globals.disable_ini = true;
+    strcpy(cWork, name);
   }
 
-  Map_Free();
+  // get the array ready
+  //++timo TODO: free the array, free the strings ourselves with delete[]
+  sys = g_ptr_array_new();
 
-  if (!Map_Unnamed(g_map))
+  QE_ExpandBspString (command, sys, cWork);
+
+  if (g_PrefsDlg.m_bWatchBSP)
+  {
+    // grab the file name for engine running
+    char *bspname = new char[1024];
+    ExtractFileName( currentmap, bspname );
+    StripExtension( bspname );
+    g_pParentWnd->GetWatchBSP()->DoMonitoringLoop( sys, bspname );
+  } else
   {
-    g_strLastMap = Map_Name(g_map);
+    // write all the steps in a single BAT / .sh file and run it, don't bother monitoring it
+    CString strSys;
+    for (i=0; i < sys->len; i++ )
+    {
+      strSys += (char *)g_ptr_array_index( sys, i);
+#ifdef _WIN32  // write temp\junk.txt in win32... NOTE: stops output to shell prompt window
+      if (i==0)
+        strSys += " >";
+      else
+        strSys += " >>";
+      strSys += "\"";
+      strSys += temppath;
+      strSys += "junk.txt\"";
+#endif
+      strSys += "\n";
+    };
+
+#if defined (__linux__) || defined (__APPLE__)
+
+    // write qe3bsp.sh
+    sprintf (batpath, "%sqe3bsp.sh", temppath);
+    Sys_Printf("Writing the compile script to '%s'\n", batpath);
+    Sys_Printf("The build output will be saved in '%sjunk.txt'\n", temppath);
+    hFile = fopen(batpath, "w");
+    if (!hFile)
+      Error ("Can't write to %s", batpath);
+    fprintf (hFile, "#!/bin/sh \n\n");
+    fprintf (hFile, strSys.GetBuffer());
+    fclose (hFile);
+    chmod (batpath, 0744);
+#endif
+
+#ifdef _WIN32
+    sprintf (batpath, "%sqe3bsp.bat", temppath);
+    Sys_Printf("Writing the compile script to '%s'\n", batpath);
+    Sys_Printf("The build output will be saved in '%sjunk.txt'\n", temppath);
+    hFile = fopen(batpath, "w");
+    if (!hFile)
+      Error ("Can't write to %s", batpath);
+    fprintf (hFile, strSys.GetBuffer());
+    fclose (hFile);
+#endif
+
+    Pointfile_Delete ();
+
+#if defined (__linux__) || defined (__APPLE__)
+
+    pid_t pid;
+
+    pid = fork ();
+    switch (pid)
+    {
+    case -1:
+      Error ("CreateProcess failed");
+      break;
+    case 0:
+      execlp (batpath, batpath, NULL);
+      printf ("execlp error !");
+      _exit (0);
+      break;
+    default:
+      break;
+    }
+#endif
+
+#ifdef _WIN32
+    Sys_Printf ("Running bsp command...\n");
+    Sys_Printf ("\n%s\n", strSys.GetBuffer());
+
+    WinExec( batpath, SW_SHOWNORMAL );
+#endif
   }
+#ifdef _DEBUG
+  // yeah, do it .. but not now right before 1.1-TA-beta release
+  Sys_Printf("TODO: erase GPtrArray\n");
+#endif
+}
 
-  delete g_pParentWnd;
+#if 0
 
-  global_accel_destroy();
+#ifdef _WIN32
 
-  Radiant_Shutdown();
+int WINAPI QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer )
+{
+  static PIXELFORMATDESCRIPTOR pfd = {
+    sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
+    1,                              // version number
+    PFD_DRAW_TO_WINDOW |            // support window
+    PFD_SUPPORT_OPENGL |            // support OpenGL
+    PFD_DOUBLEBUFFER,               // double buffered
+    PFD_TYPE_RGBA,                  // RGBA type
+    24,                             // 24-bit color depth
+    0, 0, 0, 0, 0, 0,               // color bits ignored
+    0,                              // no alpha buffer
+    0,                              // shift bit ignored
+    0,                              // no accumulation buffer
+    0, 0, 0, 0,                     // accum bits ignored
+    32,                             // depth bits
+    0,                              // no stencil buffer
+    0,                              // no auxiliary buffer
+    PFD_MAIN_PLANE,                 // main layer
+    0,                              // reserved
+    0, 0, 0                         // layer masks ignored
+  };                              //
+  int pixelformat = 0;
+
+  zbuffer = true;
+  if ( !zbuffer )
+    pfd.cDepthBits = 0;
+
+  if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
+  {
+    LPVOID lpMsgBuf;
+    FormatMessage(
+                 FORMAT_MESSAGE_ALLOCATE_BUFFER |
+                 FORMAT_MESSAGE_FROM_SYSTEM |
+                 FORMAT_MESSAGE_IGNORE_INSERTS,
+                 NULL,
+                 GetLastError(),
+                 MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
+                 (LPTSTR) &lpMsgBuf,
+                 0,
+                 NULL
+                 );
+    Sys_FPrintf (SYS_WRN, "GetLastError: %s", lpMsgBuf);
+    Error ("ChoosePixelFormat failed");
+  }
 
-  // close the log file if any
-  Sys_LogFile(false);
+  if (!SetPixelFormat(hDC, pixelformat, &pfd))
+    Error ("SetPixelFormat failed");
 
-  return EXIT_SUCCESS;
+  return pixelformat;
 }
 
+#endif
+
+#endif