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