]> git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/main.cpp
Merge commit '71ef2336cbab4cca1d1b228cdb89601f6569c107' into master-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 "debugging/debugging.h"
68
69 #include "iundo.h"
70
71 #include "uilib/uilib.h"
72
73 #include "cmdlib.h"
74 #include "os/file.h"
75 #include "os/path.h"
76 #include "stream/stringstream.h"
77 #include "stream/textfilestream.h"
78
79 #include "gtkutil/messagebox.h"
80 #include "gtkutil/image.h"
81 #include "console.h"
82 #include "texwindow.h"
83 #include "map.h"
84 #include "mainframe.h"
85 #include "commands.h"
86 #include "preferences.h"
87 #include "environment.h"
88 #include "referencecache.h"
89 #include "stacktrace.h"
90
91 #if GDEF_OS_WINDOWS
92 #include <windows.h>
93 #endif
94
95 void show_splash();
96 void hide_splash();
97
98 void error_redirect( const gchar *domain, GLogLevelFlags log_level, const gchar *message, gpointer user_data ){
99         gboolean in_recursion;
100         gboolean is_fatal;
101         char buf[256];
102
103         in_recursion = ( log_level & G_LOG_FLAG_RECURSION ) != 0;
104         is_fatal = ( log_level & G_LOG_FLAG_FATAL ) != 0;
105         log_level = (GLogLevelFlags) ( log_level & G_LOG_LEVEL_MASK );
106
107         if ( !message ) {
108                 message = "(0) message";
109         }
110
111         if ( domain ) {
112                 strcpy( buf, domain );
113         }
114         else{
115                 strcpy( buf, "**" );
116         }
117         strcat( buf, "-" );
118
119         switch ( log_level )
120         {
121         case G_LOG_LEVEL_ERROR:
122                 if ( in_recursion ) {
123                         strcat( buf, "ERROR (recursed) **: " );
124                 }
125                 else{
126                         strcat( buf, "ERROR **: " );
127                 }
128                 break;
129         case G_LOG_LEVEL_CRITICAL:
130                 if ( in_recursion ) {
131                         strcat( buf, "CRITICAL (recursed) **: " );
132                 }
133                 else{
134                         strcat( buf, "CRITICAL **: " );
135                 }
136                 break;
137         case G_LOG_LEVEL_WARNING:
138                 if ( in_recursion ) {
139                         strcat( buf, "WARNING (recursed) **: " );
140                 }
141                 else{
142                         strcat( buf, "WARNING **: " );
143                 }
144                 break;
145         case G_LOG_LEVEL_MESSAGE:
146                 if ( in_recursion ) {
147                         strcat( buf, "Message (recursed): " );
148                 }
149                 else{
150                         strcat( buf, "Message: " );
151                 }
152                 break;
153         case G_LOG_LEVEL_INFO:
154                 if ( in_recursion ) {
155                         strcat( buf, "INFO (recursed): " );
156                 }
157                 else{
158                         strcat( buf, "INFO: " );
159                 }
160                 break;
161         case G_LOG_LEVEL_DEBUG:
162                 if ( in_recursion ) {
163                         strcat( buf, "DEBUG (recursed): " );
164                 }
165                 else{
166                         strcat( buf, "DEBUG: " );
167                 }
168                 break;
169         default:
170                 /* we are used for a log level that is not defined by GLib itself,
171                  * try to make the best out of it.
172                  */
173                 if ( in_recursion ) {
174                         strcat( buf, "LOG (recursed:" );
175                 }
176                 else{
177                         strcat( buf, "LOG (" );
178                 }
179                 if ( log_level ) {
180                         gchar string[] = "0x00): ";
181                         gchar *p = string + 2;
182                         guint i;
183
184                         i = g_bit_nth_msf( log_level, -1 );
185                         *p = i >> 4;
186                         p++;
187                         *p = '0' + ( i & 0xf );
188                         if ( *p > '9' ) {
189                                 *p += 'A' - '9' - 1;
190                         }
191
192                         strcat( buf, string );
193                 }
194                 else{
195                         strcat( buf, "): " );
196                 }
197         }
198
199         strcat( buf, message );
200         if ( is_fatal ) {
201                 strcat( buf, "\naborting...\n" );
202         }
203         else{
204                 strcat( buf, "\n" );
205         }
206
207         // spam it...
208         globalErrorStream() << buf << "\n";
209
210         if (is_fatal) {
211             ERROR_MESSAGE( "GTK+ error: " << buf );
212     }
213 }
214
215 #if GDEF_COMPILER_MSVC && GDEF_DEBUG
216 #include "crtdbg.h"
217 #endif
218
219 void crt_init(){
220 #if GDEF_COMPILER_MSVC && GDEF_DEBUG
221         _CrtSetDbgFlag( _CRTDBG_ALLOC_MEM_DF | _CRTDBG_LEAK_CHECK_DF );
222 #endif
223 }
224
225 class Lock
226 {
227 bool m_locked;
228 public:
229 Lock() : m_locked( false ){
230 }
231 void lock(){
232         m_locked = true;
233 }
234 void unlock(){
235         m_locked = false;
236 }
237 bool locked() const {
238         return m_locked;
239 }
240 };
241
242 class ScopedLock
243 {
244 Lock& m_lock;
245 public:
246 ScopedLock( Lock& lock ) : m_lock( lock ){
247         m_lock.lock();
248 }
249 ~ScopedLock(){
250         m_lock.unlock();
251 }
252 };
253
254 class LineLimitedTextOutputStream : public TextOutputStream
255 {
256 TextOutputStream& outputStream;
257 std::size_t count;
258 public:
259 LineLimitedTextOutputStream( TextOutputStream& outputStream, std::size_t count )
260         : outputStream( outputStream ), count( count ){
261 }
262 std::size_t write( const char* buffer, std::size_t length ){
263         if ( count != 0 ) {
264                 const char* p = buffer;
265                 const char* end = buffer + length;
266                 for (;; )
267                 {
268                         p = std::find( p, end, '\n' );
269                         if ( p == end ) {
270                                 break;
271                         }
272                         ++p;
273                         if ( --count == 0 ) {
274                                 length = p - buffer;
275                                 break;
276                         }
277                 }
278                 outputStream.write( buffer, length );
279         }
280         return length;
281 }
282 };
283
284 class PopupDebugMessageHandler : public DebugMessageHandler
285 {
286 StringOutputStream m_buffer;
287 Lock m_lock;
288 public:
289 TextOutputStream& getOutputStream(){
290         if ( !m_lock.locked() ) {
291                 return m_buffer;
292         }
293         return globalErrorStream();
294 }
295 bool handleMessage(){
296         getOutputStream() << "----------------\n";
297         LineLimitedTextOutputStream outputStream( getOutputStream(), 24 );
298         write_stack_trace( outputStream );
299         getOutputStream() << "----------------\n";
300         globalErrorStream() << m_buffer.c_str();
301         if ( !m_lock.locked() ) {
302                 ScopedLock lock( m_lock );
303         if (GDEF_DEBUG) {
304             m_buffer << "Break into the debugger?\n";
305             bool handled = ui::alert(ui::root, m_buffer.c_str(), RADIANT_NAME " - Runtime Error", ui::alert_type::YESNO, ui::alert_icon::Error) == ui::alert_response::NO;
306             m_buffer.clear();
307             return handled;
308         } else {
309             m_buffer << "Please report this error to the developers\n";
310             ui::alert(ui::root, m_buffer.c_str(), RADIANT_NAME " - Runtime Error", ui::alert_type::OK, ui::alert_icon::Error);
311             m_buffer.clear();
312         }
313         }
314         return true;
315 }
316 };
317
318 typedef Static<PopupDebugMessageHandler> GlobalPopupDebugMessageHandler;
319
320 void streams_init(){
321         GlobalErrorStream::instance().setOutputStream( getSysPrintErrorStream() );
322         GlobalOutputStream::instance().setOutputStream( getSysPrintOutputStream() );
323 }
324
325 void paths_init(){
326         g_strSettingsPath = environment_get_home_path();
327
328         Q_mkdir( g_strSettingsPath.c_str() );
329
330         g_strAppFilePath = environment_get_app_filepath();
331         g_strAppPath = environment_get_app_path();
332         g_strLibPath = environment_get_lib_path();
333         g_strDataPath = environment_get_data_path();
334
335         // radiant is installed in the parent dir of "tools/"
336         // NOTE: this is not very easy for debugging
337         // maybe add options to lookup in several places?
338         // (for now I had to create symlinks)
339         {
340                 StringOutputStream path( 256 );
341                 path << g_strDataPath.c_str() << "bitmaps/";
342                 BitmapsPath_set( path.c_str() );
343         }
344
345         // we will set this right after the game selection is done
346         g_strGameToolsPath = g_strDataPath;
347 }
348
349 bool check_version_file( const char* filename, const char* version ){
350         TextFileInputStream file( filename );
351         if ( !file.failed() ) {
352                 char buf[10];
353                 buf[file.read( buf, 9 )] = '\0';
354
355                 // chomp it (the hard way)
356                 int chomp = 0;
357                 while ( buf[chomp] >= '0' && buf[chomp] <= '9' )
358                         chomp++;
359                 buf[chomp] = '\0';
360
361                 return string_equal( buf, version );
362         }
363         return false;
364 }
365
366 void create_global_pid(){
367         /*!
368            the global prefs loading / game selection dialog might fail for any reason we don't know about
369            we need to catch when it happens, to cleanup the stateful prefs which might be killing it
370            and to turn on console logging for lookup of the problem
371            this is the first part of the two step .pid system
372            http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
373          */
374         StringOutputStream g_pidFile( 256 ); ///< the global .pid file (only for global part of the startup)
375
376         g_pidFile << SettingsPath_get() << "radiant.pid";
377
378         FILE *pid;
379         pid = fopen( g_pidFile.c_str(), "r" );
380         if ( pid != 0 ) {
381                 fclose( pid );
382
383                 if ( remove( g_pidFile.c_str() ) == -1 ) {
384                         StringOutputStream msg( 256 );
385                         msg << "WARNING: Could not delete " << g_pidFile.c_str();
386                         ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
387                 }
388
389                 // in debug, never prompt to clean registry, turn console logging auto after a failed start
390                 if (!GDEF_DEBUG) {
391                         StringOutputStream msg(256);
392                         msg << RADIANT_NAME " failed to start properly the last time it was run.\n"
393                                         "The failure may be related to current global preferences.\n"
394                                         "Do you want to reset global preferences to defaults?";
395
396                         if (ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
397                                 g_GamesDialog.Reset();
398                         }
399
400                         msg.clear();
401                         msg << "Logging console output to " << SettingsPath_get()
402                                 << "radiant.log\nRefer to the log if " RADIANT_NAME " fails to start again.";
403
404                         ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Console Log", ui::alert_type::OK);
405                 }
406
407                 // set without saving, the class is not in a coherent state yet
408                 // just do the value change and call to start logging, CGamesDialog will pickup when relevant
409                 g_GamesDialog.m_bForceLogConsole = true;
410                 Sys_EnableLogFile( true );
411         }
412
413         // create a primary .pid for global init run
414         pid = fopen( g_pidFile.c_str(), "w" );
415         if ( pid ) {
416                 fclose( pid );
417         }
418 }
419
420 void remove_global_pid(){
421         StringOutputStream g_pidFile( 256 );
422         g_pidFile << SettingsPath_get() << "radiant.pid";
423
424         // close the primary
425         if ( remove( g_pidFile.c_str() ) == -1 ) {
426                 StringOutputStream msg( 256 );
427                 msg << "WARNING: Could not delete " << g_pidFile.c_str();
428                 ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
429         }
430 }
431
432 /*!
433    now the secondary game dependant .pid file
434    http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
435  */
436 void create_local_pid(){
437         StringOutputStream g_pidGameFile( 256 ); ///< the game-specific .pid file
438         g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
439
440         FILE *pid = fopen( g_pidGameFile.c_str(), "r" );
441         if ( pid != 0 ) {
442                 fclose( pid );
443                 if ( remove( g_pidGameFile.c_str() ) == -1 ) {
444                         StringOutputStream msg;
445                         msg << "WARNING: Could not delete " << g_pidGameFile.c_str();
446                         ui::alert( ui::root, msg.c_str(), RADIANT_NAME, ui::alert_type::OK, ui::alert_icon::Error );
447                 }
448
449                 // in debug, never prompt to clean registry, turn console logging auto after a failed start
450                 if (!GDEF_DEBUG) {
451                         StringOutputStream msg;
452                         msg << RADIANT_NAME " failed to start properly the last time it was run.\n"
453                                         "The failure may be caused by current preferences.\n"
454                                         "Do you want to reset all preferences to defaults?";
455
456                         if (ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Startup Failure", ui::alert_type::YESNO, ui::alert_icon::Question) == ui::alert_response::YES) {
457                                 Preferences_Reset();
458                         }
459
460                         msg.clear();
461                         msg << "Logging console output to " << SettingsPath_get()
462                                 << "radiant.log\nRefer to the log if " RADIANT_NAME " fails to start again.";
463
464                         ui::alert(ui::root, msg.c_str(), RADIANT_NAME " - Console Log", ui::alert_type::OK);
465                 }
466
467                 // force console logging on! (will go in prefs too)
468                 g_GamesDialog.m_bForceLogConsole = true;
469                 Sys_EnableLogFile( true );
470         }
471         else
472         {
473                 // create one, will remove right after entering message loop
474                 pid = fopen( g_pidGameFile.c_str(), "w" );
475                 if ( pid ) {
476                         fclose( pid );
477                 }
478         }
479 }
480
481
482 /*!
483    now the secondary game dependant .pid file
484    http://zerowing.idsoftware.com/bugzilla/show_bug.cgi?id=297
485  */
486 void remove_local_pid(){
487         StringOutputStream g_pidGameFile( 256 );
488         g_pidGameFile << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << "/radiant-game.pid";
489         remove( g_pidGameFile.c_str() );
490 }
491
492 void user_shortcuts_init(){
493         StringOutputStream path( 256 );
494         path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
495         LoadCommandMap( path.c_str() );
496         SaveCommandMap( path.c_str() );
497 }
498
499 void user_shortcuts_save(){
500         StringOutputStream path( 256 );
501         path << SettingsPath_get() << g_pGameDescription->mGameFile.c_str() << '/';
502         SaveCommandMap( path.c_str() );
503 }
504
505 /* HACK: If ui::main is not called yet,
506 gtk_main_quit will not quit, so tell main
507 to not call ui::main. This happens when a
508 map is loaded from command line and require
509 a restart because of wrong format.
510 Delete this when the code to not have to
511 restart to load another format is merged. */
512 bool g_dontStart = false;
513
514 int main( int argc, char* argv[] ){
515 #if GTK_TARGET == 3
516         // HACK: force legacy GL backend as we don't support GL3 yet
517         setenv("GDK_GL", "LEGACY", 0);
518 #if GDEF_OS_LINUX || GDEF_OS_BSD
519         setenv("GDK_BACKEND", "x11", 0);
520 #endif
521 #endif // GTK_TARGET == 3
522         crt_init();
523
524         streams_init();
525
526 #if GDEF_OS_WINDOWS
527         HMODULE lib;
528         lib = LoadLibrary( "dwmapi.dll" );
529         if ( lib != 0 ) {
530                 void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" );
531                 if ( qDwmEnableComposition ) {
532                         bool Aero = false;
533                         for ( int i = 1; i < argc; ++i ){
534                                 if ( !stricmp( argv[i], "-aero" ) ){
535                                         Aero = true;
536                                         qDwmEnableComposition( TRUE );
537                                         break;
538                                 }
539                         }
540                         // disable Aero
541                         if ( !Aero ){
542                                 qDwmEnableComposition( FALSE );
543                         }
544                 }
545                 FreeLibrary( lib );
546         }
547         _setmaxstdio(2048);
548 #endif
549
550         const char* mapname = NULL;
551
552 #if GDEF_OS_WINDOWS
553         StringOutputStream mapname_buffer( 256 );
554 #endif
555
556     char const *error = NULL;
557
558         if ( !ui::init( &argc, &argv, "<filename.map>", &error) ) {
559                 g_print( "%s\n", error );
560                 return -1;
561         }
562
563         // Gtk already removed parsed `--options`
564         if ( argc == 2 ) {
565                 if ( strlen( argv[ 1 ] ) > 1 ) {
566                         mapname = argv[ 1 ];
567
568                         if ( g_str_has_suffix( mapname, ".map" ) ) {
569                                 if ( !g_path_is_absolute( mapname ) ) {
570                                         mapname = g_build_filename( g_get_current_dir(), mapname, NULL );
571                                 }
572
573 #if GDEF_OS_WINDOWS
574                                 mapname_buffer << PathCleaned( mapname );
575                                 mapname = mapname_buffer.c_str();
576 #endif
577                         }
578                         else {
579                                 g_print( "bad file name, will not load: %s\n", mapname );
580                                 mapname = NULL;
581                         }
582                 }
583         }
584         else if ( argc > 2 ) {
585                 g_print ( "%s\n", "too many arguments" );
586                 return -1;
587         }
588
589         // redirect Gtk warnings to the console
590         g_log_set_handler( "Gdk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
591                                                                                                 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
592         g_log_set_handler( "Gtk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
593                                                                                                 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
594         g_log_set_handler( "GtkGLExt", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
595                                                                                                          G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
596         g_log_set_handler( "GLib", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
597                                                                                                  G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
598         g_log_set_handler( 0, (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
599                                                                                         G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
600
601         GlobalDebugMessageHandler::instance().setHandler( GlobalPopupDebugMessageHandler::instance() );
602
603         environment_init(argc, (char const **) argv);
604
605         paths_init();
606
607         show_splash();
608
609         create_global_pid();
610
611         GlobalPreferences_Init();
612
613         g_GamesDialog.Init();
614
615         g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
616
617         remove_global_pid();
618
619         g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
620
621         create_local_pid();
622
623         // in a very particular post-.pid startup
624         // we may have the console turned on and want to keep it that way
625         // so we use a latching system
626         if ( g_GamesDialog.m_bForceLogConsole ) {
627                 Sys_EnableLogFile( true );
628                 g_Console_enableLogging = true;
629                 g_GamesDialog.m_bForceLogConsole = false;
630         }
631
632
633         Radiant_Initialise();
634
635         user_shortcuts_init();
636
637         g_pParentWnd = 0;
638         g_pParentWnd = new MainFrame();
639
640         hide_splash();
641
642 #ifdef WIN32
643         if( openCmdMap && *openCmdMap ){
644                 Map_LoadFile( openCmdMap );
645         }
646         else
647 #endif // WIN32
648         if ( mapname != NULL ) {
649                 Map_LoadFile( mapname );
650         }
651         else if ( g_bLoadLastMap && !g_strLastMap.empty() ) {
652                 Map_LoadFile( g_strLastMap.c_str() );
653         }
654         else
655         {
656                 Map_New();
657         }
658
659         // load up shaders now that we have the map loaded
660         // eviltypeguy
661         TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() );
662
663
664         remove_local_pid();
665
666         /* HACK: If ui::main is not called yet,
667         gtk_main_quit will not quit, so tell main
668         to not call ui::main. This happens when a
669         map is loaded from command line and require
670         a restart because of wrong format.
671         Delete this when the code to not have to
672         restart to load another format is merged. */
673         if ( !g_dontStart )
674         {
675                 ui::main();
676         }
677
678         // avoid saving prefs when the app is minimized
679         if ( g_pParentWnd->IsSleeping() ) {
680                 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
681                 g_preferences_globals.disable_ini = true;
682         }
683
684         Map_Free();
685
686         if ( !Map_Unnamed( g_map ) ) {
687                 g_strLastMap = Map_Name( g_map );
688         }
689
690         delete g_pParentWnd;
691
692         user_shortcuts_save();
693
694         Radiant_Shutdown();
695
696         // close the log file if any
697         Sys_EnableLogFile( false );
698
699         return EXIT_SUCCESS;
700 }