2 Copyright (C) 1999-2007 id Software, Inc. and contributors.
3 For a list of contributors, see the accompanying CONTRIBUTORS file.
5 This file is part of GtkRadiant.
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.
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.
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
22 #if defined ( __linux__ ) || defined ( __APPLE__ )
37 #include <gtk/gtkgl.h>
38 #include <glib/gi18n.h>
41 #include <sys/types.h>
50 bool g_bBuildList = false;
54 // =============================================================================
57 // get rid of it when debugging
58 #if defined ( _DEBUG )
62 static GtkWidget *splash_screen;
64 // called based on a timer, or in particular cases when we don't want to keep it around
65 gint try_destroy_splash( gpointer data ){
66 if ( splash_screen ) {
67 gtk_widget_destroy( splash_screen );
73 static void create_splash(){
74 GtkWidget *alert_frame, *alert_frame1, *pixmap;
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 );
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 );
85 alert_frame = gtk_frame_new( NULL );
86 gtk_widget_show( alert_frame );
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 );
92 pixmap = gtk_preview_new( GTK_PREVIEW_COLOR );
93 gtk_widget_show( pixmap );
94 gtk_container_add( GTK_CONTAINER( alert_frame ), pixmap );
97 guint16 width, height;
100 str = g_strGameToolsPath;
101 str += "bitmaps/splash.bmp";
103 unsigned char* load_bitmap_file( const char* filename, guint16* width, guint16* height );
104 buf = load_bitmap_file( str.GetBuffer(), &width, &height );
107 str = g_strBitmapsPath;
110 buf = load_bitmap_file( str.GetBuffer(), &width, &height );
114 GtkPreview *preview = GTK_PREVIEW( pixmap );
115 gtk_preview_size( preview, width, height );
116 for ( int y = 0; y < height; y++ )
117 gtk_preview_draw_row( preview, buf + y * width * 3, 0, y, width );
120 gtk_widget_show_all( splash_screen );
122 while ( gtk_events_pending() )
123 gtk_main_iteration();
126 // =============================================================================
129 #if defined ( __linux__ ) || defined ( __APPLE__ )
131 /* A short game name, could be used as argv[0] */
132 static char game_name[100] = "";
134 /* The directory where the data files can be found (run directory) */
135 static char datapath[PATH_MAX];
137 char *loki_gethomedir( void ){
140 home = getenv( "HOME" );
141 if ( home == NULL ) {
146 while ( ( pwd = getpwent() ) != NULL )
148 if ( pwd->pw_uid == id ) {
158 /* Must be called BEFORE loki_initialize */
159 void loki_setgamename( const char *n ){
160 strncpy( game_name, n, sizeof( game_name ) );
164 /* Code to determine the mount point of a CD-ROM */
165 int loki_getmountpoint( const char *device, char *mntpt, int max_size ){
166 char devpath[PATH_MAX], mntdevpath[PATH_MAX];
168 struct mntent *mntent;
171 /* Nothing to do with no device file */
172 if ( device == NULL ) {
177 /* Get the fully qualified path of the CD-ROM device */
178 if ( realpath( device, devpath ) == NULL ) {
179 perror( "realpath() on your CD-ROM failed" );
183 /* Get the mount point */
185 memset( mntpt, 0, max_size );
186 mountfp = setmntent( _PATH_MNTTAB, "r" );
187 if ( mountfp != NULL ) {
189 while ( ( mntent = getmntent( mountfp ) ) != NULL )
191 char *tmp, mntdev[1024];
193 strcpy( mntdev, mntent->mnt_fsname );
194 if ( strcmp( mntent->mnt_type, "supermount" ) == 0 ) {
195 tmp = strstr( mntent->mnt_opts, "dev=" );
197 strcpy( mntdev, tmp + strlen( "dev=" ) );
198 tmp = strchr( mntdev, ',' );
204 if ( strncmp( mntdev, "/dev", 4 ) ||
205 realpath( mntdev, mntdevpath ) == NULL ) {
208 if ( strcmp( mntdevpath, devpath ) == 0 ) {
210 assert( (int)strlen( mntent->mnt_dir ) < max_size );
211 strncpy( mntpt, mntent->mnt_dir, max_size - 1 );
212 mntpt[max_size - 1] = '\0';
216 endmntent( mountfp );
223 This function gets the directory containing the running program.
224 argv0 - the 0'th argument to the program
227 // I don't understand this function. It looks like something cut from another piece of software
228 // we somehow get the g_strAppPath from it, but it's done through a weird scan across $PATH env. var.
229 // even worse, it doesn't behave the same in all cases .. works well when ran through gdb and borks when ran from a shell
230 void loki_initpaths( char *argv0 ){
231 char temppath[PATH_MAX]; //, env[100];
232 char *home; //, *ptr, *data_env;
234 home = loki_gethomedir();
235 if ( home == NULL ) {
239 if ( *game_name == 0 ) { /* Game name defaults to argv[0] */
240 loki_setgamename( argv0 );
243 strcpy( temppath, argv0 ); /* If this overflows, it's your own fault :) */
244 if ( !strrchr( temppath, '/' ) ) {
250 path = getenv( "PATH" );
253 /* Initialize our filename variable */
256 /* Get next entry from path variable */
257 last = strchr( path, ':' );
259 last = path + strlen( path );
262 /* Perform tilde expansion */
263 if ( *path == '~' ) {
264 strcpy( temppath, home );
268 /* Fill in the rest of the filename */
269 if ( last > ( path + 1 ) ) {
270 strncat( temppath, path, ( last - path ) );
271 strcat( temppath, "/" );
273 strcat( temppath, "./" );
274 strcat( temppath, argv0 );
276 /* See if it exists, and update path */
277 if ( access( temppath, X_OK ) == 0 ) {
282 } while ( *last && !found );
287 /* Increment argv0 to the basename */
288 argv0 = strrchr( argv0, '/' ) + 1;
291 /* Now canonicalize it to a full pathname for the data path */
292 if ( realpath( temppath, datapath ) ) {
293 /* There should always be '/' in the path */
294 *( strrchr( datapath, '/' ) ) = '\0';
298 char *loki_getdatapath( void ){
305 // =============================================================================
307 void error_redirect( const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data ){
308 gboolean in_recursion;
312 in_recursion = ( log_level & G_LOG_FLAG_RECURSION ) != 0;
313 is_fatal = ( log_level & G_LOG_FLAG_FATAL ) != 0;
314 log_level = (GLogLevelFlags) ( log_level & G_LOG_LEVEL_MASK );
317 message = "(NULL) message";
321 strcpy( buf, domain );
330 case G_LOG_LEVEL_ERROR:
331 if ( in_recursion ) {
332 strcat( buf, "ERROR (recursed) **: " );
335 strcat( buf, "ERROR **: " );
338 case G_LOG_LEVEL_CRITICAL:
339 if ( in_recursion ) {
340 strcat( buf, "CRITICAL (recursed) **: " );
343 strcat( buf, "CRITICAL **: " );
346 case G_LOG_LEVEL_WARNING:
347 if ( in_recursion ) {
348 strcat( buf, "WARNING (recursed) **: " );
351 strcat( buf, "WARNING **: " );
354 case G_LOG_LEVEL_MESSAGE:
355 if ( in_recursion ) {
356 strcat( buf, "Message (recursed): " );
359 strcat( buf, "Message: " );
362 case G_LOG_LEVEL_INFO:
363 if ( in_recursion ) {
364 strcat( buf, "INFO (recursed): " );
367 strcat( buf, "INFO: " );
370 case G_LOG_LEVEL_DEBUG:
371 if ( in_recursion ) {
372 strcat( buf, "DEBUG (recursed): " );
375 strcat( buf, "DEBUG: " );
379 /* we are used for a log level that is not defined by GLib itself,
380 * try to make the best out of it.
382 if ( in_recursion ) {
383 strcat( buf, "LOG (recursed:" );
386 strcat( buf, "LOG (" );
389 gchar string[] = "0x00): ";
390 gchar *p = string + 2;
393 i = g_bit_nth_msf( log_level, -1 );
396 *p = '0' + ( i & 0xf );
401 strcat( buf, string );
404 strcat( buf, "): " );
408 strcat( buf, message );
410 strcat( buf, "\naborting...\n" );
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);
422 #define GETTEXT_PACKAGE "radiant"
423 #define LOCALEDIR "lang"
425 int main( int argc, char* argv[] ) {
431 Rambetter on Sat Nov 13, 2010:
433 The following line fixes parsing and writing of floating point numbers in locales such as
434 Italy, Germany, and others outside of en_US. In particular, in such problem locales, users
435 are not able to use certain map entities such as "light" because the definitions of these entities
436 in the entity definition files contain floating point values written in the standard "C" format
437 (containing a dot instead of, for example, a comma). The call sscanf() is all over the code,
438 including parsing entity definition files and reading Radiant preferences. sscanf() is sensitive
439 to locale (in particular when reading floating point numbers).
441 The line below is the minimalistic way to address only this particular problem - the parsing
442 and writing of floating point values. There may be other yet-undiscovered bugs related to
443 locale still lingering in the code. When such bugs are discovered, they should be addressed by
444 setting more than just "LC_NUMERIC=C" (for example LC_CTYPE for regular expression matching)
445 or by fixing the problem in the actual code instead of fiddling with LC_* variables.
447 Another way to fix the floating point format problem is to locate all calls such as *scanf()
448 and *printf() in the code and replace them with other functions. However, we're also using
449 external libraries such as libxml and [maybe?] they use locale to parse their numeric values.
450 I'm just saying, it may get ugly if we try to fix the problem without setting LC_NUMERIC.
452 Usage of sscanf() throughout the code looks like so:
453 sscanf(str, "%f %f %f", &val1, &val2, &val3);
454 Code like this exists in many files, here are 4 examples:
455 tools/quake3/q3map2/light.c
456 tools/quake3/q3map2/model.c
457 radiant/preferences.cpp
458 plugins/entity/miscmodel.cpp
460 Also affected are printf() calls when using formats that contain "%f".
462 I did some research and putenv() seems to be the best choice for being cross-platform. It
463 used to be a function in Windows (now deprecated):
464 http://msdn.microsoft.com/en-us/library/ms235321(VS.80).aspx
465 And of course it's defined in UNIX.
467 One more thing. the gtk_init() call below modifies all of the locale settings. In fact if it
468 weren't for gtk_init(), we wouldn't have to set LC_NUMERIC (parsing of floating points with
469 a dot works just fine before the gtk_init() call on a sample Linux system). If we were to
470 just setlocale() here, it would get clobbered by gtk_init(). So instead of using setlocale()
471 _after_ gtk_init(), I chose to fix this problem via environment variable. I think it's cleaner
474 putenv( "LC_NUMERIC=C" );
477 libgl = "opengl32.dll";
480 #if defined ( __linux__ )
481 libgl = "libGL.so.1";
485 libgl = "/usr/X11R6/lib/libGL.1.dylib";
488 #if defined ( __linux__ ) || defined ( __APPLE__ )
489 // Give away unnecessary root privileges.
490 // Important: must be done before calling gtk_init().
494 if ( geteuid() == 0 && ( loginname = getlogin() ) != NULL && ( pw = getpwnam( loginname ) ) != NULL ) {
495 setuid( pw->pw_uid );
500 bindtextdomain( GETTEXT_PACKAGE, LOCALEDIR );
501 bind_textdomain_codeset( GETTEXT_PACKAGE, "UTF-8" );
502 textdomain( GETTEXT_PACKAGE );
503 // gtk_disable_setlocale();
505 gtk_init( &argc, &argv );
506 gtk_gl_init( &argc, &argv );
507 gdk_gl_init( &argc, &argv );
509 // TODO: Find a better place to call this.
510 gtk_glwidget_create_font();
512 if ( ( ptr = getenv( "Q3R_LIBGL" ) ) != NULL ) {
516 for ( i = 1; i < argc; i++ )
518 char* param = argv[i];
520 if ( param[0] == '-' && param[1] == '-' ) {
523 if ( ( strcmp( param, "libgl" ) == 0 ) && ( i != argc ) ) {
525 argv[i] = argv[i + 1] = NULL;
528 else if ( strcmp( param, "builddefs" ) == 0 ) {
535 for ( i = 1; i < argc; i++ )
537 for ( k = i; k < argc; k++ )
538 if ( argv[k] != NULL ) {
544 for ( j = i + k; j < argc; j++ )
545 argv[j - k] = argv[j];
553 g_strPluginsDir = "plugins/";
554 g_strModulesDir = "modules/";
557 // get path to the editor
558 char* pBuffer = g_strAppPath.GetBufferSetLength( _MAX_PATH + 1 );
559 GetModuleFileName( NULL, pBuffer, _MAX_PATH );
560 pBuffer[g_strAppPath.ReverseFind( '\\' ) + 1] = '\0';
561 QE_ConvertDOSToUnixName( pBuffer, pBuffer );
562 g_strAppPath.ReleaseBuffer();
564 g_strBitmapsPath = g_strAppPath;
565 g_strBitmapsPath += "bitmaps/";
567 CGameDialog::UpdateNetrun( false ); // read the netrun configuration
569 if ( CGameDialog::GetNetrun() ) {
570 // we have to find a per-user g_strTempPath
571 // this behaves the same as on Linux
572 g_strTempPath = getenv( "USERPROFILE" );
573 if ( !g_strTempPath.GetLength() ) {
575 msg = "Radiant is configured to run from a network installation.\n";
576 msg += "I couldn't find the environement variable USERPROFILE\n";
577 msg += "I'm going to use C:\\RadiantSettings. Please set USERPROFILE\n";
578 gtk_MessageBox( NULL, msg, "Radiant - Network mode", MB_OK );
579 g_strTempPath = "C:\\";
581 g_strTempPath += "\\RadiantSettings\\";
582 Q_mkdir( g_strTempPath.GetBuffer(), 0755 );
583 g_strTempPath += RADIANT_VERSION;
584 g_strTempPath += "\\";
585 Q_mkdir( g_strTempPath.GetBuffer(), 0755 );
589 // use the core path as temp (to save commandlist.txt, and do the .pid files)
590 g_strTempPath = g_strAppPath;
595 #if defined ( __linux__ ) || defined ( __APPLE__ )
597 home = g_get_home_dir();
600 Q_mkdir( home.GetBuffer(), 0775 );
601 home += RADIANT_VERSION;
602 Q_mkdir( home.GetBuffer(), 0775 );
603 g_strTempPath = home.GetBuffer();
604 AddSlash( g_strTempPath );
606 loki_initpaths( argv[0] );
608 // NOTE: we build g_strAppPath with a '/' (or '\' on WIN32)
609 // it's a general convention in Radiant to have the slash at the end of directories
611 realpath( loki_getdatapath(), real );
612 if ( real[strlen( real ) - 1] != '/' ) {
618 // radiant is installed in the parent dir of "tools/"
619 // NOTE: this is not very easy for debugging
620 // maybe add options to lookup in several places?
621 // (for now I had to create symlinks)
622 g_strBitmapsPath = g_strAppPath;
623 g_strBitmapsPath += "bitmaps/";
625 // we will set this right after the game selection is done
626 g_strGameToolsPath = g_strAppPath;
631 g_strDTDPath = g_strAppPath;
632 g_strDTDPath += "dtds/";
635 the global prefs loading / game selection dialog might fail for any reason we don't know about
636 we need to catch when it happens, to cleanup the stateful prefs which might be killing it
637 and to turn on console logging for lookup of the problem
638 this is the first part of the two step .pid system
640 g_pidFile = g_strTempPath.GetBuffer();
641 g_pidFile += "radiant.pid";
644 pid = fopen( g_pidFile.GetBuffer(), "r" );
649 if ( remove( g_pidFile.GetBuffer() ) == -1 ) {
650 msg = "WARNING: Could not delete "; msg += g_pidFile;
651 gtk_MessageBox( NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
654 // in debug, never prompt to clean registry, turn console logging auto after a failed start
655 #if !defined( _DEBUG )
656 msg = "Found the file ";
658 msg += ".\nThis indicates that Radiant failed during the game selection startup last time it was run.\n"
659 "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"
660 "WARNING: the global prefs will be lost if you choose YES.";
662 if ( gtk_MessageBox( NULL, msg, "Radiant - Reset global startup?", MB_YESNO | MB_ICONQUESTION ) == IDYES ) {
663 // remove global prefs and shutdown
664 g_PrefsDlg.mGamesDialog.Reset();
665 // remove the prefs file (like a full reset of the registry)
666 //remove (g_PrefsDlg.m_inipath->str);
667 gtk_MessageBox( NULL, "Removed global settings, choose OK to close Radiant.", "Radiant", MB_OK );
670 msg = "Logging console output to ";
671 msg += g_strTempPath;
672 msg += "radiant.log\nRefer to the log if Radiant fails to start again.";
674 gtk_MessageBox( NULL, msg, "Radiant - Console Log", MB_OK );
677 // set without saving, the class is not in a coherent state yet
678 // just do the value change and call to start logging, CGamesDialog will pickup when relevant
679 g_PrefsDlg.mGamesDialog.m_bLogConsole = true;
680 g_PrefsDlg.mGamesDialog.m_bForceLogConsole = true;
684 // create a primary .pid for global init run
685 pid = fopen( g_pidFile.GetBuffer(), "w" );
690 // a safe check to avoid people running broken installations
691 // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
692 // make something idiot proof and someone will make better idiots, this may be overkill
693 // let's leave it disabled in debug mode in any case
695 //#define CHECK_VERSION
698 // locate and open RADIANT_MAJOR and RADIANT_MINOR
699 qboolean bVerIsGood = true;
701 ver_file_name = g_strAppPath;
702 ver_file_name += "RADIANT_MAJOR";
703 FILE *ver_file = fopen( ver_file_name.GetBuffer(), "r" );
707 fread( buf, 1, 10, ver_file );
708 // chomp it (the hard way)
710 while ( buf[chomp] >= '0' && buf[chomp] <= '9' )
713 if ( strcmp( buf, RADIANT_MAJOR_VERSION ) ) {
714 Sys_Printf( "ERROR: file RADIANT_MAJOR doesn't match ('%s')\n", buf );
720 Sys_Printf( "ERROR: can't find RADIANT_MAJOR in '%s'\n", ver_file_name.GetBuffer() );
723 ver_file_name = g_strAppPath;
724 ver_file_name += "RADIANT_MINOR";
725 ver_file = fopen( ver_file_name.GetBuffer(), "r" );
729 fread( buf, 1, 10, ver_file );
730 // chomp it (the hard way)
732 while ( buf[chomp] >= '0' && buf[chomp] <= '9' )
735 if ( strcmp( buf, RADIANT_MINOR_VERSION ) ) {
736 Sys_Printf( "ERROR: file RADIANT_MINOR doesn't match ('%s')\n", buf );
742 Sys_Printf( "ERROR: can't find RADIANT_MINOR in '%s'\n", ver_file_name.GetBuffer() );
747 msg = "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n";
748 msg += "Make sure you run the right/latest editor binary you installed\n";
749 msg += g_strAppPath; msg += "\n";
750 msg += "Check http://www.qeradiant.com/faq/index.cgi?file=219 for more information";
751 gtk_MessageBox( NULL, msg.GetBuffer(), "Radiant", MB_OK, "http://www.qeradiant.com/faq/index.cgi?file=219" );
756 g_qeglobals.disable_ini = false;
760 if ( remove( g_pidFile.GetBuffer() ) == -1 ) {
762 msg = "WARNING: Could not delete "; msg += g_pidGameFile;
763 gtk_MessageBox( NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
767 now the secondary game dependant .pid file
769 g_pidGameFile = g_PrefsDlg.m_rc_path->str;
770 g_pidGameFile += "radiant-game.pid";
772 pid = fopen( g_pidGameFile.GetBuffer(), "r" );
776 if ( remove( g_pidGameFile.GetBuffer() ) == -1 ) {
777 msg = "WARNING: Could not delete "; msg += g_pidGameFile;
778 gtk_MessageBox( NULL, msg, "Radiant", MB_OK | MB_ICONERROR );
781 msg = "Found the file ";
782 msg += g_pidGameFile;
783 msg += ".\nThis indicates that Radiant failed to load the last time it was run.\n"
784 "Choose YES to clean Radiant's registry settings and shut down Radiant.\n"
785 "WARNING: preferences will be lost if you choose YES.";
787 // in debug, never prompt to clean registry, turn console logging auto after a failed start
788 #if !defined( _DEBUG )
790 if ( gtk_MessageBox( NULL, msg, "Radiant - Clean Registry?", MB_YESNO | MB_ICONQUESTION ) == IDYES ) {
791 // remove the game prefs files
792 remove( g_PrefsDlg.m_inipath->str );
794 sprintf( buf, "%sSavedInfo.bin", g_PrefsDlg.m_rc_path->str );
796 // remove the global pref too
797 g_PrefsDlg.mGamesDialog.Reset();
798 gtk_MessageBox( NULL, "Cleaned registry settings, choose OK to close Radiant.\nThe next time Radiant runs it will use default settings.", "Radiant", MB_OK );
801 msg = "Logging console output to ";
802 msg += g_strTempPath;
803 msg += "radiant.log\nRefer to the log if Radiant fails to start again.";
805 gtk_MessageBox( NULL, msg, "Radiant - Console Log", MB_OK );
808 // force console logging on! (will go in prefs too)
809 g_PrefsDlg.mGamesDialog.m_bLogConsole = true;
810 g_PrefsDlg.mGamesDialog.SavePrefs();
813 g_PrefsDlg.LoadPrefs();
818 // create one, will remove right after entering message loop
819 pid = fopen( g_pidGameFile.GetBuffer(), "w" );
824 g_PrefsDlg.LoadPrefs();
826 #ifndef _DEBUG // I can't be arsed about that prompt in debug mode
827 // if console logging is on in the prefs, warn about performance hit
828 if ( g_PrefsDlg.mGamesDialog.m_bLogConsole ) {
829 if ( gtk_MessageBox( NULL, "Preferences indicate that console logging is on. This affects performance.\n"
830 "Turn it off?", "Radiant", MB_YESNO | MB_ICONQUESTION ) == IDYES ) {
831 g_PrefsDlg.mGamesDialog.m_bLogConsole = false;
832 g_PrefsDlg.mGamesDialog.SavePrefs();
836 // toggle console logging if necessary
840 // we should search in g_strTempPath, then move over to look at g_strAppPath?
842 // fine tune the look of the app using a gtk rc file
843 // we try to load an RC file placed in the application directory
844 // build the full path
846 sRCFile = g_strAppPath;
847 sRCFile += "radiantgtkrc";
848 // we load that file with g_new in gtk_rc_parse_file (gtkrc.c), change the '/' into '\\'
849 pBuffer = (char *)sRCFile.GetBuffer();
850 for ( i = 0; i < sRCFile.GetLength(); i++ )
852 if ( pBuffer[i] == '/' ) {
856 // check the file exists
857 if ( access( sRCFile.GetBuffer(), R_OK ) != 0 ) {
858 Sys_Printf( "RC file %s not found\n", sRCFile.GetBuffer() );
862 Sys_Printf( "Attemping to load RC file %s\n", sRCFile.GetBuffer() );
863 gtk_rc_parse( sRCFile.GetBuffer() );
871 if ( !QGL_Init( libgl, "" ) ) {
872 Sys_FPrintf( SYS_ERR, "Failed to load OpenGL libraries\n" );
877 #if defined ( __linux__ ) || defined ( __APPLE__ )
878 if ( ( qglXQueryExtension == NULL ) || ( qglXQueryExtension( GDK_DISPLAY(),NULL,NULL ) != True ) ) {
879 Sys_FPrintf( SYS_ERR, "glXQueryExtension failed\n" );
885 // redirect Gtk warnings to the console
886 g_log_set_handler( "Gdk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
887 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG ), error_redirect, NULL );
888 g_log_set_handler( "Gtk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
889 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG ), error_redirect, NULL );
891 // spog - creates new filters list for the first time
892 g_qeglobals.d_savedinfo.filters = NULL;
893 g_qeglobals.d_savedinfo.filters = FilterAddBase( g_qeglobals.d_savedinfo.filters );
895 g_pParentWnd = new MainFrame();
897 // If the first parameter is a .map, load that.
898 if( g_argc > 1 && IsMap( g_argv[1] ) ){
899 Map_LoadFile( g_argv[1] );
901 else if ( g_PrefsDlg.m_bLoadLastMap && g_PrefsDlg.m_strLastMap.GetLength() > 0 ) {
902 Map_LoadFile( g_PrefsDlg.m_strLastMap.GetBuffer() );
908 // load up shaders now that we have the map loaded
910 Texture_ShowStartupShaders();
913 gdk_window_raise( splash_screen->window );
914 gtk_window_set_transient_for( GTK_WINDOW( splash_screen ), GTK_WINDOW( g_pParentWnd->m_pWidget ) );
915 gtk_timeout_add( 1000, try_destroy_splash, NULL );
918 g_pParentWnd->GetSynapseServer().DumpActiveClients();
920 //++timo: temporary debug
921 g_pParentWnd->DoWatchBSP();
925 // close the log file if any
926 // NOTE: don't save prefs past this point!
927 g_PrefsDlg.mGamesDialog.m_bLogConsole = false;
928 // set the console window to NULL to avoid Sys_Printf crashing
929 g_qeglobals_gui.d_edit = NULL;
932 // NOTE TTimo not sure what this _exit(0) call is worth
933 // restricting it to linux build
940 // ydnar: quick and dirty fix, just make the buffer bigger
941 #define BIG_PATH_MAX 4096
943 // TTimo: decompose the BSP command into several steps so we can monitor them eventually
944 void QE_ExpandBspString( char *bspaction, GPtrArray *out_array, char *mapname ){
947 char src[BIG_PATH_MAX];
948 char rsh[BIG_PATH_MAX];
949 char base[BIG_PATH_MAX];
951 strcpy( src, mapname );
953 in = strstr( src, "maps/" );
955 in = strstr( src, "maps/" );
963 if ( *out == '\\' ) {
971 ExtractFileName( mapname, base );
974 // this important step alters the map name to add fs_game
975 // NOTE: it used to add fs_basepath too
976 // the fs_basepath addition moved to being in the project file during the bug fixing rush
977 // but it may not have been the right thing to do
979 // HACK: halflife compiler tools don't support -fs_game
980 // HACK: neither does JKII/SoF2/ etc..
981 // do we use & have fs_game?
983 if ( g_pGameDescription->mGameFile != "hl.game" &&
984 *ValueForKey( g_qeglobals.d_project_entity,"gamename" ) != '\0' ) {
986 sprintf( src, "-fs_game %s \"%s\"", ValueForKey( g_qeglobals.d_project_entity,"gamename" ), mapname );
990 sprintf( src, "\"%s\"", mapname );
995 QE_ConvertDOSToUnixName( src, src );
997 // initialise the first step
998 out = new char[BIG_PATH_MAX]; //% PATH_MAX
999 g_ptr_array_add( out_array, out );
1001 in = ValueForKey( g_qeglobals.d_project_entity, bspaction );
1004 if ( in[0] == '!' ) {
1006 out += strlen( rsh );
1010 if ( in[0] == '#' ) {
1012 // we process these only if monitoring
1013 if ( g_PrefsDlg.m_bWatchBSP ) {
1014 // -connect global option (the only global option so far anyway)
1015 strcpy( tmp, " -connect 127.0.0.1:39000 " );
1017 out += strlen( tmp );
1022 if ( in[0] == '$' ) {
1025 out += strlen( src );
1029 if ( in[0] == '@' ) {
1034 if ( in[0] == '&' ) {
1035 if ( in[1] == '&' ) {
1039 out = new char[BIG_PATH_MAX]; //% PATH_MAX
1040 g_ptr_array_add( out_array, out );
1048 void FindReplace( CString& strContents, const char* pTag, const char* pValue ){
1049 if ( strcmp( pTag, pValue ) == 0 ) {
1052 for ( int nPos = strContents.Find( pTag ); nPos >= 0; nPos = strContents.Find( pTag ) )
1054 int nRightLen = strContents.GetLength() - strlen( pTag ) - nPos;
1055 CString strLeft = strContents.Left( nPos );
1056 CString strRight = strContents.Right( nRightLen );
1058 strLeft += strRight;
1059 strContents = strLeft;
1063 // save the map, deals with regioning
1064 void SaveWithRegion( char *name ){
1065 strcpy( name, currentmap );
1066 if ( region_active ) {
1067 // temporary cut the region to save regular map
1068 region_active = false;
1069 Map_SaveFile( name, false );
1070 region_active = true;
1071 StripExtension( name );
1072 strcat( name, ".reg" );
1075 Map_SaveFile( name, region_active );
1078 void RunBsp( char *command ){
1080 char batpath[BIG_PATH_MAX]; //% PATH_MAX
1081 char temppath[BIG_PATH_MAX]; //% PATH_MAX
1082 char name[BIG_PATH_MAX]; //% PATH_MAX
1083 char cWork[BIG_PATH_MAX]; //% PATH_MAX
1087 SetInspectorMode( W_CONSOLE );
1089 strcpy( temppath, g_strTempPath.GetBuffer() );
1091 SaveWithRegion( name );
1093 const char *rsh = ValueForKey( g_qeglobals.d_project_entity, "rshcmd" );
1094 if ( rsh == NULL ) {
1095 CString strPath, strFile;
1097 ExtractPath_and_Filename( name, strPath, strFile );
1098 AddSlash( strPath );
1099 strncpy( cWork, strPath, 1024 );
1100 strcat( cWork, strFile );
1104 strcpy( cWork, name );
1107 // get the array ready
1108 //++timo TODO: free the array, free the strings ourselves with delete[]
1109 sys = g_ptr_array_new();
1111 QE_ExpandBspString( command, sys, cWork );
1113 if ( g_PrefsDlg.m_bWatchBSP ) {
1114 // grab the file name for engine running
1115 char *bspname = new char[1024];
1116 ExtractFileName( currentmap, bspname );
1117 StripExtension( bspname );
1118 g_pParentWnd->GetWatchBSP()->DoMonitoringLoop( sys, bspname );
1122 // write all the steps in a single BAT / .sh file and run it, don't bother monitoring it
1124 for ( i = 0; i < sys->len; i++ )
1126 strSys += (char *)g_ptr_array_index( sys, i );
1127 #ifdef _WIN32 // write temp\junk.txt in win32... NOTE: stops output to shell prompt window
1136 strSys += "junk.txt\"";
1141 #if defined ( __linux__ ) || defined ( __APPLE__ )
1144 sprintf( batpath, "%sqe3bsp.sh", temppath );
1145 Sys_Printf( "Writing the compile script to '%s'\n", batpath );
1146 Sys_Printf( "The build output will be saved in '%sjunk.txt'\n", temppath );
1147 hFile = fopen( batpath, "w" );
1149 Error( "Can't write to %s", batpath );
1151 fprintf( hFile, "#!/bin/sh \n\n" );
1152 fprintf( hFile, "%s", strSys.GetBuffer() );
1154 chmod( batpath, 0744 );
1158 sprintf( batpath, "%sqe3bsp.bat", temppath );
1159 Sys_Printf( "Writing the compile script to '%s'\n", batpath );
1160 Sys_Printf( "The build output will be saved in '%sjunk.txt'\n", temppath );
1161 hFile = fopen( batpath, "w" );
1163 Error( "Can't write to %s", batpath );
1165 fprintf( hFile, "%s", strSys.GetBuffer() );
1171 #if defined ( __linux__ ) || defined ( __APPLE__ )
1179 Error( "CreateProcess failed" );
1182 execlp( batpath, batpath, NULL );
1183 printf( "execlp error !" );
1192 Sys_Printf( "Running bsp command...\n" );
1193 Sys_Printf( "\n%s\n", strSys.GetBuffer() );
1195 WinExec( batpath, SW_SHOWNORMAL );
1199 // yeah, do it .. but not now right before 1.1-TA-beta release
1200 Sys_Printf( "TODO: erase GPtrArray\n" );
1208 int WINAPI QEW_SetupPixelFormat( HDC hDC, qboolean zbuffer ){
1209 static PIXELFORMATDESCRIPTOR pfd = {
1210 sizeof( PIXELFORMATDESCRIPTOR ), // size of this pfd
1211 1, // version number
1212 PFD_DRAW_TO_WINDOW | // support window
1213 PFD_SUPPORT_OPENGL | // support OpenGL
1214 PFD_DOUBLEBUFFER, // double buffered
1215 PFD_TYPE_RGBA, // RGBA type
1216 24, // 24-bit color depth
1217 0, 0, 0, 0, 0, 0, // color bits ignored
1218 0, // no alpha buffer
1219 0, // shift bit ignored
1220 0, // no accumulation buffer
1221 0, 0, 0, 0, // accum bits ignored
1223 0, // no stencil buffer
1224 0, // no auxiliary buffer
1225 PFD_MAIN_PLANE, // main layer
1227 0, 0, 0 // layer masks ignored
1229 int pixelformat = 0;
1236 if ( ( pixelformat = ChoosePixelFormat( hDC, &pfd ) ) == 0 ) {
1239 FORMAT_MESSAGE_ALLOCATE_BUFFER |
1240 FORMAT_MESSAGE_FROM_SYSTEM |
1241 FORMAT_MESSAGE_IGNORE_INSERTS,
1244 MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), // Default language
1249 Sys_FPrintf( SYS_WRN, "GetLastError: %s", lpMsgBuf );
1250 Error( "ChoosePixelFormat failed" );
1253 if ( !SetPixelFormat( hDC, pixelformat, &pfd ) ) {
1254 Error( "SetPixelFormat failed" );