1 /***************************************************************************
2 main.cpp - description
3 -------------------
4 project : OpenCity
5 codename : Delphine
6 begin : may 28th, 2003
7 copyright : (C) 2003-2008 by Duong Khang NGUYEN
8 email : neoneurone @ gmail com
9
10 details :
11 This is a game project which targets the ultimate city
12 simulation just like like xxxCity or something like that :)
13
14 $Id: main.cpp 425 2009-11-01 16:38:58Z neoneurone $
15 ***************************************************************************/
16
17 /***************************************************************************
18 * *
19 * This program is free software; you can redistribute it and/or modify *
20 * it under the terms of the GNU General Public License as published by *
21 * the Free Software Foundation; either version 2 of the License, or *
22 * any later version. *
23 * *
24 ***************************************************************************/
25
26 //========================================================================
27 /**
28 \mainpage OpenCity source code documentation
29 \section intro Introduction
30 OpenCity is another 3D city simulator. It's certainly not the best
31 city simulation nor the best eye candy game out there but we love it
32 because it's our city simulation and we can do anything we want with
33 it.
34
35 \section reuse Code reuse
36 If you are reading this page, we believe that you are interested
37 in the tricks behind the scene. And we think that you are happy to know
38 that our city simulation is made with portability and object oriented
39 programming technics in mind. It means that you can reuse few parts of
40 OpenCity source code. At the moment, you can reuse the classes
41 AudioManager, Conf, ModelLoader, and MapGen::MapMaker without major
42 modifications.
43
44 \author Duong-Khang NGUYEN (email: neoneurone gmail com)
45 \author Frédéric RODRIGO
46 */
47
48 // Useful enumerations
49 #include "opencity_direction.h"
50 #include "opencity_structure_type.h"
51
52 // OpenCity headers
53 #include "main.h"
54 #include "city.h" // The project core
55 #include "conf.h" // Parser for .conf file
56 #include "agentpolice.h" // MAS testing
57 #include "agentdemonstrator.h"
58
59 // Global settings
60 #include "globalvar.h"
61 extern GlobalVar gVars;
62
63 // Libraries headers
64 #include "SDL_image.h"
65 #include "binreloc.h" // BinReloc routines from AutoPackage
66 #include "tinyxml.h"
67 #include "SimpleOpt.h" // Simple command line argument parser
68
69 // Standard headers
70 #include <cmath> // For log10
71 #include <cstdlib> // For getenv
72 #include <sstream>
73 #include <ctime> // For time
74
75 // Test XPath
76 #include "property.h"
77 #include "propertymanager2.h"
78
79 // Test Sharp Plus Framework
80 #include "framework/System/CString.h"
81 #include "framework/System/Information/Software/CSdl.h"
82
83
84 /*=====================================================================*/
85 /* LOCAL MACROS */
86 /*=====================================================================*/
87 #ifndef __WIN32__
88 #include <sys/stat.h> // mkdir
89 #else
90 // Win32 specifics
91 #include <windows.h> // MessageBox
92 #include <shlobj.h> // Windows shell technologies
93 #define DATADIR "C:/Program Files"
94 #define SYSCONFDIR DATADIR
95 #endif
96
97 // Window's settings
98 #define OC_WINDOW_POS_X 20
99 #define OC_WINDOW_POS_Y 20
100 #define OC_WINDOW_WIDTH 750
101 #define OC_WINDOW_HEIGHT 560
102 #define OC_WINDOW_BPP_DEFAULT 0 ///< Use the current video bpp
103 #define OC_FULLSCREEN_WIDTH 1024
104 #define OC_FULLSCREEN_HEIGHT 768
105
106 // Exit code. This is not an exhaustive list.
107 #define OC_ERROR_NOT_FOUND -1 ///< Config files not found
108 #define OC_ERROR_PARSE_CONFIG -2 ///< Config parsing error
109 #define OC_ERROR_ARGUMENT -3 ///< Command line argument error
110
111 #define OC_ERROR_MEMORY -10 ///< Out of memory
112
113 #define OC_ERROR_SDL_INIT -20 ///< SDL initialization failed
114 #define OC_ERROR_SDL_DOUBLEBUFFER -21 ///< Video double buffer unsupported
115 #define OC_ERROR_SDL_ACCELERATED -22 ///< Accelerated display unsupported
116 #define OC_ERROR_SDL_VIDEORESIZE -23 ///< Unable to resize the window
117 #define OC_ERROR_SDL_FULLSCREEN -24 ///< Unable to go fullscreen
118 #define OC_ERROR_SDL_OPENGL -25 ///< Unable to load OpenGL library dynamically
119
120 // Settings file
121 #define OC_CONFIG_FILE_FILENAME "config/opencity.xml"
122
123 // Others macros
124 #define OC_WINDOW_NAME PACKAGE VERSION
125 #define OC_PROGRAM_NAME "OpenCity standalone/client application"
126
127
128
129 /*=====================================================================*/
130 /* LOCAL VARIABLES */
131 /*=====================================================================*/
132 /// The current user interface is pointed by this pointer
133 static UI* uipCurrentUI = NULL;
134
135 /// Set to true when the user request to quit the program
136 static bool bQuit = false;
137 static bool bRestart = false;
138
139 /// Flags we will pass into SDL_SetVideoMode.
140 static int iVideoFlag = SDL_OPENGL;
141
142 /// The paths are static so that the others can not access this
143 static string sDataDir = "";
144 static string sSaveDir = "";
145 static string sConfigDir = "";
146
147 /// Set to true if OpenGL version string is requested from command line
148 static bool bGLVersion = false;
149
150
151 /*=====================================================================*/
ocQuit(const int quit_code)152 void ocQuit( const int quit_code )
153 {
154 cout << endl
155 << "Quit requested, quit code is : " << quit_code
156 << endl
157 << "Bye bye !"
158 << endl;
159 bQuit = true;
160 }
161
162
163 /*=====================================================================*/
ocRestart()164 void ocRestart()
165 {
166 cout << "Restart with a new city from scratch. " << endl;
167 bRestart = true;
168 }
169
170
171 /*=====================================================================*/
ocSetNewUI(UI * pcNewUI)172 void ocSetNewUI( UI * pcNewUI)
173 {
174 uipCurrentUI = pcNewUI;
175 }
176
177
178 /*=====================================================================*/
ocKeyboard(const SDL_KeyboardEvent & rcEvent)179 void ocKeyboard( const SDL_KeyboardEvent& rcEvent )
180 {
181 if (uipCurrentUI != NULL) {
182 uipCurrentUI->Keyboard( rcEvent );
183 }
184 }
185
186
187 /*=====================================================================*/
ocMouseButton(const SDL_MouseButtonEvent & rcEvent)188 void ocMouseButton( const SDL_MouseButtonEvent& rcEvent )
189 {
190 if (uipCurrentUI != NULL) {
191 uipCurrentUI->MouseButton( rcEvent );
192 }
193 }
194
195
196 /*=====================================================================*/
ocMouseMotion(const SDL_MouseMotionEvent & motionEvent)197 void ocMouseMotion( const SDL_MouseMotionEvent& motionEvent )
198 {
199 if (uipCurrentUI != NULL) {
200 uipCurrentUI->MouseMotion( motionEvent );
201 }
202 }
203
204
205 /*=====================================================================*/
ocResize(const SDL_ResizeEvent & rcsResizeEvent)206 void ocResize( const SDL_ResizeEvent& rcsResizeEvent)
207 {
208 #ifndef __WIN32__
209 // Linux needs this whereas Win32 does not
210 // Set the new window's size
211 if( SDL_SetVideoMode(
212 rcsResizeEvent.w, rcsResizeEvent.h,
213 gVars.guiVideoBpp, iVideoFlag ) == 0 ) {
214 OPENCITY_FATAL( "Video mode reset failed: " << SDL_GetError( ) );
215 exit( OC_ERROR_SDL_VIDEORESIZE );
216 }
217 gVars.gpVideoSrf = SDL_GetVideoSurface();
218 #endif
219
220 // Save the new screen size
221 gVars.guiScreenWidth = rcsResizeEvent.w;
222 gVars.guiScreenHeight = rcsResizeEvent.h;
223
224 if (uipCurrentUI != NULL) {
225 uipCurrentUI->Resize( rcsResizeEvent );
226 }
227 }
228
229
230 /*=====================================================================*/
ocActive(const SDL_ActiveEvent & e)231 void ocActive( const SDL_ActiveEvent & e)
232 {
233 // OPENCITY_DEBUG( "Active event received" );
234
235 if (e.state & SDL_APPACTIVE) {
236 gVars.gboolActive = (e.gain == 1);
237 }
238 }
239
240
241 /*=====================================================================*/
ocExpose(const SDL_ExposeEvent & rcsExposeEvent)242 void ocExpose( const SDL_ExposeEvent& rcsExposeEvent )
243 {
244 if (uipCurrentUI != NULL) {
245 uipCurrentUI->Expose( rcsExposeEvent );
246 }
247 }
248
249
250 /*=====================================================================*/
ocProcessSDLEvents(void)251 void ocProcessSDLEvents( void )
252 {
253 static SDL_Event event;
254
255 // Grab all the events off the queue.
256 while( SDL_PollEvent( &event ) ) {
257
258 switch( event.type ) {
259 case SDL_KEYDOWN:
260 case SDL_KEYUP:
261 ocKeyboard( event.key );
262 break;
263
264 case SDL_MOUSEMOTION:
265 ocMouseMotion( event.motion );
266 break;
267
268 case SDL_MOUSEBUTTONDOWN:
269 case SDL_MOUSEBUTTONUP:
270 ocMouseButton( event.button );
271 break;
272
273 case SDL_VIDEORESIZE:
274 ocResize( event.resize );
275 break;
276
277 case SDL_ACTIVEEVENT:
278 ocActive( event.active );
279 break;
280
281 case SDL_VIDEOEXPOSE:
282 ocExpose( event.expose );
283 break;
284
285 case SDL_QUIT:
286 // Handle quit requests (like Ctrl-c).
287 cout << endl << "Quit requested, stoping " << OC_PROGRAM_NAME << "..." << endl;
288 bQuit = true;
289 break;
290 }
291 }
292 }
293
294
295 /*=====================================================================*/
initSDL()296 static int initSDL()
297 {
298 // Initialization of the SDL library
299 OPENCITY_DEBUG( "SDL Initialization" );
300
301 // Test Sharp Plus Framework
302 OPENCITY_INFO( "SDL compile-time version: " << System::Information::Software::Sdl::GetCompiletimeVersion() );
303 OPENCITY_INFO( "SDL run-time version: " << System::Information::Software::Sdl::GetRuntimeVersion() );
304
305 // First, initialize SDL's video subsystem.
306 if( SDL_Init( SDL_INIT_VIDEO ) < 0 ) {
307 // Failed, exit.
308 OPENCITY_FATAL( "SDL video initialization failed: " << SDL_GetError() );
309 return OC_ERROR_SDL_INIT;
310 }
311
312 // Set the window's caption
313 SDL_WM_SetCaption( PACKAGE " " VERSION, NULL );
314 SDL_WM_SetIcon( IMG_Load(ocDataDirPrefix("graphism/icon/OpenCity32.png").c_str()), NULL );
315
316 // Set the SDL_GL_DOUBLEBUFFER attribute for smoother rendering
317 SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
318
319 // Set the SDL_GL_ACCELERATED_VISUAL attribute if enabled
320 // Note: This can lead to software rendering on Windows system with recent ATI Catalyst driver.
321 if (gVars.gboolAcceleratedVisual == true)
322 {
323 SDL_GL_SetAttribute( SDL_GL_ACCELERATED_VISUAL, 1 );
324 }
325
326 // Get the OpenGL driver name
327 const char* glDriver;
328 if (gVars.gsOpenGLDriver != "") {
329 OPENCITY_INFO( "Loading OpenGL driver: " << gVars.gsOpenGLDriver );
330 glDriver = gVars.gsOpenGLDriver.c_str();
331 }
332 else {
333 OPENCITY_INFO( "Loading default OpenGL driver..." );
334 glDriver = NULL;
335 }
336
337 // Dynamically load the default OpenGL implementation library
338 if (SDL_GL_LoadLibrary(glDriver) < 0) {
339 OPENCITY_FATAL( "Failed to load OpenGL library: " << SDL_GetError() );
340 return OC_ERROR_SDL_OPENGL;
341 }
342
343 // Will we go for fullscreen ?
344 if (gVars.gboolFullScreen == true) {
345 iVideoFlag |= SDL_FULLSCREEN;
346
347 // Use the current desktop video resolution
348 gVars.guiScreenWidth = 0;
349 gVars.guiScreenHeight = 0;
350 }
351 else {
352 iVideoFlag |= SDL_RESIZABLE;
353 }
354
355 // OK, go for the video settings now
356 gVars.gpVideoSrf = SDL_SetVideoMode( gVars.guiScreenWidth, gVars.guiScreenHeight, gVars.guiVideoBpp, iVideoFlag );
357 if ( gVars.gpVideoSrf == NULL ) {
358 // This could happen for a variety of reasons, including DISPLAY
359 // not being set, the specified resolution not being available, etc.
360 OPENCITY_ERROR(
361 "Initialization of " << gVars.guiVideoBpp <<
362 " bpp video mode failed: " << SDL_GetError()
363 );
364 return OC_ERROR_SDL_INIT;
365 }
366 else {
367 OPENCITY_INFO(
368 "Using " << (uint)gVars.gpVideoSrf->w <<
369 "x" << (uint)gVars.gpVideoSrf->h <<
370 " at " << (uint)gVars.gpVideoSrf->format->BitsPerPixel << " bpp"
371 );
372
373 // Store the fullscreen resolution for later use
374 if (gVars.gboolFullScreen == true) {
375 gVars.guiScreenWidth = (uint)gVars.gpVideoSrf->w;
376 gVars.guiScreenHeight = (uint)gVars.gpVideoSrf->h;
377 }
378 }
379
380 // Retrieve the video driver name
381 #define BUFFER_SIZE 256
382 char myBuffer[BUFFER_SIZE];
383 if (SDL_VideoDriverName(myBuffer, BUFFER_SIZE) != NULL) {
384 OPENCITY_INFO( "Current video driver: " << myBuffer );
385 }
386 else {
387 OPENCITY_ERROR( "Failed to retrieve the video driver name" );
388 }
389
390 // Display the OpenGL version string
391 if (bGLVersion == true) {
392 OPENCITY_INFO( "GL vendor: " << glGetString( GL_VENDOR ) );
393 OPENCITY_INFO( "GL renderer: " << glGetString( GL_RENDERER ) );
394 OPENCITY_INFO( "GL version: " << glGetString( GL_VERSION ) );
395 OPENCITY_INFO( "GL extensions: " << glGetString( GL_EXTENSIONS ) );
396 }
397
398 // Is doublebuffer feature available ?
399 int iValue = 0;
400 SDL_GL_GetAttribute( SDL_GL_DOUBLEBUFFER, &iValue );
401 if ( iValue == 0 ) {
402 OPENCITY_FATAL( "Video double buffer unsupported" );
403 return OC_ERROR_SDL_DOUBLEBUFFER;
404 }
405 else {
406 OPENCITY_INFO( "Video double buffer supported" );
407 }
408
409 // Is GL accelerated display available ?
410 if (gVars.gboolAcceleratedVisual == true)
411 {
412 SDL_GL_GetAttribute( SDL_GL_ACCELERATED_VISUAL, &iValue );
413 if ( iValue == 0 ) {
414 OPENCITY_FATAL( "Accelerated visual unsupported" );
415 return OC_ERROR_SDL_ACCELERATED;
416 }
417 else {
418 OPENCITY_INFO( "Accelerated visual supported" );
419 }
420 }
421
422 return 0;
423 }
424
425
426 /*=====================================================================*/
formatPath(const string & rcsPath)427 static string formatPath(const string& rcsPath)
428 {
429 string result = rcsPath;
430
431 if (result.size() > 0) {
432 // Delete all quotes "
433 string::size_type pos;
434 while ( (pos = result.find( '\"' )) != result.npos ) {
435 result.erase( pos );
436 }
437 // Append the "/" to path
438 if (result[ result.size()-1 ] != '/')
439 result += '/';
440 }
441 else {
442 result = "/";
443 }
444
445 return result;
446 }
447
448
449 /*=====================================================================*/
parseArg(int argc,char * argv[])450 static int parseArg(int argc, char *argv[])
451 {
452 // Command-line options definition
453 enum {
454 OPT_GL_VERSION,
455 OPT_GL_DRIVER,
456 OPT_FULL_SCREEN,
457 OPT_NO_AUDIO,
458 OPT_DATA_DIR,
459 OPT_CONF_DIR,
460 OPT_GENERATOR_HEIGHT_MAP,
461 OPT_GENERATOR_SEED,
462 OPT_GENERATOR_MAP,
463 OPT_GENERATOR_WATER,
464 OPT_GENERATOR_MAP_SHAPE,
465 OPT_GENERATOR_TREE_DENSITY,
466 OPT_HELP
467 };
468
469 CSimpleOpt::SOption g_rgOptions[] = {
470 { OPT_GL_VERSION, (char*)"--gl-version", SO_NONE },
471 { OPT_GL_VERSION, (char*)"-glv", SO_NONE },
472 { OPT_GL_DRIVER, (char*)"--gl-driver", SO_REQ_SEP },
473 { OPT_GL_DRIVER, (char*)"-gld", SO_REQ_SEP },
474 { OPT_FULL_SCREEN, (char*)"--full-screen", SO_NONE },
475 { OPT_FULL_SCREEN, (char*)"-fs", SO_NONE },
476 { OPT_NO_AUDIO, (char*)"--no-audio", SO_NONE },
477 { OPT_NO_AUDIO, (char*)"-na", SO_NONE },
478 { OPT_DATA_DIR, (char*)"--data-dir", SO_REQ_SEP },
479 { OPT_DATA_DIR, (char*)"-dd", SO_REQ_SEP },
480 { OPT_CONF_DIR, (char*)"--conf-dir", SO_REQ_SEP },
481 { OPT_CONF_DIR, (char*)"-cd", SO_REQ_SEP },
482 { OPT_GENERATOR_HEIGHT_MAP, (char*)"--generator-height-map", SO_REQ_SEP },
483 { OPT_GENERATOR_SEED, (char*)"--generator-seed", SO_REQ_SEP },
484 { OPT_GENERATOR_MAP, (char*)"--generator-map", SO_REQ_SEP },
485 { OPT_GENERATOR_WATER, (char*)"--generator-water", SO_REQ_SEP },
486 { OPT_GENERATOR_MAP_SHAPE, (char*)"--generator-map-shape", SO_REQ_SEP },
487 { OPT_GENERATOR_TREE_DENSITY, (char*)"--generator-tree-density", SO_REQ_SEP },
488 { OPT_HELP, (char*)"--help", SO_NONE },
489 { OPT_HELP, (char*)"-h", SO_NONE },
490 SO_END_OF_OPTIONS // END
491 };
492
493 // SimpleOpt parser initialization
494 CSimpleOpt args(argc, argv, g_rgOptions, SO_O_EXACT | SO_O_NOSLASH | SO_O_SHORTARG | SO_O_CLUMP );
495 int i = 0;
496 while (args.Next()) {
497 switch (args.LastError()) {
498 case SO_OPT_INVALID:
499 OPENCITY_OPTION( "" << args.OptionText() << " unrecognized" );
500 break;
501 case SO_OPT_MULTIPLE:
502 OPENCITY_OPTION( "" << args.OptionText() << " matched multiple options" );
503 break;
504 case SO_ARG_INVALID:
505 OPENCITY_OPTION( "" << args.OptionText() << " does not accept any argument" );
506 break;
507 case SO_ARG_INVALID_TYPE:
508 OPENCITY_OPTION( "" << args.OptionText() << " has an invalid argument format" );
509 break;
510 case SO_ARG_MISSING:
511 OPENCITY_OPTION( "" << args.OptionText() << " requires an argument" );
512 break;
513 case SO_ARG_INVALID_DATA:
514 OPENCITY_OPTION( "" << args.OptionText() << " has an invalid argument data" );
515 break;
516 case SO_SUCCESS:
517 OPENCITY_OPTION( "" << args.OptionText() << " detected" );
518 break;
519 } // switch (args.LastError())
520
521 // Exit the program on error
522 if (args.LastError() != SO_SUCCESS) {
523 OPENCITY_OPTION( "Try " << argv[0] << " --help for more usage information" );
524 exit( OC_ERROR_ARGUMENT );
525 }
526
527 switch (args.OptionId()) {
528 case OPT_GL_VERSION:
529 bGLVersion = true;
530 break;
531
532 case OPT_GL_DRIVER:
533 gVars.gsOpenGLDriver = args.OptionArg();
534 break;
535
536 case OPT_FULL_SCREEN:
537 gVars.gboolFullScreen = true;
538 break;
539
540 case OPT_NO_AUDIO:
541 gVars.gboolUseAudio = false;
542 break;
543
544 case OPT_DATA_DIR:
545 sDataDir = formatPath(args.OptionArg());
546 OPENCITY_OPTION( "The data directory is: \"" << sDataDir << "\"" );
547 break;
548
549 case OPT_CONF_DIR:
550 sConfigDir = formatPath(args.OptionArg());
551 OPENCITY_OPTION( "The configuration directory is: \"" << sConfigDir << "\"" );
552 break;
553
554 case OPT_GENERATOR_HEIGHT_MAP:
555 gVars.gsGeneratorHeightMap = args.OptionArg();
556 break;
557
558 case OPT_GENERATOR_SEED:
559 i = atoi( args.OptionArg() ); // return 0 as error value
560 if (i != 0 || ( i == 0 && strcmp(args.OptionArg(), "0") == 0 )) {
561 gVars.guiGeneratorSeed = i;
562 }
563 else {
564 OPENCITY_INFO(
565 "Argument provided to --generator-seed must be an integer. The default value is used instead."
566 );
567 }
568 break;
569
570 case OPT_GENERATOR_MAP:
571 i = atoi( args.OptionArg() ); // return 0 as error value
572 if (i != 0 || ( i == 0 && strcmp(args.OptionArg(), "0") == 0 )) {
573 gVars.guiGeneratorMapType = MapGen::MapMaker::MAP_TYPE(i);
574 }
575 else {
576 OPENCITY_INFO(
577 "Argument provided to --generator-map must be an integer. The default value is used instead."
578 );
579 }
580 break;
581
582 case OPT_GENERATOR_WATER:
583 i = atoi( args.OptionArg() ); // return 0 as error value
584 if (i != 0 || ( i == 0 && strcmp(args.OptionArg(), "0") == 0 )) {
585 gVars.guiGeneratorWaterType = MapGen::MapMaker::WATER_TYPE(i);
586 }
587 else {
588 OPENCITY_INFO(
589 "Argument provided to --generator-water must be an integer. The default value is used instead." );
590 }
591 break;
592
593 case OPT_GENERATOR_MAP_SHAPE:
594 i = atoi( args.OptionArg() ); // return 0 as error value
595 if (i != 0 || ( i == 0 && strcmp(args.OptionArg(), "0") == 0 )) {
596 gVars.guiGeneratorMapShapeType = MapGen::MapMaker::MAP_SHAPE_TYPE(i);
597 }
598 else {
599 OPENCITY_INFO(
600 "Argument provided to --generator-map-shape must be an integer. The default value is used instead."
601 );
602 }
603 break;
604
605 case OPT_GENERATOR_TREE_DENSITY:
606 i = atoi( args.OptionArg() ); // return 0 as error value
607 if (i != 0 || ( i == 0 && strcmp(args.OptionArg(), "0") == 0 )) {
608 gVars.guiGeneratorTreeDensityType = MapGen::MapMaker::TREE_DENSITY_TYPE(i);
609 }
610 else {
611 OPENCITY_INFO(
612 "Argument provided to --generator-tree-density must be an integer. The default value is used instead."
613 );
614 }
615 break;
616
617 case OPT_HELP:
618 cout << "Usage: " << argv[0]
619 << " [-fs|--full-screen] [-glv|--gl-version] [-gld|--gl-driver openGLDriverName]"
620 << " [-dd|--data-dir newDataPath] [-cd|--conf-dir newConfigPath]"
621 << " [-na|--no-audio] [--generator-height-map heightMapPicture |"
622 << " (--generator-seed seed [--generator-map MAP-TYPE] [--generator-water WATER-TYPE]"
623 << " [--generator-map-shape MAP-SHAPE-TYPE] [--generator-tree-density TREE-DENSITY-TYPE])]"
624 << endl << endl
625 << "Where the map generator constants are: " << endl
626 << " MAP-TYPE : 0=plain (default), 1=hill, 2=mountain" << endl
627 << " TREE-DENSITY-TYPE: 0=sparse (default), 1=normal, 2=dense" << endl
628 << " MAP-SHAPE-TYPE : 0=none (default), 1=island, 2=volcano, 3=crater" << endl
629 << " WATER-TYPE : 0=dry, 1=lake (default), 2=coast" << endl
630 << endl;
631 cout << "Warning: the command option overwrite the config file settings."
632 << endl;
633 exit( OC_ERROR_ARGUMENT );
634 break;
635 } // switch (args.OptionId())
636 } // while (args.Next())
637
638 return 0;
639 }
640
641
642 /*=====================================================================*/
displaySplash()643 static void displaySplash()
644 {
645 #define OC_SPLASH_CLEAR_COLOR .15, .4, .25, 1.0
646
647 glClearColor( OC_SPLASH_CLEAR_COLOR );
648 glClear( GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT );
649 gVars.gpRenderer->DisplaySplash();
650 }
651
652
653 /*=====================================================================*/
displayStatus(const string & str)654 static void displayStatus( const string & str )
655 {
656 assert( gVars.gpRenderer != NULL );
657
658 uint x, y;
659
660 displaySplash();
661
662 // Center the text on the screen
663 x = (gVars.gpVideoSrf->w - str.size()*10)/2;
664 y = (gVars.gpVideoSrf->h - 140) / 2;
665 gVars.gpRenderer->DisplayText( x, y, OC_BLUE_COLOR, str );
666 SDL_GL_SwapBuffers();
667 }
668
669
670 /*=====================================================================*/
clientMode()671 static int clientMode()
672 {
673 // Initialize SDL
674 if (gVars.gpVideoSrf == NULL) {
675 int errorCode = initSDL();
676 if (errorCode != 0) {
677 return errorCode;
678 }
679 }
680
681
682 // Create the mutex first
683 gVars.gpmutexSim = SDL_CreateMutex();
684
685
686 // Load OpenGL extensions
687 gVars.gpExtensionMgr = new ExtensionManager();
688 if (gVars.gpExtensionMgr == NULL or !gVars.gpExtensionMgr->Load()) {
689 OPENCITY_FATAL( "Failed to load OpenGL extensions" );
690 SDL_Quit();
691
692 #ifdef __WIN32__
693 MessageBox(
694 NULL,
695 "Failed to load OpenGL extensions. Please start a new \
696 thread about your errror in the forum at http://www.opencity.info. Don't \
697 forget to include the output from stdout.txt and stderr.txt files. They're \
698 located in OpenCity executable directory.",
699 "OpenGL extensions loading error",
700 MB_ICONERROR | MB_OK | MB_SETFOREGROUND | MB_APPLMODAL
701 );
702 #endif
703
704 return OC_ERROR_SDL_OPENGL;
705 }
706
707
708 // Create the global renderer in order to use its text rendering functions
709 gVars.gpRenderer = new Renderer( gVars.guiCityWidth, gVars.guiCityLength );
710
711
712 // AudioManager's initialization
713 displayStatus( "Looking for GPU freezing system... ");
714 gVars.gpAudioMgr = new AudioManager();
715
716 if ( gVars.gpAudioMgr == NULL ) {
717 OPENCITY_FATAL( "Error while creating the audio manager" );
718 return OC_ERROR_MEMORY;
719 } else
720 if ( (gVars.gboolUseAudio == true)
721 and (gVars.gpAudioMgr->OpenAudio() != OC_ERR_FREE))
722 {
723 // try to open the audio device IF we have successfully created an audio object
724 OPENCITY_INFO( "Audio open error ! OpenCity continues happily." );
725 }
726
727
728 // Load musics and sounds
729 displayStatus( "Warming up central processing unit...");
730 gVars.gpAudioMgr->LoadMusicList( ocDataDirPrefix(OC_MUSIC_LIST_FILENAME), ocDataDirPrefix("") );
731 OPENCITY_INFO( "Loaded " << gVars.gpAudioMgr->GetNumberMusic() << " musics." );
732 gVars.gpAudioMgr->LoadSoundList( ocDataDirPrefix(OC_SOUND_LIST_FILENAME), ocDataDirPrefix("") );
733 OPENCITY_INFO( "Loaded " << gVars.gpAudioMgr->GetNumberSound() << " sounds." );
734
735
736 // Create the other required global managers
737 displayStatus( "Initializing the vibration detector..." );
738 gVars.gpMapMaker = new MapGen::MapMaker(
739 gVars.guiCityWidth, gVars.guiCityLength,
740 gVars.gsGeneratorHeightMap,
741 gVars.guiGeneratorMapType,
742 gVars.guiGeneratorWaterType,
743 gVars.guiGeneratorMapShapeType,
744 gVars.guiGeneratorTreeDensityType,
745 gVars.guiGeneratorSeed
746 );
747
748 displayStatus( "Activating embedded GPS...");
749 gVars.gpMapMgr = new Map( gVars.guiCityWidth, gVars.guiCityLength );
750
751 displayStatus( "Calibrating earthquake subsystem...");
752 gVars.gpGraphicMgr = new GraphicManager();
753
754 displayStatus( "Shaking DNA mixer thread...");
755 gVars.gpPropertyMgr = new PropertyManager();
756
757 displayStatus( "Mounting intergalactic hyperlink ...");
758 gVars.gpNetworking = new Networking();
759
760 displayStatus( "Initializing the particle handler ...");
761 gVars.gpMoveMgr = new MovementManager( gVars.gpGraphicMgr, gVars.gpMapMgr );
762
763
764 // The pointer of our new city
765 City* pNewCity = new City( gVars.guiCityWidth, gVars.guiCityLength );
766 if (pNewCity == NULL) {
767 OPENCITY_FATAL( "Error while creating new city" );
768 return OC_ERROR_MEMORY;
769 }
770 displayStatus( "Almost done...");
771
772
773 // FIXME: buggy MAS
774 /*
775 // Create the necessary classes for the Multi-Agent System
776 gVars.gpKernel = new Kernel();
777 gVars.gpEnvironment = new Environment(
778 gVars.guiCityWidth, gVars.guiCityLength, pNewCity->GetLayer( OC_LAYER_BUILDING ), gVars.gpKernel );
779
780 new AgentPolice(*gVars.gpKernel, *gVars.gpEnvironment, 1, 2);
781 new AgentPolice(*gVars.gpKernel, *gVars.gpEnvironment, 3, 4);
782 new AgentDemonstrator(*gVars.gpKernel, *gVars.gpEnvironment, 4, 2);
783 */
784
785 while (!bQuit) {
786 // Process input events
787 ocProcessSDLEvents();
788
789 // Restart with a new city from scratch
790 // WARNING: code duplication
791 if (bRestart) {
792 // Lock the simulator
793 SDL_LockMutex( gVars.gpmutexSim );
794
795 // Remove all moving objects
796 gVars.gpMoveMgr->Remove();
797
798 delete pNewCity;
799 delete gVars.gpMapMgr;
800 delete gVars.gpMapMaker;
801
802 gVars.guiGeneratorSeed = time(NULL);
803 gVars.gpMapMaker = new MapGen::MapMaker(
804 gVars.guiCityWidth, gVars.guiCityLength,
805 gVars.gsGeneratorHeightMap,
806 gVars.guiGeneratorMapType,
807 gVars.guiGeneratorWaterType,
808 gVars.guiGeneratorMapShapeType,
809 gVars.guiGeneratorTreeDensityType,
810 gVars.guiGeneratorSeed
811 );
812 gVars.gpMapMgr = new Map( gVars.guiCityWidth, gVars.guiCityLength );
813 pNewCity = new City( gVars.guiCityWidth, gVars.guiCityLength );
814 if (pNewCity == NULL) {
815 OPENCITY_FATAL( "Error while creating new city" );
816 return OC_ERROR_MEMORY;
817 }
818 gVars.gpRenderer->bHeightChange = true;
819 gVars.gpRenderer->bMinimapChange = true;
820
821 // Unlock the simulator
822 SDL_UnlockMutex( gVars.gpmutexSim );
823
824 bRestart = false;
825 }
826
827 // Process city's task
828 pNewCity->Run();
829 //gVars.gpKernel->live();
830
831 // IF the application is not iconified THEN update the display
832 if (gVars.gboolActive)
833 pNewCity->Display();
834
835 #undef OC_PRINT_FPS
836 #ifndef OC_PRINT_FPS
837 SDL_Delay( gVars.guiMsPerFrame );
838 #else
839 static Uint32 uiNumberTick = SDL_GetTicks();
840 static uint uiNumberFrame = 0;
841
842 uiNumberFrame++;
843 if ((SDL_GetTicks() - uiNumberTick) > 5000) {
844 cout << uiNumberFrame << " frames per 5 seconds = "
845 << uiNumberFrame / 5 << " FPS" << endl;
846 uiNumberTick = SDL_GetTicks();
847 uiNumberFrame = 0;
848 }
849 SDL_Delay( gVars.guiMsPerFrame );
850 #endif
851 } // while (!bQuit)
852
853 //delete gVars.gpEnvironment;
854 //delete gVars.gpKernel;
855
856
857 // Close the network connection
858 gVars.gpNetworking->Close();
859
860 // WARNING: the deleting/creating order is very important !
861 delete pNewCity;
862
863 delete gVars.gpMoveMgr;
864 delete gVars.gpNetworking;
865 delete gVars.gpPropertyMgr;
866 delete gVars.gpGraphicMgr;
867 delete gVars.gpMapMgr;
868 delete gVars.gpMapMaker;
869
870 // close the audio device then delete the audio manager
871 gVars.gpAudioMgr->CloseAudio();
872 delete gVars.gpAudioMgr;
873
874 delete gVars.gpRenderer;
875 delete gVars.gpExtensionMgr;
876
877 // delete the simulators' mutex now
878 SDL_DestroyMutex( gVars.gpmutexSim );
879
880 // SDL_FreeSurface( gVars.gpVideoSrf ); // This is not recommended by SDL documentation
881 gVars.gpVideoSrf = NULL;
882
883 SDL_Quit(); // WARNING: Calls free() on an invalid pointer. Detected by glibc
884
885 return 0;
886 }
887
888
889 /*=====================================================================*/
printCopyright()890 static void printCopyright() {
891 // Output the copyright text
892 cout << "Welcome to " << PACKAGE << "-" << ocStrVersion() << endl;
893 cout << "Copyright (C) by Duong Khang NGUYEN. All rights reserved." << endl;
894 cout << " web : http://www.opencity.info" << endl;
895 cout << " email: neoneurone @ gmail com" << endl << endl;
896
897 cout << "This program is released under the terms of" << endl;
898 cout << "GNU General Public License (See the COPYING file for more details)" << endl << endl;
899
900 cout << "Starting " << OC_PROGRAM_NAME << "..." << endl << endl;
901 }
902
903
904 /*=====================================================================*/
905 /** Return the save directory.
906 \return The pointer to the absolute directory. The caller must free
907 the pointer if it's not used anymore.
908 */
findSaveDir()909 static char* findSaveDir()
910 {
911 char* ret = NULL;
912
913 #ifndef __WIN32__
914 // Get the home directory from the environment variable
915 char* env = getenv("HOME");
916 if (env != NULL) {
917 ret = (char*)malloc( strlen(env) + 1 );
918 strcpy( ret, env );
919 }
920 #else
921 // Find the directory: "C:\Documents and Settings\username\Application Data"
922 // Required shell DLL version: 5.0 or later
923 // header: shlobj.h
924 // lib: shell32.lib ?
925 // dll: shell32.dll
926
927 TCHAR szPath[MAX_PATH];
928
929 if(SUCCEEDED(
930 SHGetFolderPath(NULL, CSIDL_APPDATA|CSIDL_FLAG_CREATE, NULL, 0, szPath)
931 )) {
932 ret = (char*)malloc( strlen(szPath) + 1 );
933 strcpy( ret, szPath );
934 }
935 #endif
936
937 // The required save directory does not exist, use the current directory
938 if (ret == NULL) {
939 ret = (char*)malloc( 2 );
940 strcpy( ret, "." );
941 }
942
943 return ret;
944 }
945
946
947 /*=====================================================================*/
948 /** Try to detect and set the datadir, the confdir and the savedir using
949 BinReloc library and win32 standard function
950 */
detectProgramPath()951 static void detectProgramPath()
952 {
953 char* pTemp = NULL;
954 BrInitError brError;
955
956 // IF the datadir is not set THEN try to get it from BinReloc routines
957 if (sDataDir == "") {
958 // Default system directory settings
959 sDataDir = DATADIR;
960
961 // Init the BinReloc routines
962 if (br_init(&brError) != 1) {
963 OPENCITY_INFO(
964 "Failed to initialized BinReloc routines to search for the datadir. " <<
965 "The error was: " << brError
966 );
967 }
968
969 // Construct the datadir from the prefix
970 pTemp = br_find_data_dir( sDataDir.c_str() );
971 sDataDir = pTemp;
972 sDataDir += "/";
973 sDataDir += PACKAGE;
974 sDataDir = formatPath( sDataDir );
975 free(pTemp);
976 }
977
978 // IF the configdir is not set THEN try to get it from BinReloc routines
979 if (sConfigDir == "") {
980 // Default system directory settings
981 sConfigDir = SYSCONFDIR;
982
983 // Init the BinReloc routines
984 if (br_init(&brError) != 1) {
985 OPENCITY_INFO(
986 "Failed to initialized BinReloc routines to search for the confdir. " <<
987 "The error was: " << brError
988 );
989 }
990
991 // Construct the pkgsysconfdir from the prefix
992 pTemp = br_find_etc_dir( sConfigDir.c_str() );
993 sConfigDir = pTemp;
994 sConfigDir += "/";
995 sConfigDir += PACKAGE;
996 sConfigDir = formatPath( sConfigDir );
997 free(pTemp);
998 }
999
1000 // IF the save directory is not set the find it
1001 if (sSaveDir == "") {
1002 pTemp = findSaveDir();
1003 sSaveDir = pTemp;
1004 free(pTemp);
1005 #ifndef __WIN32__
1006 sSaveDir += "/.opencity/";
1007 mkdir( sSaveDir.c_str(), 0755 );
1008 #else
1009 // Win32 uses \ as directory separtor
1010 sSaveDir += "\\opencity\\";
1011 CreateDirectory( sSaveDir.c_str(), NULL );
1012 // Replace \ by /
1013 string::size_type pos;
1014 while ( (pos = sSaveDir.find( '\\' )) != sSaveDir.npos ) {
1015 sSaveDir.replace( pos, 1, "/" );
1016 }
1017 #endif
1018 }
1019
1020 // Print out some information
1021 OPENCITY_INFO( "Detected data directory : " << sDataDir );
1022 OPENCITY_INFO( "Detected config directory : " << sConfigDir );
1023 OPENCITY_INFO( "Detected save directory : " << sSaveDir );
1024 }
1025
1026
1027 /*=====================================================================*/
1028 /** Read the OpenCity's main settings file "opencity.xml"
1029 \return "" if OK, otherwise the error description
1030 0: if OK
1031 OC_ERROR_NOT_FOUND: the config file has not been found
1032 OC_ERROR_PARSE_CONFIG: there was a parse error
1033 */
readSettings()1034 static string readSettings()
1035 {
1036 string errorString = "";
1037 TiXmlDocument settings;
1038
1039 // Now try to open the config file then read it
1040 OPENCITY_INFO(
1041 "Reading XML config file: \"" << ocConfigDirPrefix(OC_CONFIG_FILE_FILENAME) << "\""
1042 );
1043
1044 // Load the settings file
1045 string fn = ocConfigDirPrefix(OC_CONFIG_FILE_FILENAME);
1046 if (!settings.LoadFile(fn)) {
1047 errorString = settings.ErrorDesc();
1048 return errorString;
1049 }
1050
1051 // Error testing
1052 if (settings.Error()) {
1053 errorString = settings.ErrorDesc();
1054 return errorString;
1055 }
1056
1057 // Get the root element
1058 TiXmlNode* pRoot = settings.RootElement();
1059 if (pRoot == NULL) {
1060 errorString = settings.ErrorDesc();
1061 return errorString;
1062 }
1063
1064 // Parse the settings
1065 TiXmlElement* pElement = pRoot->FirstChildElement();
1066 const char* str = NULL;
1067 while (pElement != NULL)
1068 {
1069 // Debug
1070 // cout << i++ << "||" << *pElement << std::endl;
1071 // "fullscreen" element
1072 if (pElement->ValueStr() == "fullscreen") {
1073 str = pElement->Attribute("enable");
1074 if (str != NULL && strcmp(str, "true") == 0) {
1075 gVars.gboolFullScreen |= true;
1076 }
1077 else {
1078 gVars.gboolFullScreen |= false;
1079 }
1080
1081 // IF fullscreen mode enabled THEN read size
1082 if (gVars.gboolFullScreen) {
1083 TiXmlElement* pChild = pElement->FirstChildElement();
1084 while (pChild != NULL) {
1085 if (pChild->ValueStr() == "width") {
1086 pChild->QueryIntAttribute("value", (int*)&gVars.guiScreenWidth);
1087 }
1088 else if (pChild->ValueStr() == "height") {
1089 pChild->QueryIntAttribute("value", (int*)&gVars.guiScreenHeight);
1090 }
1091 pChild = pChild->NextSiblingElement();
1092 }
1093 }
1094 }
1095 // "acceleratedVisual" element
1096 else if (pElement->ValueStr() == "acceleratedVisual") {
1097 str = pElement->Attribute("enable");
1098
1099 // Eventually ignore relative command-line option.
1100 if (str != NULL && strcmp(str, "true") == 0) {
1101 gVars.gboolAcceleratedVisual = true;
1102 } else {
1103 gVars.gboolAcceleratedVisual = false;
1104 }
1105 }
1106 // "audio" element
1107 else if (pElement->ValueStr() == "audio") {
1108 str = pElement->Attribute("enable");
1109 if (str != NULL && strcmp(str, "true") == 0) {
1110 gVars.gboolUseAudio &= true;
1111 } else {
1112 gVars.gboolUseAudio &= false;
1113 }
1114 }
1115 // "city" element, read the city's size
1116 else if (pElement->ValueStr() == "city") {
1117 TiXmlElement* pChild = pElement->FirstChildElement();
1118 while (pChild != NULL) {
1119 // Debug
1120 // cout << i++ << "||" << *pChild << std::endl;
1121 if (pChild->ValueStr() == "width") {
1122 pChild->QueryIntAttribute("value", (int*)&gVars.guiCityWidth);
1123 }
1124 else if (pChild->ValueStr() == "length") {
1125 pChild->QueryIntAttribute("value", (int*)&gVars.guiCityLength);
1126 }
1127 pChild = pChild->NextSiblingElement();
1128 }
1129 }
1130 // "msPerFrame" element
1131 else if (pElement->ValueStr() == "msPerFrame") {
1132 pElement->QueryIntAttribute("value", (int*)&gVars.guiMsPerFrame);
1133 }
1134 // "zenServer" element
1135 else if (pElement->ValueStr() == "zenServer") {
1136 if (pElement->GetText() != NULL)
1137 gVars.gsZenServer = pElement->GetText();
1138 }
1139
1140 pElement = pElement->NextSiblingElement();
1141 }
1142
1143 return errorString;
1144 }
1145
1146
1147 /*=====================================================================*/
initGlobalVar()1148 static void initGlobalVar()
1149 {
1150 // Config file and command line options
1151 gVars.gboolUseAudio = true;
1152 gVars.gboolAcceleratedVisual = false;
1153 gVars.gboolFullScreen = false;
1154 gVars.gboolServerMode = false;
1155 gVars.guiCityWidth = OC_CITY_W;
1156 gVars.guiCityLength = OC_CITY_L;
1157 gVars.guiMsPerFrame = OC_MS_PER_FRAME;
1158 gVars.guiScreenWidth = OC_WINDOW_WIDTH;
1159 gVars.guiScreenHeight = OC_WINDOW_HEIGHT;
1160 gVars.guiVideoBpp = OC_WINDOW_BPP_DEFAULT;
1161 gVars.gsOpenGLDriver = "";
1162
1163 gVars.gsGeneratorHeightMap = "";
1164 gVars.guiGeneratorSeed = time(NULL);
1165 gVars.guiGeneratorMapType = MapGen::MapMaker::PLAIN;
1166 gVars.guiGeneratorWaterType = MapGen::MapMaker::LAKE;
1167 gVars.guiGeneratorMapShapeType = MapGen::MapMaker::NONE;
1168 gVars.guiGeneratorTreeDensityType = MapGen::MapMaker::SPARSE;
1169
1170 gVars.gfMsSimDelayMax = 0;
1171 gVars.gsZenServer = "localhost";
1172
1173 // Application status
1174 gVars.gboolActive = true; // the application is active at start
1175
1176 // The mutex that all the simulators depend on
1177 gVars.gpmutexSim = NULL;
1178
1179 // The famous renderer
1180 gVars.gpRenderer = NULL;
1181
1182 // Datamanagers
1183 gVars.gpAudioMgr = NULL; // global Audio Manager
1184 gVars.gpGraphicMgr = NULL; // global Graphic Manager
1185 gVars.gpPropertyMgr = NULL; // global Property Manager
1186 gVars.gpMapMaker = NULL; // global map maker
1187 gVars.gpMapMgr = NULL; // global height Map Manager
1188 gVars.gpNetworking = NULL; // global networking support class
1189 gVars.gpPathFinder = NULL; // global pathfinder class
1190 gVars.gpMoveMgr = NULL; // global movement manager
1191
1192 // Multi-Agent System
1193 gVars.gpKernel = NULL; // global MAS Kernel
1194 gVars.gpEnvironment = NULL; // global Environement class
1195
1196 // The SDL video surface
1197 gVars.gpVideoSrf = NULL; // global video screen surface
1198 }
1199
1200
1201 /*=====================================================================*/
1202 //#ifdef __WIN32__
1203 //extern "C"
1204 //#endif
main(int argc,char * argv[])1205 int main(int argc, char *argv[])
1206 {
1207 // Initialize the global settings variable to the default values
1208 initGlobalVar();
1209
1210 // Print out the copyright
1211 printCopyright();
1212
1213 // Parse the command-line options
1214 int returnCode = parseArg( argc, argv );
1215 if (returnCode != 0) {
1216 return returnCode;
1217 }
1218
1219 // Detect the main path: sDataDir and sSaveDir
1220 detectProgramPath();
1221
1222 // Read the application settings from the XML settings file
1223 string errorDesc = readSettings();
1224 if (errorDesc != "") {
1225 OPENCITY_FATAL(
1226 "There was an error while loading the settings file: \"" << errorDesc << "\"" << endl
1227 << "If the main config file \"" << OC_CONFIG_FILE_FILENAME << "\" has not been found then" << endl
1228 << "try to specify the data directory with ""--data-dir"" "
1229 << "and the configuration directory with ""--conf-dir""." << endl
1230 << "For example:" << endl
1231 << " " << argv[0] << " --data-dir \"/absolute/path/to/opencity/data\" "
1232 << "--conf-dir \"/absolute/path/to/opencity/conf\"" << endl
1233 << "or" << endl
1234 << " " << argv[0] << " --data-dir \"../relative/path/to/opencity/data\" "
1235 << "--conf-dir \"../relative/path/to/opencity/conf\"" << endl
1236 );
1237 exit(OC_ERROR_NOT_FOUND);
1238 }
1239
1240 // Test XPath
1241 // PropertyManager2* pPropertyMgr = new PropertyManager2();
1242 // const Property* myProperty = pPropertyMgr->Get( 1 );
1243 // OPENCITY_DEBUG( "Build cost 1: " << myProperty->uiDestroyCost );
1244 // const Property* myProperty2 = pPropertyMgr->Get( "graphism/residential/little_house/little_house.ac" );
1245 // OPENCITY_DEBUG( "Build cost 2: " << myProperty2->uiIncome );
1246 // delete pPropertyMgr;
1247 // abort();
1248
1249 // Initialization of global variables
1250 uipCurrentUI = NULL;
1251 gVars.gfMsSimDelayMax = log10((OC_FLOAT)gVars.guiCityWidth*gVars.guiCityLength + 1) * OC_MS_GLOBAL_LOG_FACTOR;
1252
1253 // Initialize the random number generator
1254 srand( time(NULL) );
1255
1256 // Launch the game
1257 returnCode = clientMode();
1258
1259 return returnCode;
1260 }
1261
1262
1263 /*=====================================================================*/
1264 /* GLOBAL FUNCTIONS */
1265 /*=====================================================================*/
1266 string
ocDataDirPrefix(const string & s)1267 ocDataDirPrefix( const string& s )
1268 {
1269 return sDataDir + s;
1270 }
1271
1272
1273 /*=====================================================================*/
1274 string
ocConfigDirPrefix(const string & s)1275 ocConfigDirPrefix( const string& s )
1276 {
1277 return sConfigDir + s;
1278 }
1279
1280
1281 /*=====================================================================*/
1282 string
ocSaveDirPrefix(const string & s)1283 ocSaveDirPrefix( const string& s )
1284 {
1285 return sSaveDir + s;
1286 }
1287
1288
1289 /*=====================================================================*/
ocStrVersion()1290 string ocStrVersion()
1291 {
1292 std::ostringstream oss;
1293
1294 oss << OC_VERSION << "." << OC_PATCHLEVEL << "." << OC_SUBLEVEL;
1295 return oss.str();
1296 }
1297
1298
1299 /*=====================================================================*/
ocLongVersion()1300 long ocLongVersion()
1301 {
1302 long lVersion = 0;
1303
1304 lVersion = OC_VERSION*65536 + OC_PATCHLEVEL*256 + OC_SUBLEVEL;
1305 return lVersion;
1306 }
1307