#include <simgear/timing/sg_time.hxx>
#include <simgear/misc/sg_dir.hxx>
+#include <boost/foreach.hpp>
+
#include <math.h> // rint()
#include <stdio.h>
#include <stdlib.h> // atof(), atoi()
#include <simgear/misc/sg_path.hxx>
#include <simgear/scene/material/mat.hxx>
#include <simgear/sound/soundmgr_openal.hxx>
+#include <simgear/misc/strutils.hxx>
#include <Autopilot/route_mgr.hxx>
#include <GUI/gui.h>
#include "util.hxx"
#include "viewmgr.hxx"
#include <Main/viewer.hxx>
-#include <simgear/version.h>
+#include <Environment/presets.hxx>
+
+#include <osg/Version>
+
+#if defined( HAVE_VERSION_H ) && HAVE_VERSION_H
+# include <Include/version.h>
+# include <simgear/version.h>
+#else
+# include <Include/no_version.h>
+#endif
+
+#ifdef __APPLE__
+# include <CoreFoundation/CoreFoundation.h>
+#endif
using std::string;
using std::sort;
using std::cout;
using std::cerr;
using std::endl;
-
-#ifndef VERSION
-#define VERSION "CVS "__DATE__
-#endif
+using std::vector;
+using std::cin;
#define NEW_DEFAULT_MODEL_HZ 120
+// defined in bootstrap.cxx
+extern char *homedir;
+extern char *hostname;
+
enum
{
FG_OPTIONS_OK = 0,
FG_OPTIONS_SHOW_SOUND_DEVICES = 6
};
+static flightgear::Options* shared_instance = NULL;
+
static double
atof( const string& str )
{
return ::atoi( str.c_str() );
}
+static int fgSetupProxy( const char *arg );
+
/**
* Set a few fail-safe default property values.
*
* in case, we provide some initial sane values here. This method
* should be invoked *before* reading any init files.
*/
-void
+static void
fgSetDefaults ()
{
- // set a possibly independent location for scenery data
- const char *envp = ::getenv( "FG_SCENERY" );
- if ( envp != NULL ) {
- // fg_root could be anywhere, so default to environmental
- // variable $FG_ROOT if it is set.
- globals->set_fg_scenery(envp);
- } else {
- // Otherwise, default to Scenery being in $FG_ROOT/Scenery
- globals->set_fg_scenery("");
- }
-
// Position (deliberately out of range)
fgSetDouble("/position/longitude-deg", 9999.0);
fgSetDouble("/position/latitude-deg", 9999.0);
#elif defined(sgi)
fgSetString("/sim/startup/browser-app", "launchWebJumper");
#else
- envp = ::getenv( "WEBBROWSER" );
- if (!envp) envp = "netscape";
- fgSetString("/sim/startup/browser-app", envp);
+ const char* browserEnv = ::getenv( "WEBBROWSER" );
+ if (!browserEnv) browserEnv = "netscape";
+ fgSetString("/sim/startup/browser-app", browserEnv);
#endif
fgSetString("/sim/logging/priority", "alert");
// Features
fgSetBool("/sim/hud/color/antialiased", false);
- fgSetBool("/sim/hud/enable3d", true);
- fgSetBool("/sim/hud/visibility", false);
+ fgSetBool("/sim/hud/enable3d[1]", true);
+ fgSetBool("/sim/hud/visibility[1]", false);
fgSetBool("/sim/panel/visibility", true);
fgSetBool("/sim/sound/enabled", true);
fgSetBool("/sim/sound/working", true);
fgSetBool("/sim/rendering/enhanced-lighting", false);
fgSetBool("/sim/rendering/distance-attenuation", false);
fgSetBool("/sim/rendering/specular-highlight", true);
+ fgSetString("/sim/rendering/materials-file", "materials.xml");
fgSetInt("/sim/startup/xsize", 800);
fgSetInt("/sim/startup/ysize", 600);
fgSetInt("/sim/rendering/bits-per-pixel", 16);
// HUD options
fgSetString("/sim/startup/units", "feet");
fgSetString("/sim/hud/frame-stat-type", "tris");
-
+
// Time options
fgSetInt("/sim/startup/time-offset", 0);
fgSetString("/sim/startup/time-offset-type", "system-offset");
fgSetBool("/sim/freeze/fuel", false);
fgSetString("/sim/multiplay/callsign", "callsign");
- fgSetString("/sim/multiplay/rxhost", "0");
- fgSetString("/sim/multiplay/txhost", "0");
+ fgSetString("/sim/multiplay/rxhost", "");
+ fgSetString("/sim/multiplay/txhost", "");
fgSetInt("/sim/multiplay/rxport", 0);
fgSetInt("/sim/multiplay/txport", 0);
+
+ fgSetString("/sim/version/flightgear", FLIGHTGEAR_VERSION);
+ fgSetString("/sim/version/simgear", SG_STRINGIZE(SIMGEAR_VERSION));
+ fgSetString("/sim/version/openscenegraph", osgGetVersion());
+ fgSetString("/sim/version/revision", REVISION);
+ fgSetInt("/sim/version/build-number", HUDSON_BUILD_NUMBER);
+ fgSetString("/sim/version/build-id", HUDSON_BUILD_ID);
+
+ char* envp = ::getenv( "http_proxy" );
+ if( envp != NULL )
+ fgSetupProxy( envp );
}
static bool
return true;
}
+static bool
+parseIntValue(char** ppParserPos, int* pValue,int min, int max, const char* field, const char* argument)
+{
+ if ( !strlen(*ppParserPos) )
+ return true;
+
+ char num[256];
+ int i = 0;
+
+ while ( isdigit((*ppParserPos)[0]) && (i<255) )
+ {
+ num[i] = (*ppParserPos)[0];
+ (*ppParserPos)++;
+ i++;
+ }
+ num[i] = '\0';
+
+ switch ((*ppParserPos)[0])
+ {
+ case 0:
+ break;
+ case ':':
+ (*ppParserPos)++;
+ break;
+ default:
+ SG_LOG(SG_GENERAL, SG_ALERT, "Illegal character in time string for " << field << ": '" <<
+ (*ppParserPos)[0] << "'.");
+ // invalid field - skip rest of string to avoid further errors
+ while ((*ppParserPos)[0])
+ (*ppParserPos)++;
+ return false;
+ }
+
+ if (i<=0)
+ return true;
+
+ int value = atoi(num);
+ if ((value < min)||(value > max))
+ {
+ SG_LOG(SG_GENERAL, SG_ALERT, "Invalid " << field << " in '" << argument <<
+ "'. Valid range is " << min << "-" << max << ".");
+ return false;
+ }
+ else
+ {
+ *pValue = value;
+ return true;
+ }
+}
+
// parse a time string ([+/-]%f[:%f[:%f]]) into hours
static double
parse_time(const string& time_in) {
return(sign * result);
}
-
// parse a date string (yyyy:mm:dd:hh:mm:ss) into a time_t (seconds)
-static long int
-parse_date( const string& date)
+static long int
+parse_date( const string& date, const char* timeType)
{
- struct tm gmt;
- char * date_str, num[256];
- int i;
- // initialize to zero
- gmt.tm_sec = 0;
- gmt.tm_min = 0;
- gmt.tm_hour = 0;
- gmt.tm_mday = 0;
- gmt.tm_mon = 0;
- gmt.tm_year = 0;
- gmt.tm_isdst = 0; // ignore daylight savings time for the moment
- date_str = (char *)date.c_str();
- // get year
- if ( strlen(date_str) ) {
- i = 0;
- while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
- num[i] = date_str[0];
- date_str++;
- i++;
- }
- if ( date_str[0] == ':' ) {
- date_str++;
- }
- num[i] = '\0';
- gmt.tm_year = atoi(num) - 1900;
- }
- // get month
- if ( strlen(date_str) ) {
- i = 0;
- while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
- num[i] = date_str[0];
- date_str++;
- i++;
- }
- if ( date_str[0] == ':' ) {
- date_str++;
- }
- num[i] = '\0';
- gmt.tm_mon = atoi(num) -1;
- }
- // get day
- if ( strlen(date_str) ) {
- i = 0;
- while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
- num[i] = date_str[0];
- date_str++;
- i++;
- }
- if ( date_str[0] == ':' ) {
- date_str++;
- }
- num[i] = '\0';
- gmt.tm_mday = atoi(num);
- }
- // get hour
- if ( strlen(date_str) ) {
- i = 0;
- while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
- num[i] = date_str[0];
- date_str++;
- i++;
- }
- if ( date_str[0] == ':' ) {
- date_str++;
- }
- num[i] = '\0';
- gmt.tm_hour = atoi(num);
- }
- // get minute
- if ( strlen(date_str) ) {
- i = 0;
- while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
- num[i] = date_str[0];
- date_str++;
- i++;
- }
- if ( date_str[0] == ':' ) {
- date_str++;
- }
- num[i] = '\0';
- gmt.tm_min = atoi(num);
+ struct tm gmt,*pCurrentTime;
+ int year,month,day,hour,minute,second;
+ char *argument, *date_str;
+
+ SGTime CurrentTime = SGTime();
+ CurrentTime.update(0,0,0,0);
+
+ // FIXME This should obtain system/aircraft/GMT time depending on timeType
+ pCurrentTime = CurrentTime.getGmt();
+
+ // initialize all fields with current time
+ year = pCurrentTime->tm_year + 1900;
+ month = pCurrentTime->tm_mon + 1;
+ day = pCurrentTime->tm_mday;
+ hour = pCurrentTime->tm_hour;
+ minute = pCurrentTime->tm_min;
+ second = pCurrentTime->tm_sec;
+
+ argument = (char *)date.c_str();
+ date_str = argument;
+
+ // start with parsing year
+ if (!strlen(date_str) ||
+ !parseIntValue(&date_str,&year,0,9999,"year",argument))
+ {
+ return -1;
}
- // get second
- if ( strlen(date_str) ) {
- i = 0;
- while ( (date_str[0] != ':') && (date_str[0] != '\0') ) {
- num[i] = date_str[0];
- date_str++;
- i++;
- }
- if ( date_str[0] == ':' ) {
- date_str++;
- }
- num[i] = '\0';
- gmt.tm_sec = atoi(num);
+
+ if (year < 1970)
+ {
+ SG_LOG(SG_GENERAL, SG_ALERT, "Invalid year '" << year << "'. Use 1970 or later.");
+ return -1;
}
+
+ parseIntValue(&date_str, &month, 1, 12, "month", argument);
+ parseIntValue(&date_str, &day, 1, 31, "day", argument);
+ parseIntValue(&date_str, &hour, 0, 23, "hour", argument);
+ parseIntValue(&date_str, &minute, 0, 59, "minute", argument);
+ parseIntValue(&date_str, &second, 0, 59, "second", argument);
+
+ gmt.tm_sec = second;
+ gmt.tm_min = minute;
+ gmt.tm_hour = hour;
+ gmt.tm_mday = day;
+ gmt.tm_mon = month - 1;
+ gmt.tm_year = year -1900;
+ gmt.tm_isdst = 0; // ignore daylight savings time for the moment
+
time_t theTime = sgTimeGetGMT( gmt.tm_year, gmt.tm_mon, gmt.tm_mday,
- gmt.tm_hour, gmt.tm_min, gmt.tm_sec );
- //printf ("Date is %s\n", ctime(&theTime));
- //printf ("in seconds that is %d\n", theTime);
- //exit(1);
+ gmt.tm_hour, gmt.tm_min, gmt.tm_sec );
+
+ SG_LOG(SG_GENERAL, SG_INFO, "Configuring startup time to " << ctime(&theTime));
+
return (theTime);
}
// parse time offset string into seconds
-static int
+static long int
parse_time_offset( const string& time_str) {
- int result;
+ long int result;
// printf("time offset = %s\n", time_str);
}
-// Parse --fov=x.xx type option
+// Parse --fov=x.xx type option
static double
parse_fov( const string& arg ) {
double fov = atof(arg);
// point values are ok.
//
// Serial example "--nmea=serial,dir,hz,device,baud" where
-//
+//
// device = OS device name of serial line to be open()'ed
// baud = {300, 1200, 2400, ..., 230400}
//
return true;
}
-
-// The parse wp and parse flight-plan options don't work anymore, because
-// the route manager and the airport subsystems have not yet been initialized
-// at this stage.
-
-// Parse --wp=ID[@alt]
-static void
-parse_wp( const string& arg ) {
- string_list *waypoints = globals->get_initial_waypoints();
- if (!waypoints) {
- waypoints = new string_list;
- globals->set_initial_waypoints(waypoints);
- }
- waypoints->push_back(arg);
-}
-
-
-// Parse --flight-plan=[file]
-static bool
-parse_flightplan(const string& arg)
-{
- string_list *waypoints = globals->get_initial_waypoints();
- if (!waypoints) {
- waypoints = new string_list;
- globals->set_initial_waypoints(waypoints);
- }
-
- sg_gzifstream in(arg.c_str());
- if ( !in.is_open() )
- return false;
-
- while ( true ) {
- string line;
- getline( in, line, '\n' );
-
- // catch extraneous (DOS) line ending character
- if ( line[line.length() - 1] < 32 )
- line = line.substr( 0, line.length()-1 );
-
- if ( in.eof() )
- break;
-
- waypoints->push_back(line);
- }
- return true;
-}
-
-
static int
fgOptLanguage( const char *arg )
{
return FG_OPTIONS_OK;
}
-static int
-fgOptFgRoot( const char *arg )
-{
- // this option is dealt with by fgInitFGRoot
- return FG_OPTIONS_OK;
-}
-
static int
fgOptFgScenery( const char *arg )
{
- globals->set_fg_scenery(arg);
+ globals->append_fg_scenery(arg);
return FG_OPTIONS_OK;
}
-static int
-fgOptFgAircraft(const char* arg)
-{
- // this option is dealt with by fgInitFGAircraft
- return FG_OPTIONS_OK;
-}
-
static int
fgOptFov( const char *arg )
{
static int
fgOptTimeOffset( const char *arg )
{
- fgSetInt("/sim/startup/time-offset",
+ fgSetLong("/sim/startup/time-offset",
parse_time_offset( arg ));
fgSetString("/sim/startup/time-offset-type", "system-offset");
return FG_OPTIONS_OK;
static int
fgOptStartDateSys( const char *arg )
{
- fgSetInt("/sim/startup/time-offset", parse_date( arg ) );
- fgSetString("/sim/startup/time-offset-type", "system");
+ long int theTime = parse_date( arg, "system" );
+ if (theTime>=0)
+ {
+ fgSetLong("/sim/startup/time-offset", theTime);
+ fgSetString("/sim/startup/time-offset-type", "system");
+ }
return FG_OPTIONS_OK;
}
static int
fgOptStartDateLat( const char *arg )
{
- fgSetInt("/sim/startup/time-offset", parse_date( arg ) );
- fgSetString("/sim/startup/time-offset-type", "latitude");
+ long int theTime = parse_date( arg, "latitude" );
+ if (theTime>=0)
+ {
+ fgSetLong("/sim/startup/time-offset", theTime);
+ fgSetString("/sim/startup/time-offset-type", "latitude");
+ }
return FG_OPTIONS_OK;
}
static int
fgOptStartDateGmt( const char *arg )
{
- fgSetInt("/sim/startup/time-offset", parse_date( arg ) );
- fgSetString("/sim/startup/time-offset-type", "gmt");
+ long int theTime = parse_date( arg, "gmt" );
+ if (theTime>=0)
+ {
+ fgSetLong("/sim/startup/time-offset", theTime);
+ fgSetString("/sim/startup/time-offset-type", "gmt");
+ }
return FG_OPTIONS_OK;
}
static int
fgSetupProxy( const char *arg )
{
- string options = arg;
+ string options = simgear::strutils::strip( arg );
string host, port, auth;
string::size_type pos;
+ // this is NURLP - NURLP is not an url parser
+ if( simgear::strutils::starts_with( options, "http://" ) )
+ options = options.substr( 7 );
+ if( simgear::strutils::ends_with( options, "/" ) )
+ options = options.substr( 0, options.length() - 1 );
+
host = port = auth = "";
- if ((pos = options.find("@")) != string::npos)
+ if ((pos = options.find("@")) != string::npos)
auth = options.substr(0, pos++);
else
pos = 0;
} else {
default_view_offset = atof( woffset.c_str() ) * SGD_DEGREES_TO_RADIANS;
}
- /* apparently not used (CLO, 11 Jun 2002)
+ /* apparently not used (CLO, 11 Jun 2002)
FGViewer *pilot_view =
(FGViewer *)globals->get_viewmgr()->get_view( 0 ); */
// this will work without calls to the viewer...
static int
fgOptVisibilityMeters( const char *arg )
{
- double visibility = atof( arg );
- fgDefaultWeatherValue("visibility-m", visibility);
+ Environment::Presets::VisibilitySingleton::instance()->preset( atof( arg ) );
return FG_OPTIONS_OK;
}
static int
fgOptVisibilityMiles( const char *arg )
{
- double visibility = atof( arg ) * 5280.0 * SG_FEET_TO_METER;
- fgDefaultWeatherValue("visibility-m", visibility);
+ Environment::Presets::VisibilitySingleton::instance()->preset( atof( arg ) * 5280.0 * SG_FEET_TO_METER );
return FG_OPTIONS_OK;
}
double max_hdg = min_hdg + (20 - sqrt(sg_random() * 400));
double speed = sg_random() * sg_random() * 40;
double gust = speed + (10 - sqrt(sg_random() * 100));
- fgSetupWind(min_hdg, max_hdg, speed, gust);
+ Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
return FG_OPTIONS_OK;
}
SG_LOG( SG_GENERAL, SG_ALERT, "bad wind value " << arg );
return FG_OPTIONS_ERROR;
}
- fgSetupWind(min_hdg, max_hdg, speed, gust);
+ Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
return FG_OPTIONS_OK;
}
static int
fgOptTurbulence( const char *arg )
{
- fgDefaultWeatherValue("turbulence/magnitude-norm", atof(arg));
+ Environment::Presets::TurbulenceSingleton::instance()->preset( atof(arg) );
return FG_OPTIONS_OK;
}
elevation = atof(spec.substr(0, pos).c_str());
thickness = atof(spec.substr(pos + 1).c_str());
}
- fgSetDouble("/environment/clouds/layer[0]/elevation-ft", elevation);
- fgSetDouble("/environment/clouds/layer[0]/thickness-ft", thickness);
- fgSetString("/environment/clouds/layer[0]/coverage", "overcast");
+ Environment::Presets::CeilingSingleton::instance()->preset( elevation, thickness );
return FG_OPTIONS_OK;
}
static int
fgOptWp( const char *arg )
{
- parse_wp( arg );
- return FG_OPTIONS_OK;
-}
-
-static int
-fgOptFlightPlan( const char *arg )
-{
- parse_flightplan ( arg );
+ string_list *waypoints = globals->get_initial_waypoints();
+ if (!waypoints) {
+ waypoints = new string_list;
+ globals->set_initial_waypoints(waypoints);
+ }
+ waypoints->push_back(arg);
return FG_OPTIONS_OK;
}
}
static int
-fgOptADF( const char * arg )
+fgOptADF1( const char * arg )
{
double rot, freq;
if (parse_colon(arg, &rot, &freq))
- fgSetDouble("/instrumentation/adf/rotation-deg", rot);
- fgSetDouble("/instrumentation/adf/frequencies/selected-khz", freq);
+ fgSetDouble("/instrumentation/adf[0]/rotation-deg", rot);
+ fgSetDouble("/instrumentation/adf[0]/frequencies/selected-khz", freq);
+ return FG_OPTIONS_OK;
+}
+
+static int
+fgOptADF2( const char * arg )
+{
+ double rot, freq;
+ if (parse_colon(arg, &rot, &freq))
+ fgSetDouble("/instrumentation/adf[1]/rotation-deg", rot);
+ fgSetDouble("/instrumentation/adf[1]/frequencies/selected-khz", freq);
return FG_OPTIONS_OK;
}
fgSetString("/instrumentation/dme/frequencies/source",
"/instrumentation/nav[1]/frequencies/selected-mhz");
} else {
+ double frequency = atof(arg);
+ if (frequency==0.0)
+ {
+ SG_LOG(SG_INPUT, SG_ALERT, "Invalid DME frequency: '" << arg << "'.");
+ return FG_OPTIONS_ERROR;
+ }
fgSetInt("/instrumentation/dme/switch-position", 2);
fgSetString("/instrumentation/dme/frequencies/source",
"/instrumentation/dme/frequencies/selected-mhz");
- fgSetString("/instrumentation/dme/frequencies/selected-mhz", arg);
+ fgSetDouble("/instrumentation/dme/frequencies/selected-mhz", frequency);
}
return FG_OPTIONS_OK;
}
return FG_OPTIONS_OK;
}
+static int
+fgOptNoScenarios( const char *arg )
+{
+ SGPropertyNode_ptr ai_node = fgGetNode( "/sim/ai", true );
+ ai_node->removeChildren("scenario",false);
+ ai_node->setBoolValue( "enabled", false );
+ return FG_OPTIONS_OK;
+}
+
static int
fgOptRunway( const char *arg )
{
static int
fgOptVersion( const char *arg )
{
- cerr << "FlightGear version: " << VERSION << endl;
+ cerr << "FlightGear version: " << FLIGHTGEAR_VERSION << endl;
+ cerr << "Revision: " << REVISION << endl;
+ cerr << "Build-Id: " << HUDSON_BUILD_ID << endl;
cerr << "FG_ROOT=" << globals->get_fg_root() << endl;
cerr << "FG_HOME=" << fgGetString("/sim/fg-home") << endl;
cerr << "FG_SCENERY=";
}
static int
-fgOptFpe(const char* arg)
+fgOptCallSign(const char * arg)
{
- // Actually handled in bootstrap.cxx
+ int i;
+ char callsign[11];
+ strncpy(callsign,arg,10);
+ callsign[10]=0;
+ for (i=0;callsign[i];i++)
+ {
+ char c = callsign[i];
+ if (c >= 'A' && c <= 'Z') continue;
+ if (c >= 'a' && c <= 'z') continue;
+ if (c >= '0' && c <= '9') continue;
+ if (c == '-' || c == '_') continue;
+ // convert any other illegal characters
+ callsign[i]='-';
+ }
+ fgSetString("sim/multiplay/callsign", callsign );
return FG_OPTIONS_OK;
}
+
+// Set a property for the --prop: option. Syntax: --prop:[<type>:]<name>=<value>
+// <type> can be "double" etc. but also only the first letter "d".
+// Examples: --prop:alpha=1 --prop:bool:beta=true --prop:d:gamma=0.123
static int
-fgOptFgviewer(const char* arg)
-{
- // Actually handled in bootstrap.cxx
- return FG_OPTIONS_OK;
+fgOptSetProperty(const char* raw)
+{
+ string arg(raw);
+ string::size_type pos = arg.find('=');
+ if (pos == arg.npos || pos == 0 || pos + 1 == arg.size())
+ return FG_OPTIONS_ERROR;
+
+ string name = arg.substr(0, pos);
+ string value = arg.substr(pos + 1);
+ string type;
+ pos = name.find(':');
+
+ if (pos != name.npos && pos != 0 && pos + 1 != name.size()) {
+ type = name.substr(0, pos);
+ name = name.substr(pos + 1);
+ }
+ SGPropertyNode *n = fgGetNode(name.c_str(), true);
+
+ bool writable = n->getAttribute(SGPropertyNode::WRITE);
+ if (!writable)
+ n->setAttribute(SGPropertyNode::WRITE, true);
+
+ bool ret = false;
+ if (type.empty())
+ ret = n->setUnspecifiedValue(value.c_str());
+ else if (type == "s" || type == "string")
+ ret = n->setStringValue(value.c_str());
+ else if (type == "d" || type == "double")
+ ret = n->setDoubleValue(strtod(value.c_str(), 0));
+ else if (type == "f" || type == "float")
+ ret = n->setFloatValue(atof(value.c_str()));
+ else if (type == "l" || type == "long")
+ ret = n->setLongValue(strtol(value.c_str(), 0, 0));
+ else if (type == "i" || type == "int")
+ ret = n->setIntValue(atoi(value.c_str()));
+ else if (type == "b" || type == "bool")
+ ret = n->setBoolValue(value == "true" || atoi(value.c_str()) != 0);
+
+ if (!writable)
+ n->setAttribute(SGPropertyNode::WRITE, false);
+ return ret ? FG_OPTIONS_OK : FG_OPTIONS_ERROR;
}
-static map<string,size_t> fgOptionMap;
-
/*
option has_param type property b_param s_param func
argument.
*/
-enum OptionType { OPTION_BOOL, OPTION_STRING, OPTION_DOUBLE, OPTION_INT, OPTION_CHANNEL, OPTION_FUNC };
+enum OptionType { OPTION_BOOL = 0, OPTION_STRING, OPTION_DOUBLE, OPTION_INT, OPTION_CHANNEL, OPTION_FUNC, OPTION_IGNORE };
+const int OPTION_MULTI = 1 << 17;
+
struct OptionDesc {
const char *option;
bool has_param;
- enum OptionType type;
+ int type;
const char *property;
bool b_param;
const char *s_param;
int (*func)( const char * );
} fgOptionArray[] = {
-
+
{"language", true, OPTION_FUNC, "", false, "", fgOptLanguage },
{"disable-game-mode", false, OPTION_BOOL, "/sim/startup/game-mode", false, "", 0 },
{"enable-game-mode", false, OPTION_BOOL, "/sim/startup/game-mode", true, "", 0 },
{"enable-mouse-pointer", false, OPTION_STRING, "/sim/startup/mouse-pointer", false, "enabled", 0 },
{"disable-random-objects", false, OPTION_BOOL, "/sim/rendering/random-objects", false, "", 0 },
{"enable-random-objects", false, OPTION_BOOL, "/sim/rendering/random-objects", true, "", 0 },
- {"disable-real-weather-fetch", false, OPTION_BOOL, "/environment/params/real-world-weather-fetch", false, "", 0 },
- {"enable-real-weather-fetch", false, OPTION_BOOL, "/environment/params/real-world-weather-fetch", true, "", 0 },
+ {"disable-real-weather-fetch", false, OPTION_BOOL, "/environment/realwx/enabled", false, "", 0 },
+ {"enable-real-weather-fetch", false, OPTION_BOOL, "/environment/realwx/enabled", true, "", 0 },
{"metar", true, OPTION_STRING, "/environment/metar/data", false, "", 0 },
{"disable-ai-models", false, OPTION_BOOL, "/sim/ai/enabled", false, "", 0 },
{"enable-ai-models", false, OPTION_BOOL, "/sim/ai/enabled", true, "", 0 },
+ {"disable-ai-traffic", false, OPTION_BOOL, "/sim/traffic-manager/enabled", false, "", 0 },
+ {"enable-ai-traffic", false, OPTION_BOOL, "/sim/traffic-manager/enabled", true, "", 0 },
{"disable-freeze", false, OPTION_BOOL, "/sim/freeze/master", false, "", 0 },
{"enable-freeze", false, OPTION_BOOL, "/sim/freeze/master", true, "", 0 },
{"disable-fuel-freeze", false, OPTION_BOOL, "/sim/freeze/fuel", false, "", 0 },
{"enable-fuel-freeze", false, OPTION_BOOL, "/sim/freeze/fuel", true, "", 0 },
{"disable-clock-freeze", false, OPTION_BOOL, "/sim/freeze/clock", false, "", 0 },
{"enable-clock-freeze", false, OPTION_BOOL, "/sim/freeze/clock", true, "", 0 },
- {"disable-hud-3d", false, OPTION_BOOL, "/sim/hud/enable3d", false, "", 0 },
- {"enable-hud-3d", false, OPTION_BOOL, "/sim/hud/enable3d", true, "", 0 },
+ {"disable-hud-3d", false, OPTION_BOOL, "/sim/hud/enable3d[1]", false, "", 0 },
+ {"enable-hud-3d", false, OPTION_BOOL, "/sim/hud/enable3d[1]", true, "", 0 },
{"disable-anti-alias-hud", false, OPTION_BOOL, "/sim/hud/color/antialiased", false, "", 0 },
{"enable-anti-alias-hud", false, OPTION_BOOL, "/sim/hud/color/antialiased", true, "", 0 },
{"control", true, OPTION_STRING, "/sim/control-mode", false, "", 0 },
{"disable-auto-coordination", false, OPTION_BOOL, "/sim/auto-coordination", false, "", 0 },
{"enable-auto-coordination", false, OPTION_BOOL, "/sim/auto-coordination", true, "", 0 },
{"browser-app", true, OPTION_STRING, "/sim/startup/browser-app", false, "", 0 },
- {"disable-hud", false, OPTION_BOOL, "/sim/hud/visibility", false, "", 0 },
- {"enable-hud", false, OPTION_BOOL, "/sim/hud/visibility", true, "", 0 },
+ {"disable-hud", false, OPTION_BOOL, "/sim/hud/visibility[1]", false, "", 0 },
+ {"enable-hud", false, OPTION_BOOL, "/sim/hud/visibility[1]", true, "", 0 },
{"disable-panel", false, OPTION_BOOL, "/sim/panel/visibility", false, "", 0 },
{"enable-panel", false, OPTION_BOOL, "/sim/panel/visibility", true, "", 0 },
{"disable-sound", false, OPTION_BOOL, "/sim/sound/working", false, "", 0 },
{"airport", true, OPTION_STRING, "/sim/presets/airport-id", false, "", 0 },
{"runway", true, OPTION_FUNC, "", false, "", fgOptRunway },
{"vor", true, OPTION_FUNC, "", false, "", fgOptVOR },
+ {"vor-frequency", true, OPTION_DOUBLE, "/sim/presets/vor-freq", false, "", fgOptVOR },
{"ndb", true, OPTION_FUNC, "", false, "", fgOptNDB },
+ {"ndb-frequency", true, OPTION_DOUBLE, "/sim/presets/ndb-freq", false, "", fgOptVOR },
{"carrier", true, OPTION_FUNC, "", false, "", fgOptCarrier },
{"parkpos", true, OPTION_FUNC, "", false, "", fgOptParkpos },
{"fix", true, OPTION_FUNC, "", false, "", fgOptFIX },
{"pitch", true, OPTION_DOUBLE, "/sim/presets/pitch-deg", false, "", 0 },
{"glideslope", true, OPTION_DOUBLE, "/sim/presets/glideslope-deg", false, "", 0 },
{"roc", true, OPTION_FUNC, "", false, "", fgOptRoc },
- {"fg-root", true, OPTION_FUNC, "", false, "", fgOptFgRoot },
- {"fg-scenery", true, OPTION_FUNC, "", false, "", fgOptFgScenery },
- {"fg-aircraft", true, OPTION_FUNC, "", false, "", fgOptFgAircraft },
+ {"fg-root", true, OPTION_IGNORE, "", false, "", 0 },
+ {"fg-scenery", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptFgScenery },
+ {"fg-aircraft", true, OPTION_IGNORE | OPTION_MULTI, "", false, "", 0 },
{"fdm", true, OPTION_STRING, "/sim/flight-model", false, "", 0 },
{"aero", true, OPTION_STRING, "/sim/aero", false, "", 0 },
- {"aircraft-dir", true, OPTION_STRING, "/sim/aircraft-dir", false, "", 0 },
+ {"aircraft-dir", true, OPTION_IGNORE, "", false, "", 0 },
{"model-hz", true, OPTION_INT, "/sim/model-hz", false, "", 0 },
{"speed", true, OPTION_INT, "/sim/speed-up", false, "", 0 },
{"trim", false, OPTION_BOOL, "/sim/presets/trim", true, "", 0 },
{"enable-skyblend", false, OPTION_BOOL, "/sim/rendering/skyblend", true, "", 0 },
{"disable-textures", false, OPTION_BOOL, "/sim/rendering/textures", false, "", 0 },
{"enable-textures", false, OPTION_BOOL, "/sim/rendering/textures", true, "", 0 },
- {"texture-filtering", false, OPTION_INT, "/sim/rendering/filtering", 1, "", 0 },
+ {"texture-filtering", false, OPTION_INT, "/sim/rendering/filtering", 1, "", 0 },
{"disable-wireframe", false, OPTION_BOOL, "/sim/rendering/wireframe", false, "", 0 },
{"enable-wireframe", false, OPTION_BOOL, "/sim/rendering/wireframe", true, "", 0 },
+ {"materials-file", true, OPTION_STRING, "/sim/rendering/materials-file", false, "", 0 },
+ {"disable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", false, "", 0 },
+ {"enable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", true, "", 0 },
+ {"terrasync-dir", true, OPTION_STRING, "/sim/terrasync/scenery-dir", false, "", 0 },
{"geometry", true, OPTION_FUNC, "", false, "", fgOptGeometry },
{"bpp", true, OPTION_FUNC, "", false, "", fgOptBpp },
{"units-feet", false, OPTION_STRING, "/sim/startup/units", false, "feet", 0 },
#ifdef FG_JPEG_SERVER
{"jpg-httpd", true, OPTION_CHANNEL, "", false, "", 0 },
#endif
- {"native", true, OPTION_CHANNEL, "", false, "", 0 },
- {"native-ctrls", true, OPTION_CHANNEL, "", false, "", 0 },
- {"native-fdm", true, OPTION_CHANNEL, "", false, "", 0 },
- {"native-gui", true, OPTION_CHANNEL, "", false, "", 0 },
- {"opengc", true, OPTION_CHANNEL, "", false, "", 0 },
- {"AV400", true, OPTION_CHANNEL, "", false, "", 0 },
- {"AV400Sim", true, OPTION_CHANNEL, "", false, "", 0 },
- {"garmin", true, OPTION_CHANNEL, "", false, "", 0 },
- {"nmea", true, OPTION_CHANNEL, "", false, "", 0 },
- {"generic", true, OPTION_CHANNEL, "", false, "", 0 },
- {"props", true, OPTION_CHANNEL, "", false, "", 0 },
- {"telnet", true, OPTION_CHANNEL, "", false, "", 0 },
+ {"native", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"native-ctrls", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"native-fdm", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"native-gui", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"opengc", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"AV400", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"AV400Sim", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"AV400WSimA", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"AV400WSimB", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"garmin", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"nmea", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"generic", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"props", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+ {"telnet", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
{"pve", true, OPTION_CHANNEL, "", false, "", 0 },
{"ray", true, OPTION_CHANNEL, "", false, "", 0 },
{"rul", true, OPTION_CHANNEL, "", false, "", 0 },
{"joyclient", true, OPTION_CHANNEL, "", false, "", 0 },
{"jsclient", true, OPTION_CHANNEL, "", false, "", 0 },
{"proxy", true, OPTION_FUNC, "", false, "", fgSetupProxy },
- {"callsign", true, OPTION_STRING, "sim/multiplay/callsign", false, "", 0 },
- {"multiplay", true, OPTION_CHANNEL, "", false, "", 0 },
- {"trace-read", true, OPTION_FUNC, "", false, "", fgOptTraceRead },
- {"trace-write", true, OPTION_FUNC, "", false, "", fgOptTraceWrite },
+ {"callsign", true, OPTION_FUNC, "", false, "", fgOptCallSign},
+ {"multiplay", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
+#ifdef FG_HAVE_HLA
+ {"hla", true, OPTION_CHANNEL, "", false, "", 0 },
+#endif
+ {"trace-read", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptTraceRead },
+ {"trace-write", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptTraceWrite },
{"log-level", true, OPTION_FUNC, "", false, "", fgOptLogLevel },
- {"view-offset", true, OPTION_FUNC, "", false, "", fgOptViewOffset },
+ {"view-offset", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptViewOffset },
{"visibility", true, OPTION_FUNC, "", false, "", fgOptVisibilityMeters },
{"visibility-miles", true, OPTION_FUNC, "", false, "", fgOptVisibilityMiles },
{"random-wind", false, OPTION_FUNC, "", false, "", fgOptRandomWind },
- {"wind", true, OPTION_FUNC, "", false, "", fgOptWind },
+ {"wind", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptWind },
{"turbulence", true, OPTION_FUNC, "", false, "", fgOptTurbulence },
{"ceiling", true, OPTION_FUNC, "", false, "", fgOptCeiling },
- {"wp", true, OPTION_FUNC, "", false, "", fgOptWp },
- {"flight-plan", true, OPTION_FUNC, "", false, "", fgOptFlightPlan },
- {"config", true, OPTION_FUNC, "", false, "", fgOptConfig },
+ {"wp", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptWp },
+ {"flight-plan", true, OPTION_STRING, "/autopilot/route-manager/file-path", false, "", NULL },
+ {"config", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptConfig },
{"aircraft", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
{"vehicle", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
- {"failure", true, OPTION_FUNC, "", false, "", fgOptFailure },
+ {"failure", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptFailure },
{"com1", true, OPTION_DOUBLE, "/instrumentation/comm[0]/frequencies/selected-mhz", false, "", 0 },
{"com2", true, OPTION_DOUBLE, "/instrumentation/comm[1]/frequencies/selected-mhz", false, "", 0 },
{"nav1", true, OPTION_FUNC, "", false, "", fgOptNAV1 },
{"nav2", true, OPTION_FUNC, "", false, "", fgOptNAV2 },
- {"adf", true, OPTION_FUNC, "", false, "", fgOptADF },
+ {"adf", /*legacy*/ true, OPTION_FUNC, "", false, "", fgOptADF1 },
+ {"adf1", true, OPTION_FUNC, "", false, "", fgOptADF1 },
+ {"adf2", true, OPTION_FUNC, "", false, "", fgOptADF2 },
{"dme", true, OPTION_FUNC, "", false, "", fgOptDME },
{"min-status", true, OPTION_STRING, "/sim/aircraft-min-status", false, "all", 0 },
{"livery", true, OPTION_FUNC, "", false, "", fgOptLivery },
- {"ai-scenario", true, OPTION_FUNC, "", false, "", fgOptScenario },
+ {"ai-scenario", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptScenario },
+ {"disable-ai-scenarios", false, OPTION_FUNC, "", false, "", fgOptNoScenarios},
{"parking-id", true, OPTION_FUNC, "", false, "", fgOptParking },
{"version", false, OPTION_FUNC, "", false, "", fgOptVersion },
- {"enable-fpe", false, OPTION_FUNC, "", false, "", fgOptFpe},
- {"fgviewer", false, OPTION_FUNC, "", false, "", fgOptFgviewer},
+ {"enable-fpe", false, OPTION_IGNORE, "", false, "", 0},
+ {"fgviewer", false, OPTION_IGNORE, "", false, "", 0},
+ {"prop", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptSetProperty},
{0}
};
-// Set a property for the --prop: option. Syntax: --prop:[<type>:]<name>=<value>
-// <type> can be "double" etc. but also only the first letter "d".
-// Examples: --prop:alpha=1 --prop:bool:beta=true --prop:d:gamma=0.123
-static bool
-set_property(const string& arg)
+namespace flightgear
{
- string::size_type pos = arg.find('=');
- if (pos == arg.npos || pos == 0 || pos + 1 == arg.size())
- return false;
- string name = arg.substr(0, pos);
- string value = arg.substr(pos + 1);
- string type;
- pos = name.find(':');
+/**
+ * internal storage of a value->option binding
+ */
+class OptionValue
+{
+public:
+ OptionValue(OptionDesc* d, const string& v) :
+ desc(d), value(v)
+ {;}
+
+ OptionDesc* desc;
+ string value;
+};
- if (pos != name.npos && pos != 0 && pos + 1 != name.size()) {
- type = name.substr(0, pos);
- name = name.substr(pos + 1);
+typedef std::vector<OptionValue> OptionValueVec;
+typedef std::map<string, OptionDesc*> OptionDescDict;
+
+class Options::OptionsPrivate
+{
+public:
+
+ OptionValueVec::const_iterator findValue(const string& key) const
+ {
+ OptionValueVec::const_iterator it = values.begin();
+ for (; it != values.end(); ++it) {
+ if (!it->desc) {
+ continue; // ignore markers
+ }
+
+ if (it->desc->option == key) {
+ return it;
+ }
+ } // of set values iteration
+
+ return it; // not found
+ }
+
+ OptionDesc* findOption(const string& key) const
+ {
+ OptionDescDict::const_iterator it = options.find(key);
+ if (it == options.end()) {
+ return NULL;
}
- SGPropertyNode *n = fgGetNode(name.c_str(), true);
-
- bool writable = n->getAttribute(SGPropertyNode::WRITE);
- if (!writable)
- n->setAttribute(SGPropertyNode::WRITE, true);
-
- bool ret = false;
- if (type.empty())
- ret = n->setUnspecifiedValue(value.c_str());
- else if (type == "s" || type == "string")
- ret = n->setStringValue(value.c_str());
- else if (type == "d" || type == "double")
- ret = n->setDoubleValue(strtod(value.c_str(), 0));
- else if (type == "f" || type == "float")
- ret = n->setFloatValue(atof(value.c_str()));
- else if (type == "l" || type == "long")
- ret = n->setLongValue(strtol(value.c_str(), 0, 0));
- else if (type == "i" || type == "int")
- ret = n->setIntValue(atoi(value.c_str()));
- else if (type == "b" || type == "bool")
- ret = n->setBoolValue(value == "true" || atoi(value.c_str()) != 0);
-
- if (!writable)
- n->setAttribute(SGPropertyNode::WRITE, false);
- return ret;
-}
-
-
-// Parse a single option
-static int
-parse_option (const string& arg)
-{
- if ( fgOptionMap.size() == 0 ) {
- size_t i = 0;
- OptionDesc *pt = &fgOptionArray[ 0 ];
- while ( pt->option != 0 ) {
- fgOptionMap[ pt->option ] = i;
- i += 1;
- pt += 1;
- }
+
+ return it->second;
+ }
+
+ int processOption(OptionDesc* desc, const string& arg_value)
+ {
+ if (!desc) {
+ return FG_OPTIONS_OK; // tolerate marker options
}
-
- // General Options
- if ( (arg == "--help") || (arg == "-h") ) {
- // help/usage request
- return(FG_OPTIONS_HELP);
- } else if ( (arg == "--verbose") || (arg == "-v") ) {
- // verbose help/usage request
- return(FG_OPTIONS_VERBOSE_HELP);
- } else if ( arg.find( "--show-aircraft") == 0) {
- return(FG_OPTIONS_SHOW_AIRCRAFT);
- } else if ( arg.find( "--show-sound-devices") == 0) {
- return(FG_OPTIONS_SHOW_SOUND_DEVICES);
- } else if ( arg.find( "--prop:" ) == 0 ) {
- if (!set_property(arg.substr(7))) {
- SG_LOG( SG_GENERAL, SG_ALERT, "Bad property assignment: " << arg );
- return FG_OPTIONS_ERROR;
+
+ switch ( desc->type & 0xffff ) {
+ case OPTION_BOOL:
+ fgSetBool( desc->property, desc->b_param );
+ break;
+ case OPTION_STRING:
+ if ( desc->has_param && !arg_value.empty() ) {
+ fgSetString( desc->property, arg_value.c_str() );
+ } else if ( !desc->has_param && arg_value.empty() ) {
+ fgSetString( desc->property, desc->s_param );
+ } else if ( desc->has_param ) {
+ SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+ return FG_OPTIONS_ERROR;
+ } else {
+ SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
+ return FG_OPTIONS_ERROR;
}
- } else if ( arg.find( "--" ) == 0 ) {
- size_t pos = arg.find( '=' );
- string arg_name, arg_value;
- if ( pos == string::npos ) {
- arg_name = arg.substr( 2 );
+ break;
+ case OPTION_DOUBLE:
+ if ( !arg_value.empty() ) {
+ fgSetDouble( desc->property, atof( arg_value ) );
} else {
- arg_name = arg.substr( 2, pos - 2 );
- arg_value = arg.substr( pos + 1);
+ SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+ return FG_OPTIONS_ERROR;
}
- map<string,size_t>::iterator it = fgOptionMap.find( arg_name );
- if ( it != fgOptionMap.end() ) {
- OptionDesc *pt = &fgOptionArray[ it->second ];
- switch ( pt->type ) {
- case OPTION_BOOL:
- fgSetBool( pt->property, pt->b_param );
- break;
- case OPTION_STRING:
- if ( pt->has_param && !arg_value.empty() ) {
- fgSetString( pt->property, arg_value.c_str() );
- } else if ( !pt->has_param && arg_value.empty() ) {
- fgSetString( pt->property, pt->s_param );
- } else if ( pt->has_param ) {
- SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
- return FG_OPTIONS_ERROR;
- } else {
- SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
- return FG_OPTIONS_ERROR;
- }
- break;
- case OPTION_DOUBLE:
- if ( !arg_value.empty() ) {
- fgSetDouble( pt->property, atof( arg_value ) );
- } else {
- SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
- return FG_OPTIONS_ERROR;
- }
- break;
- case OPTION_INT:
- if ( !arg_value.empty() ) {
- fgSetInt( pt->property, atoi( arg_value ) );
- } else {
- SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
- return FG_OPTIONS_ERROR;
- }
- break;
- case OPTION_CHANNEL:
- // XXX return value of add_channel should be checked?
- if ( pt->has_param && !arg_value.empty() ) {
- add_channel( pt->option, arg_value );
- } else if ( !pt->has_param && arg_value.empty() ) {
- add_channel( pt->option, pt->s_param );
- } else if ( pt->has_param ) {
- SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
- return FG_OPTIONS_ERROR;
- } else {
- SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
- return FG_OPTIONS_ERROR;
- }
- break;
- case OPTION_FUNC:
- if ( pt->has_param && !arg_value.empty() ) {
- return pt->func( arg_value.c_str() );
- } else if ( !pt->has_param && arg_value.empty() ) {
- return pt->func( pt->s_param );
- } else if ( pt->has_param ) {
- SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' needs a parameter" );
- return FG_OPTIONS_ERROR;
- } else {
- SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << arg << "' does not have a parameter" );
- return FG_OPTIONS_ERROR;
- }
- break;
- }
+ break;
+ case OPTION_INT:
+ if ( !arg_value.empty() ) {
+ fgSetInt( desc->property, atoi( arg_value ) );
} else {
- SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
- return FG_OPTIONS_ERROR;
+ SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+ return FG_OPTIONS_ERROR;
}
- } else {
- SG_LOG( SG_GENERAL, SG_ALERT, "Unknown option '" << arg << "'" );
- return FG_OPTIONS_ERROR;
+ break;
+ case OPTION_CHANNEL:
+ // XXX return value of add_channel should be checked?
+ if ( desc->has_param && !arg_value.empty() ) {
+ add_channel( desc->option, arg_value );
+ } else if ( !desc->has_param && arg_value.empty() ) {
+ add_channel( desc->option, desc->s_param );
+ } else if ( desc->has_param ) {
+ SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+ return FG_OPTIONS_ERROR;
+ } else {
+ SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
+ return FG_OPTIONS_ERROR;
+ }
+ break;
+ case OPTION_FUNC:
+ if ( desc->has_param && !arg_value.empty() ) {
+ return desc->func( arg_value.c_str() );
+ } else if ( !desc->has_param && arg_value.empty() ) {
+ return desc->func( desc->s_param );
+ } else if ( desc->has_param ) {
+ SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
+ return FG_OPTIONS_ERROR;
+ } else {
+ SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
+ return FG_OPTIONS_ERROR;
+ }
+ break;
+
+ case OPTION_IGNORE:
+ break;
}
-
+
return FG_OPTIONS_OK;
+ }
+
+ /**
+ * insert a marker value into the values vector. This is necessary
+ * when processing options, to ensure the correct ordering, where we scan
+ * for marker values in reverse, and then forwards within each group.
+ */
+ void insertGroupMarker()
+ {
+ values.push_back(OptionValue(NULL, "-"));
+ }
+
+ /**
+ * given a current iterator into the values, find the preceeding group marker,
+ * or return the beginning of the value vector.
+ */
+ OptionValueVec::const_iterator rfindGroup(OptionValueVec::const_iterator pos) const
+ {
+ while (--pos != values.begin()) {
+ if (pos->desc == NULL) {
+ return pos; // found a marker, we're done
+ }
+ }
+
+ return pos;
+ }
+
+ bool showHelp,
+ verbose,
+ showAircraft;
+
+ OptionDescDict options;
+ OptionValueVec values;
+ simgear::PathList propertyFiles;
+};
+
+Options* Options::sharedInstance()
+{
+ if (shared_instance == NULL) {
+ shared_instance = new Options;
+ }
+
+ return shared_instance;
+}
+
+Options::Options() :
+ p(new OptionsPrivate())
+{
+ p->showHelp = false;
+ p->verbose = false;
+ p->showAircraft = false;
+
+// build option map
+ OptionDesc *desc = &fgOptionArray[ 0 ];
+ while ( desc->option != 0 ) {
+ p->options[ desc->option ] = desc;
+ ++desc;
+ }
}
+
+Options::~Options()
+{
+}
+
+void Options::init(int argc, char **argv, const SGPath& appDataPath)
+{
+ fgSetDefaults();
+
+// first, process the command line
+ bool inOptions = true;
+ for (int i=1; i<argc; ++i) {
+ if (inOptions && (argv[i][0] == '-')) {
+ if (strcmp(argv[i], "--") == 0) { // end of options delimiter
+ inOptions = true;
+ continue;
+ }
+
+ int result = parseOption(argv[i]);
+ processArgResult(result);
+ } else {
+ // XML properties file
+ SGPath f(argv[i]);
+ if (!f.exists()) {
+ SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << f.str());
+ return;
+ }
+
+ p->propertyFiles.push_back(f);
+ }
+ } // of arguments iteration
+ p->insertGroupMarker(); // command line is one group
+
+// then config files
+ SGPath config;
+
+ if( homedir && hostname && strlen(hostname) > 0 ) {
+ // Check for ~/.fgfsrc.hostname
+ config.set(homedir);
+ config.append(".fgfsrc");
+ config.concat( "." );
+ config.concat( hostname );
+ readConfig(config);
+ }
+
+// Check for ~/.fgfsrc
+ if( homedir ) {
+ config.set(homedir);
+ config.append(".fgfsrc");
+ readConfig(config);
+ }
+
+// check for a config file in app data
+ SGPath appDataConfig(appDataPath);
+ appDataConfig.append("fgfsrc");
+ if (appDataConfig.exists()) {
+ readConfig(appDataConfig);
+ }
+
+// setup FG_ROOT
+ setupRoot();
+
+// system.fgfsrc handling
+ if( hostname && strlen(hostname) > 0 ) {
+ config.set(globals->get_fg_root());
+ config.append( "system.fgfsrc" );
+ config.concat( "." );
+ config.concat( hostname );
+ readConfig(config);
+ }
-
-// Parse the command line options
-void
-fgParseArgs (int argc, char **argv)
+ config.set(globals->get_fg_root());
+ config.append( "system.fgfsrc" );
+ readConfig(config);
+}
+
+void Options::initAircraft()
{
- bool in_options = true;
- bool verbose = false;
- bool help = false;
-
- SG_LOG(SG_GENERAL, SG_ALERT, "Processing command line arguments");
-
- for (int i = 1; i < argc; i++) {
- string arg = argv[i];
-
- if (in_options && (arg.find('-') == 0)) {
- if (arg == "--") {
- in_options = false;
- } else {
- int result = parse_option(arg);
- if ((result == FG_OPTIONS_HELP) || (result == FG_OPTIONS_ERROR))
- help = true;
-
- else if (result == FG_OPTIONS_VERBOSE_HELP)
- verbose = true;
-
- else if (result == FG_OPTIONS_SHOW_AIRCRAFT) {
- fgOptLogLevel( "alert" );
- SGPath path( globals->get_fg_root() );
- path.append("Aircraft");
- fgShowAircraft(path);
- exit(0);
-
- } else if (result == FG_OPTIONS_SHOW_SOUND_DEVICES) {
- SGSoundMgr smgr;
-
- smgr.init();
- string vendor = smgr.get_vendor();
- string renderer = smgr.get_renderer();
- cout << renderer << " provided by " << vendor << endl;
- cout << endl << "No. Device" << endl;
-
- vector <const char*>devices = smgr.get_available_devices();
- for (vector <const char*>::size_type i=0; i<devices.size(); i++) {
- cout << i << ". \"" << devices[i] << "\"" << endl;
- }
- devices.clear();
- exit(0);
- }
+ BOOST_FOREACH(const string& paths, valuesForOption("fg-aircraft")) {
+ globals->append_aircraft_paths(paths);
+ }
+
+ const char* envp = ::getenv("FG_AIRCRAFT");
+ if (envp) {
+ globals->append_aircraft_paths(envp);
+ }
- else if (result == FG_OPTIONS_EXIT)
- exit(0);
- }
- } else {
- in_options = false;
- SG_LOG(SG_GENERAL, SG_INFO,
- "Reading command-line property file " << arg);
- readProperties(arg, globals->get_props());
- }
+ string aircraft;
+ if (isOptionSet("aircraft")) {
+ aircraft = valueForOption("aircraft");
+ } else if (isOptionSet("vehicle")) {
+ aircraft = valueForOption("vehicle");
+ }
+
+ if (!aircraft.empty()) {
+ SG_LOG(SG_INPUT, SG_INFO, "aircraft = " << aircraft );
+ fgSetString("/sim/aircraft", aircraft.c_str() );
+ } else {
+ SG_LOG(SG_INPUT, SG_INFO, "No user specified aircraft, using default" );
+ }
+
+ if (p->showAircraft) {
+ fgOptLogLevel( "alert" );
+ SGPath path( globals->get_fg_root() );
+ path.append("Aircraft");
+ fgShowAircraft(path);
+ exit(0);
+ }
+
+ if (isOptionSet("aircraft-dir")) {
+ // set this now, so it's available in FindAndCacheAircraft
+ fgSetString("/sim/aircraft-dir", valueForOption("aircraft-dir"));
+ }
+}
+
+void Options::processArgResult(int result)
+{
+ if ((result == FG_OPTIONS_HELP) || (result == FG_OPTIONS_ERROR))
+ p->showHelp = true;
+ else if (result == FG_OPTIONS_VERBOSE_HELP)
+ p->verbose = true;
+ else if (result == FG_OPTIONS_SHOW_AIRCRAFT) {
+ p->showAircraft = true;
+ } else if (result == FG_OPTIONS_SHOW_SOUND_DEVICES) {
+ SGSoundMgr smgr;
+
+ smgr.init();
+ string vendor = smgr.get_vendor();
+ string renderer = smgr.get_renderer();
+ cout << renderer << " provided by " << vendor << endl;
+ cout << endl << "No. Device" << endl;
+
+ vector <const char*>devices = smgr.get_available_devices();
+ for (vector <const char*>::size_type i=0; i<devices.size(); i++) {
+ cout << i << ". \"" << devices[i] << "\"" << endl;
}
-
- if (help) {
- fgOptLogLevel( "alert" );
- fgUsage(verbose);
- exit(0);
+ devices.clear();
+ exit(0);
+ } else if (result == FG_OPTIONS_EXIT) {
+ exit(0);
+ }
+}
+
+void Options::readConfig(const SGPath& path)
+{
+ sg_gzifstream in( path.str() );
+ if ( !in.is_open() ) {
+ return;
+ }
+
+ SG_LOG( SG_GENERAL, SG_INFO, "Processing config file: " << path.str() );
+
+ in >> skipcomment;
+ while ( ! in.eof() ) {
+ string line;
+ getline( in, line, '\n' );
+
+ // catch extraneous (DOS) line ending character
+ int i;
+ for (i = line.length(); i > 0; i--)
+ if (line[i - 1] > 32)
+ break;
+ line = line.substr( 0, i );
+
+ if ( parseOption( line ) == FG_OPTIONS_ERROR ) {
+ cerr << endl << "Config file parse error: " << path.str() << " '"
+ << line << "'" << endl;
+ p->showHelp = true;
}
+ in >> skipcomment;
+ }
- SG_LOG(SG_GENERAL, SG_INFO, "Finished command line arguments");
+ p->insertGroupMarker(); // each config file is a group
}
-
-
-// Parse config file options
-void
-fgParseOptions (const string& path) {
- sg_gzifstream in( path );
- if ( !in.is_open() ) {
- return;
+
+int Options::parseOption(const string& s)
+{
+ if ((s == "--help") || (s=="-h")) {
+ return FG_OPTIONS_HELP;
+ } else if ( (s == "--verbose") || (s == "-v") ) {
+ // verbose help/usage request
+ return FG_OPTIONS_VERBOSE_HELP;
+ } else if (s.find("-psn") == 0) {
+ // on Mac, when launched from the GUI, we are passed the ProcessSerialNumber
+ // as an argument (and no others). Silently ignore the argument here.
+ return FG_OPTIONS_OK;
+ } else if ( s.find( "--show-aircraft") == 0) {
+ return(FG_OPTIONS_SHOW_AIRCRAFT);
+ } else if ( s.find( "--show-sound-devices") == 0) {
+ return(FG_OPTIONS_SHOW_SOUND_DEVICES);
+ } else if ( s.find( "--prop:") == 0) {
+ // property setting has a slightly different syntax, so fudge things
+ OptionDesc* desc = p->findOption("prop");
+ if (s.find("=", 7) == string::npos) { // no equals token
+ SG_LOG(SG_GENERAL, SG_ALERT, "malformed property option:" << s);
+ return FG_OPTIONS_ERROR;
}
-
- SG_LOG( SG_GENERAL, SG_INFO, "Processing config file: " << path );
-
- in >> skipcomment;
- while ( ! in.eof() ) {
- string line;
- getline( in, line, '\n' );
-
- // catch extraneous (DOS) line ending character
- int i;
- for (i = line.length(); i > 0; i--)
- if (line[i - 1] > 32)
- break;
- line = line.substr( 0, i );
-
- if ( parse_option( line ) == FG_OPTIONS_ERROR ) {
- cerr << endl << "Config file parse error: " << path << " '"
- << line << "'" << endl;
- fgUsage();
- exit(-1);
- }
- in >> skipcomment;
+
+ p->values.push_back(OptionValue(desc, s.substr(7)));
+ return FG_OPTIONS_OK;
+ } else if ( s.find( "--" ) == 0 ) {
+ size_t eqPos = s.find( '=' );
+ string key, value;
+ if (eqPos == string::npos) {
+ key = s.substr(2);
+ } else {
+ key = s.substr( 2, eqPos - 2 );
+ value = s.substr( eqPos + 1);
}
+
+ return addOption(key, value);
+ } else {
+ SG_LOG(SG_GENERAL, SG_ALERT, "unknown option:" << s);
+ return FG_OPTIONS_ERROR;
+ }
}
-
-
-// Print usage message
-void
-fgUsage (bool verbose)
+
+int Options::addOption(const string &key, const string &value)
{
- SGPropertyNode *locale = globals->get_locale();
-
- SGPropertyNode options_root;
-
- SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on Windows
- cout << endl;
-
- try {
- fgLoadProps("options.xml", &options_root);
- } catch (const sg_exception &) {
- cout << "Unable to read the help file." << endl;
- cout << "Make sure the file options.xml is located in the FlightGear base directory," << endl;
- cout << "and the location of the base directory is specified by setting $FG_ROOT or" << endl;
- cout << "by adding --fg-root=path as a program argument." << endl;
-
- exit(-1);
+ OptionDesc* desc = p->findOption(key);
+ if (!desc) {
+ SG_LOG(SG_GENERAL, SG_ALERT, "unknown option:" << key);
+ return FG_OPTIONS_ERROR;
+ }
+
+ if (!(desc->type & OPTION_MULTI)) {
+ OptionValueVec::const_iterator it = p->findValue(key);
+ if (it != p->values.end()) {
+ SG_LOG(SG_GENERAL, SG_INFO, "multiple values forbidden for option:" << key << ", ignoring:" << value);
+ return FG_OPTIONS_OK;
}
+ }
+
+ p->values.push_back(OptionValue(desc, value));
+ return FG_OPTIONS_OK;
+}
+
+bool Options::isOptionSet(const string &key) const
+{
+ OptionValueVec::const_iterator it = p->findValue(key);
+ return (it != p->values.end());
+}
+
+string Options::valueForOption(const string& key, const string& defValue) const
+{
+ OptionValueVec::const_iterator it = p->findValue(key);
+ if (it == p->values.end()) {
+ return defValue;
+ }
+
+ return it->value;
+}
- SGPropertyNode *options = options_root.getNode("options");
- if (!options) {
- SG_LOG( SG_GENERAL, SG_ALERT,
- "Error reading options.xml: <options> directive not found." );
+string_list Options::valuesForOption(const std::string& key) const
+{
+ string_list result;
+ OptionValueVec::const_iterator it = p->values.begin();
+ for (; it != p->values.end(); ++it) {
+ if (!it->desc) {
+ continue; // ignore marker values
+ }
+
+ if (it->desc->option == key) {
+ result.push_back(it->value);
+ }
+ }
+
+ return result;
+}
+
+void Options::processOptions()
+{
+ // now FG_ROOT is setup, process various command line options that bail us
+ // out quickly, but rely on aircraft / root settings
+ if (p->showHelp) {
+ showUsage();
+ exit(0);
+ }
+
+ // processing order is complicated. We must process groups LIFO, but the
+ // values *within* each group in FIFO order, to retain consistency with
+ // older versions of FG, and existing user configs.
+ // in practice this means system.fgfsrc must be *processed* before
+ // .fgfsrc, which must be processed before the command line args, and so on.
+ OptionValueVec::const_iterator groupEnd = p->values.end();
+
+ while (groupEnd != p->values.begin()) {
+ OptionValueVec::const_iterator groupBegin = p->rfindGroup(groupEnd);
+ // run over the group in FIFO order
+ OptionValueVec::const_iterator it;
+ for (it = groupBegin; it != groupEnd; ++it) {
+ int result = p->processOption(it->desc, it->value);
+ if (result == FG_OPTIONS_ERROR) {
+ showUsage();
exit(-1);
+ }
}
-
- SGPropertyNode *usage = locale->getNode(options->getStringValue("usage"));
- if (usage) {
- cout << "Usage: " << usage->getStringValue() << endl;
+
+ groupEnd = groupBegin;
+ }
+
+ BOOST_FOREACH(const SGPath& file, p->propertyFiles) {
+ if (!file.exists()) {
+ SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << file.str());
+ continue;
}
+
+ SG_LOG(SG_GENERAL, SG_INFO,
+ "Reading command-line property file " << file.str());
+ readProperties(file.str(), globals->get_props());
+ }
- vector<SGPropertyNode_ptr>section = options->getChildren("section");
- for (unsigned int j = 0; j < section.size(); j++) {
- string msg = "";
-
- vector<SGPropertyNode_ptr>option = section[j]->getChildren("option");
- for (unsigned int k = 0; k < option.size(); k++) {
-
- SGPropertyNode *name = option[k]->getNode("name");
- SGPropertyNode *short_name = option[k]->getNode("short");
- SGPropertyNode *key = option[k]->getNode("key");
- SGPropertyNode *arg = option[k]->getNode("arg");
- bool brief = option[k]->getNode("brief") != 0;
-
- if ((brief || verbose) && name) {
- string tmp = name->getStringValue();
-
- if (key){
- tmp.append(":");
- tmp.append(key->getStringValue());
- }
- if (arg) {
- tmp.append("=");
- tmp.append(arg->getStringValue());
- }
- if (short_name) {
- tmp.append(", -");
- tmp.append(short_name->getStringValue());
- }
-
- if (tmp.size() <= 25) {
- msg+= " --";
- msg += tmp;
- msg.append( 27-tmp.size(), ' ');
- } else {
- msg += "\n --";
- msg += tmp + '\n';
- msg.append(32, ' ');
- }
- // There may be more than one <description> tag assosiated
- // with one option
-
- vector<SGPropertyNode_ptr> desc;
- desc = option[k]->getChildren("description");
- if (desc.size() > 0) {
- for ( unsigned int l = 0; l < desc.size(); l++) {
-
- // There may be more than one translation line.
-
- string t = desc[l]->getStringValue();
- SGPropertyNode *n = locale->getNode("strings");
- vector<SGPropertyNode_ptr>trans_desc =
- n->getChildren(t.substr(8).c_str());
-
- for ( unsigned int m = 0; m < trans_desc.size(); m++ ) {
- string t_str = trans_desc[m]->getStringValue();
-
- if ((m > 0) || ((l > 0) && m == 0)) {
- msg.append( 32, ' ');
- }
-
- // If the string is too large to fit on the screen,
- // then split it up in several pieces.
-
- while ( t_str.size() > 47 ) {
-
- unsigned int m = t_str.rfind(' ', 47);
- msg += t_str.substr(0, m) + '\n';
- msg.append( 32, ' ');
-
- t_str.erase(t_str.begin(), t_str.begin() + m + 1);
- }
- msg += t_str + '\n';
- }
- }
- }
- }
+// now options are process, do supplemental fixup
+ const char *envp = ::getenv( "FG_SCENERY" );
+ if (envp) {
+ globals->append_fg_scenery(envp);
+ }
+
+// terrasync directory fixup
+ if (fgGetBool("/sim/terrasync/enabled")) {
+ string terrasyncDir = fgGetString("/sim/terrasync/scenery-dir");
+ if (terrasyncDir.empty()) {
+ SGPath p(fgGetString("/sim/fg-home"));
+ p.append("TerraSync");
+ if (!p.exists()) {
+ simgear::Dir dd(p);
+ dd.create(0700);
+ }
+
+ terrasyncDir = p.str();
+ SG_LOG(SG_GENERAL, SG_INFO,
+ "Using default TerraSync dir: " << terrasyncDir);
+ fgSetString("/sim/terrasync/scenery-dir", terrasyncDir);
+ }
+
+ const string_list& scenery_paths(globals->get_fg_scenery());
+ if (std::find(scenery_paths.begin(), scenery_paths.end(), terrasyncDir) == scenery_paths.end()) {
+ // terrasync dir is not in the scenery paths, add it
+ globals->append_fg_scenery(terrasyncDir);
+ }
+ }
+
+ if (globals->get_fg_scenery().empty()) {
+ // no scenery paths set *at all*, use the data in FG_ROOT
+ SGPath root(globals->get_fg_root());
+ root.append("Scenery");
+ globals->append_fg_scenery(root.str());
+ }
+}
+
+void Options::showUsage() const
+{
+ fgOptLogLevel( "alert" );
+
+ SGPropertyNode *locale = globals->get_locale();
+ SGPropertyNode options_root;
+
+ SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on Windows
+ cout << endl;
+
+ try {
+ fgLoadProps("options.xml", &options_root);
+ } catch (const sg_exception &) {
+ cout << "Unable to read the help file." << endl;
+ cout << "Make sure the file options.xml is located in the FlightGear base directory," << endl;
+ cout << "and the location of the base directory is specified by setting $FG_ROOT or" << endl;
+ cout << "by adding --fg-root=path as a program argument." << endl;
+
+ exit(-1);
+ }
+
+ SGPropertyNode *options = options_root.getNode("options");
+ if (!options) {
+ SG_LOG( SG_GENERAL, SG_ALERT,
+ "Error reading options.xml: <options> directive not found." );
+ exit(-1);
+ }
+
+ SGPropertyNode *usage = locale->getNode(options->getStringValue("usage"));
+ if (usage) {
+ cout << "Usage: " << usage->getStringValue() << endl;
+ }
+
+ vector<SGPropertyNode_ptr>section = options->getChildren("section");
+ for (unsigned int j = 0; j < section.size(); j++) {
+ string msg = "";
+
+ vector<SGPropertyNode_ptr>option = section[j]->getChildren("option");
+ for (unsigned int k = 0; k < option.size(); k++) {
+
+ SGPropertyNode *name = option[k]->getNode("name");
+ SGPropertyNode *short_name = option[k]->getNode("short");
+ SGPropertyNode *key = option[k]->getNode("key");
+ SGPropertyNode *arg = option[k]->getNode("arg");
+ bool brief = option[k]->getNode("brief") != 0;
+
+ if ((brief || p->verbose) && name) {
+ string tmp = name->getStringValue();
+
+ if (key){
+ tmp.append(":");
+ tmp.append(key->getStringValue());
}
-
- SGPropertyNode *name;
- name = locale->getNode(section[j]->getStringValue("name"));
-
- if (!msg.empty() && name) {
- cout << endl << name->getStringValue() << ":" << endl;
- cout << msg;
- msg.erase();
+ if (arg) {
+ tmp.append("=");
+ tmp.append(arg->getStringValue());
+ }
+ if (short_name) {
+ tmp.append(", -");
+ tmp.append(short_name->getStringValue());
}
+
+ if (tmp.size() <= 25) {
+ msg+= " --";
+ msg += tmp;
+ msg.append( 27-tmp.size(), ' ');
+ } else {
+ msg += "\n --";
+ msg += tmp + '\n';
+ msg.append(32, ' ');
+ }
+ // There may be more than one <description> tag assosiated
+ // with one option
+
+ vector<SGPropertyNode_ptr> desc;
+ desc = option[k]->getChildren("description");
+ if (desc.size() > 0) {
+ for ( unsigned int l = 0; l < desc.size(); l++) {
+
+ // There may be more than one translation line.
+
+ string t = desc[l]->getStringValue();
+ SGPropertyNode *n = locale->getNode("strings");
+ vector<SGPropertyNode_ptr>trans_desc =
+ n->getChildren(t.substr(8).c_str());
+
+ for ( unsigned int m = 0; m < trans_desc.size(); m++ ) {
+ string t_str = trans_desc[m]->getStringValue();
+
+ if ((m > 0) || ((l > 0) && m == 0)) {
+ msg.append( 32, ' ');
+ }
+
+ // If the string is too large to fit on the screen,
+ // then split it up in several pieces.
+
+ while ( t_str.size() > 47 ) {
+
+ string::size_type m = t_str.rfind(' ', 47);
+ msg += t_str.substr(0, m) + '\n';
+ msg.append( 32, ' ');
+
+ t_str.erase(t_str.begin(), t_str.begin() + m + 1);
+ }
+ msg += t_str + '\n';
+ }
+ }
+ }
+ }
}
+
+ SGPropertyNode *name;
+ name = locale->getNode(section[j]->getStringValue("name"));
+
+ if (!msg.empty() && name) {
+ cout << endl << name->getStringValue() << ":" << endl;
+ cout << msg;
+ msg.erase();
+ }
+ }
+
+ if ( !p->verbose ) {
+ cout << endl;
+ cout << "For a complete list of options use --help --verbose" << endl;
+ }
+#ifdef _MSC_VER
+ std::cout << "Hit a key to continue..." << std::endl;
+ std::cin.get();
+#endif
+}
+
+#if defined(__CYGWIN__)
+string Options::platformDefaultRoot() const
+{
+ return "../data";
+}
- if ( !verbose ) {
- cout << endl;
- cout << "For a complete list of options use --help --verbose" << endl;
+#elif defined(_WIN32)
+string Options::platformDefaultRoot() const
+{
+ return "..\\data";
+}
+#elif defined(__APPLE__)
+string Options::platformDefaultRoot() const
+{
+ /*
+ The following code looks for the base package inside the application
+ bundle, in the standard Contents/Resources location.
+ */
+ CFURLRef resourcesUrl = CFBundleCopyResourcesDirectoryURL(CFBundleGetMainBundle());
+
+ // look for a 'data' subdir
+ CFURLRef dataDir = CFURLCreateCopyAppendingPathComponent(NULL, resourcesUrl, CFSTR("data"), true);
+
+ // now convert down to a path, and the a c-string
+ CFStringRef path = CFURLCopyFileSystemPath(dataDir, kCFURLPOSIXPathStyle);
+ string root = CFStringGetCStringPtr(path, CFStringGetSystemEncoding());
+
+ CFRelease(resourcesUrl);
+ CFRelease(dataDir);
+ CFRelease(path);
+
+ return root;
+}
+#else
+string Options::platformDefaultRoot() const
+{
+ return PKGLIBDIR;
+}
+#endif
+
+void Options::setupRoot()
+{
+ string root;
+ if (isOptionSet("fg-root")) {
+ root = valueForOption("fg-root"); // easy!
+ } else {
+ // Next check if fg-root is set as an env variable
+ char *envp = ::getenv( "FG_ROOT" );
+ if ( envp != NULL ) {
+ root = envp;
+ } else {
+ root = platformDefaultRoot();
}
+ }
+
+ SG_LOG(SG_INPUT, SG_INFO, "fg_root = " << root );
+ globals->set_fg_root(root);
+
+// validate it
+ static char required_version[] = "2.5.0";
+ string base_version = fgBasePackageVersion();
+ if ( !(base_version == required_version) ) {
+ // tell the operator how to use this application
+
+ SG_LOG( SG_GENERAL, SG_ALERT, "" ); // To popup the console on windows
+ cerr << endl << "Base package check failed:" << endl \
+ << " Version " << base_version << " found at: " \
+ << globals->get_fg_root() << endl \
+ << " Version " << required_version << " is required." << endl \
+ << "Please upgrade/downgrade base package and set the path to your fgdata" << endl \
+ << "with --fg-root=path_to_your_fgdata" << endl;
#ifdef _MSC_VER
- cout << "Hit a key to continue..." << endl;
+ cerr << "Hit a key to continue..." << endl;
cin.get();
#endif
+ exit(-1);
+ }
}
+
+} // of namespace flightgear
+