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