]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/main.cpp
553e878065bcc5e035dde4eafcbfacb7b0eb0a94
[xonotic/netradiant.git] / radiant / main.cpp
1 /*
2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
4
5 This file is part of GtkRadiant.
6
7 GtkRadiant is free software; you can redistribute it and/or modify
8 it under the terms of the GNU General Public License as published by
9 the Free Software Foundation; either version 2 of the License, or
10 (at your option) any later version.
11
12 GtkRadiant is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 GNU General Public License for more details.
16
17 You should have received a copy of the GNU General Public License
18 along with GtkRadiant; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA
20 */
21
22 #if defined (__linux__) || defined (__APPLE__)
23   #include <gdk/gdkx.h>
24   #include <pwd.h>
25   #include <unistd.h>
26   #ifdef __linux__
27     #include <mntent.h>
28   #endif
29   #include <dirent.h>
30   #include <pthread.h>
31   #include <sys/wait.h>
32   #include <signal.h>
33   #include <sys/stat.h>
34 #endif
35
36 #include <gtk/gtk.h>
37 #include <glib/gi18n.h>
38 #include "stdafx.h"
39 #include <assert.h>
40 #include <sys/types.h>
41 #include <sys/stat.h>
42
43 #include <stdlib.h>
44
45 #include "watchbsp.h"
46 #include "filters.h"
47
48 bool g_bBuildList = false;
49 int g_argc;
50 char** g_argv;
51
52 // =============================================================================
53 // Splash screen
54
55 // get rid of it when debugging
56 #if defined (_DEBUG)
57   #define SKIP_SPLASH
58 #endif
59
60 static GtkWidget *splash_screen;
61
62 // called based on a timer, or in particular cases when we don't want to keep it around
63 gint try_destroy_splash (gpointer data)
64 {
65   if (splash_screen)
66   {
67     gtk_widget_destroy (splash_screen);
68     splash_screen = NULL;
69   }
70   return FALSE;
71 }
72
73 static void create_splash ()
74 {
75   GtkWidget *alert_frame, *alert_frame1, *pixmap;
76
77   splash_screen = gtk_window_new (GTK_WINDOW_POPUP);
78   gtk_window_position (GTK_WINDOW (splash_screen), GTK_WIN_POS_CENTER);
79   gtk_widget_realize (splash_screen);
80
81   alert_frame1 = gtk_frame_new (NULL);
82   gtk_widget_show (alert_frame1);
83   gtk_container_add (GTK_CONTAINER (splash_screen), alert_frame1);
84   gtk_frame_set_shadow_type (GTK_FRAME (alert_frame1), GTK_SHADOW_OUT);
85
86   alert_frame = gtk_frame_new (NULL);
87   gtk_widget_show (alert_frame);
88
89   gtk_container_add (GTK_CONTAINER (alert_frame1), alert_frame);
90   gtk_frame_set_shadow_type (GTK_FRAME (alert_frame), GTK_SHADOW_IN);
91   gtk_container_border_width (GTK_CONTAINER (alert_frame), 3);
92
93   pixmap = gtk_preview_new (GTK_PREVIEW_COLOR);
94   gtk_widget_show (pixmap);
95   gtk_container_add (GTK_CONTAINER (alert_frame), pixmap);
96
97   CString str;
98   guint16 width, height;
99   unsigned char *buf;
100
101   str = g_strGameToolsPath;
102   str += "bitmaps/splash.bmp";
103
104   unsigned char* load_bitmap_file (const char* filename, guint16* width, guint16* height);
105   buf = load_bitmap_file (str.GetBuffer (), &width, &height);
106
107   if (!buf)
108   {
109     str = g_strBitmapsPath;
110     str += "splash.bmp";
111
112     buf = load_bitmap_file (str.GetBuffer (), &width, &height);
113   }
114
115   if (buf)
116   {
117     GtkPreview *preview = GTK_PREVIEW (pixmap);
118     gtk_preview_size (preview, width, height);
119     for (int y = 0; y < height; y++)
120       gtk_preview_draw_row (preview, buf+y*width*3, 0, y, width);
121   }
122
123   gtk_widget_show_all (splash_screen);
124
125   while (gtk_events_pending ())
126     gtk_main_iteration ();
127 }
128
129 // =============================================================================
130 // Loki stuff
131
132 #if defined (__linux__) || defined (__APPLE__)
133
134 /* A short game name, could be used as argv[0] */
135 static char game_name[100] = "";
136
137 /* The directory where the data files can be found (run directory) */
138 static char datapath[PATH_MAX];
139
140 char *loki_gethomedir(void)
141 {
142   char *home = NULL;
143
144   home = getenv("HOME");
145   if ( home == NULL )
146   {
147     uid_t id = getuid();
148     struct passwd *pwd;
149
150     setpwent();
151     while ( (pwd = getpwent()) != NULL )
152     {
153       if ( pwd->pw_uid == id )
154       {
155         home = pwd->pw_dir;
156         break;
157       }
158     }
159     endpwent();
160   }
161   return home;
162 }
163
164 /* Must be called BEFORE loki_initialize */
165 void loki_setgamename(const char *n)
166 {
167   strncpy(game_name, n, sizeof(game_name));
168 }
169
170   #ifdef __linux__
171 /* Code to determine the mount point of a CD-ROM */
172 int loki_getmountpoint(const char *device, char *mntpt, int max_size)
173 {
174   char devpath[PATH_MAX], mntdevpath[PATH_MAX];
175   FILE * mountfp;
176   struct mntent *mntent;
177   int mounted;
178
179   /* Nothing to do with no device file */
180   if ( device == NULL )
181   {
182     *mntpt = '\0';
183     return -1;
184   }
185
186   /* Get the fully qualified path of the CD-ROM device */
187   if ( realpath(device, devpath) == NULL )
188   {
189     perror("realpath() on your CD-ROM failed");
190     return(-1);
191   }
192
193   /* Get the mount point */
194   mounted = -1;
195   memset(mntpt, 0, max_size);
196   mountfp = setmntent( _PATH_MNTTAB, "r" );
197   if ( mountfp != NULL )
198   {
199     mounted = 0;
200     while ( (mntent = getmntent( mountfp )) != NULL )
201     {
202       char *tmp, mntdev[1024];
203
204       strcpy(mntdev, mntent->mnt_fsname);
205       if ( strcmp(mntent->mnt_type, "supermount") == 0 )
206       {
207         tmp = strstr(mntent->mnt_opts, "dev=");
208         if ( tmp )
209         {
210           strcpy(mntdev, tmp+strlen("dev="));
211           tmp = strchr(mntdev, ',');
212           if ( tmp )
213           {
214             *tmp = '\0';
215           }
216         }
217       }
218       if ( strncmp(mntdev, "/dev", 4) ||
219            realpath(mntdev, mntdevpath) == NULL )
220       {
221         continue;
222       }
223       if ( strcmp( mntdevpath, devpath ) == 0 )
224       {
225         mounted = 1;
226         assert((int)strlen( mntent->mnt_dir ) < max_size);
227         strncpy( mntpt, mntent->mnt_dir, max_size-1);
228         mntpt[max_size-1] = '\0';
229         break;
230       }
231     }
232     endmntent( mountfp );
233   }
234   return(mounted);
235 }
236   #endif
237
238 /*
239     This function gets the directory containing the running program.
240     argv0 - the 0'th argument to the program
241 */
242 // FIXME TTimo
243 // I don't understand this function. It looks like something cut from another piece of software
244 // we somehow get the g_strAppPath from it, but it's done through a weird scan across $PATH env. var.
245 // even worse, it doesn't behave the same in all cases .. works well when ran through gdb and borks when ran from a shell
246 void loki_initpaths(char *argv0)
247 {
248   char temppath[PATH_MAX]; //, env[100];
249   char *home; //, *ptr, *data_env;
250
251   home = loki_gethomedir();
252   if ( home == NULL )
253   {
254     home = ".";
255   }
256
257   if (*game_name == 0) /* Game name defaults to argv[0] */
258     loki_setgamename(argv0);
259
260   strcpy(temppath, argv0);  /* If this overflows, it's your own fault :) */
261   if ( ! strrchr(temppath, '/') )
262   {
263     char *path;
264     char *last;
265     int found;
266
267     found = 0;
268     path = getenv("PATH");
269     do
270     {
271       /* Initialize our filename variable */
272       temppath[0] = '\0';
273
274       /* Get next entry from path variable */
275       last = strchr(path, ':');
276       if ( ! last )
277         last = path+strlen(path);
278
279       /* Perform tilde expansion */
280       if ( *path == '~' )
281       {
282         strcpy(temppath, home);
283         ++path;
284       }
285
286       /* Fill in the rest of the filename */
287       if ( last > (path+1) )
288       {
289         strncat(temppath, path, (last-path));
290         strcat(temppath, "/");
291       }
292       strcat(temppath, "./");
293       strcat(temppath, argv0);
294
295       /* See if it exists, and update path */
296       if ( access(temppath, X_OK) == 0 )
297       {
298         ++found;
299       }
300       path = last+1;
301
302     } while ( *last && !found );
303
304   } else
305   {
306     /* Increment argv0 to the basename */
307     argv0 = strrchr(argv0, '/')+1;
308   }
309
310   /* Now canonicalize it to a full pathname for the data path */
311   if ( realpath(temppath, datapath) )
312   {
313     /* There should always be '/' in the path */
314     *(strrchr(datapath, '/')) = '\0';
315   }
316 }
317
318 char *loki_getdatapath(void)
319 {
320   return(datapath);
321 }
322
323 #endif
324
325 // end of Loki stuff
326 // =============================================================================
327
328 void error_redirect (const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data)
329 {
330   gboolean in_recursion;
331   gboolean is_fatal;
332   char buf[256];
333
334   in_recursion = (log_level & G_LOG_FLAG_RECURSION) != 0;
335   is_fatal = (log_level & G_LOG_FLAG_FATAL) != 0;
336   log_level = (GLogLevelFlags) (log_level & G_LOG_LEVEL_MASK);
337
338   if (!message)
339     message = "(NULL) message";
340
341   if (domain)
342     strcpy (buf, domain);
343   else
344     strcpy (buf, "**");
345   strcat (buf, "-");
346
347   switch (log_level)
348   {
349   case G_LOG_LEVEL_ERROR:
350     if (in_recursion)
351       strcat (buf, "ERROR (recursed) **: ");
352     else
353       strcat (buf, "ERROR **: ");
354     break;
355   case G_LOG_LEVEL_CRITICAL:
356     if (in_recursion)
357       strcat (buf, "CRITICAL (recursed) **: ");
358     else
359       strcat (buf, "CRITICAL **: ");
360     break;
361   case G_LOG_LEVEL_WARNING:
362     if (in_recursion)
363       strcat (buf, "WARNING (recursed) **: ");
364     else
365       strcat (buf, "WARNING **: ");
366     break;
367   case G_LOG_LEVEL_MESSAGE:
368     if (in_recursion)
369       strcat (buf, "Message (recursed): ");
370     else
371       strcat (buf, "Message: ");
372     break;
373   case G_LOG_LEVEL_INFO:
374     if (in_recursion)
375       strcat (buf, "INFO (recursed): ");
376     else
377       strcat (buf, "INFO: ");
378     break;
379   case G_LOG_LEVEL_DEBUG:
380     if (in_recursion)
381       strcat (buf, "DEBUG (recursed): ");
382     else
383       strcat (buf, "DEBUG: ");
384     break;
385   default:
386     /* we are used for a log level that is not defined by GLib itself,
387      * try to make the best out of it.
388      */
389     if (in_recursion)
390       strcat (buf, "LOG (recursed:");
391     else
392       strcat (buf, "LOG (");
393     if (log_level)
394     {
395       gchar string[] = "0x00): ";
396       gchar *p = string + 2;
397       guint i;
398
399       i = g_bit_nth_msf (log_level, -1);
400       *p = i >> 4;
401       p++;
402       *p = '0' + (i & 0xf);
403       if (*p > '9')
404         *p += 'A' - '9' - 1;
405
406       strcat (buf, string);
407     } else
408       strcat (buf, "): ");
409   }
410
411   strcat (buf, message);
412   if (is_fatal)
413     strcat (buf, "\naborting...\n");
414   else
415     strcat (buf, "\n");
416
417   printf ("%s\n", buf);
418   Sys_FPrintf (SYS_WRN, buf);
419   // TTimo NOTE: in some cases it may be handy to log only to the file
420 //  Sys_FPrintf (SYS_NOCON, buf);
421 }
422
423 #define GETTEXT_PACKAGE "radiant"
424 #define LOCALEDIR "lang"
425
426 int main( int argc, char* argv[] ) {
427         char *libgl, *ptr;
428         int i, j, k;
429
430 #ifdef _WIN32
431   libgl = "opengl32.dll";
432 #endif
433
434 #if defined (__linux__)
435   libgl = "libGL.so.1";
436 #endif
437
438 #ifdef __APPLE__
439   libgl = "/usr/X11R6/lib/libGL.1.dylib";
440 #endif
441
442 #if defined (__linux__) || defined (__APPLE__)
443   // Give away unnecessary root privileges.
444   // Important: must be done before calling gtk_init().
445   char *loginname;
446   struct passwd *pw;
447   seteuid(getuid());
448   if ( geteuid() == 0 && ( loginname = getlogin() ) != NULL && ( pw = getpwnam(loginname) ) != NULL ) {
449           setuid(pw->pw_uid);
450   }
451 #endif
452
453
454         bindtextdomain(GETTEXT_PACKAGE, LOCALEDIR);
455         bind_textdomain_codeset(GETTEXT_PACKAGE, "UTF-8");
456         textdomain(GETTEXT_PACKAGE);
457 //  gtk_disable_setlocale();
458
459   gtk_init(&argc, &argv);
460
461   if ((ptr = getenv ("Q3R_LIBGL")) != NULL)
462     libgl = ptr;
463
464   for (i = 1; i < argc; i++)
465   {
466     char* param = argv[i];
467
468     if (param[0] == '-' && param[1] == '-')
469     {
470       param += 2;
471
472       if ((strcmp (param, "libgl") == 0) && (i != argc))
473       {
474         libgl = argv[i+1];
475         argv[i] = argv[i+1] = NULL;
476         i++;
477       } else if (strcmp (param, "builddefs") == 0)
478       {
479         g_bBuildList = true;
480         argv[i] = NULL;
481       }
482     }
483   }
484
485   for (i = 1; i < argc; i++)
486   {
487     for (k = i; k < argc; k++)
488       if (argv[k] != NULL)
489         break;
490
491     if (k > i)
492     {
493       k -= i;
494       for (j = i + k; j < argc; j++)
495         argv[j-k] = argv[j];
496       argc -= k;
497     }
498   }
499
500   g_argc = argc;
501   g_argv = argv;
502
503   g_strPluginsDir = "plugins/";
504   g_strModulesDir = "modules/";
505
506 #ifdef _WIN32
507   // get path to the editor
508   char* pBuffer = g_strAppPath.GetBufferSetLength(_MAX_PATH + 1);
509   GetModuleFileName(NULL, pBuffer, _MAX_PATH);
510   pBuffer[g_strAppPath.ReverseFind('\\') + 1] = '\0';
511   QE_ConvertDOSToUnixName(pBuffer, pBuffer);
512   g_strAppPath.ReleaseBuffer();
513
514   g_strBitmapsPath = g_strAppPath;
515   g_strBitmapsPath += "bitmaps/";
516
517   CGameDialog::UpdateNetrun(false); // read the netrun configuration
518
519   if ( CGameDialog::GetNetrun() ) {
520     // we have to find a per-user g_strTempPath
521     // this behaves the same as on Linux
522     g_strTempPath = getenv("USERPROFILE");
523     if (!g_strTempPath.GetLength())
524     {
525       CString msg;
526       msg = "Radiant is configured to run from a network installation.\n";
527       msg += "I couldn't find the environement variable USERPROFILE\n";
528       msg += "I'm going to use C:\\RadiantSettings. Please set USERPROFILE\n";
529       gtk_MessageBox (NULL, msg, "Radiant - Network mode", MB_OK);
530       g_strTempPath = "C:\\";
531     }
532     g_strTempPath += "\\RadiantSettings\\";
533     Q_mkdir(g_strTempPath.GetBuffer(), 0755);
534     g_strTempPath += RADIANT_VERSION;
535     g_strTempPath += "\\";
536     Q_mkdir(g_strTempPath.GetBuffer(), 0755);
537   }
538   else
539   {
540     // use the core path as temp (to save commandlist.txt, and do the .pid files)
541     g_strTempPath = g_strAppPath;
542   }
543
544 #endif
545
546 #if defined (__linux__) || defined (__APPLE__)
547   Str home;
548   home = g_get_home_dir ();
549   AddSlash (home);
550   home += ".radiant/";
551   Q_mkdir (home.GetBuffer (), 0775);
552   home += RADIANT_VERSION;
553   Q_mkdir (home.GetBuffer (), 0775);
554   g_strTempPath = home.GetBuffer ();
555   AddSlash (g_strTempPath);
556
557   loki_initpaths(argv[0]);
558
559   // NOTE: we build g_strAppPath with a '/' (or '\' on WIN32)
560   // it's a general convention in Radiant to have the slash at the end of directories
561   char real[PATH_MAX];
562   realpath (loki_getdatapath(), real);
563   if (real[strlen(real)-1] != '/')
564     strcat(real, "/");
565
566   g_strAppPath = real;
567
568   // radiant is installed in the parent dir of "tools/"
569   // NOTE: this is not very easy for debugging
570   // maybe add options to lookup in several places?
571   // (for now I had to create symlinks)
572   g_strBitmapsPath = g_strAppPath;
573   g_strBitmapsPath += "bitmaps/";
574
575   // we will set this right after the game selection is done
576   g_strGameToolsPath = g_strAppPath;
577
578 #endif
579
580   // init the DTD path
581   g_strDTDPath = g_strAppPath;
582   g_strDTDPath += "dtds/";
583
584   /*!
585   the global prefs loading / game selection dialog might fail for any reason we don't know about
586   we need to catch when it happens, to cleanup the stateful prefs which might be killing it
587   and to turn on console logging for lookup of the problem
588   this is the first part of the two step .pid system
589   http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
590   */
591   g_pidFile = g_strTempPath.GetBuffer ();
592   g_pidFile += "radiant.pid";
593
594   FILE *pid;
595   pid = fopen( g_pidFile.GetBuffer(), "r" );
596   if ( pid != NULL ) {
597     fclose (pid);
598     CString msg;
599
600     if (remove (g_pidFile.GetBuffer ()) == -1)
601     {
602       msg = "WARNING: Could not delete "; msg += g_pidFile;
603       gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
604     }
605
606     // in debug, never prompt to clean registry, turn console logging auto after a failed start
607 #if !defined(_DEBUG)
608     msg = "Found the file ";
609     msg += g_pidFile;
610     msg += ".\nThis indicates that Radiant failed during the game selection startup last time it was run.\n"
611            "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"
612            "WARNING: the global prefs will be lost if you choose YES.";
613
614     if (gtk_MessageBox (NULL, msg, "Radiant - Reset global startup?", MB_YESNO | MB_ICONQUESTION) == IDYES)
615     {
616       // remove global prefs and shutdown
617       g_PrefsDlg.mGamesDialog.Reset();
618       // remove the prefs file (like a full reset of the registry)
619       //remove (g_PrefsDlg.m_inipath->str);
620       gtk_MessageBox(NULL, "Removed global settings, choose OK to close Radiant.", "Radiant", MB_OK );
621       _exit(-1);
622     }
623     msg = "Logging console output to ";
624     msg += g_strTempPath;
625     msg += "radiant.log\nRefer to the log if Radiant fails to start again.";
626
627     gtk_MessageBox (NULL, msg, "Radiant - Console Log", MB_OK);
628 #endif
629
630     // set without saving, the class is not in a coherent state yet
631     // just do the value change and call to start logging, CGamesDialog will pickup when relevant
632     g_PrefsDlg.mGamesDialog.m_bLogConsole = true;
633     g_PrefsDlg.mGamesDialog.m_bForceLogConsole = true;
634     Sys_LogFile();
635   }
636
637   // create a primary .pid for global init run
638   pid = fopen( g_pidFile.GetBuffer(), "w" );
639   if ( pid ) {
640           fclose( pid );
641   }
642
643   // a safe check to avoid people running broken installations
644   // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
645   // make something idiot proof and someone will make better idiots, this may be overkill
646   // let's leave it disabled in debug mode in any case
647   // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
648 #ifndef _DEBUG
649   //#define CHECK_VERSION
650 #endif
651 #ifdef CHECK_VERSION
652   // locate and open RADIANT_MAJOR and RADIANT_MINOR
653   qboolean bVerIsGood = true;
654   Str ver_file_name;
655   ver_file_name = g_strAppPath;
656   ver_file_name += "RADIANT_MAJOR";
657   FILE *ver_file = fopen (ver_file_name.GetBuffer(), "r");
658   if (ver_file)
659   {
660     char buf[10];
661     int chomp;
662     fread(buf, 1, 10, ver_file);
663     // chomp it (the hard way)
664     chomp = 0;
665     while(buf[chomp] >= '0' && buf[chomp] <= '9')
666       chomp++;
667     buf[chomp] = '\0';
668     if (strcmp(buf, RADIANT_MAJOR_VERSION))
669     {
670       Sys_Printf("ERROR: file RADIANT_MAJOR doesn't match ('%s')\n", buf);
671       bVerIsGood = false;
672     }
673   }
674   else
675   {
676     Sys_Printf("ERROR: can't find RADIANT_MAJOR in '%s'\n", ver_file_name.GetBuffer());
677     bVerIsGood = false;
678   }
679   ver_file_name = g_strAppPath;
680   ver_file_name += "RADIANT_MINOR";
681   ver_file = fopen (ver_file_name.GetBuffer(), "r");
682   if (ver_file)
683   {
684     char buf[10];
685     int chomp;
686     fread(buf, 1, 10, ver_file);
687     // chomp it (the hard way)
688     chomp = 0;
689     while(buf[chomp] >= '0' && buf[chomp] <= '9')
690       chomp++;
691     buf[chomp] = '\0';
692     if (strcmp(buf, RADIANT_MINOR_VERSION))
693     {
694       Sys_Printf("ERROR: file RADIANT_MINOR doesn't match ('%s')\n", buf);
695       bVerIsGood = false;
696     }
697   }
698   else
699   {
700     Sys_Printf("ERROR: can't find RADIANT_MINOR in '%s'\n", ver_file_name.GetBuffer());
701     bVerIsGood = false;
702   }
703   if (!bVerIsGood)
704   {
705     CString msg;
706     msg = "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n";
707     msg += "Make sure you run the right/latest editor binary you installed\n";
708     msg += g_strAppPath; msg += "\n";
709     msg += "Check http://www.qeradiant.com/faq/index.cgi?file=219 for more information";
710     gtk_MessageBox(NULL, msg.GetBuffer(), "Radiant", MB_OK, "http://www.qeradiant.com/faq/index.cgi?file=219");
711     _exit(-1);
712   }
713 #endif
714
715   g_qeglobals.disable_ini = false;
716   g_PrefsDlg.Init();
717
718   // close the primary
719   if ( remove( g_pidFile.GetBuffer () ) == -1 ) {
720     CString msg;
721     msg = "WARNING: Could not delete "; msg += g_pidGameFile;
722     gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
723   }
724
725   /*!
726   now the secondary game dependant .pid file
727   http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
728   */
729   g_pidGameFile = g_PrefsDlg.m_rc_path->str;
730   g_pidGameFile += "radiant-game.pid";
731
732   pid = fopen (g_pidGameFile.GetBuffer(), "r");
733   if (pid != NULL)
734   {
735     fclose (pid);
736     CString msg;
737     if (remove (g_pidGameFile.GetBuffer ()) == -1)
738     {
739       msg = "WARNING: Could not delete "; msg += g_pidGameFile;
740       gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
741     }
742
743     msg = "Found the file ";
744     msg += g_pidGameFile;
745     msg += ".\nThis indicates that Radiant failed to load the last time it was run.\n"
746            "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"
747            "WARNING: preferences will be lost if you choose YES.";
748
749     // in debug, never prompt to clean registry, turn console logging auto after a failed start
750 #if !defined(_DEBUG)
751     //bleh
752     if (gtk_MessageBox (NULL, msg, "Radiant - Clean Registry?", MB_YESNO | MB_ICONQUESTION) == IDYES)
753     {
754       // remove the game prefs files
755       remove (g_PrefsDlg.m_inipath->str);
756       char buf[PATH_MAX];
757       sprintf(buf, "%sSavedInfo.bin", g_PrefsDlg.m_rc_path->str);
758       remove(buf);
759       // remove the global pref too
760       g_PrefsDlg.mGamesDialog.Reset();
761       gtk_MessageBox(NULL, "Cleaned registry settings, choose OK to close Radiant.\nThe next time Radiant runs it will use default settings.", "Radiant", MB_OK );
762       _exit(-1);
763     }
764     msg = "Logging console output to ";
765     msg += g_strTempPath;
766     msg += "radiant.log\nRefer to the log if Radiant fails to start again.";
767
768     gtk_MessageBox (NULL, msg, "Radiant - Console Log", MB_OK);
769 #endif
770
771     // force console logging on! (will go in prefs too)
772     g_PrefsDlg.mGamesDialog.m_bLogConsole = true;
773     g_PrefsDlg.mGamesDialog.SavePrefs();
774     Sys_LogFile();
775
776     g_PrefsDlg.LoadPrefs();
777
778   } else
779   {
780     // create one, will remove right after entering message loop
781     pid = fopen (g_pidGameFile.GetBuffer(), "w");
782     if (pid)
783       fclose (pid);
784
785     g_PrefsDlg.LoadPrefs();
786
787 #ifndef _DEBUG // I can't be arsed about that prompt in debug mode
788     // if console logging is on in the prefs, warn about performance hit
789     if (g_PrefsDlg.mGamesDialog.m_bLogConsole)
790     {
791       if (gtk_MessageBox (NULL, "Preferences indicate that console logging is on. This affects performance.\n"
792                           "Turn it off?", "Radiant", MB_YESNO | MB_ICONQUESTION) == IDYES)
793       {
794         g_PrefsDlg.mGamesDialog.m_bLogConsole = false;
795         g_PrefsDlg.mGamesDialog.SavePrefs();
796       }
797     }
798 #endif
799     // toggle console logging if necessary
800     Sys_LogFile();
801   }
802
803   // FIXME http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=639
804   // we should search in g_strTempPath, then move over to look at g_strAppPath?
805 #ifdef _WIN32
806   // fine tune the look of the app using a gtk rc file
807   // we try to load an RC file placed in the application directory
808   // build the full path
809   Str sRCFile;
810   sRCFile = g_strAppPath;
811   sRCFile += "radiantgtkrc";
812   // we load that file with g_new in gtk_rc_parse_file (gtkrc.c), change the '/' into '\\'
813   pBuffer = (char *)sRCFile.GetBuffer();
814   for (i=0; i<sRCFile.GetLength(); i++)
815   {
816     if (pBuffer[i]=='/')
817     {
818       pBuffer[i] = '\\';
819     }
820   }
821   // check the file exists
822   if (access(sRCFile.GetBuffer(), R_OK) != 0)
823     Sys_Printf("RC file %s not found\n", sRCFile.GetBuffer());
824   else
825   {
826     Sys_Printf ("Attemping to load RC file %s\n", sRCFile.GetBuffer());
827     gtk_rc_parse (sRCFile.GetBuffer());
828   }
829 #endif
830
831 #ifndef SKIP_SPLASH
832   create_splash();
833 #endif
834
835   if (!QGL_Init(libgl, ""))
836   {
837     Sys_FPrintf (SYS_ERR, "Failed to load OpenGL libraries\n");
838     _exit (1);
839     return 1;
840   }
841
842 #if defined (__linux__) || defined (__APPLE__)
843   if ((qglXQueryExtension == NULL) || (qglXQueryExtension(GDK_DISPLAY(),NULL,NULL) != True))
844   {
845     Sys_FPrintf (SYS_ERR, "glXQueryExtension failed\n");
846     _exit (1);
847     return 1;
848   }
849 #endif
850
851   // redirect Gtk warnings to the console
852   g_log_set_handler( "Gdk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
853                                              G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, NULL);
854   g_log_set_handler( "Gtk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
855                                              G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, NULL);
856
857   // spog - creates new filters list for the first time
858   g_qeglobals.d_savedinfo.filters = NULL;
859   g_qeglobals.d_savedinfo.filters = FilterUpdate(g_qeglobals.d_savedinfo.filters);
860
861   g_pParentWnd = new MainFrame();
862
863   if ( g_PrefsDlg.m_bLoadLastMap && g_PrefsDlg.m_strLastMap.GetLength() > 0 ) {
864           Map_LoadFile(g_PrefsDlg.m_strLastMap.GetBuffer());
865   } else {
866           Map_New();
867   }
868
869   // load up shaders now that we have the map loaded
870   // eviltypeguy
871   Texture_ShowStartupShaders();
872
873 #ifndef SKIP_SPLASH
874   gdk_window_raise(splash_screen->window);
875   gtk_window_set_transient_for( GTK_WINDOW( splash_screen ), GTK_WINDOW( g_pParentWnd->m_pWidget ) );
876   gtk_timeout_add( 1000, try_destroy_splash, NULL );
877 #endif
878
879   g_pParentWnd->GetSynapseServer().DumpActiveClients();
880
881   //++timo: temporary debug
882   g_pParentWnd->DoWatchBSP();
883
884   gtk_main();
885
886   // close the log file if any
887   // NOTE: don't save prefs past this point!
888   g_PrefsDlg.mGamesDialog.m_bLogConsole = false;
889   // set the console window to NULL to avoid Sys_Printf crashing
890   g_qeglobals_gui.d_edit = NULL;
891   Sys_LogFile();
892
893   // NOTE TTimo not sure what this _exit(0) call is worth
894   //   restricting it to linux build
895 #ifdef __linux__
896   _exit( 0 );
897 #endif
898   return 0;
899 }
900
901 // ydnar: quick and dirty fix, just make the buffer bigger
902 #define BIG_PATH_MAX    4096
903
904 // TTimo: decompose the BSP command into several steps so we can monitor them eventually
905 void QE_ExpandBspString (char *bspaction, GPtrArray *out_array, char *mapname)
906 {
907   const char  *in;
908   char  *out;
909   char  src[BIG_PATH_MAX];
910   char  rsh[BIG_PATH_MAX];
911   char  base[BIG_PATH_MAX];
912
913   strcpy(src, mapname);
914   strlwr(src);
915   in = strstr(src, "maps/");
916   if (!in)
917   {
918     in = strstr(src, "maps/");
919   }
920   if (in)
921   {
922     in += 5;
923     strcpy(base, in);
924     out = base;
925     while (*out)
926     {
927       if (*out == '\\')
928       {
929         *out = '/';
930       }
931       out++;
932     }
933   } else
934   {
935     ExtractFileName (mapname, base);
936   }
937
938   // this important step alters the map name to add fs_game
939   // NOTE: it used to add fs_basepath too
940   // the fs_basepath addition moved to being in the project file during the bug fixing rush
941   // but it may not have been the right thing to do
942
943   // HACK: halflife compiler tools don't support -fs_game
944   // HACK: neither does JKII/SoF2/ etc..
945   // do we use & have fs_game?
946
947   if (g_pGameDescription->mGameFile != "hl.game" &&
948       *ValueForKey(g_qeglobals.d_project_entity,"gamename") != '\0')
949     {
950       // set with fs_game
951       sprintf(src, "-fs_game %s \"%s\"", ValueForKey(g_qeglobals.d_project_entity,"gamename"), mapname);
952     }
953     else
954     {
955       sprintf(src, "\"%s\"", mapname);
956   }
957
958   rsh[0] = 0;
959
960   QE_ConvertDOSToUnixName(src, src);
961
962   // initialise the first step
963   out = new char[BIG_PATH_MAX]; //% PATH_MAX
964   g_ptr_array_add( out_array, out );
965
966   in = ValueForKey( g_qeglobals.d_project_entity, bspaction );
967   while (*in)
968   {
969     if (in[0] == '!')
970     {
971       strcpy (out, rsh);
972       out += strlen(rsh);
973       in++;
974       continue;
975     }
976     if (in[0] == '#')
977     {
978       char tmp[2048];
979       // we process these only if monitoring
980       if (g_PrefsDlg.m_bWatchBSP)
981       {
982         // -connect global option (the only global option so far anyway)
983         strcpy (tmp, " -connect 127.0.0.1:39000 ");
984         strcpy (out, tmp);
985         out += strlen(tmp);
986       }
987       in++;
988       continue;
989     }
990     if (in[0] == '$')
991     {
992       // $ expansion
993       strcpy (out, src);
994       out += strlen(src);
995       in++;
996       continue;
997     }
998     if (in[0] == '@')
999     {
1000       *out++ = '"';
1001       in++;
1002       continue;
1003     }
1004     if (in[0] == '&')
1005       if (in[1] == '&')
1006       {
1007         // start a new step
1008         *out = 0;
1009         in = in + 2;
1010         out = new char[BIG_PATH_MAX];   //% PATH_MAX
1011         g_ptr_array_add( out_array, out );
1012       }
1013     *out++ = *in++;
1014   }
1015   *out = 0;
1016 }
1017
1018 void FindReplace(CString& strContents, const char* pTag, const char* pValue)
1019 {
1020   if (strcmp(pTag, pValue) == 0)
1021     return;
1022   for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag))
1023   {
1024     int nRightLen = strContents.GetLength() - strlen(pTag) - nPos;
1025     CString strLeft = strContents.Left(nPos);
1026     CString strRight = strContents.Right(nRightLen);
1027     strLeft += pValue;
1028     strLeft += strRight;
1029     strContents = strLeft;
1030   }
1031 }
1032
1033 // save the map, deals with regioning
1034 void SaveWithRegion(char *name)
1035 {
1036   strcpy (name, currentmap);
1037   if (region_active)
1038   {
1039     // temporary cut the region to save regular map
1040     region_active = false;
1041     Map_SaveFile (name, false);
1042     region_active = true;
1043     StripExtension (name);
1044     strcat (name, ".reg");
1045   }
1046
1047   Map_SaveFile (name, region_active);
1048 }
1049
1050 void RunBsp (char *command)
1051 {
1052   GPtrArray *sys;
1053   char  batpath[BIG_PATH_MAX];  //% PATH_MAX
1054   char  temppath[BIG_PATH_MAX]; //% PATH_MAX
1055   char  name[BIG_PATH_MAX];             //% PATH_MAX
1056   char  cWork[BIG_PATH_MAX];    //% PATH_MAX
1057   FILE  *hFile;
1058   unsigned int   i;
1059
1060   SetInspectorMode(W_CONSOLE);
1061
1062   strcpy (temppath, g_strTempPath.GetBuffer ());
1063
1064   SaveWithRegion(name);
1065
1066   const char *rsh = ValueForKey(g_qeglobals.d_project_entity, "rshcmd");
1067   if (rsh == NULL)
1068   {
1069     CString strPath, strFile;
1070
1071     ExtractPath_and_Filename(name, strPath, strFile);
1072     AddSlash(strPath);
1073     strncpy(cWork, strPath, 1024);
1074     strcat(cWork, strFile);
1075   } else
1076   {
1077     strcpy(cWork, name);
1078   }
1079
1080   // get the array ready
1081   //++timo TODO: free the array, free the strings ourselves with delete[]
1082   sys = g_ptr_array_new();
1083
1084   QE_ExpandBspString (command, sys, cWork);
1085
1086   if (g_PrefsDlg.m_bWatchBSP)
1087   {
1088     // grab the file name for engine running
1089     char *bspname = new char[1024];
1090     ExtractFileName( currentmap, bspname );
1091     StripExtension( bspname );
1092     g_pParentWnd->GetWatchBSP()->DoMonitoringLoop( sys, bspname );
1093   } else
1094   {
1095     // write all the steps in a single BAT / .sh file and run it, don't bother monitoring it
1096     CString strSys;
1097     for (i=0; i < sys->len; i++ )
1098     {
1099       strSys += (char *)g_ptr_array_index( sys, i);
1100 #ifdef _WIN32  // write temp\junk.txt in win32... NOTE: stops output to shell prompt window
1101       if (i==0)
1102         strSys += " >";
1103       else
1104         strSys += " >>";
1105       strSys += "\"";
1106       strSys += temppath;
1107       strSys += "junk.txt\"";
1108 #endif
1109       strSys += "\n";
1110     };
1111
1112 #if defined (__linux__) || defined (__APPLE__)
1113
1114     // write qe3bsp.sh
1115     sprintf (batpath, "%sqe3bsp.sh", temppath);
1116     Sys_Printf("Writing the compile script to '%s'\n", batpath);
1117     Sys_Printf("The build output will be saved in '%sjunk.txt'\n", temppath);
1118     hFile = fopen(batpath, "w");
1119     if (!hFile)
1120       Error ("Can't write to %s", batpath);
1121     fprintf (hFile, "#!/bin/sh \n\n");
1122     fprintf (hFile, strSys.GetBuffer());
1123     fclose (hFile);
1124     chmod (batpath, 0744);
1125 #endif
1126
1127 #ifdef _WIN32
1128     sprintf (batpath, "%sqe3bsp.bat", temppath);
1129     Sys_Printf("Writing the compile script to '%s'\n", batpath);
1130     Sys_Printf("The build output will be saved in '%sjunk.txt'\n", temppath);
1131     hFile = fopen(batpath, "w");
1132     if (!hFile)
1133       Error ("Can't write to %s", batpath);
1134     fprintf (hFile, strSys.GetBuffer());
1135     fclose (hFile);
1136 #endif
1137
1138     Pointfile_Delete ();
1139
1140 #if defined (__linux__) || defined (__APPLE__)
1141
1142     pid_t pid;
1143
1144     pid = fork ();
1145     switch (pid)
1146     {
1147     case -1:
1148       Error ("CreateProcess failed");
1149       break;
1150     case 0:
1151       execlp (batpath, batpath, NULL);
1152       printf ("execlp error !");
1153       _exit (0);
1154       break;
1155     default:
1156       break;
1157     }
1158 #endif
1159
1160 #ifdef _WIN32
1161     Sys_Printf ("Running bsp command...\n");
1162     Sys_Printf ("\n%s\n", strSys.GetBuffer());
1163
1164     WinExec( batpath, SW_SHOWNORMAL );
1165 #endif
1166   }
1167 #ifdef _DEBUG
1168   // yeah, do it .. but not now right before 1.1-TA-beta release
1169   Sys_Printf("TODO: erase GPtrArray\n");
1170 #endif
1171 }
1172
1173 #if 0
1174
1175 #ifdef _WIN32
1176
1177 int WINAPI QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer )
1178 {
1179   static PIXELFORMATDESCRIPTOR pfd = {
1180     sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
1181     1,                              // version number
1182     PFD_DRAW_TO_WINDOW |            // support window
1183     PFD_SUPPORT_OPENGL |            // support OpenGL
1184     PFD_DOUBLEBUFFER,               // double buffered
1185     PFD_TYPE_RGBA,                  // RGBA type
1186     24,                             // 24-bit color depth
1187     0, 0, 0, 0, 0, 0,               // color bits ignored
1188     0,                              // no alpha buffer
1189     0,                              // shift bit ignored
1190     0,                              // no accumulation buffer
1191     0, 0, 0, 0,                     // accum bits ignored
1192     32,                             // depth bits
1193     0,                              // no stencil buffer
1194     0,                              // no auxiliary buffer
1195     PFD_MAIN_PLANE,                 // main layer
1196     0,                              // reserved
1197     0, 0, 0                         // layer masks ignored
1198   };                              //
1199   int pixelformat = 0;
1200
1201   zbuffer = true;
1202   if ( !zbuffer )
1203     pfd.cDepthBits = 0;
1204
1205   if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
1206   {
1207     LPVOID lpMsgBuf;
1208     FormatMessage(
1209                  FORMAT_MESSAGE_ALLOCATE_BUFFER |
1210                  FORMAT_MESSAGE_FROM_SYSTEM |
1211                  FORMAT_MESSAGE_IGNORE_INSERTS,
1212                  NULL,
1213                  GetLastError(),
1214                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1215                  (LPTSTR) &lpMsgBuf,
1216                  0,
1217                  NULL
1218                  );
1219     Sys_FPrintf (SYS_WRN, "GetLastError: %s", lpMsgBuf);
1220     Error ("ChoosePixelFormat failed");
1221   }
1222
1223   if (!SetPixelFormat(hDC, pixelformat, &pfd))
1224     Error ("SetPixelFormat failed");
1225
1226   return pixelformat;
1227 }
1228
1229 #endif
1230
1231 #endif