]> de.git.xonotic.org Git - voretournament/voretournament.git/blobdiff - misc/mediasource/netradiant-src/radiant/main.cpp
Move the netradiant and fteqcc sources
[voretournament/voretournament.git] / misc / mediasource / netradiant-src / radiant / main.cpp
diff --git a/misc/mediasource/netradiant-src/radiant/main.cpp b/misc/mediasource/netradiant-src/radiant/main.cpp
new file mode 100644 (file)
index 0000000..a7c2242
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+Copyright (C) 1999-2006 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
+*/
+
+/*! \mainpage GtkRadiant Documentation Index
+
+\section intro_sec Introduction
+
+This documentation is generated from comments in the source code.
+
+\section links_sec Useful Links
+
+\link include/itextstream.h include/itextstream.h \endlink - Global output and error message streams, similar to std::cout and std::cerr. \n
+
+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
+
+\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
+
+::CopiedString - automatic string memory management \n
+Array - automatic array memory management \n
+HashTable - generic hashtable, similar to std::hash_map \n
+
+\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
+
+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
+
+\link generic/bitfield.h generic/bitfield.h \endlink - Type-safe bitfield \n
+\link generic/enumeration.h generic/enumeration.h \endlink - Type-safe enumeration \n
+
+DefaultAllocator - Memory allocation using new/delete, compliant with std::allocator interface \n
+
+\link debugging/debugging.h debugging/debugging.h \endlink - Debugging macros \n
+
+*/
+
+#include "main.h"
+
+#include "version.h"
+
+#include "debugging/debugging.h"
+
+#include "iundo.h"
+
+#include <gtk/gtkmain.h>
+
+#include "cmdlib.h"
+#include "os/file.h"
+#include "os/path.h"
+#include "stream/stringstream.h"
+#include "stream/textfilestream.h"
+
+#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"
+
+#ifdef WIN32
+#include <windows.h>
+#endif
+
+void show_splash();
+void hide_splash();
+
+void error_redirect (const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
+{
+  gboolean in_recursion;
+  gboolean is_fatal;
+  char buf[256];
+
+  in_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0;
+  is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
+  log_level = (GLogLevelFlags) (log_level & G_LOG_LEVEL_MASK);
+
+  if (!message)
+    message = "(0) message";
+
+  if (domain)
+    strcpy (buf, domain);
+  else
+    strcpy (buf, "**");
+  strcat (buf, "-");
+
+  switch (log_level)
+  {
+  case G_LOG_LEVEL_ERROR:
+    if (in_recursion)
+      strcat (buf, "ERROR (recursed) **: ");
+    else
+      strcat (buf, "ERROR **: ");
+    break;
+  case G_LOG_LEVEL_CRITICAL:
+    if (in_recursion)
+      strcat (buf, "CRITICAL (recursed) **: ");
+    else
+      strcat (buf, "CRITICAL **: ");
+    break;
+  case G_LOG_LEVEL_WARNING:
+    if (in_recursion)
+      strcat (buf, "WARNING (recursed) **: ");
+    else
+      strcat (buf, "WARNING **: ");
+    break;
+  case G_LOG_LEVEL_MESSAGE:
+    if (in_recursion)
+      strcat (buf, "Message (recursed): ");
+    else
+      strcat (buf, "Message: ");
+    break;
+  case G_LOG_LEVEL_INFO:
+    if (in_recursion)
+      strcat (buf, "INFO (recursed): ");
+    else
+      strcat (buf, "INFO: ");
+    break;
+  case G_LOG_LEVEL_DEBUG:
+    if (in_recursion)
+      strcat (buf, "DEBUG (recursed): ");
+    else
+      strcat (buf, "DEBUG: ");
+    break;
+  default:
+    /* we are used for a log level that is not defined by GLib itself,
+     * try to make the best out of it.
+     */
+    if (in_recursion)
+      strcat (buf, "LOG (recursed:");
+    else
+      strcat (buf, "LOG (");
+    if (log_level)
+    {
+      gchar string[] = "0x00): ";
+      gchar *p = string + 2;
+      guint i;
+
+      i = g_bit_nth_msf (log_level, -1);
+      *p = i >> 4;
+      p++;
+      *p = '0' + (i & 0xf);
+      if (*p > '9')
+        *p += 'A' - '9' - 1;
+
+      strcat (buf, string);
+    } else
+      strcat (buf, "): ");
+  }
+
+  strcat (buf, message);
+  if (is_fatal)
+    strcat (buf, "\naborting...\n");
+  else
+    strcat (buf, "\n");
+
+  // spam it...
+  globalErrorStream() << buf << "\n";
+
+  // FIXME why are warnings is_fatal?
+#ifndef _DEBUG
+  if(is_fatal)
+#endif
+  ERROR_MESSAGE("GTK+ error: " << buf);
+}
+
+#if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
+#include "crtdbg.h"
+#endif
+
+void crt_init()
+{
+#if defined (_DEBUG) && defined (WIN32) && defined (_MSC_VER)
+  _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
+#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;
+  }
+};
+
+class ScopedLock
+{
+  Lock& m_lock;
+public:
+  ScopedLock(Lock& lock) : m_lock(lock)
+  {
+    m_lock.lock();
+  }
+  ~ScopedLock()
+  {
+    m_lock.unlock();
+  }
+};
+
+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)
+  {
+    if(count != 0)
+    {
+      const char* p = buffer;
+      const char* end = buffer+length;
+      for(;;)
+      {
+        p = std::find(p, end, '\n');
+        if(p == end)
+        {
+          break;
+        }
+        ++p;
+        if(--count == 0)
+        {
+          length = p - buffer;
+          break;
+        }
+      }
+      outputStream.write(buffer, length);
+    }
+    return length;
+  }
+};
+
+class PopupDebugMessageHandler : public DebugMessageHandler
+{
+  StringOutputStream m_buffer;
+  Lock m_lock;
+public:
+  TextOutputStream& getOutputStream()
+  {
+    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())
+    {
+      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();
+#endif
+    }
+    return true;
+  }
+};
+
+typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
+
+void streams_init()
+{
+  GlobalErrorStream::instance().setOutputStream(getSysPrintErrorStream());
+  GlobalOutputStream::instance().setOutputStream(getSysPrintOutputStream());
+}
+
+void paths_init()
+{
+  const char* home = environment_get_home_path();
+  Q_mkdir(home);
+
+  {
+    StringOutputStream path(256);
+    path << home << "1." << RADIANT_MAJOR_VERSION "." << RADIANT_MINOR_VERSION << '/';
+    g_strSettingsPath = path.c_str();
+  }
+
+  Q_mkdir(g_strSettingsPath.c_str());
+
+  g_strAppPath = environment_get_app_path();
+
+  // 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());
+  }
+
+  // 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
+}
+
+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";
+
+  FILE *pid;
+  pid = fopen (g_pidFile.c_str(), "r");
+  if (pid != 0)
+  {
+    fclose (pid);
+
+    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 );
+    }
+
+    // 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?";
+
+    if (gtk_MessageBox (0, msg.c_str(), "Radiant - Startup Failure", eMB_YESNO, eMB_ICONQUESTION) == eIDYES)
+    {
+      g_GamesDialog.Reset();
+    }
+
+    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);
+#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);
+  }
+
+  // create a primary .pid for global init run
+  pid = fopen (g_pidFile.c_str(), "w");
+  if (pid)
+    fclose (pid);
+}
+
+void remove_global_pid()
+{
+  StringOutputStream g_pidFile(256);
+  g_pidFile << SettingsPath_get() << "radiant.pid";
+
+  // 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 );
+  }
+}
+
+/*!
+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";
+
+  FILE *pid = fopen (g_pidGameFile.c_str(), "r");
+  if (pid != 0)
+  {
+    fclose (pid);
+    if (remove (g_pidGameFile.c_str()) == -1)
+    {
+      StringOutputStream msg;
+      msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
+      gtk_MessageBox (0, msg.c_str(), "Radiant", eMB_OK, eMB_ICONERROR );
+    }
+
+    // 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)
+    {
+      Preferences_Reset();
+    }
+
+    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);
+#endif
+
+    // force console logging on! (will go in prefs too)
+    g_GamesDialog.m_bForceLogConsole = true;
+    Sys_LogFile(true);
+  }
+  else
+  {
+    // create one, will remove right after entering message loop
+    pid = fopen (g_pidGameFile.c_str(), "w");
+    if (pid)
+      fclose (pid);
+  }
+}
+
+
+/*!
+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());
+}
+
+void user_shortcuts_init()
+{
+  StringOutputStream path(256);
+  path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
+  LoadCommandMap(path.c_str());
+  SaveCommandMap(path.c_str());
+}
+
+void user_shortcuts_save()
+{
+  StringOutputStream path(256);
+  path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
+  SaveCommandMap(path.c_str());
+}
+
+int main (int argc, char* argv[])
+{
+  crt_init();
+
+  streams_init();
+
+#ifdef WIN32
+  HMODULE lib;
+  lib = LoadLibrary("dwmapi.dll");
+  if(lib != 0)
+  {
+         void (WINAPI *qDwmEnableComposition) (bool bEnable) = (void (WINAPI *) (bool bEnable)) GetProcAddress(lib, "DwmEnableComposition");
+         if(qDwmEnableComposition)
+                 qDwmEnableComposition(FALSE);
+         FreeLibrary(lib);
+  }
+#endif
+
+  gtk_disable_setlocale();
+  gtk_init(&argc, &argv);
+
+  // 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|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), 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|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), 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|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), 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|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), 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|G_LOG_FLAG_FATAL|G_LOG_FLAG_RECURSION), error_redirect, 0);
+
+  GlobalDebugMessageHandler::instance().setHandler(GlobalPopupDebugMessageHandler::instance());
+
+  environment_init(argc, argv);
+
+  paths_init();
+
+  if(!check_version())
+  {
+    return EXIT_FAILURE;
+  }
+
+  show_splash();
+
+  create_global_pid();
+
+  GlobalPreferences_Init();
+
+  g_GamesDialog.Init();
+
+  g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
+  
+  remove_global_pid();
+
+  g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
+
+  create_local_pid();
+
+  // 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)
+  {
+    Sys_LogFile(true);
+    g_Console_enableLogging = true;
+    g_GamesDialog.m_bForceLogConsole = false;
+  }
+
+
+  Radiant_Initialise();
+
+  global_accel_init();
+
+  user_shortcuts_init();
+
+  g_pParentWnd = 0;
+  g_pParentWnd = new MainFrame();
+
+  hide_splash();
+
+  if (g_bLoadLastMap && !g_strLastMap.empty())
+  {
+    Map_LoadFile(g_strLastMap.c_str());
+  }
+  else
+  {
+    Map_New();
+  }
+
+  // load up shaders now that we have the map loaded
+  // eviltypeguy
+  TextureBrowser_ShowStartupShaders(GlobalTextureBrowser());
+
+
+  remove_local_pid();
+
+  gtk_main();
+
+  // avoid saving prefs when the app is minimized
+  if (g_pParentWnd->IsSleeping())
+  {
+    globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
+    g_preferences_globals.disable_ini = true;
+  }
+
+  Map_Free();
+
+  if (!Map_Unnamed(g_map))
+  {
+    g_strLastMap = Map_Name(g_map);
+  }
+
+  delete g_pParentWnd;
+
+  user_shortcuts_save();
+
+  global_accel_destroy();
+
+  Radiant_Shutdown();
+
+  // close the log file if any
+  Sys_LogFile(false);
+
+  return EXIT_SUCCESS;
+}
+