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