]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/main.cpp
* fixed a lot of compiler warnings (mostly const char * stuff and use of uninitialize...
[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 int main( int argc, char* argv[] ) {
424         char *libgl, *ptr;
425         int i, j, k;
426
427 #ifdef _WIN32
428   libgl = "opengl32.dll";
429 #endif
430
431 #if defined (__linux__)
432   libgl = "libGL.so.1";
433 #endif
434
435 #ifdef __APPLE__
436   libgl = "/usr/X11R6/lib/libGL.1.dylib";
437 #endif
438
439 #if defined (__linux__) || defined (__APPLE__)
440   // Give away unnecessary root privileges.
441   // Important: must be done before calling gtk_init().
442   char *loginname;
443   struct passwd *pw;
444   seteuid(getuid());
445   if ( geteuid() == 0 && ( loginname = getlogin() ) != NULL && ( pw = getpwnam(loginname) ) != NULL ) {
446           setuid(pw->pw_uid);
447   }
448 #endif
449
450   gtk_disable_setlocale();
451
452   gtk_init(&argc, &argv);
453
454   if ((ptr = getenv ("Q3R_LIBGL")) != NULL)
455     libgl = ptr;
456
457   for (i = 1; i < argc; i++)
458   {
459     char* param = argv[i];
460
461     if (param[0] == '-' && param[1] == '-')
462     {
463       param += 2;
464
465       if ((strcmp (param, "libgl") == 0) && (i != argc))
466       {
467         libgl = argv[i+1];
468         argv[i] = argv[i+1] = NULL;
469         i++;
470       } else if (strcmp (param, "builddefs") == 0)
471       {
472         g_bBuildList = true;
473         argv[i] = NULL;
474       }
475     }
476   }
477
478   for (i = 1; i < argc; i++)
479   {
480     for (k = i; k < argc; k++)
481       if (argv[k] != NULL)
482         break;
483
484     if (k > i)
485     {
486       k -= i;
487       for (j = i + k; j < argc; j++)
488         argv[j-k] = argv[j];
489       argc -= k;
490     }
491   }
492
493   g_argc = argc;
494   g_argv = argv;
495
496   g_strPluginsDir = "plugins/";
497   g_strModulesDir = "modules/";
498
499 #ifdef _WIN32
500   // get path to the editor
501   char* pBuffer = g_strAppPath.GetBufferSetLength(_MAX_PATH + 1);
502   GetModuleFileName(NULL, pBuffer, _MAX_PATH);
503   pBuffer[g_strAppPath.ReverseFind('\\') + 1] = '\0';
504   QE_ConvertDOSToUnixName(pBuffer, pBuffer);
505   g_strAppPath.ReleaseBuffer();
506
507   g_strBitmapsPath = g_strAppPath;
508   g_strBitmapsPath += "bitmaps/";
509
510   CGameDialog::UpdateNetrun(false); // read the netrun configuration
511
512   if ( CGameDialog::GetNetrun() ) {
513     // we have to find a per-user g_strTempPath
514     // this behaves the same as on Linux
515     g_strTempPath = getenv("USERPROFILE");
516     if (!g_strTempPath.GetLength())
517     {
518       CString msg;
519       msg = "Radiant is configured to run from a network installation.\n";
520       msg += "I couldn't find the environement variable USERPROFILE\n";
521       msg += "I'm going to use C:\\RadiantSettings. Please set USERPROFILE\n";
522       gtk_MessageBox (NULL, msg, "Radiant - Network mode", MB_OK);
523       g_strTempPath = "C:\\";
524     }
525     g_strTempPath += "\\RadiantSettings\\";
526     Q_mkdir(g_strTempPath.GetBuffer(), 0755);
527     g_strTempPath += RADIANT_VERSION;
528     g_strTempPath += "\\";
529     Q_mkdir(g_strTempPath.GetBuffer(), 0755);
530   }
531   else
532   {
533     // use the core path as temp (to save commandlist.txt, and do the .pid files)
534     g_strTempPath = g_strAppPath;
535   }
536
537 #endif
538
539 #if defined (__linux__) || defined (__APPLE__)
540   Str home;
541   home = g_get_home_dir ();
542   AddSlash (home);
543   home += ".radiant/";
544   Q_mkdir (home.GetBuffer (), 0775);
545   home += RADIANT_VERSION;
546   Q_mkdir (home.GetBuffer (), 0775);
547   g_strTempPath = home.GetBuffer ();
548   AddSlash (g_strTempPath);
549
550   loki_initpaths(argv[0]);
551
552   // NOTE: we build g_strAppPath with a '/' (or '\' on WIN32)
553   // it's a general convention in Radiant to have the slash at the end of directories
554   char real[PATH_MAX];
555   realpath (loki_getdatapath(), real);
556   if (real[strlen(real)-1] != '/')
557     strcat(real, "/");
558
559   g_strAppPath = real;
560
561   // radiant is installed in the parent dir of "tools/"
562   // NOTE: this is not very easy for debugging
563   // maybe add options to lookup in several places?
564   // (for now I had to create symlinks)
565   g_strBitmapsPath = g_strAppPath;
566   g_strBitmapsPath += "bitmaps/";
567
568   // we will set this right after the game selection is done
569   g_strGameToolsPath = g_strAppPath;
570
571 #endif
572
573   // init the DTD path
574   g_strDTDPath = g_strAppPath;
575   g_strDTDPath += "dtds/";
576
577   /*!
578   the global prefs loading / game selection dialog might fail for any reason we don't know about
579   we need to catch when it happens, to cleanup the stateful prefs which might be killing it
580   and to turn on console logging for lookup of the problem
581   this is the first part of the two step .pid system
582   http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
583   */
584   g_pidFile = g_strTempPath.GetBuffer ();
585   g_pidFile += "radiant.pid";
586
587   FILE *pid;
588   pid = fopen( g_pidFile.GetBuffer(), "r" );
589   if ( pid != NULL ) {
590     fclose (pid);
591     CString msg;
592
593     if (remove (g_pidFile.GetBuffer ()) == -1)
594     {
595       msg = "WARNING: Could not delete "; msg += g_pidFile;
596       gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
597     }
598
599     // in debug, never prompt to clean registry, turn console logging auto after a failed start
600 #if !defined(_DEBUG)
601     msg = "Found the file ";
602     msg += g_pidFile;
603     msg += ".\nThis indicates that Radiant failed during the game selection startup last time it was run.\n"
604            "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"
605            "WARNING: the global prefs will be lost if you choose YES.";
606
607     if (gtk_MessageBox (NULL, msg, "Radiant - Reset global startup?", MB_YESNO | MB_ICONQUESTION) == IDYES)
608     {
609       // remove global prefs and shutdown
610       g_PrefsDlg.mGamesDialog.Reset();
611       // remove the prefs file (like a full reset of the registry)
612       //remove (g_PrefsDlg.m_inipath->str);
613       gtk_MessageBox(NULL, "Removed global settings, choose OK to close Radiant.", "Radiant", MB_OK );
614       _exit(-1);
615     }
616     msg = "Logging console output to ";
617     msg += g_strTempPath;
618     msg += "radiant.log\nRefer to the log if Radiant fails to start again.";
619
620     gtk_MessageBox (NULL, msg, "Radiant - Console Log", MB_OK);
621 #endif
622
623     // set without saving, the class is not in a coherent state yet
624     // just do the value change and call to start logging, CGamesDialog will pickup when relevant
625     g_PrefsDlg.mGamesDialog.m_bLogConsole = true;
626     g_PrefsDlg.mGamesDialog.m_bForceLogConsole = true;
627     Sys_LogFile();
628   }
629
630   // create a primary .pid for global init run
631   pid = fopen( g_pidFile.GetBuffer(), "w" );
632   if ( pid ) {
633           fclose( pid );
634   }
635
636   // a safe check to avoid people running broken installations
637   // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
638   // make something idiot proof and someone will make better idiots, this may be overkill
639   // let's leave it disabled in debug mode in any case
640   // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
641 #ifndef _DEBUG
642   //#define CHECK_VERSION
643 #endif
644 #ifdef CHECK_VERSION
645   // locate and open RADIANT_MAJOR and RADIANT_MINOR
646   qboolean bVerIsGood = true;
647   Str ver_file_name;
648   ver_file_name = g_strAppPath;
649   ver_file_name += "RADIANT_MAJOR";
650   FILE *ver_file = fopen (ver_file_name.GetBuffer(), "r");
651   if (ver_file)
652   {
653     char buf[10];
654     int chomp;
655     fread(buf, 1, 10, ver_file);
656     // chomp it (the hard way)
657     chomp = 0;
658     while(buf[chomp] >= '0' && buf[chomp] <= '9')
659       chomp++;
660     buf[chomp] = '\0';
661     if (strcmp(buf, RADIANT_MAJOR_VERSION))
662     {
663       Sys_Printf("ERROR: file RADIANT_MAJOR doesn't match ('%s')\n", buf);
664       bVerIsGood = false;
665     }
666   }
667   else
668   {
669     Sys_Printf("ERROR: can't find RADIANT_MAJOR in '%s'\n", ver_file_name.GetBuffer());
670     bVerIsGood = false;
671   }
672   ver_file_name = g_strAppPath;
673   ver_file_name += "RADIANT_MINOR";
674   ver_file = fopen (ver_file_name.GetBuffer(), "r");
675   if (ver_file)
676   {
677     char buf[10];
678     int chomp;
679     fread(buf, 1, 10, ver_file);
680     // chomp it (the hard way)
681     chomp = 0;
682     while(buf[chomp] >= '0' && buf[chomp] <= '9')
683       chomp++;
684     buf[chomp] = '\0';
685     if (strcmp(buf, RADIANT_MINOR_VERSION))
686     {
687       Sys_Printf("ERROR: file RADIANT_MINOR doesn't match ('%s')\n", buf);
688       bVerIsGood = false;
689     }
690   }
691   else
692   {
693     Sys_Printf("ERROR: can't find RADIANT_MINOR in '%s'\n", ver_file_name.GetBuffer());
694     bVerIsGood = false;
695   }
696   if (!bVerIsGood)
697   {
698     CString msg;
699     msg = "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n";
700     msg += "Make sure you run the right/latest editor binary you installed\n";
701     msg += g_strAppPath; msg += "\n";
702     msg += "Check http://www.qeradiant.com/faq/index.cgi?file=219 for more information";
703     gtk_MessageBox(NULL, msg.GetBuffer(), "Radiant", MB_OK, "http://www.qeradiant.com/faq/index.cgi?file=219");
704     _exit(-1);
705   }
706 #endif
707
708   g_qeglobals.disable_ini = false;
709   g_PrefsDlg.Init();
710
711   // close the primary
712   if ( remove( g_pidFile.GetBuffer () ) == -1 ) {
713     CString msg;
714     msg = "WARNING: Could not delete "; msg += g_pidGameFile;
715     gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
716   }
717
718   /*!
719   now the secondary game dependant .pid file
720   http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
721   */
722   g_pidGameFile = g_PrefsDlg.m_rc_path->str;
723   g_pidGameFile += "radiant-game.pid";
724
725   pid = fopen (g_pidGameFile.GetBuffer(), "r");
726   if (pid != NULL)
727   {
728     fclose (pid);
729     CString msg;
730     if (remove (g_pidGameFile.GetBuffer ()) == -1)
731     {
732       msg = "WARNING: Could not delete "; msg += g_pidGameFile;
733       gtk_MessageBox (NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
734     }
735
736     msg = "Found the file ";
737     msg += g_pidGameFile;
738     msg += ".\nThis indicates that Radiant failed to load the last time it was run.\n"
739            "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"
740            "WARNING: preferences will be lost if you choose YES.";
741
742     // in debug, never prompt to clean registry, turn console logging auto after a failed start
743 #if !defined(_DEBUG)
744     //bleh
745     if (gtk_MessageBox (NULL, msg, "Radiant - Clean Registry?", MB_YESNO | MB_ICONQUESTION) == IDYES)
746     {
747       // remove the game prefs files
748       remove (g_PrefsDlg.m_inipath->str);
749       char buf[PATH_MAX];
750       sprintf(buf, "%sSavedInfo.bin", g_PrefsDlg.m_rc_path->str);
751       remove(buf);
752       // remove the global pref too
753       g_PrefsDlg.mGamesDialog.Reset();
754       gtk_MessageBox(NULL, "Cleaned registry settings, choose OK to close Radiant.\nThe next time Radiant runs it will use default settings.", "Radiant", MB_OK );
755       _exit(-1);
756     }
757     msg = "Logging console output to ";
758     msg += g_strTempPath;
759     msg += "radiant.log\nRefer to the log if Radiant fails to start again.";
760
761     gtk_MessageBox (NULL, msg, "Radiant - Console Log", MB_OK);
762 #endif
763
764     // force console logging on! (will go in prefs too)
765     g_PrefsDlg.mGamesDialog.m_bLogConsole = true;
766     g_PrefsDlg.mGamesDialog.SavePrefs();
767     Sys_LogFile();
768
769     g_PrefsDlg.LoadPrefs();
770
771   } else
772   {
773     // create one, will remove right after entering message loop
774     pid = fopen (g_pidGameFile.GetBuffer(), "w");
775     if (pid)
776       fclose (pid);
777
778     g_PrefsDlg.LoadPrefs();
779
780 #ifndef _DEBUG // I can't be arsed about that prompt in debug mode
781     // if console logging is on in the prefs, warn about performance hit
782     if (g_PrefsDlg.mGamesDialog.m_bLogConsole)
783     {
784       if (gtk_MessageBox (NULL, "Preferences indicate that console logging is on. This affects performance.\n"
785                           "Turn it off?", "Radiant", MB_YESNO | MB_ICONQUESTION) == IDYES)
786       {
787         g_PrefsDlg.mGamesDialog.m_bLogConsole = false;
788         g_PrefsDlg.mGamesDialog.SavePrefs();
789       }
790     }
791 #endif
792     // toggle console logging if necessary
793     Sys_LogFile();
794   }
795
796   // FIXME http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=639
797   // we should search in g_strTempPath, then move over to look at g_strAppPath?
798 #ifdef _WIN32
799   // fine tune the look of the app using a gtk rc file
800   // we try to load an RC file placed in the application directory
801   // build the full path
802   Str sRCFile;
803   sRCFile = g_strAppPath;
804   sRCFile += "radiantgtkrc";
805   // we load that file with g_new in gtk_rc_parse_file (gtkrc.c), change the '/' into '\\'
806   pBuffer = (char *)sRCFile.GetBuffer();
807   for (i=0; i<sRCFile.GetLength(); i++)
808   {
809     if (pBuffer[i]=='/')
810     {
811       pBuffer[i] = '\\';
812     }
813   }
814   // check the file exists
815   if (access(sRCFile.GetBuffer(), R_OK) != 0)
816     Sys_Printf("RC file %s not found\n", sRCFile.GetBuffer());
817   else
818   {
819     Sys_Printf ("Attemping to load RC file %s\n", sRCFile.GetBuffer());
820     gtk_rc_parse (sRCFile.GetBuffer());
821   }
822 #endif
823
824 #ifndef SKIP_SPLASH
825   create_splash();
826 #endif
827
828   if (!QGL_Init(libgl, ""))
829   {
830     Sys_FPrintf (SYS_ERR, "Failed to load OpenGL libraries\n");
831     _exit (1);
832     return 1;
833   }
834
835 #if defined (__linux__) || defined (__APPLE__)
836   if ((qglXQueryExtension == NULL) || (qglXQueryExtension(GDK_DISPLAY(),NULL,NULL) != True))
837   {
838     Sys_FPrintf (SYS_ERR, "glXQueryExtension failed\n");
839     _exit (1);
840     return 1;
841   }
842 #endif
843
844   // redirect Gtk warnings to the console
845   g_log_set_handler( "Gdk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
846                                              G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, NULL);
847   g_log_set_handler( "Gtk", (GLogLevelFlags)(G_LOG_LEVEL_ERROR|G_LOG_LEVEL_CRITICAL|G_LOG_LEVEL_WARNING|
848                                              G_LOG_LEVEL_MESSAGE|G_LOG_LEVEL_INFO|G_LOG_LEVEL_DEBUG), error_redirect, NULL);
849
850   // spog - creates new filters list for the first time
851   g_qeglobals.d_savedinfo.filters = NULL;
852   g_qeglobals.d_savedinfo.filters = FilterUpdate(g_qeglobals.d_savedinfo.filters);
853
854   g_pParentWnd = new MainFrame();
855
856   if ( g_PrefsDlg.m_bLoadLastMap && g_PrefsDlg.m_strLastMap.GetLength() > 0 ) {
857           Map_LoadFile(g_PrefsDlg.m_strLastMap.GetBuffer());
858   } else {
859           Map_New();
860   }
861
862   // load up shaders now that we have the map loaded
863   // eviltypeguy
864   Texture_ShowStartupShaders();
865
866 #ifndef SKIP_SPLASH
867   gdk_window_raise(splash_screen->window);
868   gtk_window_set_transient_for( GTK_WINDOW( splash_screen ), GTK_WINDOW( g_pParentWnd->m_pWidget ) );
869   gtk_timeout_add( 1000, try_destroy_splash, NULL );
870 #endif
871
872   g_pParentWnd->GetSynapseServer().DumpActiveClients();
873
874   //++timo: temporary debug
875   g_pParentWnd->DoWatchBSP();
876
877   gtk_main();
878
879   // close the log file if any
880   // NOTE: don't save prefs past this point!
881   g_PrefsDlg.mGamesDialog.m_bLogConsole = false;
882   // set the console window to NULL to avoid Sys_Printf crashing
883   g_qeglobals_gui.d_edit = NULL;
884   Sys_LogFile();
885
886   // NOTE TTimo not sure what this _exit(0) call is worth
887   //   restricting it to linux build
888 #ifdef __linux__
889   _exit( 0 );
890 #endif
891   return 0;
892 }
893
894 // ydnar: quick and dirty fix, just make the buffer bigger
895 #define BIG_PATH_MAX    4096
896
897 // TTimo: decompose the BSP command into several steps so we can monitor them eventually
898 void QE_ExpandBspString (char *bspaction, GPtrArray *out_array, char *mapname)
899 {
900   const char  *in;
901   char  *out;
902   char  src[BIG_PATH_MAX];
903   char  rsh[BIG_PATH_MAX];
904   char  base[BIG_PATH_MAX];
905
906   strcpy(src, mapname);
907   strlwr(src);
908   in = strstr(src, "maps/");
909   if (!in)
910   {
911     in = strstr(src, "maps/");
912   }
913   if (in)
914   {
915     in += 5;
916     strcpy(base, in);
917     out = base;
918     while (*out)
919     {
920       if (*out == '\\')
921       {
922         *out = '/';
923       }
924       out++;
925     }
926   } else
927   {
928     ExtractFileName (mapname, base);
929   }
930
931   // this important step alters the map name to add fs_game
932   // NOTE: it used to add fs_basepath too
933   // the fs_basepath addition moved to being in the project file during the bug fixing rush
934   // but it may not have been the right thing to do
935
936   // HACK: halflife compiler tools don't support -fs_game
937   // HACK: neither does JKII/SoF2/ etc..
938   // do we use & have fs_game?
939
940   if (g_pGameDescription->mGameFile != "hl.game" &&
941       *ValueForKey(g_qeglobals.d_project_entity,"gamename") != '\0')
942     {
943       // set with fs_game
944       sprintf(src, "-fs_game %s \"%s\"", ValueForKey(g_qeglobals.d_project_entity,"gamename"), mapname);
945     }
946     else
947     {
948       sprintf(src, "\"%s\"", mapname);
949   }
950
951   rsh[0] = 0;
952
953   QE_ConvertDOSToUnixName(src, src);
954
955   // initialise the first step
956   out = new char[BIG_PATH_MAX]; //% PATH_MAX
957   g_ptr_array_add( out_array, out );
958
959   in = ValueForKey( g_qeglobals.d_project_entity, bspaction );
960   while (*in)
961   {
962     if (in[0] == '!')
963     {
964       strcpy (out, rsh);
965       out += strlen(rsh);
966       in++;
967       continue;
968     }
969     if (in[0] == '#')
970     {
971       char tmp[2048];
972       // we process these only if monitoring
973       if (g_PrefsDlg.m_bWatchBSP)
974       {
975         // -connect global option (the only global option so far anyway)
976         strcpy (tmp, " -connect 127.0.0.1:39000 ");
977         strcpy (out, tmp);
978         out += strlen(tmp);
979       }
980       in++;
981       continue;
982     }
983     if (in[0] == '$')
984     {
985       // $ expansion
986       strcpy (out, src);
987       out += strlen(src);
988       in++;
989       continue;
990     }
991     if (in[0] == '@')
992     {
993       *out++ = '"';
994       in++;
995       continue;
996     }
997     if (in[0] == '&')
998       if (in[1] == '&')
999       {
1000         // start a new step
1001         *out = 0;
1002         in = in + 2;
1003         out = new char[BIG_PATH_MAX];   //% PATH_MAX
1004         g_ptr_array_add( out_array, out );
1005       }
1006     *out++ = *in++;
1007   }
1008   *out = 0;
1009 }
1010
1011 void FindReplace(CString& strContents, const char* pTag, const char* pValue)
1012 {
1013   if (strcmp(pTag, pValue) == 0)
1014     return;
1015   for (int nPos = strContents.Find(pTag); nPos >= 0; nPos = strContents.Find(pTag))
1016   {
1017     int nRightLen = strContents.GetLength() - strlen(pTag) - nPos;
1018     CString strLeft = strContents.Left(nPos);
1019     CString strRight = strContents.Right(nRightLen);
1020     strLeft += pValue;
1021     strLeft += strRight;
1022     strContents = strLeft;
1023   }
1024 }
1025
1026 // save the map, deals with regioning
1027 void SaveWithRegion(char *name)
1028 {
1029   strcpy (name, currentmap);
1030   if (region_active)
1031   {
1032     // temporary cut the region to save regular map
1033     region_active = false;
1034     Map_SaveFile (name, false);
1035     region_active = true;
1036     StripExtension (name);
1037     strcat (name, ".reg");
1038   }
1039
1040   Map_SaveFile (name, region_active);
1041 }
1042
1043 void RunBsp (char *command)
1044 {
1045   GPtrArray *sys;
1046   char  batpath[BIG_PATH_MAX];  //% PATH_MAX
1047   char  temppath[BIG_PATH_MAX]; //% PATH_MAX
1048   char  name[BIG_PATH_MAX];             //% PATH_MAX
1049   char  cWork[BIG_PATH_MAX];    //% PATH_MAX
1050   FILE  *hFile;
1051   unsigned int   i;
1052
1053   SetInspectorMode(W_CONSOLE);
1054
1055   strcpy (temppath, g_strTempPath.GetBuffer ());
1056
1057   SaveWithRegion(name);
1058
1059   const char *rsh = ValueForKey(g_qeglobals.d_project_entity, "rshcmd");
1060   if (rsh == NULL)
1061   {
1062     CString strPath, strFile;
1063
1064     ExtractPath_and_Filename(name, strPath, strFile);
1065     AddSlash(strPath);
1066     strncpy(cWork, strPath, 1024);
1067     strcat(cWork, strFile);
1068   } else
1069   {
1070     strcpy(cWork, name);
1071   }
1072
1073   // get the array ready
1074   //++timo TODO: free the array, free the strings ourselves with delete[]
1075   sys = g_ptr_array_new();
1076
1077   QE_ExpandBspString (command, sys, cWork);
1078
1079   if (g_PrefsDlg.m_bWatchBSP)
1080   {
1081     // grab the file name for engine running
1082     char *bspname = new char[1024];
1083     ExtractFileName( currentmap, bspname );
1084     StripExtension( bspname );
1085     g_pParentWnd->GetWatchBSP()->DoMonitoringLoop( sys, bspname );
1086   } else
1087   {
1088     // write all the steps in a single BAT / .sh file and run it, don't bother monitoring it
1089     CString strSys;
1090     for (i=0; i < sys->len; i++ )
1091     {
1092       strSys += (char *)g_ptr_array_index( sys, i);
1093 #ifdef _WIN32  // write temp\junk.txt in win32... NOTE: stops output to shell prompt window
1094       if (i==0)
1095         strSys += " >";
1096       else
1097         strSys += " >>";
1098       strSys += "\"";
1099       strSys += temppath;
1100       strSys += "junk.txt\"";
1101 #endif
1102       strSys += "\n";
1103     };
1104
1105 #if defined (__linux__) || defined (__APPLE__)
1106
1107     // write qe3bsp.sh
1108     sprintf (batpath, "%sqe3bsp.sh", temppath);
1109     Sys_Printf("Writing the compile script to '%s'\n", batpath);
1110     Sys_Printf("The build output will be saved in '%sjunk.txt'\n", temppath);
1111     hFile = fopen(batpath, "w");
1112     if (!hFile)
1113       Error ("Can't write to %s", batpath);
1114     fprintf (hFile, "#!/bin/sh \n\n");
1115     fprintf (hFile, strSys.GetBuffer());
1116     fclose (hFile);
1117     chmod (batpath, 0744);
1118 #endif
1119
1120 #ifdef _WIN32
1121     sprintf (batpath, "%sqe3bsp.bat", temppath);
1122     Sys_Printf("Writing the compile script to '%s'\n", batpath);
1123     Sys_Printf("The build output will be saved in '%sjunk.txt'\n", temppath);
1124     hFile = fopen(batpath, "w");
1125     if (!hFile)
1126       Error ("Can't write to %s", batpath);
1127     fprintf (hFile, strSys.GetBuffer());
1128     fclose (hFile);
1129 #endif
1130
1131     Pointfile_Delete ();
1132
1133 #if defined (__linux__) || defined (__APPLE__)
1134
1135     pid_t pid;
1136
1137     pid = fork ();
1138     switch (pid)
1139     {
1140     case -1:
1141       Error ("CreateProcess failed");
1142       break;
1143     case 0:
1144       execlp (batpath, batpath, NULL);
1145       printf ("execlp error !");
1146       _exit (0);
1147       break;
1148     default:
1149       break;
1150     }
1151 #endif
1152
1153 #ifdef _WIN32
1154     Sys_Printf ("Running bsp command...\n");
1155     Sys_Printf ("\n%s\n", strSys.GetBuffer());
1156
1157     WinExec( batpath, SW_SHOWNORMAL );
1158 #endif
1159   }
1160 #ifdef _DEBUG
1161   // yeah, do it .. but not now right before 1.1-TA-beta release
1162   Sys_Printf("TODO: erase GPtrArray\n");
1163 #endif
1164 }
1165
1166 #if 0
1167
1168 #ifdef _WIN32
1169
1170 int WINAPI QEW_SetupPixelFormat(HDC hDC, qboolean zbuffer )
1171 {
1172   static PIXELFORMATDESCRIPTOR pfd = {
1173     sizeof(PIXELFORMATDESCRIPTOR),  // size of this pfd
1174     1,                              // version number
1175     PFD_DRAW_TO_WINDOW |            // support window
1176     PFD_SUPPORT_OPENGL |            // support OpenGL
1177     PFD_DOUBLEBUFFER,               // double buffered
1178     PFD_TYPE_RGBA,                  // RGBA type
1179     24,                             // 24-bit color depth
1180     0, 0, 0, 0, 0, 0,               // color bits ignored
1181     0,                              // no alpha buffer
1182     0,                              // shift bit ignored
1183     0,                              // no accumulation buffer
1184     0, 0, 0, 0,                     // accum bits ignored
1185     32,                             // depth bits
1186     0,                              // no stencil buffer
1187     0,                              // no auxiliary buffer
1188     PFD_MAIN_PLANE,                 // main layer
1189     0,                              // reserved
1190     0, 0, 0                         // layer masks ignored
1191   };                              //
1192   int pixelformat = 0;
1193
1194   zbuffer = true;
1195   if ( !zbuffer )
1196     pfd.cDepthBits = 0;
1197
1198   if ( (pixelformat = ChoosePixelFormat(hDC, &pfd)) == 0 )
1199   {
1200     LPVOID lpMsgBuf;
1201     FormatMessage(
1202                  FORMAT_MESSAGE_ALLOCATE_BUFFER |
1203                  FORMAT_MESSAGE_FROM_SYSTEM |
1204                  FORMAT_MESSAGE_IGNORE_INSERTS,
1205                  NULL,
1206                  GetLastError(),
1207                  MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language
1208                  (LPTSTR) &lpMsgBuf,
1209                  0,
1210                  NULL
1211                  );
1212     Sys_FPrintf (SYS_WRN, "GetLastError: %s", lpMsgBuf);
1213     Error ("ChoosePixelFormat failed");
1214   }
1215
1216   if (!SetPixelFormat(hDC, pixelformat, &pfd))
1217     Error ("SetPixelFormat failed");
1218
1219   return pixelformat;
1220 }
1221
1222 #endif
1223
1224 #endif