27#include <simgear/compiler.h>
28#include <simgear/structure/exception.hxx>
29#include <simgear/debug/logstream.hxx>
30#include <simgear/timing/sg_time.hxx>
31#include <simgear/misc/sg_dir.hxx>
46#include <simgear/io/HTTPClient.hxx>
47#include <simgear/io/HTTPFileRequest.hxx>
48#include <simgear/math/sg_random.hxx>
49#include <simgear/props/props_io.hxx>
50#include <simgear/io/iostreams/sgstream.hxx>
51#include <simgear/misc/sg_path.hxx>
52#include <simgear/misc/sg_dir.hxx>
53#include <simgear/scene/material/mat.hxx>
54#include <simgear/sound/soundmgr.hxx>
55#include <simgear/misc/strutils.hxx>
56#include <simgear/timing/timestamp.hxx>
89#include <flightgearBuildId.h>
90#include <simgear/version.h>
102#define NEW_DEFAULT_MODEL_HZ 120
109 return ::atof( str.c_str() );
115 return ::atoi( str.c_str() );
168 fgSetDouble(
"/sim/presets/offset-distance-nm", 0.0);
170 fgSetBool(
"/sim/presets/runway-requested",
false);
172 fgSetBool(
"/sim/presets/onground",
true);
176 fgSetBool(
"/sim/startup/splash-screen",
true);
180 fgSetBool(
"/controls/flight/auto-coordination",
false);
184 fgSetBool(
"/sim/hud/color/antialiased",
false);
186 fgSetBool(
"/sim/hud/visibility[1]",
false);
187 fgSetBool(
"/sim/panel/visibility",
true);
202 fgSetString(
"/sim/scenery/lod-levels",
"1 3 5 7 9");
204 fgSetString(
"/sim/scenery/lod-texturing",
"bluemarble");
208 fgSetBool(
"/environment/clouds/status",
true);
209 fgSetBool(
"/sim/startup/fullscreen",
false);
210 fgSetBool(
"/sim/rendering/shading",
true);
211 fgTie(
"/sim/rendering/filtering", SGGetTextureFilter, SGSetTextureFilter,
false);
212 fgSetInt(
"/sim/rendering/filtering", 1);
213 fgSetBool(
"/sim/rendering/wireframe",
false);
214 fgSetBool(
"/sim/rendering/horizon-effect",
false);
215 fgSetBool(
"/sim/rendering/distance-attenuation",
false);
216 fgSetBool(
"/sim/rendering/specular-highlight",
true);
217 fgSetString(
"/sim/rendering/materials-file",
"materials.xml");
218 fgSetInt(
"/sim/startup/xsize", 1024);
219 fgSetInt(
"/sim/startup/ysize", 768);
220 fgSetInt(
"/sim/rendering/bits-per-pixel", 32);
222 fgSetDouble(
"/sim/current-view/heading-offset-deg", 0);
229 fgSetInt(
"/sim/startup/time-offset", 0);
230 fgSetString(
"/sim/startup/time-offset-type",
"system-offset");
231 fgSetLong(
"/sim/time/cur-time-override", 0);
235 fgSetBool(
"/sim/freeze/position",
false);
239 fgSetString(
"/sim/multiplay/callsign",
"callsign");
242 fgSetInt(
"/sim/multiplay/rxport", 0);
243 fgSetInt(
"/sim/multiplay/txport", 0);
245 SGPropertyNode* v =
globals->get_props()->getNode(
"/sim/version",
true);
246 v->setValueReadOnly(
"flightgear", FLIGHTGEAR_VERSION);
247 v->setValueReadOnly(
"simgear", SG_STRINGIZE(SIMGEAR_VERSION));
248 v->setValueReadOnly(
"openscenegraph", osgGetVersion());
249 v->setValueReadOnly(
"revision", REVISION);
250 v->setValueReadOnly(
"build-date", BUILD_DATE);
251 v->setValueReadOnly(
"hla-support",
bool(FG_HAVE_HLA));
252 v->setValueReadOnly(
"build-type", FG_BUILD_TYPE);
254 char* envp = ::getenv(
"http_proxy" );
255 if( envp !=
nullptr )
269 _minStatus = getNumMaturity(
fgGetString(
"/sim/aircraft-min-status",
"all"));
273 void show(
const vector<SGPath> & path_list)
275 for (vector<SGPath>::const_iterator
p = path_list.begin();
276 p != path_list.end(); ++
p)
279 simgear::requestConsole(
false);
281 std::sort(_aircraft.begin(), _aircraft.end(),
282 [](
const std::string& lhs,
const std::string& rhs) {
283 return strcasecmp(lhs.c_str(), rhs.c_str()) < 0 ? 1 : 0;
286 cout <<
"Available aircraft:" << endl;
287 for (
unsigned int i = 0;
i < _aircraft.size();
i++ ) {
288 cout << _aircraft[
i] << endl;
297 readProperties(path, &root);
298 }
catch (sg_exception& ) {
304 descStr += path.file();
306 int nPos = descStr.rfind(
"-set.xml");
307 if (nPos == (
int)(descStr.size() - 8)) {
308 descStr.resize(nPos);
311 SGPropertyNode *node = root.getNode(
"sim");
313 SGPropertyNode* desc = node->getNode(
"description");
315 if (node->hasValue(
"status")) {
316 maturity = getNumMaturity(node->getStringValue(
"status"));
320 if (descStr.size() <= 27+3) {
321 descStr.append(29+3-descStr.size(),
' ');
324 descStr.append( 32,
' ');
326 descStr += desc->getStringValue();
330 if (maturity >= _minStatus) {
331 _aircraft.push_back(descStr);
338 int getNumMaturity(
const std::string& str)
341 const char* levels[] = {
"alpha",
"beta",
"early-production",
"production"};
347 for (
size_t i=0;
i<(
sizeof(levels)/
sizeof(levels[0]));
i++)
348 if (str == levels[
i])
371 cout <<
"Hit a key to continue..." << endl;
378parse_wind (
const string &wind,
double * min_hdg,
double * max_hdg,
379 double * speed,
double * gust)
381 string::size_type pos = wind.find(
'@');
382 if (pos == string::npos)
384 string dir = wind.substr(0, pos);
385 string spd = wind.substr(pos+1);
387 if (pos == string::npos) {
388 *min_hdg = *max_hdg =
atof(dir.c_str());
390 *min_hdg =
atof(dir.substr(0,pos).c_str());
391 *max_hdg =
atof(dir.substr(pos+1).c_str());
394 if (pos == string::npos) {
395 *speed = *gust =
atof(spd.c_str());
397 *speed =
atof(spd.substr(0,pos).c_str());
398 *gust =
atof(spd.substr(pos+1).c_str());
404parseIntValue(
char** ppParserPos,
int* pValue,
int min,
int max,
const char* field,
const char* argument)
406 if ( !strlen(*ppParserPos) )
412 while ( isdigit((*ppParserPos)[0]) && (
i<255) )
414 num[
i] = (*ppParserPos)[0];
420 switch ((*ppParserPos)[0])
428 SG_LOG(SG_GENERAL, SG_ALERT,
"Illegal character in time string for " << field <<
": '" <<
429 (*ppParserPos)[0] <<
"'.");
431 while ((*ppParserPos)[0])
439 int value =
atoi(num);
440 if ((value <
min)||(value > max))
442 SG_LOG(SG_GENERAL, SG_ALERT,
"Invalid " << field <<
" in '" << argument <<
443 "'. Valid range is " <<
min <<
"-" << max <<
".");
456 char *time_str, num[256];
457 double hours, minutes, seconds;
462 time_str = (
char *)time_in.c_str();
467 if ( strlen(time_str) ) {
468 if ( time_str[0] ==
'+' ) {
471 }
else if ( time_str[0] ==
'-' ) {
479 if ( strlen(time_str) ) {
481 while ( (time_str[0] !=
':') && (time_str[0] !=
'\0') ) {
482 num[
i] = time_str[0];
486 if ( time_str[0] ==
':' ) {
497 if ( strlen(time_str) ) {
499 while ( (time_str[0] !=
':') && (time_str[0] !=
'\0') ) {
500 num[
i] = time_str[0];
504 if ( time_str[0] ==
':' ) {
511 result += minutes / 60.0;
515 if ( strlen(time_str) ) {
517 while ( (time_str[0] !=
':') && (time_str[0] !=
'\0') ) {
518 num[
i] = time_str[0];
526 result += seconds / 3600.0;
529 SG_LOG( SG_GENERAL, SG_INFO,
" parse_time() = " << sign * result );
531 return(sign * result);
538 struct tm gmt,*pCurrentTime;
539 int year,month,day,hour,minute,second;
540 char *argument, *date_str;
543 CurrentTime.update(SGGeod(),0,0);
546 pCurrentTime = CurrentTime.getGmt();
549 year = pCurrentTime->tm_year + 1900;
550 month = pCurrentTime->tm_mon + 1;
551 day = pCurrentTime->tm_mday;
552 hour = pCurrentTime->tm_hour;
553 minute = pCurrentTime->tm_min;
554 second = pCurrentTime->tm_sec;
556 argument = (
char *)date.c_str();
560 if (!strlen(date_str) ||
568 SG_LOG(SG_GENERAL, SG_ALERT,
"Invalid year '" << year <<
"'. Use 1970 or later.");
575 parseIntValue(&date_str, &minute, 0, 59,
"minute", argument);
576 parseIntValue(&date_str, &second, 0, 59,
"second", argument);
582 gmt.tm_mon = month - 1;
583 gmt.tm_year = year -1900;
586 time_t theTime = sgTimeGetGMT( gmt.tm_year, gmt.tm_mon, gmt.tm_mday,
587 gmt.tm_hour, gmt.tm_min, gmt.tm_sec );
589 SG_LOG(SG_GENERAL, SG_INFO,
"Configuring startup time to " << ctime(&theTime));
614 result = (int)rint(
parse_time(time_str) * 3600.0);
616 result = (int)(
parse_time(time_str) * 3600.0);
628 double fov =
atof(arg);
633 fgSetDouble(
"/sim/view[0]/config/default-field-of-view-deg", fov);
670 if(!
globals->get_channel_options_list())
672 SG_LOG(SG_GENERAL, SG_ALERT,
"Option " << type <<
"=" << channel_str
676 SG_LOG(SG_GENERAL, SG_INFO,
"Channel string = " << channel_str );
677 globals->get_channel_options_list()->push_back( type +
"," + channel_str );
710 const SGPath addonPath = SGPath::fromUtf8(arg);
714 addonManager->registerAddon(addonPath);
715 }
catch (
const sg_exception &e) {
716 string msg =
"Error registering an add-on: " + e.getFormattedMessage();
717 SG_LOG(SG_GENERAL, SG_ALERT, msg);
719 "FlightGear",
"Unable to register an add-on.", msg);
728 const SGPath dataPath = SGPath::fromUtf8(arg);
729 if (!dataPath.exists()) {
730 SG_LOG(SG_GENERAL, SG_ALERT,
"--data path not found:'" << dataPath <<
"'");
732 "FlightGear",
"Data path not found: '" + dataPath.utf8Str() +
"'.");
736 globals->append_data_path(dataPath,
false );
775 fgSetBool(
"/sim/presets/parking-requested",
true);
816 fgSetBool(
"/sim/presets/onground",
false);
821 atof( arg ) * SG_METER_TO_FEET);
833 atof( arg ) * SG_METER_TO_FEET);
845 atof( arg ) * SG_METER_TO_FEET);
857 atof(arg) * SG_METER_TO_FEET);
869 atof( arg ) * SG_METER_TO_FEET);
881 atof(arg) * SG_METER_TO_FEET);
893 atof(arg) * SG_METER_TO_FEET);
925 globals->append_fg_scenery(SGPath::pathsFromUtf8(arg));
932 PathList paths = SGPath::pathsFromUtf8(arg);
933 if(paths.size() == 0) {
934 SG_LOG(SG_GENERAL, SG_WARN,
"--allow-nasal-read requires a list of directories to allow");
936 for( PathList::const_iterator it = paths.begin(); it != paths.end(); ++it ) {
937 globals->append_read_allowed_paths(*it);
952 bool geometry_ok =
true;
953 int xsize = 0, ysize = 0;
954 string geometry = arg;
955 string::size_type
i = geometry.find(
'x');
957 if (
i != string::npos) {
958 xsize =
atoi(geometry.substr(0,
i));
959 ysize =
atoi(geometry.substr(
i+1));
964 if ( xsize <= 0 || ysize <= 0 ) {
970 if ( !geometry_ok ) {
971 SG_LOG( SG_GENERAL, SG_ALERT,
"Unknown geometry: " << geometry );
972 SG_LOG( SG_GENERAL, SG_ALERT,
973 "Setting geometry to " << xsize <<
'x' << ysize <<
'\n');
975 SG_LOG( SG_GENERAL, SG_INFO,
976 "Setting geometry to " << xsize <<
'x' << ysize <<
'\n');
977 fgSetInt(
"/sim/startup/xsize", xsize);
978 fgSetInt(
"/sim/startup/ysize", ysize);
986 string bits_per_pix = arg;
987 if ( bits_per_pix ==
"16" ) {
988 fgSetInt(
"/sim/rendering/bits-per-pixel", 16);
989 }
else if ( bits_per_pix ==
"24" ) {
990 fgSetInt(
"/sim/rendering/bits-per-pixel", 24);
991 }
else if ( bits_per_pix ==
"32" ) {
992 fgSetInt(
"/sim/rendering/bits-per-pixel", 32);
994 SG_LOG(SG_GENERAL, SG_ALERT,
"Unsupported bpp " << bits_per_pix);
1004 fgSetString(
"/sim/startup/time-offset-type",
"system-offset");
1011 long int theTime =
parse_date( arg,
"system" );
1014 fgSetLong(
"/sim/startup/time-offset", theTime);
1015 fgSetString(
"/sim/startup/time-offset-type",
"system");
1023 long int theTime =
parse_date( arg,
"latitude" );
1026 fgSetLong(
"/sim/startup/time-offset", theTime);
1027 fgSetString(
"/sim/startup/time-offset-type",
"latitude");
1038 fgSetLong(
"/sim/startup/time-offset", theTime);
1039 fgSetString(
"/sim/startup/time-offset-type",
"gmt");
1047 SG_LOG(SG_ALL,SG_ALERT,
1048 "the option --jpg-httpd is no longer supported! Please use --httpd instead."
1049 " URL for the screenshot within the new httpd is http://YourFgServer:xxxx/screenshot");
1059 string port = simgear::strutils::strip(
string(arg));
1068 string options = simgear::strutils::strip( arg );
1069 string host, port, auth;
1070 string::size_type pos;
1073 if( simgear::strutils::starts_with(
options,
"http://" ) )
1075 if( simgear::strutils::ends_with(
options,
"/" ) )
1078 host = port = auth =
"";
1079 if ((pos =
options.find(
"@")) != string::npos)
1080 auth =
options.substr(0, pos++);
1085 if ((pos = host.find(
":")) != string::npos) {
1086 port = host.substr(++pos, host.size());
1087 host.erase(--pos, host.size());
1090 fgSetString(
"/sim/presets/proxy/host", host.c_str());
1091 fgSetString(
"/sim/presets/proxy/port", port.c_str());
1092 fgSetString(
"/sim/presets/proxy/authentication", auth.c_str());
1101 SG_LOG(SG_GENERAL, SG_INFO,
"Tracing reads for property " <<
name);
1103 ->setAttribute(SGPropertyNode::TRACE_READ,
true);
1129 if (!strcmp(arg,
"desktop")) {
1130 dirPath = SGPath::desktop();
1132 dirPath = SGPath::fromUtf8(arg);
1135 if (!dirPath.isDir()) {
1136 SG_LOG(SG_GENERAL, SG_ALERT,
"cannot find logging location " << dirPath);
1140 if (!dirPath.canWrite()) {
1141 SG_LOG(SG_GENERAL, SG_ALERT,
"cannot write to logging location " << dirPath);
1148 char fileNameBuffer[100];
1151 strftime(fileNameBuffer, 99,
"FlightGear_%F", localtime(&now));
1153 unsigned int logsTodayCount = 0;
1155 std::ostringstream os;
1156 os << fileNameBuffer <<
"_" << logsTodayCount++ <<
".log";
1157 logFile = dirPath / os.str();
1158 if (!logFile.exists()) {
1164 sglog().logToFile(logFile, sglog().get_log_classes(), sglog().get_log_priority());
1173 SG_LOG(SG_GENERAL, SG_INFO,
"Tracing writes for property " <<
name);
1175 ->setAttribute(SGPropertyNode::TRACE_WRITE,
true);
1184 string woffset = arg;
1185 double default_view_offset = 0.0;
1186 if ( woffset ==
"LEFT" ) {
1187 default_view_offset = SGD_PI * 0.25;
1188 }
else if ( woffset ==
"RIGHT" ) {
1189 default_view_offset = SGD_PI * 1.75;
1190 }
else if ( woffset ==
"CENTER" ) {
1191 default_view_offset = 0.00;
1193 default_view_offset =
atof( woffset.c_str() ) * SGD_DEGREES_TO_RADIANS;
1199 fgSetDouble(
"/sim/view[0]/config/heading-offset-deg",
1200 default_view_offset * SGD_RADIANS_TO_DEGREES);
1208 Environment::Presets::VisibilitySingleton::instance()->preset(
atof( arg ) );
1215 Environment::Presets::VisibilitySingleton::instance()->preset(
atof( arg ) * 5280.0 * SG_FEET_TO_METER );
1224 fgSetBool(
"/environment/realwx/enabled",
false);
1233 static bool already_done =
false;
1236 already_done =
true;
1237 simgear::requestConsole(
false);
1245 double min_hdg = sg_random() * 360.0;
1246 double max_hdg = min_hdg + (20 - sqrt(sg_random() * 400));
1247 double speed = sg_random() * sg_random() * 40;
1248 double gust = speed + (10 - sqrt(sg_random() * 100));
1249 Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
1256 double min_hdg = 0.0, max_hdg = 0.0, speed = 0.0, gust = 0.0;
1257 if (!
parse_wind( arg, &min_hdg, &max_hdg, &speed, &gust)) {
1258 SG_LOG( SG_GENERAL, SG_ALERT,
"bad wind value " << arg );
1261 Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
1268 Environment::Presets::TurbulenceSingleton::instance()->preset(
atof(arg) );
1275 double elevation, thickness;
1277 string::size_type pos = spec.find(
':');
1278 if (pos == string::npos) {
1279 elevation =
atof(spec.c_str());
1282 elevation =
atof(spec.substr(0, pos).c_str());
1283 thickness =
atof(spec.substr(pos + 1).c_str());
1285 Environment::Presets::CeilingSingleton::instance()->preset( elevation, thickness );
1295 globals->set_initial_waypoints(waypoints);
1297 waypoints->push_back(arg);
1304 string::size_type pos = s.find(
':');
1305 if (pos == string::npos) {
1309 *val1 =
atof(s.substr(0, pos).c_str());
1310 *val2 =
atof(s.substr(pos+1).c_str());
1321 fgSetBool(
"/systems/pitot/serviceable",
false);
1322 }
else if (a ==
"static") {
1323 fgSetBool(
"/systems/static/serviceable",
false);
1324 }
else if (a ==
"vacuum") {
1325 fgSetBool(
"/systems/vacuum/serviceable",
false);
1326 }
else if (a ==
"electrical") {
1327 fgSetBool(
"/systems/electrical/serviceable",
false);
1329 SG_LOG(SG_INPUT, SG_ALERT,
"Unknown failure mode: " << a);
1340 double radial, freq;
1342 fgSetDouble(
"/instrumentation/nav[0]/radials/selected-deg", radial);
1343 fgSetDouble(
"/instrumentation/nav[0]/frequencies/selected-mhz", freq);
1350 double radial, freq;
1352 fgSetDouble(
"/instrumentation/nav[1]/radials/selected-deg", radial);
1353 fgSetDouble(
"/instrumentation/nav[1]/frequencies/selected-mhz", freq);
1360 SG_LOG(SG_ALL,SG_ALERT,
1361 "the option --adf is no longer supported! Please use --adf1 "
1371 fgSetDouble(
"/instrumentation/adf[0]/rotation-deg", rot);
1372 fgSetDouble(
"/instrumentation/adf[0]/frequencies/selected-khz", freq);
1381 fgSetDouble(
"/instrumentation/adf[1]/rotation-deg", rot);
1382 fgSetDouble(
"/instrumentation/adf[1]/frequencies/selected-khz", freq);
1390 if (opt ==
"nav1") {
1391 fgSetInt(
"/instrumentation/dme/switch-position", 1);
1392 fgSetString(
"/instrumentation/dme/frequencies/source",
1393 "/instrumentation/nav[0]/frequencies/selected-mhz");
1394 }
else if (opt ==
"nav2") {
1395 fgSetInt(
"/instrumentation/dme/switch-position", 3);
1396 fgSetString(
"/instrumentation/dme/frequencies/source",
1397 "/instrumentation/nav[1]/frequencies/selected-mhz");
1399 double frequency =
atof(arg);
1402 SG_LOG(SG_INPUT, SG_ALERT,
"Invalid DME frequency: '" << arg <<
"'.");
1405 fgSetInt(
"/instrumentation/dme/switch-position", 2);
1406 fgSetString(
"/instrumentation/dme/frequencies/source",
1407 "/instrumentation/dme/frequencies/selected-mhz");
1408 fgSetDouble(
"/instrumentation/dme/frequencies/selected-mhz", frequency);
1417 string livery_path =
"livery/" + opt;
1418 fgSetString(
"/sim/model/texture-path", livery_path.c_str() );
1426 std::string
name(arg);
1427 if (path.exists()) {
1428 if (path.isRelative()) {
1430 path = simgear::Dir::current().path() / arg;
1436 SG_LOG(SG_GENERAL, SG_WARN,
"failed to read scenario file at:" << path);
1441 name = path.file_base();
1445 SGPropertyNode_ptr ai_node =
fgGetNode(
"/sim/ai",
true );
1446 ai_node->addChild(
"scenario")->setStringValue(
name);
1454 fgSetString(
"/sim/presets/airport-id", simgear::strutils::uppercase({arg}) );
1455 fgSetBool(
"/sim/presets/airport-requested",
true );
1462 fgSetString(
"/sim/presets/runway", simgear::strutils::uppercase({arg}) );
1463 fgSetBool(
"/sim/presets/runway-requested",
true );
1472 strncpy(callsign,arg,10);
1474 for (
i=0;callsign[
i];
i++)
1476 char c = callsign[
i];
1477 if (c >=
'A' && c <=
'Z')
continue;
1478 if (c >=
'a' && c <=
'z')
continue;
1479 if (c >=
'0' && c <=
'9')
continue;
1480 if (c ==
'-' || c ==
'_')
continue;
1493 fgSetBool(
"/sim/startup/ignore-autosave", param);
1495 fgSetBool(
"/sim/startup/save-on-exit", !param);
1516 string::size_type pos = arg.find(
'=');
1517 if (pos == arg.npos || pos == 0 || pos + 1 == arg.size())
1520 string name = arg.substr(0, pos);
1521 string value = arg.substr(pos + 1);
1523 pos =
name.find(
':');
1525 if (pos !=
name.npos && pos != 0 && pos + 1 !=
name.size()) {
1526 type =
name.substr(0, pos);
1531 bool writable = n->getAttribute(SGPropertyNode::WRITE);
1533 n->setAttribute(SGPropertyNode::WRITE,
true);
1537 ret = n->setUnspecifiedValue(value.c_str());
1538 else if (type ==
"s" || type ==
"string")
1539 ret = n->setStringValue(value.c_str());
1540 else if (type ==
"d" || type ==
"double")
1541 ret = n->setDoubleValue(strtod(value.c_str(), 0));
1542 else if (type ==
"f" || type ==
"float")
1543 ret = n->setFloatValue(
atof(value.c_str()));
1544 else if (type ==
"l" || type ==
"long")
1545 ret = n->setLongValue(strtol(value.c_str(), 0, 0));
1546 else if (type ==
"i" || type ==
"int")
1547 ret = n->setIntValue(
atoi(value.c_str()));
1548 else if (type ==
"b" || type ==
"bool")
1549 ret = n->setBoolValue(value ==
"true" ||
atoi(value.c_str()) != 0);
1552 n->setAttribute(SGPropertyNode::WRITE,
false);
1559 bool http = simgear::strutils::starts_with(url,
"http://");
1560 bool https = simgear::strutils::starts_with(url,
"https://");
1561 if (!
http && !https) {
1565 const char* s2 = (
http) ? url+7 : url+8;
1566 const char* s3 = strchr(s2,
'/');
1567 const char* s4 = (s3) ? strrchr(s3,
'/') : NULL;
1568 std::string path =
"url_";
1569 if (s3) path += std::string(s2, s3-s2);
1571 if (s3 && s4 > s3) {
1572 path += simgear::strutils::md5(s3, s4-s3).substr(0, 8);
1575 if (s4) path += (s4+1);
1576 if (!simgear::strutils::ends_with(path,
".fgtape")) path +=
".fgtape";
1577 std::string dir =
fgGetString(
"/sim/replay/tape-directory");
1605 struct DelayedTapeLoader : SGPropertyChangeListener {
1607 DelayedTapeLoader(
const char * tape, simgear::HTTP::FileRequestRef filerequest) :
1608 _tape(SGPath::fromUtf8(tape)),
1609 _filerequest(filerequest)
1611 fgGetNode(
"/sim/signals/fdm-initialized",
true)->addChangeListener(
this );
1614 virtual ~ DelayedTapeLoader() {}
1616 void valueChanged(SGPropertyNode* node)
override
1618 if (!
fgGetBool(
"/sim/signals/fdm-initialized")) {
1621 fgGetNode(
"/sim/signals/fdm-initialized",
true)->removeChangeListener(
this );
1626 SGPropertyNode_ptr arg =
new SGPropertyNode();
1627 arg->setStringValue(
"tape", _tape.utf8Str() );
1628 arg->setBoolValue(
"same-aircraft", 0 );
1632 fgGetBool(
"/sim/startup/load-tape/create-video"),
1633 fgGetDouble(
"/sim/startup/load-tape/fixed-dt", 0),
1639 SG_LOG(SG_GENERAL, SG_POPUP,
"Exiting because unable to load fgtape: " << _tape.str());
1647 simgear::HTTP::FileRequestRef _filerequest;
1650 SGPropertyNode_ptr properties(
new SGPropertyNode);
1651 simgear::HTTP::FileRequestRef filerequest;
1652 SGTimeStamp timeout;
1671 const char* url = arg;
1673 SG_LOG(SG_GENERAL, SG_MANDATORY_INFO,
"Replaying url " << url <<
" using local path: " << path);
1674 filerequest.reset(
new simgear::HTTP::FileRequest(url, path,
true ));
1675 filerequest->setAcceptEncoding(
"");
1677 long max_download_speed =
fgGetLong(
"/sim/replay/download-max-bytes-per-sec");
1678 if (max_download_speed != 0) {
1682 SG_LOG(SG_GENERAL, SG_MANDATORY_INFO,
"Limiting download speed"
1683 <<
" /sim/replay/download-max-bytes-per-sec=" << max_download_speed);
1684 filerequest->setMaxBytesPerSec(max_download_speed);
1686 http->client()->makeRequest(filerequest);
1687 SG_LOG(SG_GENERAL, SG_DEBUG,
""
1688 <<
" filerequest->responseCode()=" << filerequest->responseCode()
1689 <<
" filerequest->responseReason()=" << filerequest->responseReason()
1712 SG_LOG(SG_GENERAL, SG_POPUP,
"Not a Continuous recording: url=" << url <<
" local filename=" << path);
1718 if (timeout.elapsedMSec() > (30 * 1000)) {
1719 SG_LOG(SG_GENERAL, SG_POPUP,
"Timeout while reading downloaded recording from " << url <<
". local path=" << path);
1722 SGTimeStamp::sleepForMSec(1000);
1731 std::string aircraft = properties->getStringValue(
"meta/aircraft-type");
1732 std::string airport = properties->getStringValue(
"meta/closest-airport-id");
1733 SG_LOG(SG_GENERAL, SG_MANDATORY_INFO,
"From recording header: aircraft=" << aircraft <<
" airport=" << airport);
1741 new DelayedTapeLoader(path.c_str(), filerequest);
1751 globals->set_headless(!param);
1760 fgSetBool(
"/sim/presets/mp-hold-short-override", !param);
1778 fgSetBool(
"/sim/presets/onground", !param);
2161 OptionValueVec::const_iterator
findValue(
const string& key)
const
2163 OptionValueVec::const_iterator it =
values.begin();
2164 for (; it !=
values.end(); ++it) {
2169 if (it->desc->option == key) {
2179 OptionValueVec::iterator it =
values.begin();
2180 for (; it !=
values.end(); ++it) {
2185 if (it->desc->option == key) {
2195 OptionDescDict::const_iterator it =
options.find(key);
2209 switch ( desc->
type & 0xffff ) {
2224 SG_LOG( SG_GENERAL, SG_ALERT,
"Option '" << desc->
option <<
"' needs a parameter" );
2227 SG_LOG( SG_GENERAL, SG_ALERT,
"Option '" << desc->
option <<
"' does not have a parameter" );
2232 if ( !arg_value.empty() ) {
2235 SG_LOG( SG_GENERAL, SG_ALERT,
"Option '" << desc->
option <<
"' needs a parameter" );
2240 if ( !arg_value.empty() ) {
2243 SG_LOG( SG_GENERAL, SG_ALERT,
"Option '" << desc->
option <<
"' needs a parameter" );
2254 SG_LOG( SG_GENERAL, SG_ALERT,
"Option '" << desc->
option <<
"' needs a parameter" );
2257 SG_LOG( SG_GENERAL, SG_ALERT,
"Option '" << desc->
option <<
"' does not have a parameter" );
2263 return desc->
func( arg_value.c_str() );
2264 }
else if ( arg_value.empty() && strlen(desc->
s_param) ) {
2270 SG_LOG( SG_GENERAL, SG_ALERT,
"Option '" << desc->
option <<
"' needs a parameter" );
2274 SG_LOG( SG_GENERAL, SG_ALERT,
"Option '" << desc->
option <<
"' does not have a parameter" );
2298 OptionValueVec::const_iterator
rfindGroup(OptionValueVec::const_iterator pos)
const
2300 while (--pos !=
values.begin()) {
2301 if (pos->desc == NULL) {
2313 cJSON *resultNode = cJSON_CreateArray();
2314 cJSON *prevNode =
nullptr;
2315 bool isFirst =
true;
2317 for (
const SGPath& path : pl) {
2318 cJSON *pathNode = cJSON_CreateString(path.utf8Str().c_str());
2322 resultNode->child = pathNode;
2324 prevNode->next = pathNode;
2325 pathNode->prev = prevNode;
2328 prevNode = pathNode;
2363 p(new OptionsPrivate())
2365 p->showHelp =
false;
2367 p->showAircraft =
false;
2368 p->shouldLoadDefaultConfig =
true;
2373 p->options[ it->option ] = it;
2384 bool inOptions =
true;
2385 std::optional<std::string> value;
2387 for (
int i=1;
i<argc; ++
i) {
2395 if (inOptions && (argv[
i][0] ==
'-')) {
2396 if (strcmp(argv[
i],
"--") == 0) {
2402 const string currentOption(argv[
i]);
2404 if (currentOption.find(
"=") == string::npos) {
2405 value = getValueFromNextParam(
i, argc, argv);
2411 int result = parseOption(currentOption, value,
false);
2412 processArgResult(result);
2413 }
else if (value.has_value()) {
2418 SGPath f = SGPath::fromUtf8(argv[
i]);
2420 SG_LOG(SG_GENERAL, SG_ALERT,
"config file not found:" << f);
2422 p->propertyFiles.push_back(f);
2426 p->insertGroupMarker();
2435 simgear::PathList::const_iterator
i;
2436 for (
i = p->configFiles.begin();
i != p->configFiles.end(); ++
i) {
2440 if (!p->shouldLoadDefaultConfig) {
2441 return setupRoot(argc, argv);
2449 config = SGPath::home();
2450 config.append(
".fgfsrc");
2451 config.concat(
"." );
2457 config = SGPath::home();
2458 config.append(
".fgfsrc");
2462 SGPath appDataConfig(appDataPath);
2463 appDataConfig.append(
"fgfsrc");
2464 if (appDataConfig.exists()) {
2469 auto res = setupRoot(argc, argv);
2476 std::string nameForError = config.utf8Str();
2478 config =
globals->get_fg_root();
2479 config.append(
"system.fgfsrc" );
2480 config.concat(
"." );
2482 if (config.exists()) {
2484 "Unsupported configuration",
2485 "You have a '" + config.utf8Str() +
"' file, which is no longer "
2486 "processed for security reasons.",
2487 "If you created this file intentionally, please move it to '" +
2488 nameForError +
"'.");
2492 config =
globals->get_fg_root();
2493 config.append(
"system.fgfsrc" );
2494 if (config.exists()) {
2496 "Unsupported configuration",
2497 "You have a '" + config.utf8Str() +
"' file, which is no longer "
2498 "processed for security reasons.",
2499 "If you created this file intentionally, please move it to '" +
2500 nameForError +
"'.");
2509 if (simgear::strutils::starts_with(option,
"--prop:") ||
2510 simgear::strutils::starts_with(option,
"prop:")
2517 const bool withDashes = simgear::strutils::starts_with(option,
"--");
2518 const string dashes(
"--");
2521 return (withDashes && dashes + opDesc.option == option) || (!withDashes && opDesc.option == option);
2525 return desc->param_type;
2531std::optional<std::string> Options::getValueFromNextParam(
int index,
int argc,
char** argv)
2533 if (index + 1 >= argc) {
2538 const string currentOption(argv[index]);
2548 const string value(argv[index + 1]);
2556 if (simgear::strutils::starts_with(value,
"-")) {
2572 PathList paths = SGPath::pathsFromUtf8(pathOpt);
2573 globals->append_aircraft_paths(paths);
2576 const char* envp = ::getenv(
"FG_AIRCRAFT");
2578 globals->append_aircraft_paths(SGPath::pathsFromEnv(
"FG_AIRCRAFT"));
2596 if (!aircraft.empty()) {
2598 const auto lastDotPos = aircraft.rfind(
'.');
2599 if (lastDotPos != string::npos) {
2602 fgSetString(
"/sim/aircraft", aircraft.substr(lastDotPos + 1));
2607 SG_LOG(SG_INPUT, SG_INFO,
"aircraft = " << aircraft );
2609 SG_LOG(SG_INPUT, SG_INFO,
"No user specified aircraft, using default" );
2614 if (p->showAircraft) {
2621 SGPath rootAircraft =
globals->get_fg_root();
2622 rootAircraft.append(
"Aircraft");
2623 path_list.push_back(rootAircraft);
2629 path_list.insert(path_list.end(), aircraft_paths.begin(),
2630 aircraft_paths.end());
2638 SGPath aircraftDirPath = SGPath::fromUtf8(
valueForOption(
"aircraft-dir"));
2639 SGPath realAircraftPath = aircraftDirPath.realpath();
2640 globals->append_read_allowed_paths(realAircraftPath);
2648 fgSetString(
"/sim/aircraft-dir", realAircraftPath.utf8Str());
2660void Options::processArgResult(
int result)
2667 p->showAircraft =
true;
2669 p->shouldLoadDefaultConfig =
false;
2674 string vendor = smgr.get_vendor();
2675 string renderer = smgr.get_renderer();
2676 cout << renderer <<
" provided by " << vendor << endl;
2677 cout << endl <<
"No. Device" << endl;
2679 vector <std::string>devices = smgr.get_available_devices();
2680 for (vector <std::string>::size_type
i=0;
i<devices.size();
i++) {
2681 cout <<
i <<
". \"" << devices[
i] <<
"\"" << endl;
2695 sg_gzifstream in(path);
2696 if (!in.is_open()) {
2700 SG_LOG( SG_GENERAL, SG_INFO,
"Processing config file: " << path );
2703 while ( ! in.eof() ) {
2705 getline( in, line,
'\n' );
2707 line = strutils::strip(line);
2716 std::optional<std::string> value;
2717 const size_t space = line.find(
' ');
2718 const size_t equal = line.find(
'=');
2719 if (space != string::npos && space < equal) {
2723 value = strutils::strip(line.substr(space + 1));
2724 line = line.substr(0, space);
2728 cerr << endl <<
"Config file parse error: " << path <<
" '"
2729 << line <<
"'" << endl;
2735 p->insertGroupMarker();
2740 if (simgear::strutils::is_bool(param)) {
2741 return simgear::strutils::to_bool(param);
2752int Options::parseOption(
const string& s,
const std::optional<std::string>& val,
bool fromConfigFile)
2754 if ((s ==
"--help") || (s==
"-h")) {
2756 }
else if ( (s ==
"--verbose") || (s ==
"-v") ) {
2759 }
else if (simgear::strutils::starts_with(s,
"--console") || s ==
"-c") {
2760 return fgOptConsole(getValueForBooleanOption(s,
"--console", val).c_str());
2761 }
else if (simgear::strutils::starts_with(s,
"-psn")) {
2765 }
else if (simgear::strutils::starts_with(s,
"--show-aircraft")) {
2766 return paramToBool(getValueForBooleanOption(s,
"--show-aircraft", val))
2769 }
else if (simgear::strutils::starts_with(s,
"--show-sound-devices")) {
2770 return paramToBool(getValueForBooleanOption(s,
"--show-sound-devices", val))
2773 }
else if (simgear::strutils::starts_with(s,
"--no-default-config")) {
2774 return paramToBool(getValueForBooleanOption(s,
"--no-default-config", val))
2777 }
else if (simgear::strutils::starts_with(s,
"--prop:")) {
2779 const OptionDesc* desc = p->findOption(
"prop");
2781 const size_t optLen = strlen(
"--prop:");
2783 if (s.find(
'=', optLen) != string::npos) {
2784 p->values.push_back(OptionValue(desc, s.substr(optLen)));
2788 if (val.has_value()) {
2789 p->values.push_back(OptionValue(desc, s.substr(optLen) +
"=" + val.value()));
2793 SG_LOG(SG_GENERAL, SG_ALERT,
"malformed property option: " << s);
2795 }
else if (simgear::strutils::starts_with(s,
"--config=")) {
2796 return parseConfigOption(s.substr(9), fromConfigFile);
2797 }
else if (simgear::strutils::starts_with(s,
"--config") && val.has_value() ) {
2798 return parseConfigOption(val.value(), fromConfigFile);
2799 }
else if (simgear::strutils::starts_with(s,
"--")) {
2800 size_t eqPos = s.find(
'=' );
2802 if (eqPos == string::npos) {
2805 if (val.has_value()) {
2806 value = val.value();
2809 key = s.substr( 2, eqPos - 2 );
2810 value = s.substr( eqPos + 1);
2814 }
else if (s.empty()) {
2822string Options::getValueForBooleanOption(
const string& str,
const string& option,
const std::optional<std::string>& value)
2824 if (simgear::strutils::starts_with(str, option +
"=")) {
2826 return str.substr((option +
"=").size());
2828 else if (value.has_value()) {
2830 return value.value();
2837int Options::parseConfigOption(
const SGPath &path,
bool fromConfigFile)
2839 if (path.extension() ==
"xml") {
2840 p->propertyFiles.push_back(path);
2842 else if (fromConfigFile) {
2845 "Invalid use of the --config option.",
2846 "Sorry, it is currently not supported to load a configuration file "
2847 "using --config from another configuration file.\n\n"
2848 "Note: this does not apply to loading of XML PropertyList files "
2853 p->configFiles.push_back(path);
2861 if (key ==
"config") {
2864 if (!path.exists()) {
2868 if (path.extension() ==
"xml") {
2869 p->propertyFiles.push_back(path);
2871 p->insertGroupMarker();
2885 OptionValueVec::const_iterator it = p->findValue(key);
2886 if (it != p->values.end()) {
2887 SG_LOG(SG_GENERAL, SG_WARN,
"multiple values forbidden for option:" << key <<
", ignoring:" << value);
2905 OptionValueVec::iterator it = p->findValue(key);
2906 if (it != p->values.end()) {
2908 p->values.erase(it);
2918 OptionValueVec::iterator it = p->findValue(key);
2919 for (; it != p->values.end(); it = p->findValue(key)) {
2920 p->values.erase(it);
2930 OptionValueVec::const_iterator it = p->findValue(key);
2931 return (it != p->values.end());
2936 OptionValueVec::const_iterator it = p->findValue(key);
2937 if (it == p->values.end()) {
2947 OptionValueVec::const_iterator it = p->values.begin();
2948 for (; it != p->values.end(); ++it) {
2953 if (it->desc->option == key) {
2954 result.push_back(it->value);
2971 OptionValueVec::const_iterator it = p->findValue(key);
2972 if (it == p->values.end()) {
2976 if (it->value.empty()) {
2992 return value.has_value() && value.value() ==
true;
2998 return value.has_value() && value.value() ==
false;
3003#if defined(SG_WINDOWS)
3004 const SGPath
p = SGPath::home() /
"FlightGear" /
"Downloads";
3007 return globals->get_fg_home();
3012 SGPath downloadDir = SGPath::fromUtf8(
valueForOption(
"download-dir"));
3013 if (!downloadDir.isNull()) {
3043 OptionValueVec::const_iterator groupEnd = p->values.end();
3045 while (groupEnd != p->values.begin()) {
3046 OptionValueVec::const_iterator groupBegin = p->rfindGroup(groupEnd);
3048 OptionValueVec::const_iterator it;
3049 for (it = groupBegin; it != groupEnd; ++it) {
3050 int result = p->processOption(it->desc, it->value);
3064 SG_LOG(SG_GENERAL, SG_INFO,
"\toption:" << it->desc->option <<
" = " << it->value);
3068 groupEnd = groupBegin;
3071 for (
const SGPath& file : p->propertyFiles) {
3072 SG_LOG(SG_GENERAL, SG_INFO,
3073 "Reading command-line property file " << file);
3074 readProperties(file,
globals->get_props());
3078 const char *envp = ::getenv(
"FG_SCENERY" );
3080 globals->append_fg_scenery(SGPath::pathsFromEnv(
"FG_SCENERY"));
3084 SGPath downloadDir = SGPath::fromUtf8(
valueForOption(
"download-dir"));
3085 if (downloadDir.isNull()) {
3087 SG_LOG(SG_GENERAL, SG_INFO,
3088 "Using default download dir: " << downloadDir);
3090 SG_LOG(SG_GENERAL, SG_INFO,
3091 "Using explicit download dir: " << downloadDir);
3094 simgear::Dir d(downloadDir);
3096 SG_LOG(SG_GENERAL, SG_INFO,
3097 "Creating download dir: " << downloadDir);
3104 globals->set_download_dir(downloadDir);
3107 SGPath textureCacheDir = SGPath::fromUtf8(
valueForOption(
"texture-cache-dir"));
3108 if (textureCacheDir.isNull()) {
3110 SG_LOG(SG_GENERAL, SG_INFO,
3111 "Using default texture cache directory: " << textureCacheDir);
3114 SG_LOG(SG_GENERAL, SG_INFO,
3115 "Using explicit texture cache directory: " << textureCacheDir);
3118 simgear::Dir tcd(textureCacheDir);
3119 if (!tcd.exists()) {
3120 SG_LOG(SG_GENERAL, SG_INFO,
3121 "Creating texture cache directory: " << textureCacheDir);
3125 globals->set_texture_cache_dir(textureCacheDir);
3129 SGPath terrasyncDir = SGPath::fromUtf8(
valueForOption(
"terrasync-dir"));
3130 if (terrasyncDir.isNull()) {
3131 terrasyncDir = downloadDir /
"TerraSync";
3133 SG_LOG(SG_GENERAL, SG_INFO,
3134 "Using TerraSync dir: " << terrasyncDir);
3136 SG_LOG(SG_GENERAL, SG_INFO,
3137 "Using explicit TerraSync dir: " << terrasyncDir);
3140 d = simgear::Dir(terrasyncDir);
3142 SG_LOG(SG_GENERAL, SG_INFO,
3143 "Creating TerraSync dir: " << terrasyncDir);
3150 globals->set_terrasync_dir(terrasyncDir);
3153 bool addFGDataScenery =
globals->get_fg_scenery().empty();
3159 if (std::find(scenery_paths.begin(), scenery_paths.end(), terrasyncDir) == scenery_paths.end()) {
3161 globals->append_fg_scenery(terrasyncDir);
3164 if (addFGDataScenery) {
3167 SGPath root(
globals->get_fg_root());
3168 root.append(
"Scenery");
3169 globals->append_fg_scenery(root);
3199void Options::showUsage()
const
3204 SGPropertyNode options_root;
3206 simgear::requestConsole(
false);
3211 }
catch (
const sg_exception &) {
3212 cout <<
"Unable to read the help file." << endl;
3213 cout <<
"Make sure the file options.xml is located in the FlightGear base directory," << endl;
3214 cout <<
"and the location of the base directory is specified by setting $FG_ROOT or" << endl;
3215 cout <<
"by adding --fg-root=path as a program argument." << endl;
3220 SGPropertyNode *
options = options_root.getNode(
"options");
3222 SG_LOG( SG_GENERAL, SG_ALERT,
3223 "Error reading options.xml: <options> element not found." );
3228 if (!
usage.empty()) {
3229 cout <<
usage << endl;
3232 vector<SGPropertyNode_ptr>section =
options->getChildren(
"section");
3233 for (
unsigned int j = 0; j < section.size(); j++) {
3236 vector<SGPropertyNode_ptr>option = section[j]->getChildren(
"option");
3237 for (
unsigned int k = 0; k < option.size(); k++) {
3239 SGPropertyNode *
name = option[k]->getNode(
"name");
3240 SGPropertyNode *short_name = option[k]->getNode(
"short");
3241 SGPropertyNode *key = option[k]->getNode(
"key");
3242 SGPropertyNode *arg = option[k]->getNode(
"arg");
3243 SGPropertyNode *optionalArg = option[k]->getNode(
"optional-arg");
3244 bool brief = option[k]->getNode(
"brief") != 0;
3246 if ((brief || p->verbose) &&
name) {
3247 string tmp =
name->getStringValue();
3251 tmp.append(key->getStringValue());
3255 tmp.append(arg->getStringValue());
3256 }
else if (optionalArg) {
3258 tmp.append(optionalArg->getStringValue());
3264 tmp.append(short_name->getStringValue());
3267 if (tmp.size() <= 25) {
3270 msg.append( 27-tmp.size(),
' ');
3274 msg.append(32,
' ');
3279 vector<SGPropertyNode_ptr> desc;
3280 desc = option[k]->getChildren(
"description");
3281 if (! desc.empty()) {
3282 for (
unsigned int l = 0; l < desc.size(); l++) {
3283 string t = desc[l]->getStringValue();
3287 auto it = transDesc.begin();
3289 for (
int m = 0; it != transDesc.end(); it++, m++) {
3292 if ((m > 0) || ((l > 0) && m == 0)) {
3293 msg.append( 32,
' ');
3299 while ( t_str.size() > 47 ) {
3300 string::size_type m = t_str.rfind(
' ', 47);
3302 if (m == string::npos) {
3303 m = t_str.find(
' ');
3306 if (m == string::npos) {
3312 msg += t_str.substr(0, m) +
'\n';
3313 msg.append( 32,
' ');
3314 t_str.erase(t_str.begin(), t_str.begin() + m + 1);
3317 msg += t_str +
'\n';
3325 if (!msg.empty() && !
name.empty()) {
3326 cout << endl <<
name <<
":" << endl;
3332 if ( !p->verbose ) {
3334 if (!verbose_help.empty())
3335 cout << endl << verbose_help << endl;
3338 std::cout <<
"Hit a key to continue..." << std::endl;
3343void Options::showVersion()
const
3345 cout <<
"FlightGear version: " << FLIGHTGEAR_VERSION << endl;
3346 cout <<
"Revision: " << REVISION << endl;
3347 cout <<
"Build-Date: " << BUILD_DATE << endl;
3348 cout <<
"Build-Type: " << FG_BUILD_TYPE << endl;
3349 cout <<
"SimGear version: " << SG_STRINGIZE(SIMGEAR_VERSION) << endl;
3350 cout <<
"OSG version: " << osgGetVersion() << endl;
3353 cout <<
"Base Package (FGData) at " << fgRootPath <<
" is version:" <<
fgBasePackageVersion(fgRootPath) << endl;
3356 cout <<
"\tbuilt on " << fgDataInfo.value().buildDate << endl;
3357 cout <<
"\tfrom FGData Git revision: " << fgDataInfo.value().gitRevision << endl;
3361void Options::showInfo()
const
3363 cout <<
"FlightGear version: " << FLIGHTGEAR_VERSION << endl;
3370 cout <<
"FG_SCENERY=";
3372 cout << SGPath::join(scn, SGPath::pathListSep) << endl;
3382void Options::printJSONReport()
const
3384 cJSON *rootNode = cJSON_CreateObject();
3386 cJSON *metaNode = cJSON_CreateObject();
3387 cJSON_AddItemToObject(rootNode,
"meta", metaNode);
3388 cJSON_AddStringToObject(metaNode,
"type",
"FlightGear JSON report");
3395 cJSON_AddNumberToObject(metaNode,
"formatMajorVersion", 2);
3396 cJSON_AddNumberToObject(metaNode,
"formatMinorVersion", 0);
3398 cJSON *generalNode = cJSON_CreateObject();
3399 cJSON_AddItemToObject(rootNode,
"general", generalNode);
3400 cJSON_AddStringToObject(generalNode,
"name",
"FlightGear");
3401 cJSON_AddStringToObject(generalNode,
"version", FLIGHTGEAR_VERSION);
3402 cJSON_AddStringToObject(generalNode,
"buildDate", BUILD_DATE);
3403 cJSON_AddStringToObject(generalNode,
"buildType", FG_BUILD_TYPE);
3404 cJSON_AddStringToObject(generalNode,
"buildRevision", REVISION);
3406 cJSON *configNode = cJSON_CreateObject();
3407 cJSON_AddItemToObject(rootNode,
"config", configNode);
3408 cJSON_AddStringToObject(configNode,
"fgRoot",
3410 cJSON_AddStringToObject(configNode,
"fgHome",
3414 cJSON_AddItemToObject(configNode,
"sceneryPaths", sceneryPathsNode);
3416 cJSON *aircraftPathsNode = p->createJSONArrayFromPathList(
3418 cJSON_AddItemToObject(configNode,
"aircraftPaths", aircraftPathsNode);
3420 cJSON_AddStringToObject(configNode,
"terrasyncPath",
3423 cJSON_AddStringToObject(configNode,
"downloadPath",
3426 cJSON_AddStringToObject(configNode,
"autosavePath",
3429 const auto sentryUid =
fgGetString(
"sim/crashreport/sentry-user-id");
3430 cJSON_AddStringToObject(configNode,
"sentryUUID", sentryUid.c_str());
3439 cJSON *navDataNode = cJSON_CreateObject();
3440 cJSON_AddItemToObject(rootNode,
"navData", navDataNode);
3447 const NavDataCache::DatFilesGroupInfo& datFilesInfo =
3453 const auto map = [](
const auto& e) {
return e.datPath; };
3454 std::transform(std::cbegin(datFilesInfo.
paths),
3455 std::cend(datFilesInfo.
paths), std::begin(datFiles), map);
3457 cJSON *datPathsNode = p->createJSONArrayFromPathList(datFiles);
3459 cJSON_AddItemToObject(navDataNode, key.c_str(), datPathsNode);
3464 char *
report = cJSON_Print(rootNode);
3466 cJSON_Delete(rootNode);
3469#if defined(__CYGWIN__)
3472 return SGPath::fromUtf8(
"../data");
3475#elif defined(SG_WINDOWS)
3478 return SGPath::fromUtf8(
"..\\data");
3480#elif defined(SG_MAC)
3485 return SGPath::fromUtf8(PKGLIBDIR);
3493 for (
auto opt : p->values) {
3494 if (opt.desc ==
nullptr) {
3498 if (!strcmp(opt.desc->option,
"prop")) {
3499 result.push_back(
"prop:" + opt.value);
3500 }
else if (opt.value.empty()) {
3501 result.push_back(opt.desc->option);
3503 result.push_back(std::string(opt.desc->option) +
"=" + opt.value);
3512 SGPath root(
globals->get_fg_root());
3513 bool usingDefaultRoot =
false;
3516 if (!root.isNull()) {
3522 SG_LOG(SG_GENERAL, SG_INFO,
"set from command-line argument: fg_root = " << root );
3525 char *envp = ::getenv(
"FG_ROOT" );
3526 if ( envp !=
nullptr ) {
3527 root = SGPath::fromEnv(
"FG_ROOT");
3528 SG_LOG(SG_GENERAL, SG_INFO,
"set from FG_ROOT env var: fg_root = " << root );
3539 if (root.isNull()) {
3540 usingDefaultRoot =
true;
3542 SG_LOG(SG_GENERAL, SG_INFO,
"platform default fg_root = " << root );
3544 SG_LOG(SG_GENERAL, SG_INFO,
"Qt launcher set fg_root = " << root );
3555 const int versionComp = simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, base_version, 2);
3561 if (versionComp != 0) {
3569 SG_UNUSED(usingDefaultRoot);
3572 if (base_version.empty()) {
3574 "Base package not found",
3575 "Required data files not found, please check your installation.",
3576 "Looking for base-package files at: '" + root.str() +
"'");
3580 const int versionComp = simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, base_version, 2);
3581 if (versionComp != 0) {
3583 "Base package version mismatch",
3584 "Version check failed, please check your installation.",
3585 "Found data files for version '" + base_version +
"' at '" +
3587 std::string(FLIGHTGEAR_VERSION) +
"' is required.");
3595 return p->shouldLoadDefaultConfig;
3600 p->shouldLoadDefaultConfig = load;
3605 for (
int i = 0;
i < argc; ++
i) {
3606 char* arg = argv[
i];
3607 if (arg ==
nullptr) {
3620 if (strcmp(arg, checkArg) == 0) {
3630 for (
int i = 0;
i < argc; ++
i) {
3631 char* arg = argv[
i];
3632 if (arg ==
nullptr) {
3647 if (option ==
"enable-" + checkArg) {
3651 if (option ==
"disable-" + checkArg) {
3655 std::optional<std::string> value;
3656 const size_t equal = option.find(
"=");
3657 if (equal == string::npos) {
3658 value = getValueFromNextParam(
i, argc, argv);
3661 value = option.substr(equal + 1);
3662 option = option.substr(0, equal);
3665 if (option != checkArg) {
3669 if (!value.has_value()) {
3683 const std::optional<bool> value =
checkForBoolArg(argc, argv, checkArg);
3684 return value.has_value() && value.value() ==
true;
3689 const std::optional<bool> value =
checkForBoolArg(argc, argv, checkArg);
3690 return value.has_value() && value.value() ==
false;
3695 const auto len = strlen(checkArg);
3696 for (
int i = 0;
i < argc; ++
i) {
3697 char* arg = argv[
i];
3698 if (arg ==
nullptr) {
3702 if (strncmp(arg, checkArg, len) == 0) {
3703 const auto alen = strlen(arg);
3704 if ((alen - len) < 2) {
3706 std::optional<std::string> value = getValueFromNextParam(
i, argc, argv);
3707 return value.has_value() ? value.value() :
"";
3710 return std::string(arg + len + 1);
bool options(int, char **)
VisitResult visitDir(const simgear::Dir &d, unsigned int depth)
static SGPropertyNode_ptr registerScenarioFile(SGPropertyNode_ptr root, const SGPath &p)
PathList get_aircraft_paths() const
const SGPath & get_download_dir() const
const PathList & get_fg_scenery() const
const SGPath & get_fg_home() const
SGPath autosaveFilePath(SGPath userDataPath=SGPath()) const
Return an SGPath instance for the autosave file under 'userDataPath', which defaults to $FG_HOME.
const SGPath & get_fg_root() const
void set_fg_root(const SGPath &root)
const SGPath & get_terrasync_dir() const
static FGHTTPClient * getOrCreate()
std::vector< std::string > getLocalizedStrings(const std::string &id, const std::string &resource)
Obtain a list of translations that share the same tag name (id stem).
std::string getLocalizedString(const std::string &id, const std::string &resource, const std::string &defaultValue={})
Obtain a single string matching the given id, with fallback.
void show(const vector< SGPath > &path_list)
const DatFilesGroupInfo & getDatFilesInfo(DatFileType datFileType) const
static NavDataCache * createInstance()
static const std::string datTypeStr[]
static NavDataCache * instance()
internal storage of a value->option binding
OptionValue(const OptionDesc *d, const string &v)
OptionValueVec::const_iterator findValue(const string &key) const
cJSON * createJSONArrayFromPathList(const PathList &pl) const
const OptionDesc * findOption(const string &key) const
OptionValueVec::iterator findValue(const string &key)
bool shouldLoadDefaultConfig
simgear::PathList configFiles
simgear::PathList propertyFiles
OptionValueVec::const_iterator rfindGroup(OptionValueVec::const_iterator pos) const
given a current iterator into the values, find the preceding group marker, or return the beginning of...
int processOption(const OptionDesc *desc, const string &arg_value)
void insertGroupMarker()
insert a marker value into the values vector.
static std::optional< bool > checkForBoolArg(int argc, char *argv[], const std::string &checkArg)
Check if the user has specified a given boolean option.
void setShouldLoadDefaultConfig(bool load)
when using the built-in launcher, we disable the default config files.
int addOption(const std::string &key, const std::string &value)
set an option value, assuming it is not already set (or multiple values are permitted) This can be us...
static void reset()
Delete the entire options object.
OptionResult init(int argc, char *argv[], const SGPath &appDataPath)
pass command line arguments, read default config files
int setOption(const std::string &key, const std::string &value)
set an option, overwriting any existing value which might be set
SGPath platformDefaultRoot() const
OptionResult processOptions()
apply option values to the simulation state (set properties, etc).
string_list extractOptions() const
extractOptions - extract the currently set options as a string array.
static bool checkForArgEnable(int argc, char *argv[], const std::string &checkArg)
Return true when user explicitly enabled boolean option, otherwise false.
void initPaths()
process command line options relating to scenery / aircraft / data paths
string_list valuesForOption(const std::string &key) const
return all values for a multi-valued option
static std::string getArgValue(int argc, char *argv[], const char *checkArg)
getArgValue - get the value of an argument if it exists, or an empty string otherwise
bool isBoolOptionDisable(const std::string &key) const
An overlay on checkBoolOptionSet, to check whether user used the option with explicitly disable it.
void readConfig(const SGPath &path)
parse a config file (eg, .fgfsrc)
static bool paramToBool(const std::string ¶m)
Convert string to bool for boolean options.
bool isBoolOptionEnable(const std::string &key) const
An overlay on checkBoolOptionSet, except that when the user has not used the option at all then false...
static bool checkForArg(int argc, char *argv[], const char *arg)
Check if the arguments array contains a particular string (with a '–' or '-' prefix).
SGPath actualDownloadDir()
the actual download dir in use, which may be the default or a user-supplied value
OptionResult initAircraft()
init the aircraft options
std::string valueForOption(const std::string &key, const std::string &defValue=std::string()) const
read the value for an option, if it has been set
static bool checkForArgDisable(int argc, char *argv[], const std::string &checkArg)
Return true when user explicitly disabled boolean option by set false value.
bool shouldLoadDefaultConfig() const
should defualt configuration files be loaded and processed or not?
std::optional< bool > checkBoolOptionSet(const std::string &key) const
Check if the user has specified a given boolean option.
bool isOptionSet(const std::string &key) const
Check if a particular option has been set (so far).
static Options * sharedInstance()
void clearOption(const std::string &key)
static const std::unique_ptr< AddonManager > & instance()
string fgBasePackageVersion(const SGPath &base_path)
std::optional< FGBasePackageInfo > fgBasePackageInfo(const SGPath &path)
Parse the base package info JSON.
long fgGetLong(const char *name, long defaultValue)
Get a long value for a property.
bool fgLoadProps(const std::string &path, SGPropertyNode *props, bool in_fg_root, int default_mode)
Load properties from a file.
void setLoggingPriority(const char *p)
Set the logging priority.
std::string fgGetString(const char *name, const char *defaultValue)
Get a string value for a property.
bool fgSetLong(const char *name, long val)
Set a long value for a property.
bool fgSetInt(const char *name, int val)
Set an int value for a property.
void setLoggingClasses(const char *c)
Set the logging classes.
void fgTie(const char *name, V(*getter)(), void(*setter)(V)=0, bool useDefault=true)
Tie a property to a pair of simple functions.
std::vector< SGPath > PathList
std::vector< std::string > string_list
FlightGear Localization Support.
const std::string & getStringValue(const char *spec)
const char * PROPERTY_ROOT
FlightPlan.hxx - defines a full flight-plan object, including departure, cruise, arrival information ...
MessageBoxResult modalMessageBox(const std::string &caption, const std::string &msg, const std::string &moreText)
MessageBoxResult fatalMessageBoxWithoutExit(const std::string &caption, const std::string &msg, const std::string &moreText, bool reportToSentry)
SGPath defaultDownloadDir()
return the default platform dependant download directory.
void initApp(int &argc, char **argv, bool doInitQSettings)
SGPath defaultTextureCacheDir()
std::map< string, const OptionDesc * > OptionDescDict
std::string sentryUserId()
retrive the anonymous user ID (a UUID) for this installation.
void fatalMessageBoxThenExit(const std::string &caption, const std::string &msg, const std::string &moreText, int exitStatus, bool reportToSentry)
std::vector< OptionValue > OptionValueVec
bool showSetupRootDialog(bool usingDefaultRoot)
static int getOptionParamType(string option)
SetupRootResult restoreUserSelectedRoot(SGPath &path)
OptionResult
option processing can have various result values depending on what the user requested.
@ FG_OPTIONS_VERBOSE_HELP
@ FG_OPTIONS_SHOW_AIRCRAFT
@ FG_OPTIONS_SHOW_SOUND_DEVICES
@ FG_OPTIONS_NO_DEFAULT_CONFIG
const std::initializer_list< OptionDesc > fgOptionArray
static int fgOptLogClasses(const char *arg)
static int fgOptRoc(const char *arg)
static int fgOptVNorth(const char *arg)
static bool parse_colon(const string &s, double *val1, double *val2)
static int fgOptHttpd(const char *arg)
static int fgOptStartDateLat(const char *arg)
static int fgOptTurbulence(const char *arg)
static int fgOptADF(const char *arg)
static int fgOptCallSign(const char *arg)
static int fgOptADF2(const char *arg)
static int fgOptCeiling(const char *arg)
static double parse_fov(const string &arg)
static int fgOptVDown(const char *arg)
static int fgOptLogDir(const char *arg)
static int fgOptAirport(const char *arg)
static long int parse_time_offset(const string &time_str)
static int fgOptNoTrim(const char *arg)
static int fgOptCarrierPos(const char *arg)
static int fgOptHoldShort(const char *arg)
static int fgOptVisibilityMeters(const char *arg)
static int fgOptLoadTape(const char *arg)
static int fgOptTraceWrite(const char *arg)
static int fgOptAllowNasalRead(const char *arg)
#define NEW_DEFAULT_MODEL_HZ
static int fgOptAltitude(const char *arg)
static void clearLocation()
static int fgOptScenario(const char *arg)
static int fgOptParkpos(const char *arg)
static int fgOptVisibilityMiles(const char *arg)
void fgSetDefaults()
Set a few fail-safe default property values.
static int fgOptWind(const char *arg)
static double atof(const string &str)
static int fgOptLivery(const char *arg)
static int fgOptRunway(const char *arg)
static int fgOptFailure(const char *arg)
static long int parse_date(const string &date, const char *timeType)
static int fgOptCarrier(const char *arg)
static int fgOptTimeOffset(const char *arg)
static int fgOptMach(const char *arg)
static int fgOptFgScenery(const char *arg)
static int fgOptADF1(const char *arg)
static double parse_degree(const string °ree_str)
static int fgOptUBody(const char *arg)
static int fgOptAdditionalDataDir(const char *arg)
static std::string urlToLocalPath(const char *url)
static int fgOptAddon(const char *arg)
static int fgOptStartDateGmt(const char *arg)
static int fgOptWp(const char *arg)
static int fgOptTACAN(const char *arg)
static int fgOptGUI(const char *arg)
static int fgOptNAV2(const char *arg)
static int fgOptViewOffset(const char *arg)
static int fgOptLon(const char *arg)
static int fgOptWBody(const char *arg)
static int fgOptVc(const char *arg)
static int fgSetupProxy(const char *arg)
static int fgOptGeometry(const char *arg)
static int atoi(const string &str)
static flightgear::Options * shared_instance
static int fgOptFreeze(const char *arg)
static int fgOptNDB(const char *arg)
static std::string g_load_tape_aircraft
static int fgOptFIX(const char *arg)
static int fgOptLat(const char *arg)
static int fgOptNAV1(const char *arg)
static int fgOptBpp(const char *arg)
static int fgOptVBody(const char *arg)
static int fgOptJpgHttpd(const char *arg)
static int fgOptLogLevel(const char *arg)
static bool parse_wind(const string &wind, double *min_hdg, double *max_hdg, double *speed, double *gust)
static int fgOptStartDateSys(const char *arg)
static int fgOptVEast(const char *arg)
static int fgOptVOR(const char *arg)
static int fgOptInAir(const char *arg)
static int fgOptDME(const char *arg)
static int fgOptIgnoreAutosave(const char *arg)
static int fgOptRandomWind(const char *arg)
static int fgOptConsole(const char *arg)
static std::string g_load_tape_airport
static bool parseIntValue(char **ppParserPos, int *pValue, int min, int max, const char *field, const char *argument)
static int fgOptFov(const char *arg)
static bool add_channel(const string &type, const string &channel_str)
static double parse_time(const string &time_in)
void fgShowAircraft(const vector< SGPath > &path_list)
static int fgOptSetProperty(const char *raw)
static int fgOptMetar(const char *arg)
static int fgOptTraceRead(const char *arg)
bool fgSetDouble(const char *name, double defaultValue)
Set a double value for a property.
bool fgGetBool(char const *name, bool def)
Get a bool value for a property.
bool fgSetBool(char const *name, bool val)
Set a bool value for a property.
double fgGetDouble(const char *name, double defaultValue)
Get a double value for a property.
bool fgSetString(char const *name, char const *str)
Set a string value for a property.
SGPropertyNode * fgGetNode(const char *path, bool create)
Get a property node.
bool replay(FGReplayInternal &self, double time)
Replay a saved frame based on time, interpolate from the two nearest saved frames.
static std::string makeTapePath(const std::string &tape_name)
static int loadContinuousHeader(const std::string &path, std::istream *in, SGPropertyNode *properties)
int(* func)(const char *)
SceneryLocationList paths