]> de.git.xonotic.org Git - xonotic/netradiant.git/blob - radiant/main.cpp
ff141f6f529a436a3441584aa54ce9b5fc77d7c0
[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 int main( int argc, char* argv[] ){
506 #if GTK_TARGET == 3
507         // HACK: force legacy GL backend as we don't support GL3 yet
508         setenv("GDK_GL", "LEGACY", 0);
509 #endif
510         crt_init();
511
512         streams_init();
513
514 #if GDEF_OS_WINDOWS
515         HMODULE lib;
516         lib = LoadLibrary( "dwmapi.dll" );
517         if ( lib != 0 ) {
518                 void ( WINAPI *qDwmEnableComposition )( bool bEnable ) = ( void (WINAPI *) ( bool bEnable ) )GetProcAddress( lib, "DwmEnableComposition" );
519                 if ( qDwmEnableComposition ) {
520                         qDwmEnableComposition( FALSE );
521                 }
522                 FreeLibrary( lib );
523         }
524 #endif
525
526         const char* mapname = NULL;
527
528 #if GDEF_OS_WINDOWS
529         StringOutputStream mapname_buffer( 256 );
530 #endif
531
532     char const *error = NULL;
533
534         if ( !ui::init( &argc, &argv, "<filename.map>", &error) ) {
535                 g_print( "%s\n", error );
536                 return -1;
537         }
538
539         // Gtk already removed parsed `--options`
540         if ( argc == 2 ) {
541                 if ( strlen( argv[ 1 ] ) > 1 ) {
542                         mapname = argv[ 1 ];
543
544                         if ( g_str_has_suffix( mapname, ".map" ) ) {
545                                 if ( !g_path_is_absolute( mapname ) ) {
546                                         mapname = g_build_filename( g_get_current_dir(), mapname, NULL );
547                                 }
548
549 #if GDEF_OS_WINDOWS
550                                 mapname_buffer << PathCleaned( mapname );
551                                 mapname = mapname_buffer.c_str();
552 #endif
553                         }
554                         else {
555                                 g_print( "bad file name, will not load: %s\n", mapname );
556                                 mapname = NULL;
557                         }
558                 }
559         }
560         else if ( argc > 2 ) {
561                 g_print ( "%s\n", "too many arguments" );
562                 return -1;
563         }
564
565         // redirect Gtk warnings to the console
566         g_log_set_handler( "Gdk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
567                                                                                                 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
568         g_log_set_handler( "Gtk", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
569                                                                                                 G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
570         g_log_set_handler( "GtkGLExt", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
571                                                                                                          G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
572         g_log_set_handler( "GLib", (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
573                                                                                                  G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
574         g_log_set_handler( 0, (GLogLevelFlags)( G_LOG_LEVEL_ERROR | G_LOG_LEVEL_CRITICAL | G_LOG_LEVEL_WARNING |
575                                                                                         G_LOG_LEVEL_MESSAGE | G_LOG_LEVEL_INFO | G_LOG_LEVEL_DEBUG | G_LOG_FLAG_FATAL | G_LOG_FLAG_RECURSION ), error_redirect, 0 );
576
577         GlobalDebugMessageHandler::instance().setHandler( GlobalPopupDebugMessageHandler::instance() );
578
579         environment_init(argc, (char const **) argv);
580
581         paths_init();
582
583         show_splash();
584
585         create_global_pid();
586
587         GlobalPreferences_Init();
588
589         g_GamesDialog.Init();
590
591         g_strGameToolsPath = g_pGameDescription->mGameToolsPath;
592
593         remove_global_pid();
594
595         g_Preferences.Init(); // must occur before create_local_pid() to allow preferences to be reset
596
597         create_local_pid();
598
599         // in a very particular post-.pid startup
600         // we may have the console turned on and want to keep it that way
601         // so we use a latching system
602         if ( g_GamesDialog.m_bForceLogConsole ) {
603                 Sys_EnableLogFile( true );
604                 g_Console_enableLogging = true;
605                 g_GamesDialog.m_bForceLogConsole = false;
606         }
607
608
609         Radiant_Initialise();
610
611         user_shortcuts_init();
612
613         g_pParentWnd = 0;
614         g_pParentWnd = new MainFrame();
615
616         hide_splash();
617
618         if ( mapname != NULL ) {
619                 Map_LoadFile( mapname );
620         }
621         else if ( g_bLoadLastMap && !g_strLastMap.empty() ) {
622                 Map_LoadFile( g_strLastMap.c_str() );
623         }
624         else
625         {
626                 Map_New();
627         }
628
629         // load up shaders now that we have the map loaded
630         // eviltypeguy
631         TextureBrowser_ShowStartupShaders( GlobalTextureBrowser() );
632
633
634         remove_local_pid();
635
636         ui::main();
637
638         // avoid saving prefs when the app is minimized
639         if ( g_pParentWnd->IsSleeping() ) {
640                 globalOutputStream() << "Shutdown while sleeping, not saving prefs\n";
641                 g_preferences_globals.disable_ini = true;
642         }
643
644         Map_Free();
645
646         if ( !Map_Unnamed( g_map ) ) {
647                 g_strLastMap = Map_Name( g_map );
648         }
649
650         delete g_pParentWnd;
651
652         user_shortcuts_save();
653
654         Radiant_Shutdown();
655
656         // close the log file if any
657         Sys_EnableLogFile( false );
658
659         return EXIT_SUCCESS;
660 }