]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/main.cpp
Merge commit '86023af46cb7b0057fd3c9dae743660b2968e255' into garux-merge
[xonotic/netradiant.git] / radiant / main.cpp
1 /*
2    Copyright (C) 1999-2006 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 /*! \mainpage GtkRadiant Documentation Index
23
24    \section intro_sec Introduction
25
26    This documentation is generated from comments in the source code.
27
28    \section links_sec Useful Links
29
30    \link include/itextstream.h include/itextstream.h \endlink - Global output and error message streams, similar to std::cout and std::cerr. \n
31
32    FileInputStream - similar to std::ifstream (binary mode) \n
33    FileOutputStream - similar to std::ofstream (binary mode) \n
34    TextFileInputStream - similar to std::ifstream (text mode) \n
35    TextFileOutputStream - similar to std::ofstream (text mode) \n
36    StringOutputStream - similar to std::stringstream \n
37
38    \link string/string.h string/string.h \endlink - C-style string comparison and memory management. \n
39    \link os/path.h os/path.h \endlink - Path manipulation for radiant's standard path format \n
40    \link os/file.h os/file.h \endlink - OS file-system access. \n
41
42    ::CopiedString - automatic string memory management \n
43    Array - automatic array memory management \n
44    HashTable - generic hashtable, similar to std::hash_map \n
45
46    \link math/vector.h math/vector.h \endlink - Vectors \n
47    \link math/matrix.h math/matrix.h \endlink - Matrices \n
48    \link math/quaternion.h math/quaternion.h \endlink - Quaternions \n
49    \link math/plane.h math/plane.h \endlink - Planes \n
50    \link math/aabb.h math/aabb.h \endlink - AABBs \n
51
52    Callback MemberCaller0 FunctionCaller - callbacks similar to using boost::function with boost::bind \n
53    SmartPointer SmartReference - smart-pointer and smart-reference similar to Loki's SmartPtr \n
54
55    \link generic/bitfield.h generic/bitfield.h \endlink - Type-safe bitfield \n
56    \link generic/enumeration.h generic/enumeration.h \endlink - Type-safe enumeration \n
57
58    DefaultAllocator - Memory allocation using new/delete, compliant with std::allocator interface \n
59
60    \link debugging/debugging.h debugging/debugging.h \endlink - Debugging macros \n
61
62  */
63
64 #include "main.h"
65 #include "globaldefs.h"
66
67 #include "version.h"
68
69 #include "debugging/debugging.h"
70
71 #include "iundo.h"
72
73 #include "uilib/uilib.h"
74
75 #include "cmdlib.h"
76 #include "os/file.h"
77 #include "os/path.h"
78 #include "stream/stringstream.h"
79 #include "stream/textfilestream.h"
80
81 #include "gtkutil/messagebox.h"
82 #include "gtkutil/image.h"
83 #include "console.h"
84 #include "texwindow.h"
85 #include "map.h"
86 #include "mainframe.h"
87 #include "commands.h"
88 #include "preferences.h"
89 #include "environment.h"
90 #include "referencecache.h"
91 #include "stacktrace.h"
92
93 #if GDEF_OS_WINDOWS
94 #include <windows.h>
95 #endif
96
97 void show_splash();
98 void hide_splash();
99
100 void error_redirect( const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data ){
101         gboolean in_recursion;
102         gboolean is_fatal;
103         char buf[256];
104
105         in_recursion = ( log_level & G_LOG_FLAG_RECURSION ) != 0;
106         is_fatal = ( log_level & G_LOG_FLAG_FATAL ) != 0;
107         log_level = (GLogLevelFlags) ( log_level & G_LOG_LEVEL_MASK );
108
109         if ( !message ) {
110                 message = "(0) message";
111         }
112
113         if ( domain ) {
114                 strcpy( buf, domain );
115         }
116         else{
117                 strcpy( buf, "**" );
118         }
119         strcat( buf, "-" );
120
121         switch ( log_level )
122         {
123         case G_LOG_LEVEL_ERROR:
124                 if ( in_recursion ) {
125                         strcat( buf, "ERROR (recursed) **: " );
126                 }
127                 else{
128                         strcat( buf, "ERROR **: " );
129                 }
130                 break;
131         case G_LOG_LEVEL_CRITICAL:
132                 if ( in_recursion ) {
133                         strcat( buf, "CRITICAL (recursed) **: " );
134                 }
135                 else{
136                         strcat( buf, "CRITICAL **: " );
137                 }
138                 break;
139         case G_LOG_LEVEL_WARNING:
140                 if ( in_recursion ) {
141                         strcat( buf, "WARNING (recursed) **: " );
142                 }
143                 else{
144                         strcat( buf, "WARNING **: " );
145                 }
146                 break;
147         case G_LOG_LEVEL_MESSAGE:
148                 if ( in_recursion ) {
149                         strcat( buf, "Message (recursed): " );
150                 }
151                 else{
152                         strcat( buf, "Message: " );
153                 }
154                 break;
155         case G_LOG_LEVEL_INFO:
156                 if ( in_recursion ) {
157                         strcat( buf, "INFO (recursed): " );
158                 }
159                 else{
160                         strcat( buf, "INFO: " );
161                 }
162                 break;
163         case G_LOG_LEVEL_DEBUG:
164                 if ( in_recursion ) {
165                         strcat( buf, "DEBUG (recursed): " );
166                 }
167                 else{
168                         strcat( buf, "DEBUG: " );
169                 }
170                 break;
171         default:
172                 /* we are used for a log level that is not defined by GLib itself,
173                  * try to make the best out of it.
174                  */
175                 if ( in_recursion ) {
176                         strcat( buf, "LOG (recursed:" );
177                 }
178                 else{
179                         strcat( buf, "LOG (" );
180                 }
181                 if ( log_level ) {
182                         gchar string[] = "0x00): ";
183                         gchar *p = string + 2;
184                         guint i;
185
186                         i = g_bit_nth_msf( log_level, -1 );
187                         *p = i >> 4;
188                         p++;
189                         *p = '0' + ( i & 0xf );
190                         if ( *p > '9' ) {
191                                 *p += 'A' - '9' - 1;
192                         }
193
194                         strcat( buf, string );
195                 }
196                 else{
197                         strcat( buf, "): " );
198                 }
199         }
200
201         strcat( buf, message );
202         if ( is_fatal ) {
203                 strcat( buf, "\naborting...\n" );
204         }
205         else{
206                 strcat( buf, "\n" );
207         }
208
209         // spam it...
210         globalErrorStream() << buf << "\n";
211
212         if (is_fatal) {
213             ERROR_MESSAGE( "GTK+ error: " << buf );
214     }
215 }
216
217 #if GDEF_COMPILER_MSVC && GDEF_DEBUG
218 #include "crtdbg.h"
219 #endif
220
221 void crt_init(){
222 #if GDEF_COMPILER_MSVC && GDEF_DEBUG
223         _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
224 #endif
225 }
226
227 class Lock
228 {
229 bool m_locked;
230 public:
231 Lock() : m_locked( false ){
232 }
233 void lock(){
234         m_locked = true;
235 }
236 void unlock(){
237         m_locked = false;
238 }
239 bool locked() const {
240         return m_locked;
241 }
242 };
243
244 class ScopedLock
245 {
246 Lock& m_lock;
247 public:
248 ScopedLock( Lock& lock ) : m_lock( lock ){
249         m_lock.lock();
250 }
251 ~ScopedLock(){
252         m_lock.unlock();
253 }
254 };
255
256 class LineLimitedTextOutputStream : public TextOutputStream
257 {
258 TextOutputStream& outputStream;
259 std::size_t count;
260 public:
261 LineLimitedTextOutputStream( TextOutputStream& outputStream, std::size_t count )
262         : outputStream( outputStream ), count( count ){
263 }
264 std::size_t write( const char* buffer, std::size_t length ){
265         if ( count != 0 ) {
266                 const char* p = buffer;
267                 const char* end = buffer + length;
268                 for (;; )
269                 {
270                         p = std::find( p, end, '\n' );
271                         if ( p == end ) {
272                                 break;
273                         }
274                         ++p;
275                         if ( --count == 0 ) {
276                                 length = p - buffer;
277                                 break;
278                         }
279                 }
280                 outputStream.write( buffer, length );
281         }
282         return length;
283 }
284 };
285
286 class PopupDebugMessageHandler : public DebugMessageHandler
287 {
288 StringOutputStream m_buffer;
289 Lock m_lock;
290 public:
291 TextOutputStream& getOutputStream(){
292         if ( !m_lock.locked() ) {
293                 return m_buffer;
294         }
295         return globalErrorStream();
296 }
297 bool handleMessage(){
298         getOutputStream() << "----------------\n";
299         LineLimitedTextOutputStream outputStream( getOutputStream(), 24 );
300         write_stack_trace( outputStream );
301         getOutputStream() << "----------------\n";
302         globalErrorStream() << m_buffer.c_str();
303         if ( !m_lock.locked() ) {
304                 ScopedLock lock( m_lock );
305         if (GDEF_DEBUG) {
306             m_buffer << "Break into the debugger?\n";
307             bool handled = ui::alert(ui::root, m_buffer.c_str(), "Radiant - Runtime Error", ui::alert_type::YESNO, ui::alert_icon::Error) == ui::alert_response::NO;
308             m_buffer.clear();
309             return handled;
310         } else {
311             m_buffer << "Please report this error to the developers\n";
312             ui::alert(ui::root, m_buffer.c_str(), "Radiant - Runtime Error", ui::alert_type::OK, ui::alert_icon::Error);
313             m_buffer.clear();
314         }
315         }
316         return true;
317 }
318 };
319
320 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
321
322 void streams_init(){
323         GlobalErrorStream::instance().setOutputStream( getSysPrintErrorStream() );
324         GlobalOutputStream::instance().setOutputStream( getSysPrintOutputStream() );
325 }
326
327 void paths_init(){
328         g_strSettingsPath = environment_get_home_path();
329
330         Q_mkdir( g_strSettingsPath.c_str() );
331
332         g_strAppPath = environment_get_app_path();
333
334         // radiant is installed in the parent dir of "tools/"
335         // NOTE: this is not very easy for debugging
336         // maybe add options to lookup in several places?
337         // (for now I had to create symlinks)
338         {
339                 StringOutputStream path( 256 );
340                 path << g_strAppPath.c_str() << "bitmaps/";
341                 BitmapsPath_set( path.c_str() );
342         }
343
344         // we will set this right after the game selection is done
345         g_strGameToolsPath = g_strAppPath;
346 }
347
348 bool check_version_file( const char* filename, const char* version ){
349         TextFileInputStream file( filename );
350         if ( !file.failed() ) {
351                 char buf[10];
352                 buf[file.read( buf, 9 )] = '\0';
353
354                 // chomp it (the hard way)
355                 int chomp = 0;
356                 while ( buf[chomp] >= '0' && buf[chomp] <= '9' )
357                         chomp++;
358                 buf[chomp] = '\0';
359
360                 return string_equal( buf, version );
361         }
362         return false;
363 }
364
365 bool check_version(){
366         // a safe check to avoid people running broken installations
367         // (otherwise, they run it, crash it, and blame us for not forcing them hard enough to pay attention while installing)
368         // make something idiot proof and someone will make better idiots, this may be overkill
369         // let's leave it disabled in debug mode in any case
370         // http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=431
371     if (GDEF_DEBUG) {
372         return true;
373     }
374     // locate and open RADIANT_MAJOR and RADIANT_MINOR
375     bool bVerIsGood = true;
376     {
377         StringOutputStream ver_file_name(256);
378         ver_file_name << AppPath_get() << "RADIANT_MAJOR";
379         bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MAJOR_VERSION);
380     }
381     {
382         StringOutputStream ver_file_name(256);
383         ver_file_name << AppPath_get() << "RADIANT_MINOR";
384         bVerIsGood = check_version_file(ver_file_name.c_str(), RADIANT_MINOR_VERSION);
385     }
386
387     if (!bVerIsGood) {
388         StringOutputStream msg(256);
389         msg
390                 << "This editor binary (" RADIANT_VERSION ") doesn't match what the latest setup has configured in this directory\n"
391                         "Make sure you run the right/latest editor binary you installed\n"
392                 << AppPath_get();
393         ui::alert(ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Default);
394     }
395     return bVerIsGood;
396 }
397
398 void create_global_pid(){
399         /*!
400            the global prefs loading / game selection dialog might fail for any reason we don't know about
401            we need to catch when it happens, to cleanup the stateful prefs which might be killing it
402            and to turn on console logging for lookup of the problem
403            this is the first part of the two step .pid system
404            http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
405          */
406         StringOutputStream g_pidFile( 256 ); ///< the global .pid file (only for global part of the startup)
407
408         g_pidFile << SettingsPath_get() << "radiant.pid";
409
410         FILE *pid;
411         pid = fopen( g_pidFile.c_str(), "r" );
412         if ( pid != 0 ) {
413                 fclose( pid );
414
415                 if ( remove( g_pidFile.c_str() ) == -1 ) {
416                         StringOutputStream msg( 256 );
417                         msg << "WARNING: Could not delete " << g_pidFile.c_str();
418                         ui::alert( ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error );
419                 }
420
421                 // in debug, never prompt to clean registry, turn console logging auto after a failed start
422                 if (!GDEF_DEBUG) {
423                         StringOutputStream msg(256);
424                         msg << "Radiant failed to start properly the last time it was run.\n"
425                                         "The failure may be related to current global preferences.\n"
426                                         "Do you want to reset global preferences to defaults?";
427
428                         if (ui::alert(ui::root, msg.c_str(), "Radiant - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
429                                 g_GamesDialog.Reset();
430                         }
431
432                         msg.clear();
433                         msg << "Logging console output to " << SettingsPath_get()
434                                 << "radiant.log\nRefer to the log if Radiant fails to start again.";
435
436                         ui::alert(ui::root, msg.c_str(), "Radiant - Console Log", ui::alert_type::OK);
437                 }
438
439                 // set without saving, the class is not in a coherent state yet
440                 // just do the value change and call to start logging, CGamesDialog will pickup when relevant
441                 g_GamesDialog.m_bForceLogConsole = true;
442                 Sys_LogFile( true );
443         }
444
445         // create a primary .pid for global init run
446         pid = fopen( g_pidFile.c_str(), "w" );
447         if ( pid ) {
448                 fclose( pid );
449         }
450 }
451
452 void remove_global_pid(){
453         StringOutputStream g_pidFile( 256 );
454         g_pidFile << SettingsPath_get() << "radiant.pid";
455
456         // close the primary
457         if ( remove( g_pidFile.c_str() ) == -1 ) {
458                 StringOutputStream msg( 256 );
459                 msg << "WARNING: Could not delete " << g_pidFile.c_str();
460                 ui::alert( ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error );
461         }
462 }
463
464 /*!
465    now the secondary game dependant .pid file
466    http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
467  */
468 void create_local_pid(){
469         StringOutputStream g_pidGameFile( 256 ); ///< the game-specific .pid file
470         g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
471
472         FILE *pid = fopen( g_pidGameFile.c_str(), "r" );
473         if ( pid != 0 ) {
474                 fclose( pid );
475                 if ( remove( g_pidGameFile.c_str() ) == -1 ) {
476                         StringOutputStream msg;
477                         msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
478                         ui::alert( ui::root, msg.c_str(), "Radiant", ui::alert_type::OK, ui::alert_icon::Error );
479                 }
480
481                 // in debug, never prompt to clean registry, turn console logging auto after a failed start
482                 if (!GDEF_DEBUG) {
483                         StringOutputStream msg;
484                         msg << "Radiant failed to start properly the last time it was run.\n"
485                                         "The failure may be caused by current preferences.\n"
486                                         "Do you want to reset all preferences to defaults?";
487
488                         if (ui::alert(ui::root, msg.c_str(), "Radiant - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
489                                 Preferences_Reset();
490                         }
491
492                         msg.clear();
493                         msg << "Logging console output to " << SettingsPath_get()
494                                 << "radiant.log\nRefer to the log if Radiant fails to start again.";
495
496                         ui::alert(ui::root, msg.c_str(), "Radiant - Console Log", ui::alert_type::OK);
497                 }
498
499                 // force console logging on! (will go in prefs too)
500                 g_GamesDialog.m_bForceLogConsole = true;
501                 Sys_LogFile( true );
502         }
503         else
504         {
505                 // create one, will remove right after entering message loop
506                 pid = fopen( g_pidGameFile.c_str(), "w" );
507                 if ( pid ) {
508                         fclose( pid );
509                 }
510         }
511 }
512
513
514 /*!
515    now the secondary game dependant .pid file
516    http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
517  */
518 void remove_local_pid(){
519         StringOutputStream g_pidGameFile( 256 );
520         g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
521         remove( g_pidGameFile.c_str() );
522 }
523
524 void user_shortcuts_init(){
525         StringOutputStream path( 256 );
526         path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
527         LoadCommandMap( path.c_str() );
528         SaveCommandMap( path.c_str() );
529 }
530
531 void user_shortcuts_save(){
532         StringOutputStream path( 256 );
533         path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
534         SaveCommandMap( path.c_str() );
535 }
536
537 /* FIXME: HACK: not GTK3 compatible
538  https://developer.gnome.org/gtk2/stable/gtk2-Resource-Files.html#gtk-rc-add-default-file
539  https://developer.gnome.org/gtk3/stable/gtk3-Resource-Files.html#gtk-rc-add-default-file
540  > gtk_rc_add_default_file has been deprecated since version 3.0 and should not be used in newly-written code.
541  > Use GtkStyleContext with a custom GtkStyleProvider instead
542 */
543 void gtk_rc_add_default_file (const gchar *filename);
544
545 void add_local_rc_files(){
546         {
547                 StringOutputStream path( 512 );
548                 path << AppPath_get() << ".gtkrc-2.0.radiant";
549                 gtk_rc_add_default_file( path.c_str() );
550         }
551 #ifdef WIN32
552         {
553                 StringOutputStream path( 512 );
554                 path << AppPath_get() << ".gtkrc-2.0.win";
555                 gtk_rc_add_default_file( path.c_str() );
556         }
557 #endif
558 }
559
560 int main( int argc, char* argv[] ){
561         crt_init();
562
563         streams_init();
564
565 #if GDEF_OS_WINDOWS
566         HMODULE lib;
567         lib = LoadLibrary( "dwmapi.dll" );
568         if ( lib != 0 ) {
569                 void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" );
570                 if ( qDwmEnableComposition ) {
571                         bool Aero = false;
572                         for ( int i = 1; i < argc; ++i ){
573                                 if ( !stricmp( argv[i], "-aero" ) ){
574                                         Aero = true;
575                                         qDwmEnableComposition( TRUE );
576                                         break;
577                                 }
578                         }
579                         // disable Aero
580                         if ( !Aero ){
581                                 qDwmEnableComposition( FALSE );
582                         }
583                 }
584                 FreeLibrary( lib );
585         }
586         _setmaxstdio(2048);
587 #endif
588
589         const char* mapname = NULL;
590     char const *error = NULL;
591         if ( !ui::init( &argc, &argv, "<filename.map>", &error) ) {
592                 g_print( "%s\n", error );
593                 return -1;
594         }
595
596         // Gtk already removed parsed `--options`
597         if (argc == 2) {
598                 if ( strlen( argv[1] ) > 1 ) {
599                         if ( g_str_has_suffix( argv[1], ".map" ) ) {
600                                 if ( g_path_is_absolute( argv[1] ) ) {
601                                         mapname = argv[1];
602                                 }
603                                 else {
604                                         mapname = g_build_filename( g_get_current_dir(), argv[1], NULL );
605                                 }
606                         }
607                         else {
608                                 g_print( "bad file name, will not load: %s\n", argv[1] );
609                         }
610                 }
611         }
612         else if (argc > 2) {
613                 g_print ( "%s\n", "too many arguments" );
614                 return -1;
615         }
616
617         // redirect Gtk warnings to the console
618         g_log_set_handler( "Gdk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
619                                                                                                 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
620         g_log_set_handler( "Gtk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
621                                                                                                 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
622         g_log_set_handler( "GtkGLExt", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
623                                                                                                          G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
624         g_log_set_handler( "GLib", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
625                                                                                                  G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
626         g_log_set_handler( 0, (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
627                                                                                         G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
628
629         GlobalDebugMessageHandler::instance().setHandler( GlobalPopupDebugMessageHandler::instance() );
630
631         environment_init(argc, (char const **) argv);
632
633         paths_init();
634
635         add_local_rc_files();
636
637         if ( !check_version() ) {
638                 return EXIT_FAILURE;
639         }
640
641         show_splash();
642
643         create_global_pid();
644
645         GlobalPreferences_Init();
646
647         g_GamesDialog.Init();
648
649         g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
650
651         remove_global_pid();
652
653         g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
654
655         create_local_pid();
656
657         // in a very particular post-.pid startup
658         // we may have the console turned on and want to keep it that way
659         // so we use a latching system
660         if ( g_GamesDialog.m_bForceLogConsole ) {
661                 Sys_LogFile( true );
662                 g_Console_enableLogging = true;
663                 g_GamesDialog.m_bForceLogConsole = false;
664         }
665
666
667         Radiant_Initialise();
668
669         user_shortcuts_init();
670
671         g_pParentWnd = 0;
672         g_pParentWnd = new MainFrame();
673
674         hide_splash();
675
676 #ifdef WIN32
677         if( openCmdMap && *openCmdMap ){
678                 Map_LoadFile( openCmdMap );
679         }
680         else
681 #endif // WIN32
682         if ( mapname != NULL ) {
683                 Map_LoadFile( mapname );
684         }
685         else if ( g_bLoadLastMap && !g_strLastMap.empty() ) {
686                 Map_LoadFile( g_strLastMap.c_str() );
687         }
688         else
689         {
690                 Map_New();
691         }
692
693         // load up shaders now that we have the map loaded
694         // eviltypeguy
695         TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() );
696
697
698         remove_local_pid();
699
700         ui::main();
701
702         // avoid saving prefs when the app is minimized
703         if ( g_pParentWnd->IsSleeping() ) {
704                 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
705                 g_preferences_globals.disable_ini = true;
706         }
707
708         Map_Free();
709
710         if ( !Map_Unnamed( g_map ) ) {
711                 g_strLastMap = Map_Name( g_map );
712         }
713
714         delete g_pParentWnd;
715
716         user_shortcuts_save();
717
718         Radiant_Shutdown();
719
720         // close the log file if any
721         Sys_LogFile( false );
722
723         return EXIT_SUCCESS;
724 }