1998-04-24 00:49:17 +00:00
|
|
|
// options.cxx -- class to handle command line options
|
|
|
|
//
|
|
|
|
// Written by Curtis Olson, started April 1998.
|
|
|
|
//
|
2004-11-19 22:10:41 +00:00
|
|
|
// Copyright (C) 1998 Curtis L. Olson - http://www.flightgear.org/~curt
|
1998-04-24 00:49:17 +00:00
|
|
|
//
|
|
|
|
// This program is free software; you can redistribute it and/or
|
|
|
|
// modify it under the terms of the GNU General Public License as
|
|
|
|
// published by the Free Software Foundation; either version 2 of the
|
|
|
|
// License, or (at your option) any later version.
|
|
|
|
//
|
|
|
|
// This program is distributed in the hope that it will be useful, but
|
|
|
|
// WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
// General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU General Public License
|
|
|
|
// along with this program; if not, write to the Free Software
|
2006-02-21 01:16:04 +00:00
|
|
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
|
1998-04-24 00:49:17 +00:00
|
|
|
//
|
|
|
|
// $Id$
|
|
|
|
|
|
|
|
|
2020-04-09 14:48:03 +00:00
|
|
|
#include <config.h>
|
1998-04-26 05:01:19 +00:00
|
|
|
|
2000-02-15 03:30:01 +00:00
|
|
|
#include <simgear/compiler.h>
|
2003-09-24 17:20:55 +00:00
|
|
|
#include <simgear/structure/exception.hxx>
|
2003-03-22 10:38:15 +00:00
|
|
|
#include <simgear/debug/logstream.hxx>
|
2010-07-01 21:43:28 +00:00
|
|
|
#include <simgear/timing/sg_time.hxx>
|
2010-07-05 07:03:29 +00:00
|
|
|
#include <simgear/misc/sg_dir.hxx>
|
1999-03-02 01:02:31 +00:00
|
|
|
|
2020-08-19 13:49:50 +00:00
|
|
|
#include <cJSON.h>
|
2011-10-16 17:35:40 +00:00
|
|
|
|
2015-03-24 16:11:42 +00:00
|
|
|
#include <cmath> // rint()
|
2016-12-26 22:03:22 +00:00
|
|
|
#include <cstdio>
|
|
|
|
#include <cstdlib> // atof(), atoi()
|
|
|
|
#include <cstring> // strcmp()
|
2003-02-11 21:17:36 +00:00
|
|
|
#include <algorithm>
|
2017-05-15 09:06:47 +00:00
|
|
|
#include <map>
|
1999-03-02 01:02:31 +00:00
|
|
|
|
2008-08-14 18:13:39 +00:00
|
|
|
#include <iostream>
|
2008-07-25 18:38:29 +00:00
|
|
|
#include <string>
|
2015-01-04 17:50:10 +00:00
|
|
|
#include <sstream>
|
1998-04-24 00:49:17 +00:00
|
|
|
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
#include <simgear/io/HTTPClient.hxx>
|
|
|
|
#include <simgear/io/HTTPFileRequest.hxx>
|
2002-07-26 02:49:14 +00:00
|
|
|
#include <simgear/math/sg_random.h>
|
2008-07-31 12:04:32 +00:00
|
|
|
#include <simgear/props/props_io.hxx>
|
2017-02-09 18:48:33 +00:00
|
|
|
#include <simgear/io/iostreams/sgstream.hxx>
|
2002-03-16 00:18:38 +00:00
|
|
|
#include <simgear/misc/sg_path.hxx>
|
2017-01-29 15:18:10 +00:00
|
|
|
#include <simgear/misc/sg_dir.hxx>
|
2007-05-28 05:21:45 +00:00
|
|
|
#include <simgear/scene/material/mat.hxx>
|
2016-07-06 11:27:14 +00:00
|
|
|
#include <simgear/sound/soundmgr.hxx>
|
2011-02-20 16:14:30 +00:00
|
|
|
#include <simgear/misc/strutils.hxx>
|
2021-02-19 10:16:34 +00:00
|
|
|
#include <simgear/timing/timestamp.hxx>
|
|
|
|
|
2004-01-31 19:47:45 +00:00
|
|
|
#include <Autopilot/route_mgr.hxx>
|
2014-09-17 20:38:40 +00:00
|
|
|
#include <Aircraft/replay.hxx>
|
2016-06-17 16:53:35 +00:00
|
|
|
#include <Aircraft/initialstate.hxx>
|
2013-11-06 23:49:58 +00:00
|
|
|
|
2001-07-19 04:53:13 +00:00
|
|
|
#include <GUI/gui.h>
|
2013-11-06 23:49:58 +00:00
|
|
|
#include <GUI/MessageBox.hxx>
|
2017-03-21 20:43:42 +00:00
|
|
|
|
2018-06-11 09:35:09 +00:00
|
|
|
#if defined(HAVE_QT)
|
2015-03-06 18:52:06 +00:00
|
|
|
#include <GUI/QtLauncher.hxx>
|
|
|
|
#include <GUI/SetupRootDialog.hxx>
|
2015-03-08 09:40:44 +00:00
|
|
|
#endif
|
2001-07-19 04:53:13 +00:00
|
|
|
|
2019-02-03 10:37:01 +00:00
|
|
|
#include <AIModel/AIManager.hxx>
|
Improved infrastructure for add-ons: C++ classes, metadata file, Nasal interface
This commit adds C++ classes for add-on management, most notably
AddonManager, Addon and AddonVersion. The AddonManager is used to
register add-ons. It relies on an std::map<std::string, AddonRef> to
hold the metadata of each registered add-on (keys of the std::map are
add-on identifiers, and AddonRef is currently SGSharedPtr<Addon>).
Accessor methods are available for:
- retrieving the list of registered or loaded add-ons (terminology
explained in $FG_ROOT/Docs/README.add-ons);
- checking if a particular add-on has already been registered or
loaded;
- for each add-on, obtaining an Addon instance which can be queried
for its name, id, version, base path, the minimum and maximum
FlightGear versions it requires, its base node in the Property Tree,
its order in the load sequence, short and long description strings,
home page, etc.
The most important metadata is made accessible in the Property Tree
under /addons/by-id/<addon-id> and the property
/addons/by-id/<addon-id>/loaded can be checked or listened to, in
order to determine when a particular add-on is loaded. There is also a
Nasal interface to access add-on metadata in a convenient way.
In order to provide this metadata, each add-on must from now on have in
its base directory a file called 'addon-metadata.xml'.
All this is documented in much more detail in
$FG_ROOT/Docs/README.add-ons.
Mailing-list discussion:
https://sourceforge.net/p/flightgear/mailman/message/36146017/
2017-11-06 12:58:14 +00:00
|
|
|
#include <Add-ons/AddonManager.hxx>
|
2013-11-06 23:49:58 +00:00
|
|
|
#include <Main/locale.hxx>
|
2016-10-31 21:21:52 +00:00
|
|
|
#include <Navaids/NavDataCache.hxx>
|
2000-07-07 20:28:51 +00:00
|
|
|
#include "globals.hxx"
|
2001-01-13 22:06:39 +00:00
|
|
|
#include "fg_init.hxx"
|
2020-09-08 21:52:24 +00:00
|
|
|
#include "fg_os.hxx"
|
2001-01-13 22:06:39 +00:00
|
|
|
#include "fg_props.hxx"
|
1999-05-06 22:16:12 +00:00
|
|
|
#include "options.hxx"
|
2003-06-11 14:18:24 +00:00
|
|
|
#include "util.hxx"
|
2012-04-21 07:38:41 +00:00
|
|
|
#include "main.hxx"
|
2012-04-25 21:28:00 +00:00
|
|
|
#include "locale.hxx"
|
2016-01-25 23:17:17 +00:00
|
|
|
#include <Viewer/view.hxx>
|
2012-04-25 21:28:00 +00:00
|
|
|
#include <Viewer/viewmgr.hxx>
|
2011-01-19 18:36:04 +00:00
|
|
|
#include <Environment/presets.hxx>
|
2014-03-03 20:59:16 +00:00
|
|
|
#include <Network/http/httpd.hxx>
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
#include <Network/HTTPClient.hxx>
|
2013-11-16 14:22:34 +00:00
|
|
|
#include "AircraftDirVisitorBase.hxx"
|
2010-11-14 22:55:55 +00:00
|
|
|
|
|
|
|
#include <osg/Version>
|
2020-08-24 13:32:22 +00:00
|
|
|
#include <flightgearBuildId.h>
|
2015-03-06 18:52:06 +00:00
|
|
|
#include <simgear/version.h>
|
1998-11-16 13:59:58 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
using std::string;
|
|
|
|
using std::sort;
|
|
|
|
using std::cout;
|
|
|
|
using std::cerr;
|
|
|
|
using std::endl;
|
2011-10-17 16:39:47 +00:00
|
|
|
using std::vector;
|
2011-11-13 21:27:55 +00:00
|
|
|
using std::cin;
|
2011-10-16 17:35:40 +00:00
|
|
|
|
2013-10-15 21:16:50 +00:00
|
|
|
using namespace flightgear;
|
2001-01-13 22:06:39 +00:00
|
|
|
|
2013-10-15 21:16:50 +00:00
|
|
|
#define NEW_DEFAULT_MODEL_HZ 120
|
2001-01-13 22:06:39 +00:00
|
|
|
|
2017-03-27 14:37:54 +00:00
|
|
|
static flightgear::Options* shared_instance = nullptr;
|
2011-10-16 17:35:40 +00:00
|
|
|
|
2001-01-13 22:06:39 +00:00
|
|
|
static double
|
1998-08-27 17:01:55 +00:00
|
|
|
atof( const string& str )
|
|
|
|
{
|
|
|
|
return ::atof( str.c_str() );
|
|
|
|
}
|
|
|
|
|
2001-01-13 22:06:39 +00:00
|
|
|
static int
|
1998-08-27 17:01:55 +00:00
|
|
|
atoi( const string& str )
|
|
|
|
{
|
|
|
|
return ::atoi( str.c_str() );
|
|
|
|
}
|
1998-04-24 00:49:17 +00:00
|
|
|
|
2011-02-20 10:42:21 +00:00
|
|
|
static int fgSetupProxy( const char *arg );
|
|
|
|
|
2001-01-13 22:06:39 +00:00
|
|
|
/**
|
|
|
|
* Set a few fail-safe default property values.
|
|
|
|
*
|
2017-01-11 17:58:22 +00:00
|
|
|
* These should all be set in $FG_ROOT/defaults.xml, but just
|
2003-03-19 20:45:09 +00:00
|
|
|
* in case, we provide some initial sane values here. This method
|
2001-01-13 22:06:39 +00:00
|
|
|
* should be invoked *before* reading any init files.
|
|
|
|
*/
|
2013-11-16 12:00:00 +00:00
|
|
|
void fgSetDefaults ()
|
1998-08-27 17:01:55 +00:00
|
|
|
{
|
2010-06-27 07:04:10 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Position (deliberately out of range)
|
2002-05-01 23:13:36 +00:00
|
|
|
fgSetDouble("/position/longitude-deg", 9999.0);
|
|
|
|
fgSetDouble("/position/latitude-deg", 9999.0);
|
2001-07-02 22:27:24 +00:00
|
|
|
fgSetDouble("/position/altitude-ft", -9999.0);
|
2001-01-12 15:37:40 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Orientation
|
2007-10-04 17:14:37 +00:00
|
|
|
fgSetDouble("/orientation/heading-deg", 9999.0);
|
2007-10-04 21:52:50 +00:00
|
|
|
fgSetDouble("/orientation/roll-deg", 0.0);
|
|
|
|
fgSetDouble("/orientation/pitch-deg", 0.424);
|
2001-01-12 15:37:40 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Velocities
|
2001-07-02 22:27:24 +00:00
|
|
|
fgSetDouble("/velocities/uBody-fps", 0.0);
|
|
|
|
fgSetDouble("/velocities/vBody-fps", 0.0);
|
|
|
|
fgSetDouble("/velocities/wBody-fps", 0.0);
|
|
|
|
fgSetDouble("/velocities/speed-north-fps", 0.0);
|
|
|
|
fgSetDouble("/velocities/speed-east-fps", 0.0);
|
|
|
|
fgSetDouble("/velocities/speed-down-fps", 0.0);
|
|
|
|
fgSetDouble("/velocities/airspeed-kt", 0.0);
|
2001-01-13 22:06:39 +00:00
|
|
|
fgSetDouble("/velocities/mach", 0.0);
|
2001-01-12 15:37:40 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Presets
|
2002-11-15 21:13:29 +00:00
|
|
|
fgSetDouble("/sim/presets/longitude-deg", 9999.0);
|
|
|
|
fgSetDouble("/sim/presets/latitude-deg", 9999.0);
|
|
|
|
fgSetDouble("/sim/presets/altitude-ft", -9999.0);
|
|
|
|
|
2007-10-04 17:14:37 +00:00
|
|
|
fgSetDouble("/sim/presets/heading-deg", 9999.0);
|
2007-10-04 21:52:50 +00:00
|
|
|
fgSetDouble("/sim/presets/roll-deg", 0.0);
|
|
|
|
fgSetDouble("/sim/presets/pitch-deg", 0.424);
|
2002-11-15 21:13:29 +00:00
|
|
|
|
|
|
|
fgSetString("/sim/presets/speed-set", "knots");
|
|
|
|
fgSetDouble("/sim/presets/airspeed-kt", 0.0);
|
|
|
|
fgSetDouble("/sim/presets/mach", 0.0);
|
|
|
|
fgSetDouble("/sim/presets/uBody-fps", 0.0);
|
|
|
|
fgSetDouble("/sim/presets/vBody-fps", 0.0);
|
|
|
|
fgSetDouble("/sim/presets/wBody-fps", 0.0);
|
|
|
|
fgSetDouble("/sim/presets/speed-north-fps", 0.0);
|
|
|
|
fgSetDouble("/sim/presets/speed-east-fps", 0.0);
|
|
|
|
fgSetDouble("/sim/presets/speed-down-fps", 0.0);
|
2018-12-04 08:50:48 +00:00
|
|
|
fgSetDouble("/sim/presets/offset-distance-nm", 0.0);
|
2002-11-15 21:13:29 +00:00
|
|
|
|
2018-12-04 08:50:48 +00:00
|
|
|
fgSetBool("/sim/presets/runway-requested", false);
|
2020-04-05 22:13:32 +00:00
|
|
|
|
2002-11-15 21:13:29 +00:00
|
|
|
fgSetBool("/sim/presets/onground", true);
|
|
|
|
fgSetBool("/sim/presets/trim", false);
|
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Miscellaneous
|
2001-01-13 22:06:39 +00:00
|
|
|
fgSetBool("/sim/startup/splash-screen", true);
|
2001-02-26 13:58:33 +00:00
|
|
|
// we want mouse-pointer to have an undefined value if nothing is
|
|
|
|
// specified so we can do the right thing for voodoo-1/2 cards.
|
|
|
|
// fgSetString("/sim/startup/mouse-pointer", "disabled");
|
2012-03-13 20:00:22 +00:00
|
|
|
fgSetBool("/controls/flight/auto-coordination", false);
|
2003-11-25 17:10:53 +00:00
|
|
|
fgSetString("/sim/logging/priority", "alert");
|
2003-03-22 10:38:15 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Features
|
2006-06-10 07:23:15 +00:00
|
|
|
fgSetBool("/sim/hud/color/antialiased", false);
|
2011-01-16 15:31:39 +00:00
|
|
|
fgSetBool("/sim/hud/enable3d[1]", true);
|
|
|
|
fgSetBool("/sim/hud/visibility[1]", false);
|
2001-01-13 22:06:39 +00:00
|
|
|
fgSetBool("/sim/panel/visibility", true);
|
2009-10-05 13:47:03 +00:00
|
|
|
fgSetBool("/sim/sound/enabled", true);
|
2009-11-30 12:05:20 +00:00
|
|
|
fgSetBool("/sim/sound/working", true);
|
2013-05-19 14:29:49 +00:00
|
|
|
fgSetBool("/sim/fgcom/enabled", false);
|
2001-01-12 15:37:40 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Flight Model options
|
2001-07-24 01:02:59 +00:00
|
|
|
fgSetString("/sim/flight-model", "jsb");
|
2001-12-06 23:10:50 +00:00
|
|
|
fgSetString("/sim/aero", "c172");
|
2001-01-13 22:06:39 +00:00
|
|
|
fgSetInt("/sim/model-hz", NEW_DEFAULT_MODEL_HZ);
|
2012-11-17 19:01:19 +00:00
|
|
|
fgSetDouble("/sim/speed-up", 1.0);
|
2001-01-12 15:37:40 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Scenery
|
|
|
|
fgSetString("/sim/scenery/engine", "tilecache");
|
|
|
|
|
|
|
|
// ( scenery = pagedLOD )
|
|
|
|
fgSetString("/sim/scenery/lod-levels", "1 3 5 7 9");
|
|
|
|
fgSetString("/sim/scenery/lod-res", "1");
|
|
|
|
fgSetString("/sim/scenery/lod-texturing", "bluemarble");
|
|
|
|
|
|
|
|
// Rendering options
|
2001-01-13 22:06:39 +00:00
|
|
|
fgSetString("/sim/rendering/fog", "nicest");
|
|
|
|
fgSetBool("/environment/clouds/status", true);
|
|
|
|
fgSetBool("/sim/startup/fullscreen", false);
|
|
|
|
fgSetBool("/sim/rendering/shading", true);
|
2009-11-30 14:24:16 +00:00
|
|
|
fgTie( "/sim/rendering/filtering", SGGetTextureFilter, SGSetTextureFilter, false);
|
2007-05-28 05:21:45 +00:00
|
|
|
fgSetInt("/sim/rendering/filtering", 1);
|
2001-01-13 22:06:39 +00:00
|
|
|
fgSetBool("/sim/rendering/wireframe", false);
|
2003-06-22 09:45:43 +00:00
|
|
|
fgSetBool("/sim/rendering/horizon-effect", false);
|
2002-12-30 21:49:56 +00:00
|
|
|
fgSetBool("/sim/rendering/distance-attenuation", false);
|
2003-09-12 21:43:01 +00:00
|
|
|
fgSetBool("/sim/rendering/specular-highlight", true);
|
2011-09-02 18:26:55 +00:00
|
|
|
fgSetString("/sim/rendering/materials-file", "materials.xml");
|
2013-10-27 13:02:54 +00:00
|
|
|
fgSetInt("/sim/startup/xsize", 1024);
|
|
|
|
fgSetInt("/sim/startup/ysize", 768);
|
|
|
|
fgSetInt("/sim/rendering/bits-per-pixel", 32);
|
2001-01-13 22:06:39 +00:00
|
|
|
fgSetString("/sim/view-mode", "pilot");
|
2002-04-18 16:51:47 +00:00
|
|
|
fgSetDouble("/sim/current-view/heading-offset-deg", 0);
|
2001-01-12 15:37:40 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// HUD options
|
2001-01-13 22:06:39 +00:00
|
|
|
fgSetString("/sim/startup/units", "feet");
|
|
|
|
fgSetString("/sim/hud/frame-stat-type", "tris");
|
2010-06-27 07:04:10 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Time options
|
2001-01-13 22:06:39 +00:00
|
|
|
fgSetInt("/sim/startup/time-offset", 0);
|
|
|
|
fgSetString("/sim/startup/time-offset-type", "system-offset");
|
2002-02-10 04:27:56 +00:00
|
|
|
fgSetLong("/sim/time/cur-time-override", 0);
|
1998-11-25 01:33:58 +00:00
|
|
|
|
2016-07-30 12:26:45 +00:00
|
|
|
// Freeze options
|
2002-01-20 03:52:36 +00:00
|
|
|
fgSetBool("/sim/freeze/master", false);
|
|
|
|
fgSetBool("/sim/freeze/position", false);
|
2002-02-13 02:37:44 +00:00
|
|
|
fgSetBool("/sim/freeze/clock", false);
|
2002-01-22 15:39:51 +00:00
|
|
|
fgSetBool("/sim/freeze/fuel", false);
|
2003-03-19 20:45:09 +00:00
|
|
|
|
|
|
|
fgSetString("/sim/multiplay/callsign", "callsign");
|
2011-08-24 08:44:52 +00:00
|
|
|
fgSetString("/sim/multiplay/rxhost", "");
|
|
|
|
fgSetString("/sim/multiplay/txhost", "");
|
2003-03-19 20:45:09 +00:00
|
|
|
fgSetInt("/sim/multiplay/rxport", 0);
|
|
|
|
fgSetInt("/sim/multiplay/txport", 0);
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-10-20 10:58:20 +00:00
|
|
|
SGPropertyNode* v = globals->get_props()->getNode("/sim/version", true);
|
|
|
|
v->setValueReadOnly("flightgear", FLIGHTGEAR_VERSION);
|
|
|
|
v->setValueReadOnly("simgear", SG_STRINGIZE(SIMGEAR_VERSION));
|
|
|
|
v->setValueReadOnly("openscenegraph", osgGetVersion());
|
2017-04-02 00:09:38 +00:00
|
|
|
#if OSG_VERSION_LESS_THAN(3,5,2)
|
2013-10-20 10:58:20 +00:00
|
|
|
v->setValueReadOnly("openscenegraph-thread-safe-reference-counting",
|
|
|
|
osg::Referenced::getThreadSafeReferenceCounting());
|
2017-03-21 20:43:42 +00:00
|
|
|
#endif
|
2013-10-20 10:58:20 +00:00
|
|
|
v->setValueReadOnly("revision", REVISION);
|
2018-08-11 09:09:11 +00:00
|
|
|
v->setValueReadOnly("build-number", JENKINS_BUILD_NUMBER);
|
|
|
|
v->setValueReadOnly("build-id", JENKINS_BUILD_ID);
|
2013-10-20 10:58:20 +00:00
|
|
|
v->setValueReadOnly("hla-support", bool(FG_HAVE_HLA));
|
2017-02-27 23:15:07 +00:00
|
|
|
v->setValueReadOnly("build-type", FG_BUILD_TYPE);
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-10-20 10:58:20 +00:00
|
|
|
char* envp = ::getenv( "http_proxy" );
|
2018-10-12 10:37:39 +00:00
|
|
|
if( envp != nullptr )
|
2011-02-20 10:42:21 +00:00
|
|
|
fgSetupProxy( envp );
|
1998-05-13 18:29:56 +00:00
|
|
|
}
|
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// helper object to implement the --show-aircraft command.
|
|
|
|
// resides here so we can share the fgFindAircraftInDir template above,
|
|
|
|
// and hence ensure this command lists exectly the same aircraft as the normal
|
|
|
|
// loading path.
|
|
|
|
class ShowAircraft : public AircraftDirVistorBase
|
|
|
|
{
|
|
|
|
public:
|
|
|
|
ShowAircraft()
|
|
|
|
{
|
|
|
|
_minStatus = getNumMaturity(fgGetString("/sim/aircraft-min-status", "all"));
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
|
|
|
|
2015-04-09 14:31:09 +00:00
|
|
|
void show(const vector<SGPath> & path_list)
|
2013-11-16 14:22:34 +00:00
|
|
|
{
|
2015-04-09 14:31:09 +00:00
|
|
|
for (vector<SGPath>::const_iterator p = path_list.begin();
|
|
|
|
p != path_list.end(); ++p)
|
|
|
|
visitDir(*p, 0);
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
simgear::requestConsole(); // ensure console is shown on Windows
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2020-06-22 09:18:00 +00:00
|
|
|
std::sort(_aircraft.begin(), _aircraft.end(),
|
|
|
|
[](const std::string& lhs, const std::string& rhs) {
|
|
|
|
return strcasecmp(lhs.c_str(), rhs.c_str()) < 0 ? 1 : 0;
|
|
|
|
});
|
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
cout << "Available aircraft:" << endl;
|
|
|
|
for ( unsigned int i = 0; i < _aircraft.size(); i++ ) {
|
|
|
|
cout << _aircraft[i] << endl;
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
private:
|
|
|
|
virtual VisitResult visit(const SGPath& path)
|
|
|
|
{
|
|
|
|
SGPropertyNode root;
|
|
|
|
try {
|
2016-06-23 13:26:34 +00:00
|
|
|
readProperties(path, &root);
|
2013-11-16 14:22:34 +00:00
|
|
|
} catch (sg_exception& ) {
|
|
|
|
return VISIT_CONTINUE;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
int maturity = 0;
|
|
|
|
string descStr(" ");
|
|
|
|
descStr += path.file();
|
|
|
|
// trim common suffix from file names
|
|
|
|
int nPos = descStr.rfind("-set.xml");
|
|
|
|
if (nPos == (int)(descStr.size() - 8)) {
|
|
|
|
descStr.resize(nPos);
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
SGPropertyNode *node = root.getNode("sim");
|
|
|
|
if (node) {
|
|
|
|
SGPropertyNode* desc = node->getNode("description");
|
|
|
|
// if a status tag is found, read it in
|
|
|
|
if (node->hasValue("status")) {
|
|
|
|
maturity = getNumMaturity(node->getStringValue("status"));
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
if (desc) {
|
|
|
|
if (descStr.size() <= 27+3) {
|
|
|
|
descStr.append(29+3-descStr.size(), ' ');
|
|
|
|
} else {
|
|
|
|
descStr += '\n';
|
|
|
|
descStr.append( 32, ' ');
|
|
|
|
}
|
|
|
|
descStr += desc->getStringValue();
|
|
|
|
}
|
|
|
|
} // of have 'sim' node
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
if (maturity >= _minStatus) {
|
|
|
|
_aircraft.push_back(descStr);
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
return VISIT_CONTINUE;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
int getNumMaturity(const char * str)
|
|
|
|
{
|
2017-01-05 05:31:52 +00:00
|
|
|
// Changes should also be reflected in $FG_ROOT/options.xml
|
2013-11-16 14:22:34 +00:00
|
|
|
const char* levels[] = {"alpha","beta","early-production","production"};
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
if (!strcmp(str, "all")) {
|
|
|
|
return 0;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
for (size_t i=0; i<(sizeof(levels)/sizeof(levels[0]));i++)
|
|
|
|
if (strcmp(str,levels[i])==0)
|
|
|
|
return i;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
return 0;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
int _minStatus;
|
|
|
|
string_list _aircraft;
|
|
|
|
};
|
|
|
|
|
|
|
|
/*
|
|
|
|
* Search in the current directory, and in on directory deeper
|
|
|
|
* for <aircraft>-set.xml configuration files and show the aircaft name
|
|
|
|
* and the contents of the<description> tag in a sorted manner.
|
|
|
|
*
|
|
|
|
* @parampath the directory to search for configuration files
|
|
|
|
*/
|
2015-04-09 14:31:09 +00:00
|
|
|
void fgShowAircraft(const vector<SGPath> &path_list)
|
2013-11-16 14:22:34 +00:00
|
|
|
{
|
|
|
|
ShowAircraft s;
|
2015-04-09 14:31:09 +00:00
|
|
|
s.show(path_list);
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-11-16 14:22:34 +00:00
|
|
|
#ifdef _MSC_VER
|
|
|
|
cout << "Hit a key to continue..." << endl;
|
|
|
|
std::cin.get();
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2002-06-09 21:28:17 +00:00
|
|
|
static bool
|
|
|
|
parse_wind (const string &wind, double * min_hdg, double * max_hdg,
|
|
|
|
double * speed, double * gust)
|
|
|
|
{
|
2002-07-02 20:45:02 +00:00
|
|
|
string::size_type pos = wind.find('@');
|
2002-06-09 21:28:17 +00:00
|
|
|
if (pos == string::npos)
|
|
|
|
return false;
|
|
|
|
string dir = wind.substr(0, pos);
|
|
|
|
string spd = wind.substr(pos+1);
|
|
|
|
pos = dir.find(':');
|
|
|
|
if (pos == string::npos) {
|
|
|
|
*min_hdg = *max_hdg = atof(dir.c_str());
|
|
|
|
} else {
|
|
|
|
*min_hdg = atof(dir.substr(0,pos).c_str());
|
|
|
|
*max_hdg = atof(dir.substr(pos+1).c_str());
|
|
|
|
}
|
|
|
|
pos = spd.find(':');
|
|
|
|
if (pos == string::npos) {
|
|
|
|
*speed = *gust = atof(spd.c_str());
|
|
|
|
} else {
|
|
|
|
*speed = atof(spd.substr(0,pos).c_str());
|
|
|
|
*gust = atof(spd.substr(pos+1).c_str());
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2011-10-30 13:48:53 +00:00
|
|
|
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;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-03-17 21:06:43 +00:00
|
|
|
// parse a time string ([+/-]%f[:%f[:%f]]) into hours
|
2001-01-13 22:06:39 +00:00
|
|
|
static double
|
|
|
|
parse_time(const string& time_in) {
|
1998-08-27 17:01:55 +00:00
|
|
|
char *time_str, num[256];
|
1998-04-24 00:49:17 +00:00
|
|
|
double hours, minutes, seconds;
|
|
|
|
double result = 0.0;
|
|
|
|
int sign = 1;
|
|
|
|
int i;
|
|
|
|
|
1998-08-27 17:01:55 +00:00
|
|
|
time_str = (char *)time_in.c_str();
|
|
|
|
|
1998-04-24 00:49:17 +00:00
|
|
|
// printf("parse_time(): %s\n", time_str);
|
|
|
|
|
|
|
|
// check for sign
|
|
|
|
if ( strlen(time_str) ) {
|
|
|
|
if ( time_str[0] == '+' ) {
|
|
|
|
sign = 1;
|
|
|
|
time_str++;
|
|
|
|
} else if ( time_str[0] == '-' ) {
|
|
|
|
sign = -1;
|
|
|
|
time_str++;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
// printf("sign = %d\n", sign);
|
|
|
|
|
|
|
|
// get hours
|
|
|
|
if ( strlen(time_str) ) {
|
|
|
|
i = 0;
|
|
|
|
while ( (time_str[0] != ':') && (time_str[0] != '\0') ) {
|
|
|
|
num[i] = time_str[0];
|
|
|
|
time_str++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if ( time_str[0] == ':' ) {
|
|
|
|
time_str++;
|
|
|
|
}
|
|
|
|
num[i] = '\0';
|
|
|
|
hours = atof(num);
|
|
|
|
// printf("hours = %.2lf\n", hours);
|
|
|
|
|
1998-07-30 23:48:24 +00:00
|
|
|
result += hours;
|
1998-04-24 00:49:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// get minutes
|
|
|
|
if ( strlen(time_str) ) {
|
|
|
|
i = 0;
|
|
|
|
while ( (time_str[0] != ':') && (time_str[0] != '\0') ) {
|
|
|
|
num[i] = time_str[0];
|
|
|
|
time_str++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
if ( time_str[0] == ':' ) {
|
|
|
|
time_str++;
|
|
|
|
}
|
|
|
|
num[i] = '\0';
|
|
|
|
minutes = atof(num);
|
|
|
|
// printf("minutes = %.2lf\n", minutes);
|
|
|
|
|
1998-07-30 23:48:24 +00:00
|
|
|
result += minutes / 60.0;
|
1998-04-24 00:49:17 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// get seconds
|
|
|
|
if ( strlen(time_str) ) {
|
|
|
|
i = 0;
|
|
|
|
while ( (time_str[0] != ':') && (time_str[0] != '\0') ) {
|
|
|
|
num[i] = time_str[0];
|
|
|
|
time_str++;
|
|
|
|
i++;
|
|
|
|
}
|
|
|
|
num[i] = '\0';
|
|
|
|
seconds = atof(num);
|
|
|
|
// printf("seconds = %.2lf\n", seconds);
|
|
|
|
|
1998-07-30 23:48:24 +00:00
|
|
|
result += seconds / 3600.0;
|
1998-04-24 00:49:17 +00:00
|
|
|
}
|
|
|
|
|
2003-03-20 10:43:02 +00:00
|
|
|
SG_LOG( SG_GENERAL, SG_INFO, " parse_time() = " << sign * result );
|
2002-11-16 20:17:11 +00:00
|
|
|
|
1998-04-24 00:49:17 +00:00
|
|
|
return(sign * result);
|
|
|
|
}
|
|
|
|
|
2001-03-17 21:06:43 +00:00
|
|
|
// parse a date string (yyyy:mm:dd:hh:mm:ss) into a time_t (seconds)
|
2010-06-27 07:04:10 +00:00
|
|
|
static long int
|
2011-10-30 13:48:53 +00:00
|
|
|
parse_date( const string& date, const char* timeType)
|
1999-04-11 13:19:29 +00:00
|
|
|
{
|
2011-10-30 13:48:53 +00:00
|
|
|
struct tm gmt,*pCurrentTime;
|
|
|
|
int year,month,day,hour,minute,second;
|
|
|
|
char *argument, *date_str;
|
|
|
|
|
2012-09-23 23:03:23 +00:00
|
|
|
SGTime CurrentTime;
|
|
|
|
CurrentTime.update(SGGeod(),0,0);
|
2011-10-30 13:48:53 +00:00
|
|
|
|
|
|
|
// 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;
|
1999-04-11 13:19:29 +00:00
|
|
|
}
|
2011-10-30 13:48:53 +00:00
|
|
|
|
|
|
|
if (year < 1970)
|
|
|
|
{
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Invalid year '" << year << "'. Use 1970 or later.");
|
|
|
|
return -1;
|
1999-04-11 13:19:29 +00:00
|
|
|
}
|
2011-10-30 13:48:53 +00:00
|
|
|
|
|
|
|
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
|
|
|
|
|
2000-07-07 23:56:43 +00:00
|
|
|
time_t theTime = sgTimeGetGMT( gmt.tm_year, gmt.tm_mon, gmt.tm_mday,
|
2011-10-30 13:48:53 +00:00
|
|
|
gmt.tm_hour, gmt.tm_min, gmt.tm_sec );
|
|
|
|
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO, "Configuring startup time to " << ctime(&theTime));
|
|
|
|
|
1999-04-11 13:19:29 +00:00
|
|
|
return (theTime);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-03-17 21:06:43 +00:00
|
|
|
// parse angle in the form of [+/-]ddd:mm:ss into degrees
|
2001-01-13 22:06:39 +00:00
|
|
|
static double
|
|
|
|
parse_degree( const string& degree_str) {
|
1998-08-27 17:01:55 +00:00
|
|
|
double result = parse_time( degree_str );
|
1998-07-30 23:48:24 +00:00
|
|
|
|
|
|
|
// printf("Degree = %.4f\n", result);
|
|
|
|
|
|
|
|
return(result);
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2001-03-17 21:06:43 +00:00
|
|
|
// parse time offset string into seconds
|
2011-10-30 13:48:53 +00:00
|
|
|
static long int
|
2001-01-13 22:06:39 +00:00
|
|
|
parse_time_offset( const string& time_str) {
|
2011-10-30 13:48:53 +00:00
|
|
|
long int result;
|
1998-04-24 00:49:17 +00:00
|
|
|
|
|
|
|
// printf("time offset = %s\n", time_str);
|
|
|
|
|
1998-04-26 05:01:19 +00:00
|
|
|
#ifdef HAVE_RINT
|
1998-07-30 23:48:24 +00:00
|
|
|
result = (int)rint(parse_time(time_str) * 3600.0);
|
1998-04-26 05:01:19 +00:00
|
|
|
#else
|
1998-07-30 23:48:24 +00:00
|
|
|
result = (int)(parse_time(time_str) * 3600.0);
|
1998-04-26 05:01:19 +00:00
|
|
|
#endif
|
1998-04-24 00:49:17 +00:00
|
|
|
|
1998-07-30 23:48:24 +00:00
|
|
|
// printf("parse_time_offset(): %d\n", result);
|
1998-04-24 00:49:17 +00:00
|
|
|
|
|
|
|
return( result );
|
1998-05-06 03:16:23 +00:00
|
|
|
}
|
|
|
|
|
1998-04-24 00:49:17 +00:00
|
|
|
|
2010-06-27 07:04:10 +00:00
|
|
|
// Parse --fov=x.xx type option
|
2001-01-13 22:06:39 +00:00
|
|
|
static double
|
|
|
|
parse_fov( const string& arg ) {
|
1998-08-27 17:01:55 +00:00
|
|
|
double fov = atof(arg);
|
1998-05-13 18:29:56 +00:00
|
|
|
|
|
|
|
if ( fov < FG_FOV_MIN ) { fov = FG_FOV_MIN; }
|
|
|
|
if ( fov > FG_FOV_MAX ) { fov = FG_FOV_MAX; }
|
|
|
|
|
2007-12-29 21:51:39 +00:00
|
|
|
fgSetDouble("/sim/view[0]/config/default-field-of-view-deg", fov);
|
2000-11-01 23:27:32 +00:00
|
|
|
|
1998-07-30 23:48:24 +00:00
|
|
|
// printf("parse_fov(): result = %.4f\n", fov);
|
1998-05-13 18:29:56 +00:00
|
|
|
|
2000-11-01 23:27:32 +00:00
|
|
|
return fov;
|
1998-05-13 18:29:56 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
1999-11-19 02:10:24 +00:00
|
|
|
// Parse I/O channel option
|
1998-11-16 13:59:58 +00:00
|
|
|
//
|
1999-11-19 02:10:24 +00:00
|
|
|
// Format is "--protocol=medium,direction,hz,medium_options,..."
|
|
|
|
//
|
2009-01-20 17:30:53 +00:00
|
|
|
// protocol = { native, nmea, garmin, AV400, AV400Sim, fgfs, rul, pve, etc. }
|
1999-11-19 02:10:24 +00:00
|
|
|
// medium = { serial, socket, file, etc. }
|
|
|
|
// direction = { in, out, bi }
|
|
|
|
// hz = number of times to process channel per second (floating
|
|
|
|
// point values are ok.
|
|
|
|
//
|
|
|
|
// Serial example "--nmea=serial,dir,hz,device,baud" where
|
2010-06-27 07:04:10 +00:00
|
|
|
//
|
1999-11-19 02:10:24 +00:00
|
|
|
// device = OS device name of serial line to be open()'ed
|
1998-11-16 13:59:58 +00:00
|
|
|
// baud = {300, 1200, 2400, ..., 230400}
|
1999-11-19 02:10:24 +00:00
|
|
|
//
|
2017-11-24 21:28:25 +00:00
|
|
|
// Socket example "--native=socket,dir,hz,machine,port,style" where
|
2003-03-19 20:45:09 +00:00
|
|
|
//
|
1999-11-20 15:40:15 +00:00
|
|
|
// machine = machine name or ip address if client (leave empty if server)
|
1999-11-19 02:10:24 +00:00
|
|
|
// port = port, leave empty to let system choose
|
2000-07-14 00:50:56 +00:00
|
|
|
// style = tcp or udp
|
1999-11-19 02:10:24 +00:00
|
|
|
//
|
|
|
|
// File example "--garmin=file,dir,hz,filename" where
|
2003-03-19 20:45:09 +00:00
|
|
|
//
|
1999-11-19 02:10:24 +00:00
|
|
|
// filename = file system file name
|
1998-11-25 01:33:58 +00:00
|
|
|
|
2003-03-19 20:45:09 +00:00
|
|
|
static bool
|
2001-08-21 21:13:55 +00:00
|
|
|
add_channel( const string& type, const string& channel_str ) {
|
2017-11-24 21:28:25 +00:00
|
|
|
// This check is necessary to prevent fgviewer from segfaulting when given
|
|
|
|
// weird options. (It doesn't run the full initialization)
|
2010-05-22 20:30:03 +00:00
|
|
|
if(!globals->get_channel_options_list())
|
|
|
|
{
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Option " << type << "=" << channel_str
|
|
|
|
<< " ignored.");
|
|
|
|
return false;
|
|
|
|
}
|
2007-05-05 09:16:59 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_INFO, "Channel string = " << channel_str );
|
2001-01-26 00:21:36 +00:00
|
|
|
globals->get_channel_options_list()->push_back( type + "," + channel_str );
|
1998-11-16 13:59:58 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2003-02-21 02:46:44 +00:00
|
|
|
static void
|
|
|
|
clearLocation ()
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/airport-id", "");
|
|
|
|
fgSetString("/sim/presets/vor-id", "");
|
|
|
|
fgSetString("/sim/presets/ndb-id", "");
|
Mathias Frhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
fgSetString("/sim/presets/carrier", "");
|
|
|
|
fgSetString("/sim/presets/parkpos", "");
|
2020-04-17 19:25:18 +00:00
|
|
|
fgSetString("/sim/presets/carrier-position", "");
|
2003-02-21 02:46:44 +00:00
|
|
|
fgSetString("/sim/presets/fix", "");
|
|
|
|
}
|
|
|
|
|
2017-10-11 19:20:19 +00:00
|
|
|
/*
|
2018-02-10 18:18:49 +00:00
|
|
|
Using --addon=/foo/bar does:
|
|
|
|
- register the add-on with the AddonManager (enabling, among other things,
|
|
|
|
add-on-specific resources for simgear::ResourceManager);
|
|
|
|
- load /foo/bar/addon-config.xml into the Global Property Tree;
|
2017-10-11 19:20:19 +00:00
|
|
|
- add /foo/bar to the list of aircraft paths to provide read access:
|
2018-02-10 18:18:49 +00:00
|
|
|
- set various properties related to the add-on under /addons;
|
|
|
|
- load /foo/bar/addon-main.nas into namespace __addon[ADDON_ID]__
|
|
|
|
(see $FG_ROOT/Nasal/addons.nas);
|
|
|
|
- call the main() function defined in that file.
|
2017-10-11 19:20:19 +00:00
|
|
|
|
2018-02-10 18:18:49 +00:00
|
|
|
For more details, see $FG_ROOT/Docs/README.add-ons.
|
2017-10-11 19:20:19 +00:00
|
|
|
*/
|
|
|
|
static int
|
|
|
|
fgOptAddon(const char *arg)
|
|
|
|
{
|
2020-03-12 10:51:49 +00:00
|
|
|
const SGPath addonPath = SGPath::fromUtf8(arg);
|
2017-12-15 22:53:46 +00:00
|
|
|
const auto& addonManager = addons::AddonManager::instance();
|
Improved infrastructure for add-ons: C++ classes, metadata file, Nasal interface
This commit adds C++ classes for add-on management, most notably
AddonManager, Addon and AddonVersion. The AddonManager is used to
register add-ons. It relies on an std::map<std::string, AddonRef> to
hold the metadata of each registered add-on (keys of the std::map are
add-on identifiers, and AddonRef is currently SGSharedPtr<Addon>).
Accessor methods are available for:
- retrieving the list of registered or loaded add-ons (terminology
explained in $FG_ROOT/Docs/README.add-ons);
- checking if a particular add-on has already been registered or
loaded;
- for each add-on, obtaining an Addon instance which can be queried
for its name, id, version, base path, the minimum and maximum
FlightGear versions it requires, its base node in the Property Tree,
its order in the load sequence, short and long description strings,
home page, etc.
The most important metadata is made accessible in the Property Tree
under /addons/by-id/<addon-id> and the property
/addons/by-id/<addon-id>/loaded can be checked or listened to, in
order to determine when a particular add-on is loaded. There is also a
Nasal interface to access add-on metadata in a convenient way.
In order to provide this metadata, each add-on must from now on have in
its base directory a file called 'addon-metadata.xml'.
All this is documented in much more detail in
$FG_ROOT/Docs/README.add-ons.
Mailing-list discussion:
https://sourceforge.net/p/flightgear/mailman/message/36146017/
2017-11-06 12:58:14 +00:00
|
|
|
|
|
|
|
try {
|
|
|
|
addonManager->registerAddon(addonPath);
|
|
|
|
} catch (const sg_exception &e) {
|
|
|
|
string msg = "Error registering an add-on: " + e.getFormattedMessage();
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, msg);
|
2017-10-11 19:20:19 +00:00
|
|
|
flightgear::fatalMessageBoxThenExit(
|
Improved infrastructure for add-ons: C++ classes, metadata file, Nasal interface
This commit adds C++ classes for add-on management, most notably
AddonManager, Addon and AddonVersion. The AddonManager is used to
register add-ons. It relies on an std::map<std::string, AddonRef> to
hold the metadata of each registered add-on (keys of the std::map are
add-on identifiers, and AddonRef is currently SGSharedPtr<Addon>).
Accessor methods are available for:
- retrieving the list of registered or loaded add-ons (terminology
explained in $FG_ROOT/Docs/README.add-ons);
- checking if a particular add-on has already been registered or
loaded;
- for each add-on, obtaining an Addon instance which can be queried
for its name, id, version, base path, the minimum and maximum
FlightGear versions it requires, its base node in the Property Tree,
its order in the load sequence, short and long description strings,
home page, etc.
The most important metadata is made accessible in the Property Tree
under /addons/by-id/<addon-id> and the property
/addons/by-id/<addon-id>/loaded can be checked or listened to, in
order to determine when a particular add-on is loaded. There is also a
Nasal interface to access add-on metadata in a convenient way.
In order to provide this metadata, each add-on must from now on have in
its base directory a file called 'addon-metadata.xml'.
All this is documented in much more detail in
$FG_ROOT/Docs/README.add-ons.
Mailing-list discussion:
https://sourceforge.net/p/flightgear/mailman/message/36146017/
2017-11-06 12:58:14 +00:00
|
|
|
"FlightGear", "Unable to register an add-on.", msg);
|
2017-10-11 19:20:19 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2020-06-30 16:08:29 +00:00
|
|
|
static int
|
|
|
|
fgOptAdditionalDataDir(const char* arg)
|
|
|
|
{
|
|
|
|
const SGPath dataPath = SGPath::fromUtf8(arg);
|
|
|
|
if (!dataPath.exists()) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "--data path not found:'" << dataPath << "'");
|
2020-07-28 07:58:50 +00:00
|
|
|
flightgear::fatalMessageBoxWithoutExit(
|
|
|
|
"FlightGear", "Data path not found: '" + dataPath.utf8Str() + "'.");
|
2020-06-30 16:08:29 +00:00
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
globals->append_data_path(dataPath, false /* = before FG_ROOT */);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-02-21 02:46:44 +00:00
|
|
|
static int
|
|
|
|
fgOptVOR( const char * arg )
|
|
|
|
{
|
|
|
|
clearLocation();
|
|
|
|
fgSetString("/sim/presets/vor-id", arg);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptNDB( const char * arg )
|
|
|
|
{
|
|
|
|
clearLocation();
|
|
|
|
fgSetString("/sim/presets/ndb-id", arg);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
Mathias Frhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
static int
|
|
|
|
fgOptCarrier( const char * arg )
|
|
|
|
{
|
|
|
|
clearLocation();
|
|
|
|
fgSetString("/sim/presets/carrier", arg);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2020-04-17 19:25:18 +00:00
|
|
|
static int
|
|
|
|
fgOptCarrierPos( const char * arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/carrier-position", arg);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
Mathias Frhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
static int
|
|
|
|
fgOptParkpos( const char * arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/parkpos", arg);
|
2018-09-20 09:26:48 +00:00
|
|
|
fgSetBool("/sim/presets/parking-requested", true);
|
Mathias Frhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-02-21 02:46:44 +00:00
|
|
|
static int
|
|
|
|
fgOptFIX( const char * arg )
|
|
|
|
{
|
|
|
|
clearLocation();
|
|
|
|
fgSetString("/sim/presets/fix", arg);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-01-19 23:02:51 +00:00
|
|
|
static int
|
|
|
|
fgOptLon( const char *arg )
|
|
|
|
{
|
2003-02-21 02:46:44 +00:00
|
|
|
clearLocation();
|
2003-01-19 23:02:51 +00:00
|
|
|
fgSetDouble("/sim/presets/longitude-deg", parse_degree( arg ));
|
|
|
|
fgSetDouble("/position/longitude-deg", parse_degree( arg ));
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptLat( const char *arg )
|
|
|
|
{
|
2003-02-21 02:46:44 +00:00
|
|
|
clearLocation();
|
2003-01-19 23:02:51 +00:00
|
|
|
fgSetDouble("/sim/presets/latitude-deg", parse_degree( arg ));
|
|
|
|
fgSetDouble("/position/latitude-deg", parse_degree( arg ));
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptAltitude( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetBool("/sim/presets/onground", false);
|
|
|
|
if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
|
|
|
|
fgSetDouble("/sim/presets/altitude-ft", atof( arg ));
|
|
|
|
else
|
|
|
|
fgSetDouble("/sim/presets/altitude-ft",
|
|
|
|
atof( arg ) * SG_METER_TO_FEET);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptUBody( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/speed-set", "UVW");
|
|
|
|
if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
|
|
|
|
fgSetDouble("/sim/presets/uBody-fps", atof( arg ));
|
|
|
|
else
|
|
|
|
fgSetDouble("/sim/presets/uBody-fps",
|
|
|
|
atof( arg ) * SG_METER_TO_FEET);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptVBody( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/speed-set", "UVW");
|
|
|
|
if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
|
|
|
|
fgSetDouble("/sim/presets/vBody-fps", atof( arg ));
|
|
|
|
else
|
|
|
|
fgSetDouble("/sim/presets/vBody-fps",
|
|
|
|
atof( arg ) * SG_METER_TO_FEET);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptWBody( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/speed-set", "UVW");
|
|
|
|
if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
|
|
|
|
fgSetDouble("/sim/presets/wBody-fps", atof(arg));
|
|
|
|
else
|
|
|
|
fgSetDouble("/sim/presets/wBody-fps",
|
|
|
|
atof(arg) * SG_METER_TO_FEET);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptVNorth( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/speed-set", "NED");
|
|
|
|
if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
|
|
|
|
fgSetDouble("/sim/presets/speed-north-fps", atof( arg ));
|
|
|
|
else
|
|
|
|
fgSetDouble("/sim/presets/speed-north-fps",
|
|
|
|
atof( arg ) * SG_METER_TO_FEET);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptVEast( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/speed-set", "NED");
|
|
|
|
if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
|
|
|
|
fgSetDouble("/sim/presets/speed-east-fps", atof(arg));
|
|
|
|
else
|
|
|
|
fgSetDouble("/sim/presets/speed-east-fps",
|
|
|
|
atof(arg) * SG_METER_TO_FEET);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptVDown( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/speed-set", "NED");
|
|
|
|
if ( !strcmp(fgGetString("/sim/startup/units"), "feet") )
|
|
|
|
fgSetDouble("/sim/presets/speed-down-fps", atof(arg));
|
|
|
|
else
|
|
|
|
fgSetDouble("/sim/presets/speed-down-fps",
|
|
|
|
atof(arg) * SG_METER_TO_FEET);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptVc( const char *arg )
|
|
|
|
{
|
|
|
|
// fgSetString("/sim/presets/speed-set", "knots");
|
|
|
|
// fgSetDouble("/velocities/airspeed-kt", atof(arg.substr(5)));
|
|
|
|
fgSetString("/sim/presets/speed-set", "knots");
|
|
|
|
fgSetDouble("/sim/presets/airspeed-kt", atof(arg));
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptMach( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/speed-set", "mach");
|
|
|
|
fgSetDouble("/sim/presets/mach", atof(arg));
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptRoc( const char *arg )
|
|
|
|
{
|
2012-07-08 09:39:50 +00:00
|
|
|
fgSetDouble("/sim/presets/vertical-speed-fps", atof(arg)/60);
|
2003-01-19 23:02:51 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptFgScenery( const char *arg )
|
|
|
|
{
|
2020-03-12 10:51:49 +00:00
|
|
|
globals->append_fg_scenery(SGPath::pathsFromUtf8(arg));
|
2003-01-19 23:02:51 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2017-06-04 14:49:53 +00:00
|
|
|
static int
|
|
|
|
fgOptEnhancedLighting( const char *arg )
|
|
|
|
{
|
|
|
|
SG_LOG(SG_ALL,SG_ALERT,
|
|
|
|
"the options --enable-enhanced-lighting and "
|
|
|
|
"--disable-enhanced-lighting are no longer supported and will be "
|
|
|
|
"removed in a future FlightGear version! Please do not use them");
|
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
}
|
|
|
|
|
2016-10-09 14:21:09 +00:00
|
|
|
static int
|
|
|
|
fgOptAllowNasalRead( const char *arg )
|
|
|
|
{
|
2020-03-12 10:51:49 +00:00
|
|
|
PathList paths = SGPath::pathsFromUtf8(arg);
|
2016-10-09 14:21:09 +00:00
|
|
|
if(paths.size() == 0) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_WARN, "--allow-nasal-read requires a list of directories to allow");
|
|
|
|
}
|
|
|
|
for( PathList::const_iterator it = paths.begin(); it != paths.end(); ++it ) {
|
|
|
|
globals->append_read_allowed_paths(*it);
|
|
|
|
}
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-01-19 23:02:51 +00:00
|
|
|
static int
|
|
|
|
fgOptFov( const char *arg )
|
|
|
|
{
|
|
|
|
parse_fov( arg );
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptGeometry( const char *arg )
|
|
|
|
{
|
|
|
|
bool geometry_ok = true;
|
|
|
|
int xsize = 0, ysize = 0;
|
|
|
|
string geometry = arg;
|
|
|
|
string::size_type i = geometry.find('x');
|
|
|
|
|
|
|
|
if (i != string::npos) {
|
|
|
|
xsize = atoi(geometry.substr(0, i));
|
|
|
|
ysize = atoi(geometry.substr(i+1));
|
|
|
|
} else {
|
|
|
|
geometry_ok = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( xsize <= 0 || ysize <= 0 ) {
|
|
|
|
xsize = 640;
|
|
|
|
ysize = 480;
|
|
|
|
geometry_ok = false;
|
|
|
|
}
|
|
|
|
|
|
|
|
if ( !geometry_ok ) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Unknown geometry: " << geometry );
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT,
|
|
|
|
"Setting geometry to " << xsize << 'x' << ysize << '\n');
|
|
|
|
} else {
|
|
|
|
SG_LOG( SG_GENERAL, SG_INFO,
|
|
|
|
"Setting geometry to " << xsize << 'x' << ysize << '\n');
|
|
|
|
fgSetInt("/sim/startup/xsize", xsize);
|
|
|
|
fgSetInt("/sim/startup/ysize", ysize);
|
|
|
|
}
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptBpp( const char *arg )
|
|
|
|
{
|
|
|
|
string bits_per_pix = arg;
|
|
|
|
if ( bits_per_pix == "16" ) {
|
|
|
|
fgSetInt("/sim/rendering/bits-per-pixel", 16);
|
|
|
|
} else if ( bits_per_pix == "24" ) {
|
|
|
|
fgSetInt("/sim/rendering/bits-per-pixel", 24);
|
|
|
|
} else if ( bits_per_pix == "32" ) {
|
|
|
|
fgSetInt("/sim/rendering/bits-per-pixel", 32);
|
|
|
|
} else {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Unsupported bpp " << bits_per_pix);
|
|
|
|
}
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptTimeOffset( const char *arg )
|
|
|
|
{
|
2011-10-30 13:48:53 +00:00
|
|
|
fgSetLong("/sim/startup/time-offset",
|
2003-01-19 23:02:51 +00:00
|
|
|
parse_time_offset( arg ));
|
|
|
|
fgSetString("/sim/startup/time-offset-type", "system-offset");
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptStartDateSys( const char *arg )
|
|
|
|
{
|
2011-10-30 13:48:53 +00:00
|
|
|
long int theTime = parse_date( arg, "system" );
|
|
|
|
if (theTime>=0)
|
|
|
|
{
|
|
|
|
fgSetLong("/sim/startup/time-offset", theTime);
|
|
|
|
fgSetString("/sim/startup/time-offset-type", "system");
|
|
|
|
}
|
2003-01-19 23:02:51 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptStartDateLat( const char *arg )
|
|
|
|
{
|
2011-10-30 13:48:53 +00:00
|
|
|
long int theTime = parse_date( arg, "latitude" );
|
|
|
|
if (theTime>=0)
|
|
|
|
{
|
|
|
|
fgSetLong("/sim/startup/time-offset", theTime);
|
|
|
|
fgSetString("/sim/startup/time-offset-type", "latitude");
|
|
|
|
}
|
2003-01-19 23:02:51 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptStartDateGmt( const char *arg )
|
|
|
|
{
|
2011-10-30 13:48:53 +00:00
|
|
|
long int theTime = parse_date( arg, "gmt" );
|
|
|
|
if (theTime>=0)
|
|
|
|
{
|
|
|
|
fgSetLong("/sim/startup/time-offset", theTime);
|
|
|
|
fgSetString("/sim/startup/time-offset-type", "gmt");
|
|
|
|
}
|
2003-01-19 23:02:51 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2014-10-02 09:33:45 +00:00
|
|
|
static int
|
|
|
|
fgOptJpgHttpd( const char * arg )
|
|
|
|
{
|
|
|
|
SG_LOG(SG_ALL,SG_ALERT,
|
|
|
|
"the option --jpg-httpd is no longer supported! Please use --httpd instead."
|
|
|
|
" URL for the screenshot within the new httpd is http://YourFgServer:xxxx/screenshot");
|
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
}
|
|
|
|
|
2014-03-03 20:59:16 +00:00
|
|
|
static int
|
|
|
|
fgOptHttpd( const char * arg )
|
|
|
|
{
|
2014-03-29 21:07:12 +00:00
|
|
|
// port may be any valid address:port notation
|
|
|
|
// like 127.0.0.1:8080
|
|
|
|
// or just the port 8080
|
|
|
|
string port = simgear::strutils::strip(string(arg));
|
|
|
|
if( port.empty() ) return FG_OPTIONS_ERROR;
|
|
|
|
fgSetString( string(flightgear::http::PROPERTY_ROOT).append("/options/listening-port").c_str(), port );
|
2014-03-03 20:59:16 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2004-02-26 12:57:38 +00:00
|
|
|
static int
|
|
|
|
fgSetupProxy( const char *arg )
|
|
|
|
{
|
2011-02-20 16:14:30 +00:00
|
|
|
string options = simgear::strutils::strip( arg );
|
2004-02-26 12:57:38 +00:00
|
|
|
string host, port, auth;
|
2006-12-17 18:32:56 +00:00
|
|
|
string::size_type pos;
|
2004-02-26 12:57:38 +00:00
|
|
|
|
2011-02-20 16:14:30 +00:00
|
|
|
// 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 );
|
|
|
|
|
2004-02-26 12:57:38 +00:00
|
|
|
host = port = auth = "";
|
2010-06-27 07:04:10 +00:00
|
|
|
if ((pos = options.find("@")) != string::npos)
|
2004-02-26 12:57:38 +00:00
|
|
|
auth = options.substr(0, pos++);
|
|
|
|
else
|
|
|
|
pos = 0;
|
|
|
|
|
|
|
|
host = options.substr(pos, options.size());
|
|
|
|
if ((pos = host.find(":")) != string::npos) {
|
|
|
|
port = host.substr(++pos, host.size());
|
|
|
|
host.erase(--pos, host.size());
|
|
|
|
}
|
|
|
|
|
|
|
|
fgSetString("/sim/presets/proxy/host", host.c_str());
|
|
|
|
fgSetString("/sim/presets/proxy/port", port.c_str());
|
|
|
|
fgSetString("/sim/presets/proxy/authentication", auth.c_str());
|
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-01-19 23:02:51 +00:00
|
|
|
static int
|
|
|
|
fgOptTraceRead( const char *arg )
|
|
|
|
{
|
|
|
|
string name = arg;
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO, "Tracing reads for property " << name);
|
|
|
|
fgGetNode(name.c_str(), true)
|
|
|
|
->setAttribute(SGPropertyNode::TRACE_READ, true);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-09-22 09:22:05 +00:00
|
|
|
static int
|
|
|
|
fgOptLogLevel( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/logging/priority", arg);
|
2011-12-11 12:55:56 +00:00
|
|
|
setLoggingPriority(arg);
|
2003-09-22 09:31:36 +00:00
|
|
|
|
2003-09-22 09:22:05 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2011-12-11 12:55:56 +00:00
|
|
|
static int
|
|
|
|
fgOptLogClasses( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/logging/classes", arg);
|
|
|
|
setLoggingClasses (arg);
|
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
2003-09-22 09:22:05 +00:00
|
|
|
|
2017-01-29 15:18:10 +00:00
|
|
|
static int
|
2017-03-11 08:51:16 +00:00
|
|
|
fgOptLogDir(const char* arg)
|
2017-01-29 15:18:10 +00:00
|
|
|
{
|
|
|
|
SGPath dirPath;
|
|
|
|
if (!strcmp(arg, "desktop")) {
|
|
|
|
dirPath = SGPath::desktop();
|
|
|
|
} else {
|
2020-03-12 10:51:49 +00:00
|
|
|
dirPath = SGPath::fromUtf8(arg);
|
2017-01-29 15:18:10 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!dirPath.isDir()) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "cannot find logging location " << dirPath);
|
|
|
|
return FG_OPTIONS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!dirPath.canWrite()) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "cannot write to logging location " << dirPath);
|
|
|
|
return FG_OPTIONS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
// generate the log file name
|
|
|
|
SGPath logFile;
|
|
|
|
{
|
|
|
|
char fileNameBuffer[100];
|
|
|
|
time_t now;
|
|
|
|
time(&now);
|
|
|
|
strftime(fileNameBuffer, 99, "FlightGear_%F", localtime(&now));
|
|
|
|
|
|
|
|
unsigned int logsTodayCount = 0;
|
|
|
|
while (true) {
|
|
|
|
std::ostringstream os;
|
|
|
|
os << fileNameBuffer << "_" << logsTodayCount++ << ".log";
|
|
|
|
logFile = dirPath / os.str();
|
|
|
|
if (!logFile.exists()) {
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
sglog().logToFile(logFile, sglog().get_log_classes(), sglog().get_log_priority());
|
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-01-19 23:02:51 +00:00
|
|
|
static int
|
|
|
|
fgOptTraceWrite( const char *arg )
|
|
|
|
{
|
|
|
|
string name = arg;
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO, "Tracing writes for property " << name);
|
|
|
|
fgGetNode(name.c_str(), true)
|
|
|
|
->setAttribute(SGPropertyNode::TRACE_WRITE, true);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptViewOffset( const char *arg )
|
|
|
|
{
|
|
|
|
// $$$ begin - added VS Renganathan, 14 Oct 2K
|
|
|
|
// for multi-window outside window imagery
|
|
|
|
string woffset = arg;
|
|
|
|
double default_view_offset = 0.0;
|
|
|
|
if ( woffset == "LEFT" ) {
|
|
|
|
default_view_offset = SGD_PI * 0.25;
|
|
|
|
} else if ( woffset == "RIGHT" ) {
|
|
|
|
default_view_offset = SGD_PI * 1.75;
|
|
|
|
} else if ( woffset == "CENTER" ) {
|
|
|
|
default_view_offset = 0.00;
|
|
|
|
} else {
|
|
|
|
default_view_offset = atof( woffset.c_str() ) * SGD_DEGREES_TO_RADIANS;
|
|
|
|
}
|
2010-06-27 07:04:10 +00:00
|
|
|
/* apparently not used (CLO, 11 Jun 2002)
|
2003-01-19 23:02:51 +00:00
|
|
|
FGViewer *pilot_view =
|
|
|
|
(FGViewer *)globals->get_viewmgr()->get_view( 0 ); */
|
|
|
|
// this will work without calls to the viewer...
|
|
|
|
fgSetDouble( "/sim/current-view/heading-offset-deg",
|
|
|
|
default_view_offset * SGD_RADIANS_TO_DEGREES );
|
|
|
|
// $$$ end - added VS Renganathan, 14 Oct 2K
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-06-11 14:18:24 +00:00
|
|
|
static int
|
|
|
|
fgOptVisibilityMeters( const char *arg )
|
|
|
|
{
|
2011-01-19 18:36:04 +00:00
|
|
|
Environment::Presets::VisibilitySingleton::instance()->preset( atof( arg ) );
|
2003-06-11 14:18:24 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-01-19 23:02:51 +00:00
|
|
|
static int
|
|
|
|
fgOptVisibilityMiles( const char *arg )
|
|
|
|
{
|
2011-01-19 18:36:04 +00:00
|
|
|
Environment::Presets::VisibilitySingleton::instance()->preset( atof( arg ) * 5280.0 * SG_FEET_TO_METER );
|
2003-01-19 23:02:51 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2016-03-20 18:56:18 +00:00
|
|
|
static int
|
2017-05-30 15:53:02 +00:00
|
|
|
fgOptMetar(const char *arg)
|
2016-03-20 18:56:18 +00:00
|
|
|
{
|
|
|
|
// The given METAR string cannot be effective without disabling
|
|
|
|
// real weather fetching.
|
|
|
|
fgSetBool("/environment/realwx/enabled", false);
|
|
|
|
// The user-supplied METAR string
|
|
|
|
fgSetString("/environment/metar/data", arg);
|
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
2017-05-30 15:53:02 +00:00
|
|
|
static int
|
|
|
|
fgOptConsole(const char *arg)
|
|
|
|
{
|
|
|
|
static bool already_done = false;
|
|
|
|
if (!already_done)
|
|
|
|
{
|
|
|
|
already_done = true;
|
|
|
|
simgear::requestConsole();
|
|
|
|
}
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
2016-03-20 18:56:18 +00:00
|
|
|
|
2003-01-19 23:02:51 +00:00
|
|
|
static int
|
|
|
|
fgOptRandomWind( const char *arg )
|
|
|
|
{
|
|
|
|
double min_hdg = sg_random() * 360.0;
|
|
|
|
double max_hdg = min_hdg + (20 - sqrt(sg_random() * 400));
|
2003-09-24 00:04:28 +00:00
|
|
|
double speed = sg_random() * sg_random() * 40;
|
2003-01-19 23:02:51 +00:00
|
|
|
double gust = speed + (10 - sqrt(sg_random() * 100));
|
2011-01-19 18:36:04 +00:00
|
|
|
Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
|
2003-01-19 23:02:51 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptWind( const char *arg )
|
|
|
|
{
|
2009-08-24 16:03:49 +00:00
|
|
|
double min_hdg = 0.0, max_hdg = 0.0, speed = 0.0, gust = 0.0;
|
2003-01-19 23:02:51 +00:00
|
|
|
if (!parse_wind( arg, &min_hdg, &max_hdg, &speed, &gust)) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "bad wind value " << arg );
|
|
|
|
return FG_OPTIONS_ERROR;
|
|
|
|
}
|
2011-01-19 18:36:04 +00:00
|
|
|
Environment::Presets::WindSingleton::instance()->preset(min_hdg, max_hdg, speed, gust);
|
2003-01-19 23:02:51 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-06-11 14:18:24 +00:00
|
|
|
static int
|
2003-09-01 22:58:19 +00:00
|
|
|
fgOptTurbulence( const char *arg )
|
2003-06-11 14:18:24 +00:00
|
|
|
{
|
2011-01-19 18:36:04 +00:00
|
|
|
Environment::Presets::TurbulenceSingleton::instance()->preset( atof(arg) );
|
2003-06-11 14:18:24 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-09-01 22:58:19 +00:00
|
|
|
static int
|
|
|
|
fgOptCeiling( const char *arg )
|
|
|
|
{
|
|
|
|
double elevation, thickness;
|
|
|
|
string spec = arg;
|
|
|
|
string::size_type pos = spec.find(':');
|
|
|
|
if (pos == string::npos) {
|
|
|
|
elevation = atof(spec.c_str());
|
|
|
|
thickness = 2000;
|
|
|
|
} else {
|
|
|
|
elevation = atof(spec.substr(0, pos).c_str());
|
|
|
|
thickness = atof(spec.substr(pos + 1).c_str());
|
|
|
|
}
|
2011-01-19 18:36:04 +00:00
|
|
|
Environment::Presets::CeilingSingleton::instance()->preset( elevation, thickness );
|
2003-09-01 22:58:19 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-01-19 23:02:51 +00:00
|
|
|
static int
|
|
|
|
fgOptWp( const char *arg )
|
|
|
|
{
|
2011-05-25 07:21:45 +00:00
|
|
|
string_list *waypoints = globals->get_initial_waypoints();
|
|
|
|
if (!waypoints) {
|
|
|
|
waypoints = new string_list;
|
|
|
|
globals->set_initial_waypoints(waypoints);
|
|
|
|
}
|
|
|
|
waypoints->push_back(arg);
|
2003-01-19 23:02:51 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2003-07-16 23:34:07 +00:00
|
|
|
static bool
|
|
|
|
parse_colon (const string &s, double * val1, double * val2)
|
|
|
|
{
|
|
|
|
string::size_type pos = s.find(':');
|
|
|
|
if (pos == string::npos) {
|
|
|
|
*val2 = atof(s);
|
|
|
|
return false;
|
|
|
|
} else {
|
|
|
|
*val1 = atof(s.substr(0, pos).c_str());
|
|
|
|
*val2 = atof(s.substr(pos+1).c_str());
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-09-10 01:29:19 +00:00
|
|
|
static int
|
|
|
|
fgOptFailure( const char * arg )
|
|
|
|
{
|
|
|
|
string a = arg;
|
|
|
|
if (a == "pitot") {
|
|
|
|
fgSetBool("/systems/pitot/serviceable", false);
|
|
|
|
} else if (a == "static") {
|
|
|
|
fgSetBool("/systems/static/serviceable", false);
|
|
|
|
} else if (a == "vacuum") {
|
|
|
|
fgSetBool("/systems/vacuum/serviceable", false);
|
|
|
|
} else if (a == "electrical") {
|
|
|
|
fgSetBool("/systems/electrical/serviceable", false);
|
|
|
|
} else {
|
|
|
|
SG_LOG(SG_INPUT, SG_ALERT, "Unknown failure mode: " << a);
|
|
|
|
return FG_OPTIONS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2003-07-16 23:34:07 +00:00
|
|
|
static int
|
|
|
|
fgOptNAV1( const char * arg )
|
|
|
|
{
|
|
|
|
double radial, freq;
|
|
|
|
if (parse_colon(arg, &radial, &freq))
|
2004-12-04 21:06:57 +00:00
|
|
|
fgSetDouble("/instrumentation/nav[0]/radials/selected-deg", radial);
|
|
|
|
fgSetDouble("/instrumentation/nav[0]/frequencies/selected-mhz", freq);
|
2003-07-16 23:34:07 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptNAV2( const char * arg )
|
|
|
|
{
|
|
|
|
double radial, freq;
|
|
|
|
if (parse_colon(arg, &radial, &freq))
|
2004-12-04 21:06:57 +00:00
|
|
|
fgSetDouble("/instrumentation/nav[1]/radials/selected-deg", radial);
|
|
|
|
fgSetDouble("/instrumentation/nav[1]/frequencies/selected-mhz", freq);
|
2003-07-16 23:34:07 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2017-06-04 14:49:53 +00:00
|
|
|
static int
|
|
|
|
fgOptADF( const char * arg )
|
|
|
|
{
|
|
|
|
SG_LOG(SG_ALL,SG_ALERT,
|
|
|
|
"the option --adf is no longer supported! Please use --adf1 "
|
|
|
|
"instead.");
|
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
}
|
|
|
|
|
2003-07-16 23:34:07 +00:00
|
|
|
static int
|
2011-04-17 15:35:31 +00:00
|
|
|
fgOptADF1( const char * arg )
|
2003-07-16 23:34:07 +00:00
|
|
|
{
|
|
|
|
double rot, freq;
|
|
|
|
if (parse_colon(arg, &rot, &freq))
|
2011-04-17 15:35:31 +00:00
|
|
|
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);
|
2003-07-16 23:34:07 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptDME( const char *arg )
|
|
|
|
{
|
|
|
|
string opt = arg;
|
|
|
|
if (opt == "nav1") {
|
|
|
|
fgSetInt("/instrumentation/dme/switch-position", 1);
|
|
|
|
fgSetString("/instrumentation/dme/frequencies/source",
|
2004-12-04 21:06:57 +00:00
|
|
|
"/instrumentation/nav[0]/frequencies/selected-mhz");
|
2003-07-16 23:34:07 +00:00
|
|
|
} else if (opt == "nav2") {
|
|
|
|
fgSetInt("/instrumentation/dme/switch-position", 3);
|
|
|
|
fgSetString("/instrumentation/dme/frequencies/source",
|
2004-12-04 21:06:57 +00:00
|
|
|
"/instrumentation/nav[1]/frequencies/selected-mhz");
|
2003-07-16 23:34:07 +00:00
|
|
|
} else {
|
2011-07-10 10:52:45 +00:00
|
|
|
double frequency = atof(arg);
|
|
|
|
if (frequency==0.0)
|
|
|
|
{
|
|
|
|
SG_LOG(SG_INPUT, SG_ALERT, "Invalid DME frequency: '" << arg << "'.");
|
|
|
|
return FG_OPTIONS_ERROR;
|
|
|
|
}
|
2003-07-16 23:34:07 +00:00
|
|
|
fgSetInt("/instrumentation/dme/switch-position", 2);
|
|
|
|
fgSetString("/instrumentation/dme/frequencies/source",
|
|
|
|
"/instrumentation/dme/frequencies/selected-mhz");
|
2011-07-10 10:52:45 +00:00
|
|
|
fgSetDouble("/instrumentation/dme/frequencies/selected-mhz", frequency);
|
2003-07-16 23:34:07 +00:00
|
|
|
}
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2005-10-08 11:55:18 +00:00
|
|
|
static int
|
|
|
|
fgOptLivery( const char *arg )
|
|
|
|
{
|
|
|
|
string opt = arg;
|
|
|
|
string livery_path = "livery/" + opt;
|
|
|
|
fgSetString("/sim/model/texture-path", livery_path.c_str() );
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2006-05-20 12:19:33 +00:00
|
|
|
static int
|
|
|
|
fgOptScenario( const char *arg )
|
|
|
|
{
|
2019-02-03 10:37:01 +00:00
|
|
|
SGPath path(arg);
|
|
|
|
std::string name(arg);
|
|
|
|
if (path.exists()) {
|
|
|
|
if (path.isRelative()) {
|
|
|
|
// make absolute
|
|
|
|
path = simgear::Dir::current().path() / arg;
|
|
|
|
}
|
2020-04-05 22:13:32 +00:00
|
|
|
|
2019-02-03 10:37:01 +00:00
|
|
|
// create description node
|
2020-03-18 16:34:02 +00:00
|
|
|
auto n = FGAIManager::registerScenarioFile(globals->get_props(), path);
|
2019-02-03 10:37:01 +00:00
|
|
|
if (!n) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_WARN, "failed to read scenario file at:" << path);
|
|
|
|
return FG_OPTIONS_ERROR;
|
2006-05-20 12:19:33 +00:00
|
|
|
}
|
2020-04-05 22:13:32 +00:00
|
|
|
|
2019-02-03 10:37:01 +00:00
|
|
|
// also set the /sim/ai/scenario entry so we load it on startup
|
|
|
|
name = path.file_base();
|
2006-05-20 12:19:33 +00:00
|
|
|
}
|
2020-04-05 22:13:32 +00:00
|
|
|
|
2019-02-03 10:37:01 +00:00
|
|
|
// add the 'load it' node
|
|
|
|
SGPropertyNode_ptr ai_node = fgGetNode( "/sim/ai", true );
|
|
|
|
ai_node->addChild("scenario")->setStringValue(name);
|
2020-04-05 22:13:32 +00:00
|
|
|
|
2011-06-26 14:08:19 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2017-03-19 11:54:49 +00:00
|
|
|
static int
|
|
|
|
fgOptAirport( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/airport-id", arg );
|
|
|
|
fgSetBool("/sim/presets/airport-requested", true );
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2006-05-31 07:20:10 +00:00
|
|
|
static int
|
|
|
|
fgOptRunway( const char *arg )
|
|
|
|
{
|
|
|
|
fgSetString("/sim/presets/runway", arg );
|
|
|
|
fgSetBool("/sim/presets/runway-requested", true );
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2010-12-09 15:57:12 +00:00
|
|
|
static int
|
|
|
|
fgOptCallSign(const char * arg)
|
|
|
|
{
|
|
|
|
int i;
|
|
|
|
char callsign[11];
|
|
|
|
strncpy(callsign,arg,10);
|
|
|
|
callsign[10]=0;
|
|
|
|
for (i=0;callsign[i];i++)
|
|
|
|
{
|
2010-12-10 07:28:05 +00:00
|
|
|
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]='-';
|
2010-12-09 15:57:12 +00:00
|
|
|
}
|
|
|
|
fgSetString("sim/multiplay/callsign", callsign );
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
2009-08-10 21:43:55 +00:00
|
|
|
|
2013-10-26 17:29:23 +00:00
|
|
|
static int
|
|
|
|
fgOptIgnoreAutosave(const char* arg)
|
|
|
|
{
|
|
|
|
fgSetBool("/sim/startup/ignore-autosave", true);
|
|
|
|
// don't overwrite autosave on exit
|
|
|
|
fgSetBool("/sim/startup/save-on-exit", false);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
2009-08-10 21:43:55 +00:00
|
|
|
|
2015-11-13 22:01:16 +00:00
|
|
|
static int
|
|
|
|
fgOptEnableFreeze(const char* arg)
|
|
|
|
{
|
|
|
|
fgSetBool("/sim/freeze/master", true);
|
|
|
|
fgSetBool("/sim/freeze/clock", true);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int
|
|
|
|
fgOptDisableFreeze(const char* arg)
|
|
|
|
{
|
|
|
|
fgSetBool("/sim/freeze/master", false);
|
|
|
|
fgSetBool("/sim/freeze/clock", false);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
// 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
|
|
|
|
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;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
string name = arg.substr(0, pos);
|
|
|
|
string value = arg.substr(pos + 1);
|
|
|
|
string type;
|
|
|
|
pos = name.find(':');
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
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);
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
bool writable = n->getAttribute(SGPropertyNode::WRITE);
|
|
|
|
if (!writable)
|
|
|
|
n->setAttribute(SGPropertyNode::WRITE, true);
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
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);
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (!writable)
|
|
|
|
n->setAttribute(SGPropertyNode::WRITE, false);
|
|
|
|
return ret ? FG_OPTIONS_OK : FG_OPTIONS_ERROR;
|
|
|
|
}
|
|
|
|
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
/* If <url> is a URL, return suitable name for downloaded file. */
|
|
|
|
static std::string urlToLocalPath(const char* url)
|
|
|
|
{
|
|
|
|
bool http = simgear::strutils::starts_with(url, "http://");
|
|
|
|
bool https = simgear::strutils::starts_with(url, "https://");
|
|
|
|
if (!http && !https) {
|
|
|
|
return "";
|
|
|
|
}
|
|
|
|
// e.g. http://fg.com/foo/bar/wibble.fgtape
|
|
|
|
const char* s2 = (http) ? url+7 : url+8; // fg.com/foo/bar/wibble.fgtape
|
|
|
|
const char* s3 = strchr(s2, '/'); // /foo/bar/wibble.fgtape
|
|
|
|
const char* s4 = (s3) ? strrchr(s3, '/') : NULL; // /wibble.fgtape
|
2021-02-22 17:12:55 +00:00
|
|
|
std::string path = "url_";
|
|
|
|
if (s3) path += std::string(s2, s3-s2); // url_fg.com
|
|
|
|
path += '_'; // url_fg.com_
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
if (s3 && s4 > s3) {
|
|
|
|
path += simgear::strutils::md5(s3, s4-s3).substr(0, 8);
|
2021-02-22 17:12:55 +00:00
|
|
|
path += '_'; // url_fg.com_12345678_
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
}
|
2021-02-22 17:12:55 +00:00
|
|
|
if (s4) path += (s4+1); // url_fg.com_12345678_wibble.fgtape
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
if (!simgear::strutils::ends_with(path, ".fgtape")) path += ".fgtape";
|
2021-02-22 17:12:55 +00:00
|
|
|
std::string dir = fgGetString("/sim/replay/tape-directory");
|
|
|
|
if (dir != "") {
|
|
|
|
SGPath path2 = dir;
|
|
|
|
path2.append(path);
|
|
|
|
path = path2.str();
|
|
|
|
}
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
return path;
|
|
|
|
}
|
|
|
|
|
2021-02-26 12:39:15 +00:00
|
|
|
// When loading a Continuous recording at startup, we need to override the
|
|
|
|
// aircraft and airport. Unfortunately we can't simply set /sim/aircraft
|
|
|
|
// because there may be --aircraft options later on in the command line. Also
|
|
|
|
// fgMainInit() ends up calling Options::initAircraft() after we have processed
|
|
|
|
// all options, and Options::initAircraft() seems to look directly at the
|
|
|
|
// options again, instead of using /sim/aircraft.
|
|
|
|
//
|
|
|
|
// So we store any aircraft/airport override here, so that
|
|
|
|
// Options::initAircraft() can use them if they are set, instead of going back
|
|
|
|
// to any user-specified aircraft.
|
|
|
|
//
|
|
|
|
static std::string g_load_tape_aircraft;
|
|
|
|
static std::string g_load_tape_airport;
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
|
2014-09-17 20:38:40 +00:00
|
|
|
static int
|
|
|
|
fgOptLoadTape(const char* arg)
|
|
|
|
{
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
// load a flight recorder tape but wait until the fdm is initialized.
|
|
|
|
//
|
|
|
|
struct DelayedTapeLoader : SGPropertyChangeListener {
|
|
|
|
|
2021-02-20 13:24:49 +00:00
|
|
|
DelayedTapeLoader( const char * tape, simgear::HTTP::FileRequestRef filerequest) :
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
_tape(SGPath::fromUtf8(tape)),
|
|
|
|
_filerequest(filerequest)
|
|
|
|
{
|
|
|
|
fgGetNode("/sim/signals/fdm-initialized", true)->addChangeListener( this );
|
|
|
|
}
|
2014-09-17 20:38:40 +00:00
|
|
|
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
virtual ~ DelayedTapeLoader() {}
|
2014-09-17 20:38:40 +00:00
|
|
|
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
virtual void valueChanged(SGPropertyNode * node)
|
|
|
|
{
|
|
|
|
if (!fgGetBool("/sim/signals/fdm-initialized")) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
fgGetNode("/sim/signals/fdm-initialized", true)->removeChangeListener( this );
|
|
|
|
|
|
|
|
// tell the replay subsystem to load the tape
|
|
|
|
FGReplay* replay = globals->get_subsystem<FGReplay>();
|
|
|
|
assert(replay);
|
|
|
|
SGPropertyNode_ptr arg = new SGPropertyNode();
|
|
|
|
arg->setStringValue("tape", _tape.utf8Str() );
|
|
|
|
arg->setBoolValue( "same-aircraft", 0 );
|
|
|
|
if (!replay->loadTape(_tape, false /*preview*/, *arg, _filerequest)) {
|
|
|
|
// Force shutdown if we can't load tape specified on command-line.
|
|
|
|
SG_LOG(SG_GENERAL, SG_POPUP, "Exiting because unable to load fgtape: " << _tape.str());
|
|
|
|
flightgear::modalMessageBox("Exiting because unable to load fgtape", _tape.str(), "");
|
|
|
|
fgOSExit(1);
|
|
|
|
}
|
|
|
|
delete this; // commence suicide
|
|
|
|
}
|
|
|
|
private:
|
|
|
|
SGPath _tape;
|
2021-02-20 13:24:49 +00:00
|
|
|
simgear::HTTP::FileRequestRef _filerequest;
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
SGPropertyNode_ptr properties(new SGPropertyNode);
|
2021-02-20 13:24:49 +00:00
|
|
|
simgear::HTTP::FileRequestRef filerequest;
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
|
|
|
|
std::string path = urlToLocalPath(arg);
|
|
|
|
if (path == "") {
|
|
|
|
// <arg> is a local file.
|
|
|
|
//
|
|
|
|
// Load the recording's header if it is a Continuous recording.
|
|
|
|
//
|
|
|
|
(void) FGReplay::loadContinuousHeader(arg, nullptr /*in*/, properties);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
// <arg> is a URL. Start download.
|
|
|
|
//
|
|
|
|
// Load the recording's header if it is a Continuous recording.
|
|
|
|
//
|
|
|
|
// This is a little messy - we need to create a FGHTTPClient subsystem
|
|
|
|
// in order to do the download, and we call its update() method
|
|
|
|
// directly in order to download at least the header.
|
|
|
|
//
|
|
|
|
const char* url = arg;
|
|
|
|
FGHTTPClient* http = globals->add_new_subsystem<FGHTTPClient>();
|
|
|
|
http->init();
|
2021-02-22 17:12:55 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Replaying url " << url << " using local path: " << path);
|
2021-02-20 13:24:49 +00:00
|
|
|
filerequest.reset(new simgear::HTTP::FileRequest(url, path, true /*append*/));
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
long max_download_speed = fgGetLong("/sim/replay/download-max-bytes-per-sec");
|
|
|
|
if (max_download_speed != 0) {
|
|
|
|
// Can be useful to limite download speed for testing background
|
|
|
|
// download.
|
|
|
|
//
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "Limiting download speed"
|
|
|
|
<< " /sim/replay/download-max-bytes-per-sec=" << max_download_speed
|
|
|
|
);
|
|
|
|
filerequest->setMaxBytesPerSec(max_download_speed);
|
|
|
|
}
|
|
|
|
http->client()->makeRequest(filerequest);
|
|
|
|
SG_LOG(SG_GENERAL, SG_DEBUG, ""
|
|
|
|
<< " filerequest->responseCode()=" << filerequest->responseCode()
|
|
|
|
<< " filerequest->responseReason()=" << filerequest->responseReason()
|
|
|
|
);
|
|
|
|
|
|
|
|
// Load recording header, looping so that we wait for the initial
|
|
|
|
// portion of the recording to be downloaded. We give up after a fixed
|
|
|
|
// timeout.
|
|
|
|
//
|
|
|
|
time_t t0 = time(NULL);
|
|
|
|
for(;;) {
|
|
|
|
// Run http client's update() to download any pending data.
|
|
|
|
http->update(0);
|
|
|
|
|
|
|
|
// Try to load properties from recording header.
|
|
|
|
int e = FGReplay::loadContinuousHeader(path, nullptr /*in*/, properties);
|
|
|
|
if (e == 0) {
|
|
|
|
// Success. We leave <filerequest> active - it will carry
|
|
|
|
// on downloading when the main update loop gets going
|
|
|
|
// later. Hopefully the delay before that happens will not
|
|
|
|
// cause a server timeout.
|
|
|
|
//
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
if (e == -1) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_POPUP, "Not a Continuous recording: url=" << url << " local filename=" << path);
|
|
|
|
// Replay from URL only works with Continuous recordings.
|
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
}
|
|
|
|
|
|
|
|
// If we get here, need to download some more.
|
|
|
|
if (time(NULL) - t0 > 30) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_POPUP, "Timeout while reading downloaded recording from " << url << ". local path=" << path);
|
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
}
|
2021-02-19 10:16:34 +00:00
|
|
|
SGTimeStamp::sleepForMSec(1000);
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// Set aircraft from recording header if we loaded it above; this has to
|
|
|
|
// happen now, before the FDM is initialised. Also set the airport; we
|
|
|
|
// don't actually have to do this because the replay doesn't need terrain
|
|
|
|
// to work, but we might as well load the correct terrain.
|
|
|
|
//
|
|
|
|
std::string aircraft = properties->getStringValue("meta/aircraft-type");
|
|
|
|
std::string airport = properties->getStringValue("meta/closest-airport-id");
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "From recording header: aircraft=" << aircraft << " airport=" << airport);
|
2021-02-26 12:39:15 +00:00
|
|
|
// Override aircraft and airport settings to match what is in the
|
|
|
|
// recording.
|
|
|
|
//
|
|
|
|
g_load_tape_aircraft = aircraft;
|
|
|
|
g_load_tape_airport = airport;
|
2014-09-17 20:38:40 +00:00
|
|
|
|
Allow replay of Continuous recordings if --load-tape is given a URL.
E.g. for --load-tape=http[s]://foo.com/foo/bar/wibble.fgtape, we download in
the background to a file called foo.com_[MD5]_wibble.fgtape, where [MD5] is an
8-character hash of /foo/bar.
We assume any existing file contains valid data and only download any remaining
data (by specifying an http Range header).
Also, when loading/downloading and replaying continuous recordings at startup,
we set the aircraft and airport from the recording.
src/Aircraft/replay.cxx
src/Aircraft/replay.hxx
FGReplay::loadContinuousHeader()
Loads properties from Continuous recording's header, distinguishing
between failure due to incorrect header, or due to a truncated file.
FGReplay::indexContinuousRecording()
Contains Continuous recording indexing code, in a form that can be used
in background while we are downloading.
Added a mutex to protect m_continuous_in_time_to_frameinfo, which can now
be modified in background as Continuous recording is downloaded.
src/Main/fg_init.cxx
src/Main/options.cxx
fgOptLoadTape():
Modified to handle --load-tape=<url>. We start download, and read
the header before returning, so that we can force the FDM to use the
recording's aircraft instead of the user's default. Limit recording
download rate if /sim/replay/download-max-bytes-per-sec is set, by
calling new filerequest->setMaxBytesPerSec().
2021-02-15 15:51:23 +00:00
|
|
|
// Arrange to load the recording after FDM has initialised.
|
|
|
|
new DelayedTapeLoader(path.c_str(), filerequest);
|
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
2014-09-17 20:38:40 +00:00
|
|
|
}
|
|
|
|
|
2020-07-14 11:05:53 +00:00
|
|
|
static int fgOptDisableGUI(const char*)
|
|
|
|
{
|
|
|
|
globals->set_headless(true);
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
2003-01-19 23:02:51 +00:00
|
|
|
|
2003-03-22 14:52:15 +00:00
|
|
|
/*
|
|
|
|
option has_param type property b_param s_param func
|
|
|
|
|
|
|
|
where:
|
|
|
|
option : name of the option
|
|
|
|
has_param : option is --name=value if true or --name if false
|
|
|
|
type : OPTION_BOOL - property is a boolean
|
|
|
|
OPTION_STRING - property is a string
|
|
|
|
OPTION_DOUBLE - property is a double
|
|
|
|
OPTION_INT - property is an integer
|
|
|
|
OPTION_CHANNEL - name of option is the name of a channel
|
|
|
|
OPTION_FUNC - the option trigger a function
|
CompositeViewer: Support for multiple view windows using osgViewer::CompositeViewer.
Overview:
Previously Flightgear always used a single osgViewer::Viewer(), which
inherits from both osgViewer::ViewerBase and osgViewer::View, giving a
single view window.
If CompositeViewer is enabled, we instead use a osgViewer::CompositeViewer
which contains a list of osgViewer::View's. Each of these View's can have
its own eye position, so we can have multiple different views of the same
scene.
Enable at runtime with: --composite-viewer=1
Changes to allow use of osgViewer::CompositeViewer:
Previously FGRenderer had this method:
osgViewer::Viewer* getViewer();
This has been replaced by these two new methods:
osgViewer::ViewerBase* getViewerBase();
osgViewer::View* getView();
If CompositeViewer is not enabled (the default), the actual runtime state
is unchanged, and getViewerBase() and getView() both return a pointer to
the singleton osgViewer::Viewer() object.
If CompositeViewer is enabled, getViewerBase() returns a pointer to a
singleton osgViewer::CompositeViewer object, and getView() returns a
pointer to the first osgViewer::View in the osgViewer::CompositeViewer's
list.
The other significant change to FGRenderer() is the new method:
osg::FrameStamp* getFrameStamp()
If CompositeViewer is not enabled, this simply returns
getView()->getFrameStamp(). If CompositeViewer is enabled it returns
getViewerBase()->getFrameStamp(). It is important that code that previously
called getView()->getFrameStamp() is changed to use the new method, because
when CompositeViewer is enabled individual osgViewer::View's appear to
return an osg::FrameStamp with zero frame number).
All code that uses FGRenderer has been patched up to use the new methods so
that things work as before regardless of whether CompositeViewer is enabled
or not.
We make FGRenderer::update() call SviewUpdate() which updates any extra
views.
Extra view windows:
If CompositeViewer is enabled, one can create top-level extra view windows
by calling SviewCreate(). See src/Viewer/sview.hxx for details.
Each extra view window has its own simgear::compositor::Compositor
instance.
Currently SviewCreate() can create extra view windows that clone the
current view, or view from one point to another (e.g. from one multiplayer
aircraft to the user's aircradt) or keep two aircraft in view, one at a
fixed distance in the foreground.
SviewCreate() can be called from nasal via new nasal commands "view-clone",
"view-last-pair", "view-last-pair-double" and "view-push". Associated
changes to fgdata gives access to these via the View menu. The "view-push"
command tags the current view for later use by "view-last-pair" and
"view-last-pair-double".
Extra view windows created by SviewCreate() use a new view system called
Sview, which allows views to be constructed at runtime instead of being
hard-coded in *-set.xml files. This is work in progress and views aren't
all fully implemented. For example Pilot view gets things slightly wrong
with large roll values, Tower View AGL is not implemented, and we don't
implement damping. See top of src/Viewer/sview.cxx for an overview.
OpenSceneGraph-3.4 issues:
OSG-3.4's event handling seems to be incorrect with CompositeViewer -
events get sent for the wrong window which causes issues with resize and
closing. It doesn't seem to be possible to work around this, so closing
extra view windows can end up closing the main window for example.
OSG-3.6 seems to fix the problems.
We warn if CompositeViewer is enabled and OpenSceneGraph is 3.4.
2020-11-16 18:43:46 +00:00
|
|
|
property :
|
2003-03-22 14:52:15 +00:00
|
|
|
b_param : if type==OPTION_BOOL,
|
|
|
|
value set to the property (has_param is false for boolean)
|
|
|
|
s_param : if type==OPTION_STRING,
|
|
|
|
value set to the property if has_param is false
|
|
|
|
func : function called if type==OPTION_FUNC. if has_param is true,
|
|
|
|
the value is passed to the function as a string, otherwise,
|
2008-12-18 13:08:44 +00:00
|
|
|
s_param is passed.
|
2003-03-22 14:52:15 +00:00
|
|
|
|
|
|
|
For OPTION_DOUBLE and OPTION_INT, the parameter value is converted into a
|
|
|
|
double or an integer and set to the property.
|
|
|
|
|
|
|
|
For OPTION_CHANNEL, add_channel is called with the parameter value as the
|
|
|
|
argument.
|
|
|
|
*/
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
enum OptionType { OPTION_BOOL = 0, OPTION_STRING, OPTION_DOUBLE, OPTION_INT, OPTION_CHANNEL, OPTION_FUNC, OPTION_IGNORE };
|
|
|
|
const int OPTION_MULTI = 1 << 17;
|
|
|
|
|
2003-01-19 23:02:51 +00:00
|
|
|
struct OptionDesc {
|
2008-08-01 15:57:29 +00:00
|
|
|
const char *option;
|
2003-01-19 23:02:51 +00:00
|
|
|
bool has_param;
|
2011-10-16 17:35:40 +00:00
|
|
|
int type;
|
2008-08-01 15:57:29 +00:00
|
|
|
const char *property;
|
2003-01-19 23:02:51 +00:00
|
|
|
bool b_param;
|
2008-08-01 15:57:29 +00:00
|
|
|
const char *s_param;
|
2003-01-19 23:02:51 +00:00
|
|
|
int (*func)( const char * );
|
|
|
|
} fgOptionArray[] = {
|
2010-06-27 07:04:10 +00:00
|
|
|
|
2012-04-18 11:46:26 +00:00
|
|
|
{"language", true, OPTION_IGNORE, "", false, "", 0 },
|
2020-06-14 17:20:28 +00:00
|
|
|
{"console", false, OPTION_FUNC, "", false, "", fgOptConsole },
|
|
|
|
{"launcher", false, OPTION_IGNORE, "", false, "", 0 },
|
2020-08-17 12:10:12 +00:00
|
|
|
{"enable-sentry", false, OPTION_BOOL, "/sim/startup/sentry-crash-reporting-enabled", true, "", nullptr },
|
|
|
|
{"disable-sentry", false, OPTION_BOOL, "/sim/startup/sentry-crash-reporting-enabled", false, "", nullptr },
|
2020-06-14 17:20:28 +00:00
|
|
|
{"allow-nasal-from-sockets", false, OPTION_IGNORE, "", false, "", 0 },
|
2019-02-20 02:09:17 +00:00
|
|
|
{"compositor", true, OPTION_STRING, "/sim/rendering/default-compositor", false, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"disable-splash-screen", false, OPTION_BOOL, "/sim/startup/splash-screen", false, "", 0 },
|
|
|
|
{"enable-splash-screen", false, OPTION_BOOL, "/sim/startup/splash-screen", true, "", 0 },
|
|
|
|
{"disable-mouse-pointer", false, OPTION_STRING, "/sim/startup/mouse-pointer", false, "disabled", 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 },
|
2012-07-15 08:56:59 +00:00
|
|
|
{"disable-random-vegetation", false, OPTION_BOOL, "/sim/rendering/random-vegetation", false, "", 0 },
|
|
|
|
{"enable-random-vegetation", false, OPTION_BOOL, "/sim/rendering/random-vegetation", true, "", 0 },
|
|
|
|
{"disable-random-buildings", false, OPTION_BOOL, "/sim/rendering/random-buildings", false, "", 0 },
|
|
|
|
{"enable-random-buildings", false, OPTION_BOOL, "/sim/rendering/random-buildings", true, "", 0 },
|
2010-09-11 14:11:35 +00:00
|
|
|
{"disable-real-weather-fetch", false, OPTION_BOOL, "/environment/realwx/enabled", false, "", 0 },
|
|
|
|
{"enable-real-weather-fetch", false, OPTION_BOOL, "/environment/realwx/enabled", true, "", 0 },
|
2016-03-20 18:56:18 +00:00
|
|
|
{"metar", true, OPTION_FUNC, "", false, "", fgOptMetar },
|
2004-05-19 13:55:49 +00:00
|
|
|
{"disable-ai-models", false, OPTION_BOOL, "/sim/ai/enabled", false, "", 0 },
|
|
|
|
{"enable-ai-models", false, OPTION_BOOL, "/sim/ai/enabled", true, "", 0 },
|
2011-01-08 20:38:26 +00:00
|
|
|
{"disable-ai-traffic", false, OPTION_BOOL, "/sim/traffic-manager/enabled", false, "", 0 },
|
|
|
|
{"enable-ai-traffic", false, OPTION_BOOL, "/sim/traffic-manager/enabled", true, "", 0 },
|
2015-11-13 22:01:16 +00:00
|
|
|
{"disable-freeze", false, OPTION_FUNC, "", false, "", fgOptDisableFreeze },
|
|
|
|
{"enable-freeze", false, OPTION_FUNC, "", true, "", fgOptEnableFreeze },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"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 },
|
2011-01-16 15:31:39 +00:00
|
|
|
{"disable-hud-3d", false, OPTION_BOOL, "/sim/hud/enable3d[1]", false, "", 0 },
|
|
|
|
{"enable-hud-3d", false, OPTION_BOOL, "/sim/hud/enable3d[1]", true, "", 0 },
|
2006-06-10 07:23:15 +00:00
|
|
|
{"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 },
|
2012-03-13 20:00:22 +00:00
|
|
|
{"disable-auto-coordination", false, OPTION_BOOL, "/controls/flight/auto-coordination", false, "", 0 },
|
|
|
|
{"enable-auto-coordination", false, OPTION_BOOL, "/controls/flight/auto-coordination", true, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"browser-app", true, OPTION_STRING, "/sim/startup/browser-app", false, "", 0 },
|
2011-01-16 15:31:39 +00:00
|
|
|
{"disable-hud", false, OPTION_BOOL, "/sim/hud/visibility[1]", false, "", 0 },
|
|
|
|
{"enable-hud", false, OPTION_BOOL, "/sim/hud/visibility[1]", true, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"disable-panel", false, OPTION_BOOL, "/sim/panel/visibility", false, "", 0 },
|
|
|
|
{"enable-panel", false, OPTION_BOOL, "/sim/panel/visibility", true, "", 0 },
|
2009-11-30 12:05:20 +00:00
|
|
|
{"disable-sound", false, OPTION_BOOL, "/sim/sound/working", false, "", 0 },
|
|
|
|
{"enable-sound", false, OPTION_BOOL, "/sim/sound/working", true, "", 0 },
|
2009-11-28 10:42:52 +00:00
|
|
|
{"sound-device", true, OPTION_STRING, "/sim/sound/device-name", false, "", 0 },
|
2017-03-19 11:54:49 +00:00
|
|
|
{"airport", true, OPTION_FUNC, "", false, "", fgOptAirport },
|
2006-05-31 07:20:10 +00:00
|
|
|
{"runway", true, OPTION_FUNC, "", false, "", fgOptRunway },
|
2003-02-21 02:46:44 +00:00
|
|
|
{"vor", true, OPTION_FUNC, "", false, "", fgOptVOR },
|
2011-07-18 09:09:43 +00:00
|
|
|
{"vor-frequency", true, OPTION_DOUBLE, "/sim/presets/vor-freq", false, "", fgOptVOR },
|
2003-02-21 02:46:44 +00:00
|
|
|
{"ndb", true, OPTION_FUNC, "", false, "", fgOptNDB },
|
2011-07-18 09:09:43 +00:00
|
|
|
{"ndb-frequency", true, OPTION_DOUBLE, "/sim/presets/ndb-freq", false, "", fgOptVOR },
|
Mathias Frhlich:
I have introduced the posibility to start directly on the carrier.
With that patch you will have a --carrrier=id argument where id can either be
the pennant number configured in the nimitz scenario or the carriers name
also configured in the carriers scenario.
Additionaly you can use --parkpos=id to select different positions on the
carrier. They are also configured in the scenario file.
That includes the switch of the whole FGInterface class to make use of the
groundcache.
That means that an aircraft no longer uses the current elevation value from
the scenery class. It rather has its own local cache of the aircrafts
environment which is setup in the common_init method of FGInterface and
updated either manually by calling
FGInterface::get_groundlevel_m(lat, lon, alt_m);
or implicitly by calling the above method in the
FGInterface::_updateGeo*Position(lat, lon, alt);
methods.
A call get_groundlevel_m rebuilds the groundcache if the request is outside
the range of the cache.
Note that for the real usage of the groundcache including the correct
information about the movement of objects and the velocity information, you
still need to set up the groundcache in the usual way like YASim and JSBSim
currently does.
If you use the native interface, you will get only static objects correctly.
But for FDM's only using one single ground level for a whole step this is IMO
sufficient.
The AIManager gets a way to return the location of a object which is placed
wrt an AI Object. At the moment it only honours AICarriers for that.
That method is a static one, which loads the scenario file for that reason and
throws it away afterwards. This looked like the aprioriate way, because the
AIManager is initialized much later in flightgears bootstrap, and I did not
find an easy way to reorder that for my needs. Since this additional load is
very small and does only happen if such a relative location is required, I
think that this is ok.
Note that moving on the carrier will only work correctly for JSBSim and YASim,
but you should now be able to start and move on every not itself moving
object with any FDM.
2005-07-03 09:39:14 +00:00
|
|
|
{"carrier", true, OPTION_FUNC, "", false, "", fgOptCarrier },
|
2020-04-17 19:25:18 +00:00
|
|
|
{"carrier-position", true, OPTION_FUNC, "", false, "", fgOptCarrierPos },
|
2003-02-21 02:46:44 +00:00
|
|
|
{"fix", true, OPTION_FUNC, "", false, "", fgOptFIX },
|
2008-05-01 21:14:02 +00:00
|
|
|
{"offset-distance", true, OPTION_DOUBLE, "/sim/presets/offset-distance-nm", false, "", 0 },
|
|
|
|
{"offset-azimuth", true, OPTION_DOUBLE, "/sim/presets/offset-azimuth-deg", false, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"lon", true, OPTION_FUNC, "", false, "", fgOptLon },
|
|
|
|
{"lat", true, OPTION_FUNC, "", false, "", fgOptLat },
|
|
|
|
{"altitude", true, OPTION_FUNC, "", false, "", fgOptAltitude },
|
|
|
|
{"uBody", true, OPTION_FUNC, "", false, "", fgOptUBody },
|
|
|
|
{"vBody", true, OPTION_FUNC, "", false, "", fgOptVBody },
|
|
|
|
{"wBody", true, OPTION_FUNC, "", false, "", fgOptWBody },
|
|
|
|
{"vNorth", true, OPTION_FUNC, "", false, "", fgOptVNorth },
|
|
|
|
{"vEast", true, OPTION_FUNC, "", false, "", fgOptVEast },
|
|
|
|
{"vDown", true, OPTION_FUNC, "", false, "", fgOptVDown },
|
|
|
|
{"vc", true, OPTION_FUNC, "", false, "", fgOptVc },
|
|
|
|
{"mach", true, OPTION_FUNC, "", false, "", fgOptMach },
|
|
|
|
{"heading", true, OPTION_DOUBLE, "/sim/presets/heading-deg", false, "", 0 },
|
|
|
|
{"roll", true, OPTION_DOUBLE, "/sim/presets/roll-deg", false, "", 0 },
|
|
|
|
{"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 },
|
2020-06-14 17:20:28 +00:00
|
|
|
{"fg-root", true, OPTION_IGNORE, "", false, "", 0 },
|
2011-10-16 17:35:40 +00:00
|
|
|
{"fg-scenery", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptFgScenery },
|
2016-07-30 12:26:45 +00:00
|
|
|
{"terrain-engine", true, OPTION_STRING, "/sim/scenery/engine", false, "tilecache", 0 },
|
|
|
|
{"lod-levels", true, OPTION_STRING, "/sim/scenery/lod-levels", false, "1 3 5 7", 0 },
|
|
|
|
{"lod-res", true, OPTION_STRING, "/sim/scenery/lod-res", false, "1", 0 },
|
|
|
|
{"lod-texturing", true, OPTION_STRING, "/sim/scenery/lod-texturing", false, "bluemarble", 0 },
|
|
|
|
{"lod-range-mult", true, OPTION_STRING, "/sim/scenery/lod-range-mult", false, "2", 0 },
|
2011-10-16 17:35:40 +00:00
|
|
|
{"fg-aircraft", true, OPTION_IGNORE | OPTION_MULTI, "", false, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"fdm", true, OPTION_STRING, "/sim/flight-model", false, "", 0 },
|
|
|
|
{"aero", true, OPTION_STRING, "/sim/aero", false, "", 0 },
|
2020-06-14 17:20:28 +00:00
|
|
|
{"aircraft-dir", true, OPTION_IGNORE, "", false, "", 0 },
|
|
|
|
{"state", true, OPTION_IGNORE, "", false, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"model-hz", true, OPTION_INT, "/sim/model-hz", false, "", 0 },
|
2012-04-01 14:04:12 +00:00
|
|
|
{"max-fps", true, OPTION_DOUBLE, "/sim/frame-rate-throttle-hz", false, "", 0 },
|
2012-11-17 19:01:19 +00:00
|
|
|
{"speed", true, OPTION_DOUBLE, "/sim/speed-up", false, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"trim", false, OPTION_BOOL, "/sim/presets/trim", true, "", 0 },
|
|
|
|
{"notrim", false, OPTION_BOOL, "/sim/presets/trim", false, "", 0 },
|
|
|
|
{"on-ground", false, OPTION_BOOL, "/sim/presets/onground", true, "", 0 },
|
|
|
|
{"in-air", false, OPTION_BOOL, "/sim/presets/onground", false, "", 0 },
|
2020-04-09 14:48:03 +00:00
|
|
|
{"disable-hold-short", false, OPTION_BOOL, "/sim/presets/mp-hold-short-override", true, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"fog-disable", false, OPTION_STRING, "/sim/rendering/fog", false, "disabled", 0 },
|
|
|
|
{"fog-fastest", false, OPTION_STRING, "/sim/rendering/fog", false, "fastest", 0 },
|
2003-06-22 09:45:43 +00:00
|
|
|
{"fog-nicest", false, OPTION_STRING, "/sim/rendering/fog", false, "nicest", 0 },
|
|
|
|
{"disable-horizon-effect", false, OPTION_BOOL, "/sim/rendering/horizon-effect", false, "", 0 },
|
|
|
|
{"enable-horizon-effect", false, OPTION_BOOL, "/sim/rendering/horizon-effect", true, "", 0 },
|
2017-06-04 14:49:53 +00:00
|
|
|
{"disable-enhanced-lighting", false, OPTION_FUNC, "", false, "", fgOptEnhancedLighting },
|
|
|
|
{"enable-enhanced-lighting", false, OPTION_FUNC, "", false, "", fgOptEnhancedLighting },
|
2003-06-22 09:45:43 +00:00
|
|
|
{"disable-distance-attenuation", false, OPTION_BOOL, "/sim/rendering/distance-attenuation", false, "", 0 },
|
|
|
|
{"enable-distance-attenuation", false, OPTION_BOOL, "/sim/rendering/distance-attenuation", true, "", 0 },
|
2003-09-12 21:43:01 +00:00
|
|
|
{"disable-specular-highlight", false, OPTION_BOOL, "/sim/rendering/specular-highlight", false, "", 0 },
|
|
|
|
{"enable-specular-highlight", false, OPTION_BOOL, "/sim/rendering/specular-highlight", true, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"disable-clouds", false, OPTION_BOOL, "/environment/clouds/status", false, "", 0 },
|
|
|
|
{"enable-clouds", false, OPTION_BOOL, "/environment/clouds/status", true, "", 0 },
|
2005-06-25 11:21:18 +00:00
|
|
|
{"disable-clouds3d", false, OPTION_BOOL, "/sim/rendering/clouds3d-enable", false, "", 0 },
|
|
|
|
{"enable-clouds3d", false, OPTION_BOOL, "/sim/rendering/clouds3d-enable", true, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"fov", true, OPTION_FUNC, "", false, "", fgOptFov },
|
2005-02-25 21:20:17 +00:00
|
|
|
{"aspect-ratio-multiplier", true, OPTION_DOUBLE, "/sim/current-view/aspect-ratio-multiplier", false, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"disable-fullscreen", false, OPTION_BOOL, "/sim/startup/fullscreen", false, "", 0 },
|
|
|
|
{"enable-fullscreen", false, OPTION_BOOL, "/sim/startup/fullscreen", true, "", 0 },
|
2005-12-22 13:51:11 +00:00
|
|
|
{"disable-save-on-exit", false, OPTION_BOOL, "/sim/startup/save-on-exit", false, "", 0 },
|
|
|
|
{"enable-save-on-exit", false, OPTION_BOOL, "/sim/startup/save-on-exit", true, "", 0 },
|
2013-11-14 16:48:14 +00:00
|
|
|
{"read-only", false, OPTION_BOOL, "/sim/fghome-readonly", true, "", 0 },
|
2013-10-26 17:29:23 +00:00
|
|
|
{"ignore-autosave", false, OPTION_FUNC, "", false, "", fgOptIgnoreAutosave },
|
2012-07-15 08:56:59 +00:00
|
|
|
{"restore-defaults", false, OPTION_BOOL, "/sim/startup/restore-defaults", true, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"shading-flat", false, OPTION_BOOL, "/sim/rendering/shading", false, "", 0 },
|
|
|
|
{"shading-smooth", false, OPTION_BOOL, "/sim/rendering/shading", true, "", 0 },
|
2011-01-08 20:38:26 +00:00
|
|
|
{"texture-filtering", false, OPTION_INT, "/sim/rendering/filtering", 1, "", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"disable-wireframe", false, OPTION_BOOL, "/sim/rendering/wireframe", false, "", 0 },
|
|
|
|
{"enable-wireframe", false, OPTION_BOOL, "/sim/rendering/wireframe", true, "", 0 },
|
2011-09-02 18:26:55 +00:00
|
|
|
{"materials-file", true, OPTION_STRING, "/sim/rendering/materials-file", false, "", 0 },
|
2011-06-11 22:44:07 +00:00
|
|
|
{"disable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", false, "", 0 },
|
|
|
|
{"enable-terrasync", false, OPTION_BOOL, "/sim/terrasync/enabled", true, "", 0 },
|
2020-06-14 17:20:28 +00:00
|
|
|
{"terrasync-dir", true, OPTION_IGNORE, "", false, "", 0 },
|
|
|
|
{"download-dir", true, OPTION_IGNORE, "", false, "", 0 },
|
|
|
|
{"texture-cache-dir", true, OPTION_IGNORE, "", false, "", 0 },
|
2019-03-01 18:06:42 +00:00
|
|
|
{"enable-texture-cache", false, OPTION_BOOL, "/sim/rendering/texture-cache/cache-enabled", true, "", 0 },
|
|
|
|
{"disable-texture-cache", false, OPTION_BOOL, "/sim/rendering/texture-cache/cache-enabled", false, "", 0 },
|
2016-10-09 14:21:09 +00:00
|
|
|
{"allow-nasal-read", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptAllowNasalRead },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"geometry", true, OPTION_FUNC, "", false, "", fgOptGeometry },
|
|
|
|
{"bpp", true, OPTION_FUNC, "", false, "", fgOptBpp },
|
|
|
|
{"units-feet", false, OPTION_STRING, "/sim/startup/units", false, "feet", 0 },
|
|
|
|
{"units-meters", false, OPTION_STRING, "/sim/startup/units", false, "meters", 0 },
|
2003-09-15 22:55:39 +00:00
|
|
|
{"timeofday", true, OPTION_STRING, "/sim/startup/time-offset-type", false, "noon", 0 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"time-offset", true, OPTION_FUNC, "", false, "", fgOptTimeOffset },
|
|
|
|
{"time-match-real", false, OPTION_STRING, "/sim/startup/time-offset-type", false, "system-offset", 0 },
|
|
|
|
{"time-match-local", false, OPTION_STRING, "/sim/startup/time-offset-type", false, "latitude-offset", 0 },
|
|
|
|
{"start-date-sys", true, OPTION_FUNC, "", false, "", fgOptStartDateSys },
|
|
|
|
{"start-date-lat", true, OPTION_FUNC, "", false, "", fgOptStartDateLat },
|
|
|
|
{"start-date-gmt", true, OPTION_FUNC, "", false, "", fgOptStartDateGmt },
|
|
|
|
{"hud-tris", false, OPTION_STRING, "/sim/hud/frame-stat-type", false, "tris", 0 },
|
|
|
|
{"hud-culled", false, OPTION_STRING, "/sim/hud/frame-stat-type", false, "culled", 0 },
|
2004-11-16 19:47:11 +00:00
|
|
|
{"atcsim", true, OPTION_CHANNEL, "", false, "dummy", 0 },
|
2011-12-11 12:12:18 +00:00
|
|
|
{"atlas", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
|
2014-03-03 20:59:16 +00:00
|
|
|
{"httpd", true, OPTION_FUNC , "", false, "", fgOptHttpd },
|
2014-10-02 09:33:45 +00:00
|
|
|
{"jpg-httpd", true, OPTION_FUNC, "", false, "", fgOptJpgHttpd },
|
2011-10-29 15:34:06 +00:00
|
|
|
{"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 },
|
2017-11-24 21:28:25 +00:00
|
|
|
{"flarm", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
|
2011-10-29 15:34:06 +00:00
|
|
|
{"garmin", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
|
2013-10-14 20:13:31 +00:00
|
|
|
{"igc", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
|
2011-10-29 10:15:45 +00:00
|
|
|
{"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 },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"pve", true, OPTION_CHANNEL, "", false, "", 0 },
|
|
|
|
{"ray", true, OPTION_CHANNEL, "", false, "", 0 },
|
|
|
|
{"rul", true, OPTION_CHANNEL, "", false, "", 0 },
|
|
|
|
{"joyclient", true, OPTION_CHANNEL, "", false, "", 0 },
|
2003-11-12 10:06:18 +00:00
|
|
|
{"jsclient", true, OPTION_CHANNEL, "", false, "", 0 },
|
2004-02-26 12:57:38 +00:00
|
|
|
{"proxy", true, OPTION_FUNC, "", false, "", fgSetupProxy },
|
2010-12-09 15:57:12 +00:00
|
|
|
{"callsign", true, OPTION_FUNC, "", false, "", fgOptCallSign},
|
2011-11-02 11:00:03 +00:00
|
|
|
{"multiplay", true, OPTION_CHANNEL | OPTION_MULTI, "", false, "", 0 },
|
2013-10-20 10:58:20 +00:00
|
|
|
#if FG_HAVE_HLA
|
2010-06-27 07:04:10 +00:00
|
|
|
{"hla", true, OPTION_CHANNEL, "", false, "", 0 },
|
2012-11-21 18:41:51 +00:00
|
|
|
{"hla-local", true, OPTION_CHANNEL, "", false, "", 0 },
|
2010-06-27 07:04:10 +00:00
|
|
|
#endif
|
2011-10-29 15:34:06 +00:00
|
|
|
{"trace-read", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptTraceRead },
|
|
|
|
{"trace-write", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptTraceWrite },
|
2003-09-22 09:22:05 +00:00
|
|
|
{"log-level", true, OPTION_FUNC, "", false, "", fgOptLogLevel },
|
2011-12-11 12:55:56 +00:00
|
|
|
{"log-class", true, OPTION_FUNC, "", false, "", fgOptLogClasses },
|
2017-03-11 08:51:16 +00:00
|
|
|
{"log-dir", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptLogDir },
|
2011-10-16 17:35:40 +00:00
|
|
|
{"view-offset", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptViewOffset },
|
2003-06-11 14:18:24 +00:00
|
|
|
{"visibility", true, OPTION_FUNC, "", false, "", fgOptVisibilityMeters },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"visibility-miles", true, OPTION_FUNC, "", false, "", fgOptVisibilityMiles },
|
|
|
|
{"random-wind", false, OPTION_FUNC, "", false, "", fgOptRandomWind },
|
2011-10-16 17:35:40 +00:00
|
|
|
{"wind", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptWind },
|
2003-06-11 14:18:24 +00:00
|
|
|
{"turbulence", true, OPTION_FUNC, "", false, "", fgOptTurbulence },
|
2003-09-01 22:58:19 +00:00
|
|
|
{"ceiling", true, OPTION_FUNC, "", false, "", fgOptCeiling },
|
2011-10-16 17:35:40 +00:00
|
|
|
{"wp", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptWp },
|
2011-05-25 07:21:45 +00:00
|
|
|
{"flight-plan", true, OPTION_STRING, "/autopilot/route-manager/file-path", false, "", NULL },
|
2016-09-07 22:30:34 +00:00
|
|
|
{"config", true, OPTION_IGNORE | OPTION_MULTI, "", false, "", 0 },
|
2017-10-11 19:20:19 +00:00
|
|
|
{"addon", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptAddon },
|
2020-06-30 16:08:29 +00:00
|
|
|
{"data", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptAdditionalDataDir },
|
2003-01-19 23:02:51 +00:00
|
|
|
{"aircraft", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
|
2007-11-02 18:39:10 +00:00
|
|
|
{"vehicle", true, OPTION_STRING, "/sim/aircraft", false, "", 0 },
|
2011-10-16 17:35:40 +00:00
|
|
|
{"failure", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptFailure },
|
2013-05-19 14:29:49 +00:00
|
|
|
#ifdef ENABLE_IAX
|
|
|
|
{"enable-fgcom", false, OPTION_BOOL, "/sim/fgcom/enabled", true, "", 0 },
|
|
|
|
{"disable-fgcom", false, OPTION_BOOL, "/sim/fgcom/enabled", false, "", 0 },
|
|
|
|
#endif
|
2004-12-04 21:06:57 +00:00
|
|
|
{"com1", true, OPTION_DOUBLE, "/instrumentation/comm[0]/frequencies/selected-mhz", false, "", 0 },
|
|
|
|
{"com2", true, OPTION_DOUBLE, "/instrumentation/comm[1]/frequencies/selected-mhz", false, "", 0 },
|
2003-07-16 23:34:07 +00:00
|
|
|
{"nav1", true, OPTION_FUNC, "", false, "", fgOptNAV1 },
|
|
|
|
{"nav2", true, OPTION_FUNC, "", false, "", fgOptNAV2 },
|
2017-06-04 14:49:53 +00:00
|
|
|
{"adf", /*legacy*/ true, OPTION_FUNC, "", false, "", fgOptADF },
|
2011-04-17 15:35:31 +00:00
|
|
|
{"adf1", true, OPTION_FUNC, "", false, "", fgOptADF1 },
|
|
|
|
{"adf2", true, OPTION_FUNC, "", false, "", fgOptADF2 },
|
2003-07-16 23:34:07 +00:00
|
|
|
{"dme", true, OPTION_FUNC, "", false, "", fgOptDME },
|
2004-10-28 09:37:21 +00:00
|
|
|
{"min-status", true, OPTION_STRING, "/sim/aircraft-min-status", false, "all", 0 },
|
2005-10-08 11:55:18 +00:00
|
|
|
{"livery", true, OPTION_FUNC, "", false, "", fgOptLivery },
|
2011-10-16 17:35:40 +00:00
|
|
|
{"ai-scenario", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptScenario },
|
2018-09-20 09:26:48 +00:00
|
|
|
{"parking-id", true, OPTION_FUNC, "", false, "", fgOptParkpos },
|
2020-04-17 19:25:18 +00:00
|
|
|
{"parkpos", true, OPTION_FUNC, "", false, "", fgOptParkpos },
|
Fix the --version option
1) Paths and the FG_SCENERY paths list are now printed like:
FG_ROOT=/home/flo/flightgear/src/fgdata
instead of:
FG_ROOT=Path "/home/flo/flightgear/src/fgdata"
Moreover, FG_SCENERY is now correctly printed (without 'Path ""'
wrapping, without Terrain, Objects and markers instead of the actual
scenery paths) regardless of the position of --version relatively to
--fg-scenery, --terrasync-dir, etc. Of course, the values given to
these options do influence the output of --version.
Simplify printing of FG_SCENERY via SGPath::join() and use the
correct, OS-dependent separator between paths, SGPath::pathListSep.
Write the --version output to stdout, as already done for --help;
then it can be easily piped to $PAGER and doesn't get mixed with the
output of SG_LOG (which is on stderr). This is of course backward
incompatible for programs reading the stderr output of
'fgfs --version', but has been agreed upon on FlightGear-devel (cf.
<https://sourceforge.net/p/flightgear/mailman/message/35461619/>).
2) Don't write explicit values in the OptionResult enum: they don't
matter, but giving them is error-prone in case one does a copy-paste
to add a new member to the enum and forgets to change the value.
2016-10-30 16:25:06 +00:00
|
|
|
{"version", false, OPTION_IGNORE, "", false, "", 0 },
|
2016-10-31 21:21:52 +00:00
|
|
|
{"json-report", false, OPTION_IGNORE, "", false, "", 0 },
|
|
|
|
{"enable-fpe", false, OPTION_IGNORE, "", false, "", 0},
|
|
|
|
{"fgviewer", false, OPTION_IGNORE, "", false, "", 0},
|
2012-09-29 18:02:03 +00:00
|
|
|
{"no-default-config", false, OPTION_IGNORE, "", false, "", 0},
|
2011-10-16 17:35:40 +00:00
|
|
|
{"prop", true, OPTION_FUNC | OPTION_MULTI, "", false, "", fgOptSetProperty},
|
2014-09-17 20:38:40 +00:00
|
|
|
{"load-tape", true, OPTION_FUNC, "", false, "", fgOptLoadTape },
|
2017-02-27 23:15:07 +00:00
|
|
|
{"developer", true, OPTION_IGNORE | OPTION_BOOL, "", false, "", nullptr },
|
2020-01-25 15:05:30 +00:00
|
|
|
{"jsbsim-output-directive-file", true, OPTION_STRING, "/sim/jsbsim/output-directive-file", false, "", nullptr },
|
2020-07-14 11:05:53 +00:00
|
|
|
{"disable-gui", false, OPTION_FUNC, "", false, "", fgOptDisableGUI },
|
2020-11-14 14:31:18 +00:00
|
|
|
{"graphics-preset", true, OPTION_STRING, "/sim/rendering/preset", false, "", nullptr},
|
CompositeViewer: Support for multiple view windows using osgViewer::CompositeViewer.
Overview:
Previously Flightgear always used a single osgViewer::Viewer(), which
inherits from both osgViewer::ViewerBase and osgViewer::View, giving a
single view window.
If CompositeViewer is enabled, we instead use a osgViewer::CompositeViewer
which contains a list of osgViewer::View's. Each of these View's can have
its own eye position, so we can have multiple different views of the same
scene.
Enable at runtime with: --composite-viewer=1
Changes to allow use of osgViewer::CompositeViewer:
Previously FGRenderer had this method:
osgViewer::Viewer* getViewer();
This has been replaced by these two new methods:
osgViewer::ViewerBase* getViewerBase();
osgViewer::View* getView();
If CompositeViewer is not enabled (the default), the actual runtime state
is unchanged, and getViewerBase() and getView() both return a pointer to
the singleton osgViewer::Viewer() object.
If CompositeViewer is enabled, getViewerBase() returns a pointer to a
singleton osgViewer::CompositeViewer object, and getView() returns a
pointer to the first osgViewer::View in the osgViewer::CompositeViewer's
list.
The other significant change to FGRenderer() is the new method:
osg::FrameStamp* getFrameStamp()
If CompositeViewer is not enabled, this simply returns
getView()->getFrameStamp(). If CompositeViewer is enabled it returns
getViewerBase()->getFrameStamp(). It is important that code that previously
called getView()->getFrameStamp() is changed to use the new method, because
when CompositeViewer is enabled individual osgViewer::View's appear to
return an osg::FrameStamp with zero frame number).
All code that uses FGRenderer has been patched up to use the new methods so
that things work as before regardless of whether CompositeViewer is enabled
or not.
We make FGRenderer::update() call SviewUpdate() which updates any extra
views.
Extra view windows:
If CompositeViewer is enabled, one can create top-level extra view windows
by calling SviewCreate(). See src/Viewer/sview.hxx for details.
Each extra view window has its own simgear::compositor::Compositor
instance.
Currently SviewCreate() can create extra view windows that clone the
current view, or view from one point to another (e.g. from one multiplayer
aircraft to the user's aircradt) or keep two aircraft in view, one at a
fixed distance in the foreground.
SviewCreate() can be called from nasal via new nasal commands "view-clone",
"view-last-pair", "view-last-pair-double" and "view-push". Associated
changes to fgdata gives access to these via the View menu. The "view-push"
command tags the current view for later use by "view-last-pair" and
"view-last-pair-double".
Extra view windows created by SviewCreate() use a new view system called
Sview, which allows views to be constructed at runtime instead of being
hard-coded in *-set.xml files. This is work in progress and views aren't
all fully implemented. For example Pilot view gets things slightly wrong
with large roll values, Tower View AGL is not implemented, and we don't
implement damping. See top of src/Viewer/sview.cxx for an overview.
OpenSceneGraph-3.4 issues:
OSG-3.4's event handling seems to be incorrect with CompositeViewer -
events get sent for the wrong window which causes issues with resize and
closing. It doesn't seem to be possible to work around this, so closing
extra view windows can end up closing the main window for example.
OSG-3.6 seems to fix the problems.
We warn if CompositeViewer is enabled and OpenSceneGraph is 3.4.
2020-11-16 18:43:46 +00:00
|
|
|
{"composite-viewer", true, OPTION_INT, "/sim/rendering/composite-viewer-enabled", "", "", nullptr},
|
2020-06-14 17:20:28 +00:00
|
|
|
{nullptr, false, 0, nullptr, false, nullptr, nullptr}
|
2003-01-19 23:02:51 +00:00
|
|
|
};
|
2003-03-22 14:52:15 +00:00
|
|
|
|
2000-11-28 23:38:24 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
namespace flightgear
|
allow to specify a node type: --prop:[type:]name=value
where type is one of string (default), double, float, long, int, bool
Using only the first letter works, too.
--prop:foo=123 ... sets property foo to string 123 (old behavior)
--prop:string:foo=123 ... verbose version of above
--prop:s:foo=123 ... slightly less verbose version of above
--prop:bool:foo=1 ... makes property a bool of value 'true'
I hope this isn't considered a silly gimmick. I need this often and maybe
other developers do, too. It's useful in cases where the difference really
matters. if (getprop("/foo")), for instance, is TRUE even when the property
contains string "false", as all strings are TRUE.
2007-06-28 11:26:40 +00:00
|
|
|
{
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
/**
|
|
|
|
* internal storage of a value->option binding
|
|
|
|
*/
|
2016-06-23 13:26:34 +00:00
|
|
|
class OptionValue
|
2011-10-16 17:35:40 +00:00
|
|
|
{
|
|
|
|
public:
|
|
|
|
OptionValue(OptionDesc* d, const string& v) :
|
|
|
|
desc(d), value(v)
|
|
|
|
{;}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
OptionDesc* desc;
|
|
|
|
string value;
|
|
|
|
};
|
2007-07-12 14:45:46 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
typedef std::vector<OptionValue> OptionValueVec;
|
|
|
|
typedef std::map<string, OptionDesc*> OptionDescDict;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
class Options::OptionsPrivate
|
|
|
|
{
|
|
|
|
public:
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
OptionValueVec::const_iterator findValue(const string& key) const
|
|
|
|
{
|
|
|
|
OptionValueVec::const_iterator it = values.begin();
|
|
|
|
for (; it != values.end(); ++it) {
|
2011-11-10 09:15:41 +00:00
|
|
|
if (!it->desc) {
|
|
|
|
continue; // ignore markers
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (it->desc->option == key) {
|
|
|
|
return it;
|
|
|
|
}
|
|
|
|
} // of set values iteration
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
return it; // not found
|
|
|
|
}
|
2016-03-24 15:05:03 +00:00
|
|
|
|
|
|
|
OptionValueVec::iterator findValue(const string& key)
|
|
|
|
{
|
|
|
|
OptionValueVec::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
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2016-03-24 15:05:03 +00:00
|
|
|
return it; // not found
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
OptionDesc* findOption(const string& key) const
|
|
|
|
{
|
|
|
|
OptionDescDict::const_iterator it = options.find(key);
|
|
|
|
if (it == options.end()) {
|
|
|
|
return NULL;
|
allow to specify a node type: --prop:[type:]name=value
where type is one of string (default), double, float, long, int, bool
Using only the first letter works, too.
--prop:foo=123 ... sets property foo to string 123 (old behavior)
--prop:string:foo=123 ... verbose version of above
--prop:s:foo=123 ... slightly less verbose version of above
--prop:bool:foo=1 ... makes property a bool of value 'true'
I hope this isn't considered a silly gimmick. I need this often and maybe
other developers do, too. It's useful in cases where the difference really
matters. if (getprop("/foo")), for instance, is TRUE even when the property
contains string "false", as all strings are TRUE.
2007-06-28 11:26:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
return it->second;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
int processOption(OptionDesc* desc, const string& arg_value)
|
|
|
|
{
|
2011-11-10 09:15:41 +00:00
|
|
|
if (!desc) {
|
|
|
|
return FG_OPTIONS_OK; // tolerate marker options
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
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;
|
2003-01-19 23:02:51 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
break;
|
|
|
|
case OPTION_DOUBLE:
|
|
|
|
if ( !arg_value.empty() ) {
|
|
|
|
fgSetDouble( desc->property, atof( arg_value ) );
|
|
|
|
} else {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
|
|
|
|
return FG_OPTIONS_ERROR;
|
2006-03-21 14:43:38 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
break;
|
|
|
|
case OPTION_INT:
|
|
|
|
if ( !arg_value.empty() ) {
|
|
|
|
fgSetInt( desc->property, atoi( arg_value ) );
|
2003-01-19 23:02:51 +00:00
|
|
|
} else {
|
2011-10-16 17:35:40 +00:00
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' needs a parameter" );
|
|
|
|
return FG_OPTIONS_ERROR;
|
2003-01-19 23:02:51 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
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;
|
2003-01-19 23:02:51 +00:00
|
|
|
} else {
|
2011-10-16 17:35:40 +00:00
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
|
|
|
|
return FG_OPTIONS_ERROR;
|
2003-01-19 23:02:51 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
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;
|
|
|
|
}
|
2020-08-29 16:20:40 +00:00
|
|
|
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT, "Option '" << desc->option << "' does not have a parameter" );
|
|
|
|
return FG_OPTIONS_ERROR;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
case OPTION_IGNORE:
|
|
|
|
break;
|
2003-01-19 23:02:51 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
1998-11-06 21:17:31 +00:00
|
|
|
return FG_OPTIONS_OK;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-11-10 09:15:41 +00:00
|
|
|
/**
|
|
|
|
* 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, "-"));
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-11-10 09:15:41 +00:00
|
|
|
/**
|
2015-04-06 09:28:10 +00:00
|
|
|
* given a current iterator into the values, find the preceding group marker,
|
2011-11-10 09:15:41 +00:00
|
|
|
* 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
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-11-10 09:15:41 +00:00
|
|
|
return pos;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2016-10-31 21:21:52 +00:00
|
|
|
// Return a pointer to a new JSON array node
|
|
|
|
// (["/foo/bar", "/other/path", ...]) created from the given PathList.
|
|
|
|
cJSON *createJSONArrayFromPathList(const PathList& pl) const
|
|
|
|
{
|
|
|
|
cJSON *resultNode = cJSON_CreateArray();
|
2017-01-06 12:07:00 +00:00
|
|
|
cJSON *prevNode = nullptr;
|
2016-10-31 21:21:52 +00:00
|
|
|
bool isFirst = true;
|
|
|
|
|
|
|
|
for (const SGPath& path : pl) {
|
|
|
|
cJSON *pathNode = cJSON_CreateString(path.utf8Str().c_str());
|
|
|
|
|
|
|
|
if (isFirst) {
|
|
|
|
isFirst = false;
|
|
|
|
resultNode->child = pathNode;
|
|
|
|
} else {
|
|
|
|
prevNode->next = pathNode;
|
|
|
|
pathNode->prev = prevNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
prevNode = pathNode;
|
|
|
|
}
|
|
|
|
|
|
|
|
return resultNode;
|
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
bool showHelp,
|
|
|
|
verbose,
|
2012-09-29 18:02:03 +00:00
|
|
|
showAircraft,
|
|
|
|
shouldLoadDefaultConfig;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
OptionDescDict options;
|
|
|
|
OptionValueVec values;
|
2016-09-07 22:30:34 +00:00
|
|
|
simgear::PathList configFiles;
|
2011-10-16 17:35:40 +00:00
|
|
|
simgear::PathList propertyFiles;
|
|
|
|
};
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
Options* Options::sharedInstance()
|
|
|
|
{
|
|
|
|
if (shared_instance == NULL) {
|
|
|
|
shared_instance = new Options;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
return shared_instance;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2017-03-27 14:37:54 +00:00
|
|
|
void Options::reset()
|
|
|
|
{
|
|
|
|
if (shared_instance != nullptr) {
|
|
|
|
delete shared_instance;
|
|
|
|
shared_instance = nullptr;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
Options::Options() :
|
|
|
|
p(new OptionsPrivate())
|
|
|
|
{
|
|
|
|
p->showHelp = false;
|
|
|
|
p->verbose = false;
|
|
|
|
p->showAircraft = false;
|
2012-09-29 18:02:03 +00:00
|
|
|
p->shouldLoadDefaultConfig = true;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
// build option map
|
|
|
|
OptionDesc *desc = &fgOptionArray[ 0 ];
|
|
|
|
while ( desc->option != 0 ) {
|
2020-08-22 22:23:55 +00:00
|
|
|
// REVIEW: Memory Leak - 15,768 bytes in 219 blocks are still reachable
|
2011-10-17 16:39:47 +00:00
|
|
|
p->options[ desc->option ] = desc;
|
|
|
|
++desc;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
1998-04-24 00:49:17 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
Options::~Options()
|
|
|
|
{
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2019-01-17 14:52:36 +00:00
|
|
|
OptionResult Options::init(int argc, char** argv, const SGPath& appDataPath)
|
2011-10-16 17:35:40 +00:00
|
|
|
{
|
|
|
|
// first, process the command line
|
|
|
|
bool inOptions = true;
|
|
|
|
for (int i=1; i<argc; ++i) {
|
2020-05-25 13:13:21 +00:00
|
|
|
// important : on first run after the Gatekeeper quarantine flag is
|
|
|
|
// cleared, launchd passes us a null argument here. Avoid dying in
|
|
|
|
// that case.
|
|
|
|
if (!argv[i]) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (inOptions && (argv[i][0] == '-')) {
|
|
|
|
if (strcmp(argv[i], "--") == 0) { // end of options delimiter
|
2016-10-28 22:04:12 +00:00
|
|
|
inOptions = false;
|
2011-10-16 17:35:40 +00:00
|
|
|
continue;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2017-05-15 09:06:47 +00:00
|
|
|
int result = parseOption(argv[i], /* fromConfigFile */ false);
|
2011-10-16 17:35:40 +00:00
|
|
|
processArgResult(result);
|
|
|
|
} else {
|
|
|
|
// XML properties file
|
2020-03-12 10:51:49 +00:00
|
|
|
SGPath f = SGPath::fromUtf8(argv[i]);
|
2011-10-16 17:35:40 +00:00
|
|
|
if (!f.exists()) {
|
2016-06-23 13:26:34 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "config file not found:" << f);
|
2014-03-27 08:05:02 +00:00
|
|
|
} else {
|
|
|
|
p->propertyFiles.push_back(f);
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
} // of arguments iteration
|
2011-11-10 09:15:41 +00:00
|
|
|
p->insertGroupMarker(); // command line is one group
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2012-05-08 20:47:32 +00:00
|
|
|
// establish log-level before anything else - otherwise it is not possible
|
|
|
|
// to show extra (debug/info/warning) messages for the start-up phase.
|
2019-11-07 13:50:25 +00:00
|
|
|
// Leave the simgear logstream default value of SG_ALERT if the argument is
|
|
|
|
// not supplied.
|
|
|
|
if (isOptionSet("log-level"))
|
|
|
|
fgOptLogLevel(valueForOption("log-level").c_str());
|
2012-05-08 20:47:32 +00:00
|
|
|
|
2016-09-07 22:30:34 +00:00
|
|
|
simgear::PathList::const_iterator i;
|
|
|
|
for (i = p->configFiles.begin(); i != p->configFiles.end(); ++i) {
|
|
|
|
readConfig(*i);
|
|
|
|
}
|
|
|
|
|
2012-09-29 18:02:03 +00:00
|
|
|
if (!p->shouldLoadDefaultConfig) {
|
2019-01-17 14:52:36 +00:00
|
|
|
return setupRoot(argc, argv);
|
2012-09-29 18:02:03 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
// then config files
|
|
|
|
SGPath config;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
|
|
|
if( !hostname.empty() ) {
|
2011-10-16 17:35:40 +00:00
|
|
|
// Check for ~/.fgfsrc.hostname
|
2016-06-23 13:26:34 +00:00
|
|
|
config = SGPath::home();
|
2011-10-16 17:35:40 +00:00
|
|
|
config.append(".fgfsrc");
|
|
|
|
config.concat( "." );
|
|
|
|
config.concat( hostname );
|
|
|
|
readConfig(config);
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
// Check for ~/.fgfsrc
|
2016-06-23 13:26:34 +00:00
|
|
|
config = SGPath::home();
|
2011-10-16 17:35:40 +00:00
|
|
|
config.append(".fgfsrc");
|
|
|
|
readConfig(config);
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-21 15:49:15 +00:00
|
|
|
// check for a config file in app data
|
|
|
|
SGPath appDataConfig(appDataPath);
|
|
|
|
appDataConfig.append("fgfsrc");
|
|
|
|
if (appDataConfig.exists()) {
|
|
|
|
readConfig(appDataConfig);
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
// setup FG_ROOT
|
2019-01-17 14:52:36 +00:00
|
|
|
auto res = setupRoot(argc, argv);
|
|
|
|
if (res != FG_OPTIONS_OK) {
|
|
|
|
return res;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2015-03-13 18:14:51 +00:00
|
|
|
// system.fgfsrc is disabled, as we no longer allow anything in fgdata to set
|
|
|
|
// fg-root/fg-home/fg-aircraft and hence control what files Nasal can access
|
2017-04-09 16:38:38 +00:00
|
|
|
std::string nameForError = config.utf8Str();
|
2013-03-22 02:42:22 +00:00
|
|
|
if( ! hostname.empty() ) {
|
2016-06-21 11:29:04 +00:00
|
|
|
config = globals->get_fg_root();
|
2011-10-16 17:35:40 +00:00
|
|
|
config.append( "system.fgfsrc" );
|
|
|
|
config.concat( "." );
|
|
|
|
config.concat( hostname );
|
2015-03-13 18:14:51 +00:00
|
|
|
if (config.exists()) {
|
2017-04-09 16:38:38 +00:00
|
|
|
flightgear::fatalMessageBoxThenExit(
|
|
|
|
"Unsupported configuration",
|
|
|
|
"You have a '" + config.utf8Str() + "' file, which is no longer "
|
|
|
|
"processed for security reasons.",
|
|
|
|
"If you created this file intentionally, please move it to '" +
|
|
|
|
nameForError + "'.");
|
2015-03-13 18:14:51 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
1998-04-24 00:49:17 +00:00
|
|
|
|
2016-06-21 11:29:04 +00:00
|
|
|
config = globals->get_fg_root();
|
2011-10-16 17:35:40 +00:00
|
|
|
config.append( "system.fgfsrc" );
|
2015-03-13 18:14:51 +00:00
|
|
|
if (config.exists()) {
|
2017-04-09 16:38:38 +00:00
|
|
|
flightgear::fatalMessageBoxThenExit(
|
|
|
|
"Unsupported configuration",
|
|
|
|
"You have a '" + config.utf8Str() + "' file, which is no longer "
|
|
|
|
"processed for security reasons.",
|
|
|
|
"If you created this file intentionally, please move it to '" +
|
|
|
|
nameForError + "'.");
|
2015-03-13 18:14:51 +00:00
|
|
|
}
|
2019-01-17 14:52:36 +00:00
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2014-12-26 12:20:51 +00:00
|
|
|
|
|
|
|
void Options::initPaths()
|
2002-01-04 20:58:48 +00:00
|
|
|
{
|
2018-08-10 11:51:51 +00:00
|
|
|
for (const string& pathOpt : valuesForOption("fg-aircraft")) {
|
2020-03-12 10:51:49 +00:00
|
|
|
PathList paths = SGPath::pathsFromUtf8(pathOpt);
|
2014-12-26 12:20:51 +00:00
|
|
|
globals->append_aircraft_paths(paths);
|
|
|
|
}
|
2008-03-15 12:10:44 +00:00
|
|
|
|
2014-12-26 12:20:51 +00:00
|
|
|
const char* envp = ::getenv("FG_AIRCRAFT");
|
|
|
|
if (envp) {
|
2016-06-21 11:29:04 +00:00
|
|
|
globals->append_aircraft_paths(SGPath::pathsFromEnv("FG_AIRCRAFT"));
|
2014-12-26 12:20:51 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
}
|
|
|
|
|
2020-04-29 13:55:37 +00:00
|
|
|
OptionResult Options::initAircraft()
|
2014-12-26 12:20:51 +00:00
|
|
|
{
|
2011-10-16 17:35:40 +00:00
|
|
|
string aircraft;
|
2021-02-26 12:39:15 +00:00
|
|
|
if (g_load_tape_aircraft != "") {
|
|
|
|
// Use Continuous recording's aircraft if we are replaying on startup.
|
|
|
|
aircraft = g_load_tape_aircraft;
|
|
|
|
}
|
|
|
|
else if (isOptionSet("aircraft")) {
|
2011-10-16 17:35:40 +00:00
|
|
|
aircraft = valueForOption("aircraft");
|
|
|
|
} else if (isOptionSet("vehicle")) {
|
|
|
|
aircraft = valueForOption("vehicle");
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (!aircraft.empty()) {
|
2021-01-24 15:01:09 +00:00
|
|
|
fgSetString("/sim/aircraft-id", aircraft);
|
|
|
|
const auto lastDotPos = aircraft.rfind('.');
|
|
|
|
if (lastDotPos != string::npos) {
|
|
|
|
// ensure /sim/aircraft is only the local ID, not the fully-qualified ID
|
|
|
|
// otherwise some existing logic gets confused.
|
|
|
|
fgSetString("/sim/aircraft", aircraft.substr(lastDotPos + 1));
|
|
|
|
} else {
|
|
|
|
fgSetString("/sim/aircraft", aircraft);
|
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "aircraft = " << aircraft );
|
|
|
|
} else {
|
|
|
|
SG_LOG(SG_INPUT, SG_INFO, "No user specified aircraft, using default" );
|
2021-01-24 15:01:09 +00:00
|
|
|
// ensure aircraft-id is valid
|
|
|
|
fgSetString("/sim/aircraft-id", fgGetString("/sim/aircraft"));
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (p->showAircraft) {
|
2016-06-21 11:29:04 +00:00
|
|
|
PathList path_list;
|
2015-04-09 14:31:09 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
fgOptLogLevel( "alert" );
|
2015-04-09 14:31:09 +00:00
|
|
|
|
|
|
|
// First place to check is the 'Aircraft' sub-directory in $FG_ROOT
|
|
|
|
|
2016-06-21 11:29:04 +00:00
|
|
|
SGPath rootAircraft = globals->get_fg_root();
|
|
|
|
rootAircraft.append("Aircraft");
|
|
|
|
path_list.push_back(rootAircraft);
|
2015-04-09 14:31:09 +00:00
|
|
|
|
|
|
|
// Additionally, aircraft may also be found in user-defined places
|
|
|
|
// (via $FG_AIRCRAFT or with the '--fg-aircraft' option)
|
2016-06-21 11:29:04 +00:00
|
|
|
PathList aircraft_paths = globals->get_aircraft_paths();
|
2015-04-09 14:31:09 +00:00
|
|
|
|
2016-06-21 11:29:04 +00:00
|
|
|
path_list.insert(path_list.end(), aircraft_paths.begin(),
|
|
|
|
aircraft_paths.end());
|
2015-04-09 14:31:09 +00:00
|
|
|
|
|
|
|
fgShowAircraft(path_list);
|
2020-04-29 13:55:37 +00:00
|
|
|
// this is to indicate that we did show it
|
|
|
|
return FG_OPTIONS_SHOW_AIRCRAFT;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 20:15:54 +00:00
|
|
|
if (isOptionSet("aircraft-dir")) {
|
2020-03-12 10:51:49 +00:00
|
|
|
SGPath aircraftDirPath = SGPath::fromUtf8(valueForOption("aircraft-dir"));
|
2020-04-29 11:33:25 +00:00
|
|
|
SGPath realAircraftPath = aircraftDirPath.realpath();
|
|
|
|
globals->append_read_allowed_paths(realAircraftPath);
|
Use SGPath::realpath() on the value supplied for --aircraft-dir
* Before setting /sim/aircraft-dir from the --aircraft-dir option,
canonicalize its value with SGPath::realpath() as is already done in
FGGlobals::append_aircraft_path() for the paths given with --fg-aircraft
or via the FG_AIRCRAFT environment variable.
* This fixes a bug when --aircraft-dir is used, due to the fact that
fgValidatePath() canonicalizes its 'path' argument before matching it
against the allowed patterns, and therefore will not validate paths
under the directory specified with --aircraft-dir if this directory has
been given in a non-canonical form by the user (e.g., containing at
least one symlink component).
* This fix does not lower security: the path which is canonicalized has
been explicitely given by the user. This operation is already done for
all paths specified with --fg-aircraft or via the FG_AIRCRAFT
environment variable, via Options::initPaths() which calls
FGGlobals::append_aircraft_paths().
* To reproduce the bug, create a symlink (e.g., /tmp/aircrafts) to a
directory suitable for --fg-aircraft, then run:
fgfs ... --fg-aircraft=/tmp/aircrafts \
--aircraft-dir=/tmp/aircrafts/SenecaII --aircraft=SenecaII
This will trigger many failures such as:
loadxml: reading '/tmp/aircrafts/SenecaII/Dialogs/registration.xml'
denied (unauthorized directory - authorization no longer follows
symlinks; to authorize reading additional directories, add them to
--fg-aircraft)
(from do_load_xml_to_proptree() in flightgear/src/Main/fg_commands.cxx)
I have also tested this with the ec130b4 and the 777-200ER. Same
problem, same fix.
2015-10-06 10:20:54 +00:00
|
|
|
|
|
|
|
// Set this now, so it's available in FindAndCacheAircraft. Use realpath()
|
|
|
|
// as in FGGlobals::append_aircraft_path(), otherwise fgValidatePath()
|
|
|
|
// will deny access to resources under this path if one of its components
|
|
|
|
// is a symlink (which is not a problem, since it was given as is by the
|
|
|
|
// user---this is very different from a symlink *under* the aircraft dir
|
|
|
|
// or a scenery dir).
|
2020-04-29 11:33:25 +00:00
|
|
|
fgSetString("/sim/aircraft-dir", realAircraftPath.utf8Str());
|
2011-10-16 20:15:54 +00:00
|
|
|
}
|
2016-06-17 16:53:35 +00:00
|
|
|
|
|
|
|
if (isOptionSet("state")) {
|
|
|
|
std::string stateName = valueForOption("state");
|
|
|
|
// can't validate this until the -set.xml is parsed
|
|
|
|
fgSetString("/sim/aircraft-state", stateName);
|
|
|
|
}
|
2020-04-29 13:55:37 +00:00
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
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) {
|
2012-09-29 18:02:03 +00:00
|
|
|
p->showAircraft = true;
|
|
|
|
} else if (result == FG_OPTIONS_NO_DEFAULT_CONFIG) {
|
|
|
|
p->shouldLoadDefaultConfig = false;
|
2011-10-16 17:35:40 +00:00
|
|
|
} else if (result == FG_OPTIONS_SHOW_SOUND_DEVICES) {
|
|
|
|
SGSoundMgr smgr;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
smgr.init();
|
|
|
|
string vendor = smgr.get_vendor();
|
|
|
|
string renderer = smgr.get_renderer();
|
|
|
|
cout << renderer << " provided by " << vendor << endl;
|
|
|
|
cout << endl << "No. Device" << endl;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2018-06-02 12:06:36 +00:00
|
|
|
vector <std::string>devices = smgr.get_available_devices();
|
|
|
|
for (vector <std::string>::size_type i=0; i<devices.size(); i++) {
|
2011-10-16 17:35:40 +00:00
|
|
|
cout << i << ". \"" << devices[i] << "\"" << endl;
|
1998-04-24 00:49:17 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
devices.clear();
|
2012-09-21 11:26:33 +00:00
|
|
|
smgr.stop();
|
2011-10-16 17:35:40 +00:00
|
|
|
exit(0);
|
|
|
|
} else if (result == FG_OPTIONS_EXIT) {
|
|
|
|
exit(0);
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
void Options::readConfig(const SGPath& path)
|
|
|
|
{
|
2016-06-23 13:26:34 +00:00
|
|
|
sg_gzifstream in( path );
|
2011-10-16 17:35:40 +00:00
|
|
|
if ( !in.is_open() ) {
|
|
|
|
return;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
|
|
|
SG_LOG( SG_GENERAL, SG_INFO, "Processing config file: " << path );
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
in >> skipcomment;
|
|
|
|
while ( ! in.eof() ) {
|
|
|
|
string line;
|
|
|
|
getline( in, line, '\n' );
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
// 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 );
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2017-05-15 09:06:47 +00:00
|
|
|
if ( parseOption(line, /* fromConfigFile */ true) == FG_OPTIONS_ERROR ) {
|
2016-06-23 13:26:34 +00:00
|
|
|
cerr << endl << "Config file parse error: " << path << " '"
|
2011-10-16 17:35:40 +00:00
|
|
|
<< line << "'" << endl;
|
|
|
|
p->showHelp = true;
|
2002-08-25 22:38:20 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
in >> skipcomment;
|
|
|
|
}
|
2002-08-25 22:38:20 +00:00
|
|
|
|
2011-11-10 09:15:41 +00:00
|
|
|
p->insertGroupMarker(); // each config file is a group
|
1998-04-24 00:49:17 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2017-05-15 09:06:47 +00:00
|
|
|
int Options::parseOption(const string& s, bool fromConfigFile)
|
2011-10-16 17:35:40 +00:00
|
|
|
{
|
|
|
|
if ((s == "--help") || (s=="-h")) {
|
|
|
|
return FG_OPTIONS_HELP;
|
|
|
|
} else if ( (s == "--verbose") || (s == "-v") ) {
|
|
|
|
// verbose help/usage request
|
|
|
|
return FG_OPTIONS_VERBOSE_HELP;
|
2017-05-31 09:38:13 +00:00
|
|
|
} else if ((s == "--console") || (s == "-c")) {
|
|
|
|
fgOptConsole(nullptr);
|
|
|
|
return FG_OPTIONS_OK;
|
2011-10-16 17:35:40 +00:00
|
|
|
} 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);
|
2012-09-29 18:02:03 +00:00
|
|
|
} else if ( s.find( "--no-default-config") == 0) {
|
|
|
|
return FG_OPTIONS_NO_DEFAULT_CONFIG;
|
2011-10-16 17:35:40 +00:00
|
|
|
} 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;
|
2001-05-18 16:09:39 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
p->values.push_back(OptionValue(desc, s.substr(7)));
|
2016-09-07 22:30:34 +00:00
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
} else if ( s.find("--config=") == 0) {
|
|
|
|
SGPath path = s.substr(9);
|
|
|
|
if (path.extension() == "xml") {
|
|
|
|
p->propertyFiles.push_back(path);
|
2017-05-15 09:06:47 +00:00
|
|
|
} else if (fromConfigFile) {
|
|
|
|
flightgear::fatalMessageBoxThenExit(
|
|
|
|
"FlightGear",
|
|
|
|
"Invalid use of the --config option.",
|
|
|
|
"Sorry, it is currently not supported to load a configuration file "
|
|
|
|
"using --config from another configuration file.\n\n"
|
|
|
|
"Note: this does not apply to loading of XML PropertyList files "
|
|
|
|
"with --config.");
|
|
|
|
} else { // the --config option comes from the command line
|
2016-09-07 22:30:34 +00:00
|
|
|
p->configFiles.push_back(path);
|
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
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);
|
1998-05-13 18:29:56 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
return addOption(key, value);
|
|
|
|
} else {
|
2014-01-25 10:36:00 +00:00
|
|
|
flightgear::modalMessageBox("Unknown option", "Unknown command-line option: " + s);
|
2011-10-16 17:35:40 +00:00
|
|
|
return FG_OPTIONS_ERROR;
|
|
|
|
}
|
1998-05-13 18:29:56 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
int Options::addOption(const string &key, const string &value)
|
2001-01-13 22:06:39 +00:00
|
|
|
{
|
2018-08-10 11:51:51 +00:00
|
|
|
if (key == "config") {
|
|
|
|
// occurs when the launcher adds --config options
|
|
|
|
SGPath path(value);
|
|
|
|
if (!path.exists()) {
|
|
|
|
return FG_OPTIONS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (path.extension() == "xml") {
|
|
|
|
p->propertyFiles.push_back(path);
|
|
|
|
} else {
|
|
|
|
p->insertGroupMarker(); // begin a group for the config file
|
|
|
|
readConfig(path);
|
|
|
|
}
|
|
|
|
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
OptionDesc* desc = p->findOption(key);
|
|
|
|
if (!desc) {
|
2014-01-25 10:36:00 +00:00
|
|
|
flightgear::modalMessageBox("Unknown option", "Unknown command-line option: " + key);
|
2011-10-16 17:35:40 +00:00
|
|
|
return FG_OPTIONS_ERROR;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (!(desc->type & OPTION_MULTI)) {
|
|
|
|
OptionValueVec::const_iterator it = p->findValue(key);
|
|
|
|
if (it != p->values.end()) {
|
2014-01-25 10:36:00 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_WARN, "multiple values forbidden for option:" << key << ", ignoring:" << value);
|
2011-10-16 17:35:40 +00:00
|
|
|
return FG_OPTIONS_OK;
|
2002-06-10 23:30:23 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
p->values.push_back(OptionValue(desc, value));
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
2016-03-24 15:05:03 +00:00
|
|
|
|
|
|
|
int Options::setOption(const string &key, const string &value)
|
|
|
|
{
|
|
|
|
OptionDesc* desc = p->findOption(key);
|
|
|
|
if (!desc) {
|
|
|
|
flightgear::modalMessageBox("Unknown option", "Unknown command-line option: " + key);
|
|
|
|
return FG_OPTIONS_ERROR;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!(desc->type & OPTION_MULTI)) {
|
2016-03-24 20:23:39 +00:00
|
|
|
OptionValueVec::iterator it = p->findValue(key);
|
2016-03-24 15:05:03 +00:00
|
|
|
if (it != p->values.end()) {
|
|
|
|
// remove existing valye
|
|
|
|
p->values.erase(it);
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2016-03-24 15:05:03 +00:00
|
|
|
p->values.push_back(OptionValue(desc, value));
|
|
|
|
return FG_OPTIONS_OK;
|
|
|
|
}
|
|
|
|
|
|
|
|
void Options::clearOption(const std::string& key)
|
|
|
|
{
|
|
|
|
OptionValueVec::iterator it = p->findValue(key);
|
|
|
|
for (; it != p->values.end(); it = p->findValue(key)) {
|
|
|
|
p->values.erase(it);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
bool Options::isOptionSet(const string &key) const
|
|
|
|
{
|
|
|
|
OptionValueVec::const_iterator it = p->findValue(key);
|
|
|
|
return (it != p->values.end());
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
string Options::valueForOption(const string& key, const string& defValue) const
|
|
|
|
{
|
|
|
|
OptionValueVec::const_iterator it = p->findValue(key);
|
|
|
|
if (it == p->values.end()) {
|
|
|
|
return defValue;
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
return it->value;
|
|
|
|
}
|
2002-06-10 23:30:23 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
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) {
|
2011-11-10 09:15:41 +00:00
|
|
|
if (!it->desc) {
|
|
|
|
continue; // ignore marker values
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (it->desc->option == key) {
|
|
|
|
result.push_back(it->value);
|
2002-06-10 23:30:23 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
return result;
|
|
|
|
}
|
2012-04-21 07:38:41 +00:00
|
|
|
|
2016-06-23 13:26:34 +00:00
|
|
|
SGPath defaultDownloadDir()
|
2014-01-26 16:05:45 +00:00
|
|
|
{
|
|
|
|
#if defined(SG_WINDOWS)
|
2020-12-02 11:54:04 +00:00
|
|
|
const SGPath p = SGPath::home() / "FlightGear" / "Downloads";
|
|
|
|
return p;
|
2014-01-26 16:05:45 +00:00
|
|
|
#endif
|
2018-06-15 10:10:51 +00:00
|
|
|
return globals->get_fg_home();
|
2014-01-26 16:05:45 +00:00
|
|
|
}
|
|
|
|
|
2018-11-24 19:19:02 +00:00
|
|
|
SGPath defaultTextureCacheDir()
|
|
|
|
{
|
|
|
|
return defaultDownloadDir() / "TextureCache";
|
|
|
|
}
|
|
|
|
|
2013-10-15 21:16:50 +00:00
|
|
|
OptionResult Options::processOptions()
|
2011-10-16 17:35:40 +00:00
|
|
|
{
|
2012-04-21 18:17:42 +00:00
|
|
|
// establish locale before showing help (this selects the default locale,
|
|
|
|
// when no explicit option was set)
|
|
|
|
globals->get_locale()->selectLanguage(valueForOption("language").c_str());
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
// 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();
|
Fix the --version option
1) Paths and the FG_SCENERY paths list are now printed like:
FG_ROOT=/home/flo/flightgear/src/fgdata
instead of:
FG_ROOT=Path "/home/flo/flightgear/src/fgdata"
Moreover, FG_SCENERY is now correctly printed (without 'Path ""'
wrapping, without Terrain, Objects and markers instead of the actual
scenery paths) regardless of the position of --version relatively to
--fg-scenery, --terrasync-dir, etc. Of course, the values given to
these options do influence the output of --version.
Simplify printing of FG_SCENERY via SGPath::join() and use the
correct, OS-dependent separator between paths, SGPath::pathListSep.
Write the --version output to stdout, as already done for --help;
then it can be easily piped to $PAGER and doesn't get mixed with the
output of SG_LOG (which is on stderr). This is of course backward
incompatible for programs reading the stderr output of
'fgfs --version', but has been agreed upon on FlightGear-devel (cf.
<https://sourceforge.net/p/flightgear/mailman/message/35461619/>).
2) Don't write explicit values in the OptionResult enum: they don't
matter, but giving them is error-prone in case one does a copy-paste
to add a new member to the enum and forgets to change the value.
2016-10-30 16:25:06 +00:00
|
|
|
return FG_OPTIONS_EXIT;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-11-10 09:15:41 +00:00
|
|
|
// 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();
|
2012-04-01 14:04:12 +00:00
|
|
|
|
2011-11-10 09:15:41 +00:00
|
|
|
while (groupEnd != p->values.begin()) {
|
|
|
|
OptionValueVec::const_iterator groupBegin = p->rfindGroup(groupEnd);
|
|
|
|
// run over the group in FIFO order
|
|
|
|
OptionValueVec::const_iterator it;
|
2016-06-23 13:26:34 +00:00
|
|
|
for (it = groupBegin; it != groupEnd; ++it) {
|
2011-11-10 09:15:41 +00:00
|
|
|
int result = p->processOption(it->desc, it->value);
|
2012-03-17 11:01:51 +00:00
|
|
|
switch(result)
|
|
|
|
{
|
|
|
|
case FG_OPTIONS_ERROR:
|
|
|
|
showUsage();
|
2013-10-15 21:16:50 +00:00
|
|
|
return FG_OPTIONS_ERROR;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2012-03-17 11:01:51 +00:00
|
|
|
case FG_OPTIONS_EXIT:
|
2013-10-15 21:16:50 +00:00
|
|
|
return FG_OPTIONS_EXIT;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2012-03-17 11:01:51 +00:00
|
|
|
default:
|
|
|
|
break;
|
2011-11-10 09:15:41 +00:00
|
|
|
}
|
2016-01-12 23:33:57 +00:00
|
|
|
if (it->desc) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO, "\toption:" << it->desc->option << " = " << it->value);
|
|
|
|
}
|
2002-06-10 23:30:23 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-11-10 09:15:41 +00:00
|
|
|
groupEnd = groupBegin;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2012-04-01 14:04:12 +00:00
|
|
|
|
2018-08-10 11:51:51 +00:00
|
|
|
for (const SGPath& file : p->propertyFiles) {
|
2011-10-16 17:35:40 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
2016-06-23 13:26:34 +00:00
|
|
|
"Reading command-line property file " << file);
|
|
|
|
readProperties(file, globals->get_props());
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2002-06-10 23:30:23 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
// now options are process, do supplemental fixup
|
|
|
|
const char *envp = ::getenv( "FG_SCENERY" );
|
|
|
|
if (envp) {
|
2016-10-18 21:29:44 +00:00
|
|
|
globals->append_fg_scenery(SGPath::pathsFromEnv("FG_SCENERY"));
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2015-03-17 22:10:49 +00:00
|
|
|
|
Add FGGlobals::get/set_download_dir(), unify TerraSync and download dirs setup
Similar to the existing FGGlobals::get/set_terrasync_dir(), add
FGGlobals::get_download_dir() and FGGlobals::set_download_dir() methods,
and of course the corresponding FGGlobals::download_dir public member
variable. FGGlobals::set_download_dir() stores the realpath() of the
given directory, including into the /sim/paths/download-dir property,
which is marked as read-only just as /sim/terrasync/scenery-dir already
is.
Handle the setup of the TerraSync and download dirs all in the same
place (Options::processOptions()), since most of the work is already
done there. This allows one to get rid of fgOptTerrasyncDir() and
fgOptDownloadDir(), and to make it easier to see that
globals->set_terrasync_dir() (resp. globals->set_download_dir()) is
called on the correct SGPath, regardless of whether --terrasync-dir
(resp. --download-dir) was passed.
Always create the TerraSync and download dirs when they don't already
exist, regardless of whether --terrasync-dir or --download-dir has been
given on the command line.
Add comments explaining how to avoid security pitfalls with download and
TerraSync dirs (cf. discussion around
<https://sourceforge.net/p/flightgear/mailman/message/35461636/>).
Adjust indentation where it was too broken, hampering readbility.
2016-10-31 20:48:32 +00:00
|
|
|
// Download dir fix-up
|
2020-03-12 10:51:49 +00:00
|
|
|
SGPath downloadDir = SGPath::fromUtf8(valueForOption("download-dir"));
|
2016-06-23 13:26:34 +00:00
|
|
|
if (downloadDir.isNull()) {
|
2015-03-17 22:10:49 +00:00
|
|
|
downloadDir = defaultDownloadDir();
|
Add FGGlobals::get/set_download_dir(), unify TerraSync and download dirs setup
Similar to the existing FGGlobals::get/set_terrasync_dir(), add
FGGlobals::get_download_dir() and FGGlobals::set_download_dir() methods,
and of course the corresponding FGGlobals::download_dir public member
variable. FGGlobals::set_download_dir() stores the realpath() of the
given directory, including into the /sim/paths/download-dir property,
which is marked as read-only just as /sim/terrasync/scenery-dir already
is.
Handle the setup of the TerraSync and download dirs all in the same
place (Options::processOptions()), since most of the work is already
done there. This allows one to get rid of fgOptTerrasyncDir() and
fgOptDownloadDir(), and to make it easier to see that
globals->set_terrasync_dir() (resp. globals->set_download_dir()) is
called on the correct SGPath, regardless of whether --terrasync-dir
(resp. --download-dir) was passed.
Always create the TerraSync and download dirs when they don't already
exist, regardless of whether --terrasync-dir or --download-dir has been
given on the command line.
Add comments explaining how to avoid security pitfalls with download and
TerraSync dirs (cf. discussion around
<https://sourceforge.net/p/flightgear/mailman/message/35461636/>).
Adjust indentation where it was too broken, hampering readbility.
2016-10-31 20:48:32 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
|
|
"Using default download dir: " << downloadDir);
|
2015-03-17 22:10:49 +00:00
|
|
|
} else {
|
Add FGGlobals::get/set_download_dir(), unify TerraSync and download dirs setup
Similar to the existing FGGlobals::get/set_terrasync_dir(), add
FGGlobals::get_download_dir() and FGGlobals::set_download_dir() methods,
and of course the corresponding FGGlobals::download_dir public member
variable. FGGlobals::set_download_dir() stores the realpath() of the
given directory, including into the /sim/paths/download-dir property,
which is marked as read-only just as /sim/terrasync/scenery-dir already
is.
Handle the setup of the TerraSync and download dirs all in the same
place (Options::processOptions()), since most of the work is already
done there. This allows one to get rid of fgOptTerrasyncDir() and
fgOptDownloadDir(), and to make it easier to see that
globals->set_terrasync_dir() (resp. globals->set_download_dir()) is
called on the correct SGPath, regardless of whether --terrasync-dir
(resp. --download-dir) was passed.
Always create the TerraSync and download dirs when they don't already
exist, regardless of whether --terrasync-dir or --download-dir has been
given on the command line.
Add comments explaining how to avoid security pitfalls with download and
TerraSync dirs (cf. discussion around
<https://sourceforge.net/p/flightgear/mailman/message/35461636/>).
Adjust indentation where it was too broken, hampering readbility.
2016-10-31 20:48:32 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
|
|
"Using explicit download dir: " << downloadDir);
|
2015-03-17 22:10:49 +00:00
|
|
|
}
|
|
|
|
|
Add FGGlobals::get/set_download_dir(), unify TerraSync and download dirs setup
Similar to the existing FGGlobals::get/set_terrasync_dir(), add
FGGlobals::get_download_dir() and FGGlobals::set_download_dir() methods,
and of course the corresponding FGGlobals::download_dir public member
variable. FGGlobals::set_download_dir() stores the realpath() of the
given directory, including into the /sim/paths/download-dir property,
which is marked as read-only just as /sim/terrasync/scenery-dir already
is.
Handle the setup of the TerraSync and download dirs all in the same
place (Options::processOptions()), since most of the work is already
done there. This allows one to get rid of fgOptTerrasyncDir() and
fgOptDownloadDir(), and to make it easier to see that
globals->set_terrasync_dir() (resp. globals->set_download_dir()) is
called on the correct SGPath, regardless of whether --terrasync-dir
(resp. --download-dir) was passed.
Always create the TerraSync and download dirs when they don't already
exist, regardless of whether --terrasync-dir or --download-dir has been
given on the command line.
Add comments explaining how to avoid security pitfalls with download and
TerraSync dirs (cf. discussion around
<https://sourceforge.net/p/flightgear/mailman/message/35461636/>).
Adjust indentation where it was too broken, hampering readbility.
2016-10-31 20:48:32 +00:00
|
|
|
simgear::Dir d(downloadDir);
|
|
|
|
if (!d.exists()) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
|
|
"Creating download dir: " << downloadDir);
|
|
|
|
d.create(0755);
|
|
|
|
}
|
2014-01-26 16:05:45 +00:00
|
|
|
|
Add FGGlobals::get/set_download_dir(), unify TerraSync and download dirs setup
Similar to the existing FGGlobals::get/set_terrasync_dir(), add
FGGlobals::get_download_dir() and FGGlobals::set_download_dir() methods,
and of course the corresponding FGGlobals::download_dir public member
variable. FGGlobals::set_download_dir() stores the realpath() of the
given directory, including into the /sim/paths/download-dir property,
which is marked as read-only just as /sim/terrasync/scenery-dir already
is.
Handle the setup of the TerraSync and download dirs all in the same
place (Options::processOptions()), since most of the work is already
done there. This allows one to get rid of fgOptTerrasyncDir() and
fgOptDownloadDir(), and to make it easier to see that
globals->set_terrasync_dir() (resp. globals->set_download_dir()) is
called on the correct SGPath, regardless of whether --terrasync-dir
(resp. --download-dir) was passed.
Always create the TerraSync and download dirs when they don't already
exist, regardless of whether --terrasync-dir or --download-dir has been
given on the command line.
Add comments explaining how to avoid security pitfalls with download and
TerraSync dirs (cf. discussion around
<https://sourceforge.net/p/flightgear/mailman/message/35461636/>).
Adjust indentation where it was too broken, hampering readbility.
2016-10-31 20:48:32 +00:00
|
|
|
// This is safe because the value of 'downloadDir' is trustworthy. In
|
|
|
|
// particular, it can't be influenced by Nasal code, not even indirectly
|
|
|
|
// via a Nasal-writable place such as the property tree.
|
|
|
|
globals->set_download_dir(downloadDir);
|
|
|
|
|
2018-11-24 19:19:02 +00:00
|
|
|
// Texture Cache directory handling
|
2020-03-12 10:51:49 +00:00
|
|
|
SGPath textureCacheDir = SGPath::fromUtf8(valueForOption("texture-cache-dir"));
|
2018-11-24 19:19:02 +00:00
|
|
|
if (textureCacheDir.isNull()) {
|
|
|
|
textureCacheDir = defaultTextureCacheDir();
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
|
|
"Using default texture cache directory: " << textureCacheDir);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
|
|
"Using explicit texture cache directory: " << textureCacheDir);
|
|
|
|
}
|
|
|
|
|
|
|
|
simgear::Dir tcd(textureCacheDir);
|
|
|
|
if (!tcd.exists()) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
|
|
"Creating texture cache directory: " << textureCacheDir);
|
|
|
|
tcd.create(0755);
|
|
|
|
}
|
|
|
|
|
|
|
|
globals->set_texture_cache_dir(textureCacheDir);
|
|
|
|
|
|
|
|
|
Add FGGlobals::get/set_download_dir(), unify TerraSync and download dirs setup
Similar to the existing FGGlobals::get/set_terrasync_dir(), add
FGGlobals::get_download_dir() and FGGlobals::set_download_dir() methods,
and of course the corresponding FGGlobals::download_dir public member
variable. FGGlobals::set_download_dir() stores the realpath() of the
given directory, including into the /sim/paths/download-dir property,
which is marked as read-only just as /sim/terrasync/scenery-dir already
is.
Handle the setup of the TerraSync and download dirs all in the same
place (Options::processOptions()), since most of the work is already
done there. This allows one to get rid of fgOptTerrasyncDir() and
fgOptDownloadDir(), and to make it easier to see that
globals->set_terrasync_dir() (resp. globals->set_download_dir()) is
called on the correct SGPath, regardless of whether --terrasync-dir
(resp. --download-dir) was passed.
Always create the TerraSync and download dirs when they don't already
exist, regardless of whether --terrasync-dir or --download-dir has been
given on the command line.
Add comments explaining how to avoid security pitfalls with download and
TerraSync dirs (cf. discussion around
<https://sourceforge.net/p/flightgear/mailman/message/35461636/>).
Adjust indentation where it was too broken, hampering readbility.
2016-10-31 20:48:32 +00:00
|
|
|
// TerraSync directory fixup
|
2020-03-12 10:51:49 +00:00
|
|
|
SGPath terrasyncDir = SGPath::fromUtf8(valueForOption("terrasync-dir"));
|
Add FGGlobals::get/set_download_dir(), unify TerraSync and download dirs setup
Similar to the existing FGGlobals::get/set_terrasync_dir(), add
FGGlobals::get_download_dir() and FGGlobals::set_download_dir() methods,
and of course the corresponding FGGlobals::download_dir public member
variable. FGGlobals::set_download_dir() stores the realpath() of the
given directory, including into the /sim/paths/download-dir property,
which is marked as read-only just as /sim/terrasync/scenery-dir already
is.
Handle the setup of the TerraSync and download dirs all in the same
place (Options::processOptions()), since most of the work is already
done there. This allows one to get rid of fgOptTerrasyncDir() and
fgOptDownloadDir(), and to make it easier to see that
globals->set_terrasync_dir() (resp. globals->set_download_dir()) is
called on the correct SGPath, regardless of whether --terrasync-dir
(resp. --download-dir) was passed.
Always create the TerraSync and download dirs when they don't already
exist, regardless of whether --terrasync-dir or --download-dir has been
given on the command line.
Add comments explaining how to avoid security pitfalls with download and
TerraSync dirs (cf. discussion around
<https://sourceforge.net/p/flightgear/mailman/message/35461636/>).
Adjust indentation where it was too broken, hampering readbility.
2016-10-31 20:48:32 +00:00
|
|
|
if (terrasyncDir.isNull()) {
|
|
|
|
terrasyncDir = downloadDir / "TerraSync";
|
|
|
|
// No “default” qualifier here, because 'downloadDir' may be non-default
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
|
|
"Using TerraSync dir: " << terrasyncDir);
|
|
|
|
} else {
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
|
|
"Using explicit TerraSync dir: " << terrasyncDir);
|
|
|
|
}
|
2014-01-26 16:05:45 +00:00
|
|
|
|
Add FGGlobals::get/set_download_dir(), unify TerraSync and download dirs setup
Similar to the existing FGGlobals::get/set_terrasync_dir(), add
FGGlobals::get_download_dir() and FGGlobals::set_download_dir() methods,
and of course the corresponding FGGlobals::download_dir public member
variable. FGGlobals::set_download_dir() stores the realpath() of the
given directory, including into the /sim/paths/download-dir property,
which is marked as read-only just as /sim/terrasync/scenery-dir already
is.
Handle the setup of the TerraSync and download dirs all in the same
place (Options::processOptions()), since most of the work is already
done there. This allows one to get rid of fgOptTerrasyncDir() and
fgOptDownloadDir(), and to make it easier to see that
globals->set_terrasync_dir() (resp. globals->set_download_dir()) is
called on the correct SGPath, regardless of whether --terrasync-dir
(resp. --download-dir) was passed.
Always create the TerraSync and download dirs when they don't already
exist, regardless of whether --terrasync-dir or --download-dir has been
given on the command line.
Add comments explaining how to avoid security pitfalls with download and
TerraSync dirs (cf. discussion around
<https://sourceforge.net/p/flightgear/mailman/message/35461636/>).
Adjust indentation where it was too broken, hampering readbility.
2016-10-31 20:48:32 +00:00
|
|
|
d = simgear::Dir(terrasyncDir);
|
|
|
|
if (!d.exists()) {
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO,
|
|
|
|
"Creating TerraSync dir: " << terrasyncDir);
|
|
|
|
d.create(0755);
|
|
|
|
}
|
|
|
|
|
|
|
|
// This is safe because the value of 'terrasyncDir' is trustworthy. In
|
|
|
|
// particular, it can't be influenced by Nasal code, not even indirectly
|
|
|
|
// via a Nasal-writable place such as the property tree.
|
|
|
|
globals->set_terrasync_dir(terrasyncDir);
|
2015-01-04 17:50:10 +00:00
|
|
|
|
2015-03-17 22:10:49 +00:00
|
|
|
// check if we setup a scenery path so far
|
|
|
|
bool addFGDataScenery = globals->get_fg_scenery().empty();
|
2015-01-04 17:50:10 +00:00
|
|
|
|
2015-03-17 22:10:49 +00:00
|
|
|
// always add the terrasync location, regardless of whether terrasync
|
|
|
|
// is enabled or not. This allows us to toggle terrasync on/off at
|
|
|
|
// runtime and have things work as expected
|
2016-06-21 11:29:04 +00:00
|
|
|
const PathList& scenery_paths(globals->get_fg_scenery());
|
2016-06-23 13:26:34 +00:00
|
|
|
if (std::find(scenery_paths.begin(), scenery_paths.end(), terrasyncDir) == scenery_paths.end()) {
|
2015-03-17 22:10:49 +00:00
|
|
|
// terrasync dir is not in the scenery paths, add it
|
2016-06-23 13:26:34 +00:00
|
|
|
globals->append_fg_scenery(terrasyncDir);
|
2011-10-16 20:46:27 +00:00
|
|
|
}
|
2015-03-17 22:10:49 +00:00
|
|
|
|
|
|
|
if (addFGDataScenery) {
|
|
|
|
// no scenery paths set at all, use the data in FG_ROOT
|
|
|
|
// ensure this path is added last
|
|
|
|
SGPath root(globals->get_fg_root());
|
|
|
|
root.append("Scenery");
|
2016-06-21 11:29:04 +00:00
|
|
|
globals->append_fg_scenery(root);
|
2015-03-17 22:10:49 +00:00
|
|
|
}
|
|
|
|
|
2021-02-26 12:39:15 +00:00
|
|
|
if (g_load_tape_aircraft != "") {
|
|
|
|
// This might not be necessary, because we always end up calling
|
|
|
|
// Options::initAircraft() later on, which also knows to use
|
|
|
|
// g_load_tape_aircraft if it is not "".
|
|
|
|
//
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "overriding aircraft from " << fgGetString("/sim/aircraft") << " to " << g_load_tape_aircraft);
|
|
|
|
fgSetString("/sim/aircraft", g_load_tape_aircraft);
|
|
|
|
}
|
|
|
|
if (g_load_tape_airport != "") {
|
|
|
|
SG_LOG(SG_GENERAL, SG_ALERT, "overriding airport from " << fgGetString("/sim/presets/airport-id") << " to " << g_load_tape_airport);
|
|
|
|
fgOptAirport(g_load_tape_airport.c_str());
|
|
|
|
}
|
|
|
|
|
2016-10-31 21:21:52 +00:00
|
|
|
if (isOptionSet("json-report")) {
|
|
|
|
printJSONReport();
|
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
} else if (isOptionSet("version")) {
|
Fix the --version option
1) Paths and the FG_SCENERY paths list are now printed like:
FG_ROOT=/home/flo/flightgear/src/fgdata
instead of:
FG_ROOT=Path "/home/flo/flightgear/src/fgdata"
Moreover, FG_SCENERY is now correctly printed (without 'Path ""'
wrapping, without Terrain, Objects and markers instead of the actual
scenery paths) regardless of the position of --version relatively to
--fg-scenery, --terrasync-dir, etc. Of course, the values given to
these options do influence the output of --version.
Simplify printing of FG_SCENERY via SGPath::join() and use the
correct, OS-dependent separator between paths, SGPath::pathListSep.
Write the --version output to stdout, as already done for --help;
then it can be easily piped to $PAGER and doesn't get mixed with the
output of SG_LOG (which is on stderr). This is of course backward
incompatible for programs reading the stderr output of
'fgfs --version', but has been agreed upon on FlightGear-devel (cf.
<https://sourceforge.net/p/flightgear/mailman/message/35461619/>).
2) Don't write explicit values in the OptionResult enum: they don't
matter, but giving them is error-prone in case one does a copy-paste
to add a new member to the enum and forgets to change the value.
2016-10-30 16:25:06 +00:00
|
|
|
showVersion();
|
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
}
|
|
|
|
|
2013-12-17 18:09:12 +00:00
|
|
|
return FG_OPTIONS_OK;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
void Options::showUsage() const
|
|
|
|
{
|
|
|
|
fgOptLogLevel( "alert" );
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2012-04-21 18:17:42 +00:00
|
|
|
FGLocale *locale = globals->get_locale();
|
2011-10-16 17:35:40 +00:00
|
|
|
SGPropertyNode options_root;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2013-10-26 20:06:03 +00:00
|
|
|
simgear::requestConsole(); // ensure console is shown on Windows
|
2011-10-16 17:35:40 +00:00
|
|
|
cout << endl;
|
2012-04-21 18:17:42 +00:00
|
|
|
|
Load --help output and translated strings from $FG_INSTALL_PREFIX/share/flightgear
$FG_INSTALL_PREFIX represents the FlightGear installation prefix, such
as /usr, /usr/local or /opt/FlightGear on Unix systems. Copying the
--help output and translated strings there avoids having to write to
$FG_ROOT when 'make install' (or some OS-dependent equivalent) is run
from the FlightGear build directory---that would be ugly when $FG_ROOT
points to the FGData Git repository.
In FGLocale::FGLocale(), Translations/locale.xml is loaded using
readProperties() and fatalMessageBox() (in case an error is
encountered). Note that it couldn't be loaded via fgLoadProps() in the
current state, because this function relies on guiErrorMessage() when an
error is encountered, which calls mkDialog(), which itself does
globals->get_subsystem("gui"). This last call can't be done from
FGGlobals' constructor---where the 'globals' pointer is still
NULL---hence the need for a different mechanism not relying on
FGGlobals.
For consistency, and also because it provides a better user experience[1],
load options.xml using the same method instead of with fgLoadProps().
[1] I.e., in case of an error, the user gets to see a graphical popup
window with an explanatory message before FG exits, assuming he is
either on Windows, or on Mac, or has Qt support built in FG, as
opposed to only an SG_LOG() call [because when options.xml is
loaded, guiErrorMessage() used by fgLoadProps() can't use the 'gui'
subsystem].
2016-12-26 15:56:04 +00:00
|
|
|
try {
|
2017-01-05 05:45:26 +00:00
|
|
|
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;
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
exit(-1);
|
|
|
|
}
|
2012-04-21 18:17:42 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
SGPropertyNode *options = options_root.getNode("options");
|
|
|
|
if (!options) {
|
|
|
|
SG_LOG( SG_GENERAL, SG_ALERT,
|
2016-12-26 22:03:22 +00:00
|
|
|
"Error reading options.xml: <options> element not found." );
|
2011-10-16 17:35:40 +00:00
|
|
|
exit(-1);
|
|
|
|
}
|
2012-04-21 18:17:42 +00:00
|
|
|
|
|
|
|
if (!locale->loadResource("options"))
|
|
|
|
{
|
2017-01-05 05:45:26 +00:00
|
|
|
cout << "Unable to read the language resource." << endl;
|
2012-04-21 18:17:42 +00:00
|
|
|
exit(-1);
|
|
|
|
}
|
|
|
|
|
2017-02-26 21:59:05 +00:00
|
|
|
std::string usage = locale->getLocalizedString(options->getStringValue("usage"), "options");
|
|
|
|
if (!usage.empty()) {
|
2012-04-21 18:17:42 +00:00
|
|
|
cout << usage << endl;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
vector<SGPropertyNode_ptr>section = options->getChildren("section");
|
|
|
|
for (unsigned int j = 0; j < section.size(); j++) {
|
|
|
|
string msg = "";
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
vector<SGPropertyNode_ptr>option = section[j]->getChildren("option");
|
|
|
|
for (unsigned int k = 0; k < option.size(); k++) {
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
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;
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if ((brief || p->verbose) && name) {
|
|
|
|
string tmp = name->getStringValue();
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (key){
|
|
|
|
tmp.append(":");
|
|
|
|
tmp.append(key->getStringValue());
|
2002-06-10 23:30:23 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
if (arg) {
|
|
|
|
tmp.append("=");
|
|
|
|
tmp.append(arg->getStringValue());
|
|
|
|
}
|
|
|
|
if (short_name) {
|
|
|
|
tmp.append(", -");
|
|
|
|
tmp.append(short_name->getStringValue());
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (tmp.size() <= 25) {
|
|
|
|
msg+= " --";
|
|
|
|
msg += tmp;
|
|
|
|
msg.append( 27-tmp.size(), ' ');
|
|
|
|
} else {
|
|
|
|
msg += "\n --";
|
|
|
|
msg += tmp + '\n';
|
|
|
|
msg.append(32, ' ');
|
|
|
|
}
|
2012-04-21 18:17:42 +00:00
|
|
|
// There may be more than one <description> tag associated
|
2011-10-16 17:35:40 +00:00
|
|
|
// with one option
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
vector<SGPropertyNode_ptr> desc;
|
|
|
|
desc = option[k]->getChildren("description");
|
2013-03-22 02:42:22 +00:00
|
|
|
if (! desc.empty()) {
|
2011-10-16 17:35:40 +00:00
|
|
|
for ( unsigned int l = 0; l < desc.size(); l++) {
|
|
|
|
string t = desc[l]->getStringValue();
|
2012-04-21 18:17:42 +00:00
|
|
|
|
|
|
|
// There may be more than one translation line.
|
|
|
|
vector<SGPropertyNode_ptr>trans_desc = locale->getLocalizedStrings(t.c_str(),"options");
|
2011-10-16 17:35:40 +00:00
|
|
|
for ( unsigned int m = 0; m < trans_desc.size(); m++ ) {
|
|
|
|
string t_str = trans_desc[m]->getStringValue();
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if ((m > 0) || ((l > 0) && m == 0)) {
|
|
|
|
msg.append( 32, ' ');
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
// If the string is too large to fit on the screen,
|
|
|
|
// then split it up in several pieces.
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
while ( t_str.size() > 47 ) {
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
string::size_type m = t_str.rfind(' ', 47);
|
|
|
|
msg += t_str.substr(0, m) + '\n';
|
|
|
|
msg.append( 32, ' ');
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
t_str.erase(t_str.begin(), t_str.begin() + m + 1);
|
|
|
|
}
|
|
|
|
msg += t_str + '\n';
|
|
|
|
}
|
|
|
|
}
|
2002-08-25 22:38:20 +00:00
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2002-08-25 22:38:20 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2017-02-26 21:59:05 +00:00
|
|
|
std::string name = locale->getLocalizedString(section[j]->getStringValue("name"),"options");
|
|
|
|
if (!msg.empty() && !name.empty()) {
|
2012-04-21 18:17:42 +00:00
|
|
|
cout << endl << name << ":" << endl;
|
2011-10-16 17:35:40 +00:00
|
|
|
cout << msg;
|
|
|
|
msg.erase();
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if ( !p->verbose ) {
|
2017-02-26 21:59:05 +00:00
|
|
|
std::string verbose_help = locale->getLocalizedString(options->getStringValue("verbose-help"),"options");
|
|
|
|
if (!verbose_help.empty())
|
2012-04-21 18:17:42 +00:00
|
|
|
cout << endl << verbose_help << endl;
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
|
|
|
#ifdef _MSC_VER
|
2011-11-13 21:27:55 +00:00
|
|
|
std::cout << "Hit a key to continue..." << std::endl;
|
|
|
|
std::cin.get();
|
2011-10-16 17:35:40 +00:00
|
|
|
#endif
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
Fix the --version option
1) Paths and the FG_SCENERY paths list are now printed like:
FG_ROOT=/home/flo/flightgear/src/fgdata
instead of:
FG_ROOT=Path "/home/flo/flightgear/src/fgdata"
Moreover, FG_SCENERY is now correctly printed (without 'Path ""'
wrapping, without Terrain, Objects and markers instead of the actual
scenery paths) regardless of the position of --version relatively to
--fg-scenery, --terrasync-dir, etc. Of course, the values given to
these options do influence the output of --version.
Simplify printing of FG_SCENERY via SGPath::join() and use the
correct, OS-dependent separator between paths, SGPath::pathListSep.
Write the --version output to stdout, as already done for --help;
then it can be easily piped to $PAGER and doesn't get mixed with the
output of SG_LOG (which is on stderr). This is of course backward
incompatible for programs reading the stderr output of
'fgfs --version', but has been agreed upon on FlightGear-devel (cf.
<https://sourceforge.net/p/flightgear/mailman/message/35461619/>).
2) Don't write explicit values in the OptionResult enum: they don't
matter, but giving them is error-prone in case one does a copy-paste
to add a new member to the enum and forgets to change the value.
2016-10-30 16:25:06 +00:00
|
|
|
void Options::showVersion() const
|
|
|
|
{
|
|
|
|
cout << "FlightGear version: " << FLIGHTGEAR_VERSION << endl;
|
|
|
|
cout << "Revision: " << REVISION << endl;
|
2018-08-11 09:09:11 +00:00
|
|
|
cout << "Build-Id: " << JENKINS_BUILD_ID << endl;
|
2017-02-27 23:15:07 +00:00
|
|
|
cout << "Build-Type: " << FG_BUILD_TYPE << endl;
|
Fix the --version option
1) Paths and the FG_SCENERY paths list are now printed like:
FG_ROOT=/home/flo/flightgear/src/fgdata
instead of:
FG_ROOT=Path "/home/flo/flightgear/src/fgdata"
Moreover, FG_SCENERY is now correctly printed (without 'Path ""'
wrapping, without Terrain, Objects and markers instead of the actual
scenery paths) regardless of the position of --version relatively to
--fg-scenery, --terrasync-dir, etc. Of course, the values given to
these options do influence the output of --version.
Simplify printing of FG_SCENERY via SGPath::join() and use the
correct, OS-dependent separator between paths, SGPath::pathListSep.
Write the --version output to stdout, as already done for --help;
then it can be easily piped to $PAGER and doesn't get mixed with the
output of SG_LOG (which is on stderr). This is of course backward
incompatible for programs reading the stderr output of
'fgfs --version', but has been agreed upon on FlightGear-devel (cf.
<https://sourceforge.net/p/flightgear/mailman/message/35461619/>).
2) Don't write explicit values in the OptionResult enum: they don't
matter, but giving them is error-prone in case one does a copy-paste
to add a new member to the enum and forgets to change the value.
2016-10-30 16:25:06 +00:00
|
|
|
cout << "FG_ROOT=" << globals->get_fg_root().utf8Str() << endl;
|
|
|
|
cout << "FG_HOME=" << globals->get_fg_home().utf8Str() << endl;
|
|
|
|
cout << "FG_SCENERY=";
|
|
|
|
|
2016-11-19 13:19:19 +00:00
|
|
|
PathList scn = globals->get_fg_scenery();
|
2017-04-15 07:40:44 +00:00
|
|
|
cout << SGPath::join(scn, SGPath::pathListSep) << endl;
|
Fix the --version option
1) Paths and the FG_SCENERY paths list are now printed like:
FG_ROOT=/home/flo/flightgear/src/fgdata
instead of:
FG_ROOT=Path "/home/flo/flightgear/src/fgdata"
Moreover, FG_SCENERY is now correctly printed (without 'Path ""'
wrapping, without Terrain, Objects and markers instead of the actual
scenery paths) regardless of the position of --version relatively to
--fg-scenery, --terrasync-dir, etc. Of course, the values given to
these options do influence the output of --version.
Simplify printing of FG_SCENERY via SGPath::join() and use the
correct, OS-dependent separator between paths, SGPath::pathListSep.
Write the --version output to stdout, as already done for --help;
then it can be easily piped to $PAGER and doesn't get mixed with the
output of SG_LOG (which is on stderr). This is of course backward
incompatible for programs reading the stderr output of
'fgfs --version', but has been agreed upon on FlightGear-devel (cf.
<https://sourceforge.net/p/flightgear/mailman/message/35461619/>).
2) Don't write explicit values in the OptionResult enum: they don't
matter, but giving them is error-prone in case one does a copy-paste
to add a new member to the enum and forgets to change the value.
2016-10-30 16:25:06 +00:00
|
|
|
cout << "SimGear version: " << SG_STRINGIZE(SIMGEAR_VERSION) << endl;
|
|
|
|
cout << "OSG version: " << osgGetVersion() << endl;
|
|
|
|
cout << "PLIB version: " << PLIB_VERSION << endl;
|
|
|
|
}
|
|
|
|
|
2016-11-12 10:11:03 +00:00
|
|
|
// Print a report using JSON syntax on the standard output, encoded in UTF-8.
|
|
|
|
//
|
|
|
|
// The report format is versioned, don't forget to update it when making
|
|
|
|
// changes (see below).
|
2016-10-31 21:21:52 +00:00
|
|
|
void Options::printJSONReport() const
|
|
|
|
{
|
|
|
|
cJSON *rootNode = cJSON_CreateObject();
|
|
|
|
|
|
|
|
cJSON *metaNode = cJSON_CreateObject();
|
|
|
|
cJSON_AddItemToObject(rootNode, "meta", metaNode);
|
|
|
|
cJSON_AddStringToObject(metaNode, "type", "FlightGear JSON report");
|
2016-11-12 10:11:03 +00:00
|
|
|
// When making compatible changes to the format (e.g., adding members to
|
|
|
|
// JSON objects), only the minor version number should be increased.
|
|
|
|
// Increase the major version number when a change is backward-incompatible
|
|
|
|
// (such as the removal, renaming or semantic change of a member). Of
|
|
|
|
// course, incompatible changes should only be considered as a last
|
|
|
|
// recourse.
|
|
|
|
cJSON_AddNumberToObject(metaNode, "format major version", 1);
|
|
|
|
cJSON_AddNumberToObject(metaNode, "format minor version", 0);
|
2016-10-31 21:21:52 +00:00
|
|
|
|
|
|
|
cJSON *generalNode = cJSON_CreateObject();
|
|
|
|
cJSON_AddItemToObject(rootNode, "general", generalNode);
|
|
|
|
cJSON_AddStringToObject(generalNode, "name", "FlightGear");
|
|
|
|
cJSON_AddStringToObject(generalNode, "version", FLIGHTGEAR_VERSION);
|
2018-08-11 09:09:11 +00:00
|
|
|
cJSON_AddStringToObject(generalNode, "build ID", JENKINS_BUILD_ID);
|
2017-02-27 23:15:07 +00:00
|
|
|
cJSON_AddStringToObject(generalNode, "build type", FG_BUILD_TYPE);
|
2016-10-31 21:21:52 +00:00
|
|
|
|
|
|
|
cJSON *configNode = cJSON_CreateObject();
|
|
|
|
cJSON_AddItemToObject(rootNode, "config", configNode);
|
|
|
|
cJSON_AddStringToObject(configNode, "FG_ROOT",
|
|
|
|
globals->get_fg_root().utf8Str().c_str());
|
|
|
|
cJSON_AddStringToObject(configNode, "FG_HOME",
|
|
|
|
globals->get_fg_home().utf8Str().c_str());
|
|
|
|
|
2016-11-19 13:19:19 +00:00
|
|
|
cJSON *sceneryPathsNode = p->createJSONArrayFromPathList(globals->get_fg_scenery());
|
2016-10-31 21:21:52 +00:00
|
|
|
cJSON_AddItemToObject(configNode, "scenery paths", sceneryPathsNode);
|
|
|
|
|
|
|
|
cJSON *aircraftPathsNode = p->createJSONArrayFromPathList(
|
|
|
|
globals->get_aircraft_paths());
|
|
|
|
cJSON_AddItemToObject(configNode, "aircraft paths", aircraftPathsNode);
|
|
|
|
|
|
|
|
cJSON_AddStringToObject(configNode, "TerraSync directory",
|
|
|
|
globals->get_terrasync_dir().utf8Str().c_str());
|
|
|
|
|
|
|
|
cJSON_AddStringToObject(configNode, "download directory",
|
|
|
|
globals->get_download_dir().utf8Str().c_str());
|
|
|
|
|
|
|
|
cJSON_AddStringToObject(configNode, "autosave file",
|
|
|
|
globals->autosaveFilePath().utf8Str().c_str());
|
|
|
|
|
2017-03-10 22:04:39 +00:00
|
|
|
// Get the ordered lists of apt.dat, fix.dat and nav.dat files used by the
|
|
|
|
// NavCache
|
2016-10-31 21:21:52 +00:00
|
|
|
NavDataCache* cache = NavDataCache::instance();
|
|
|
|
if (!cache) {
|
|
|
|
cache = NavDataCache::createInstance();
|
|
|
|
}
|
|
|
|
|
|
|
|
cJSON *navDataNode = cJSON_CreateObject();
|
|
|
|
cJSON_AddItemToObject(rootNode, "navigation data", navDataNode);
|
2017-03-10 22:04:39 +00:00
|
|
|
|
|
|
|
// Write each list to the JSON tree
|
|
|
|
for (const auto& datType: {NavDataCache::DATFILETYPE_APT,
|
|
|
|
NavDataCache::DATFILETYPE_FIX,
|
|
|
|
NavDataCache::DATFILETYPE_NAV}) {
|
|
|
|
// For this method, it doesn't matter if the cache is out-of-date
|
|
|
|
const NavDataCache::DatFilesGroupInfo& datFilesInfo =
|
|
|
|
cache->getDatFilesInfo(datType);
|
|
|
|
cJSON *datPathsNode = p->createJSONArrayFromPathList(datFilesInfo.paths);
|
|
|
|
string key = NavDataCache::datTypeStr[datType] + ".dat files";
|
|
|
|
cJSON_AddItemToObject(navDataNode, key.c_str(), datPathsNode);
|
|
|
|
}
|
2016-10-31 21:21:52 +00:00
|
|
|
|
|
|
|
// Print the JSON tree to the standard output
|
|
|
|
char *report = cJSON_Print(rootNode);
|
|
|
|
cout << report << endl;
|
|
|
|
cJSON_Delete(rootNode);
|
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
#if defined(__CYGWIN__)
|
2016-06-21 11:29:04 +00:00
|
|
|
SGPath Options::platformDefaultRoot() const
|
2011-10-16 17:35:40 +00:00
|
|
|
{
|
2016-06-23 13:26:34 +00:00
|
|
|
return SGPath::fromUtf8("../data");
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2002-08-25 22:38:20 +00:00
|
|
|
|
2013-11-19 22:01:11 +00:00
|
|
|
#elif defined(SG_WINDOWS)
|
2016-06-21 11:29:04 +00:00
|
|
|
SGPath Options::platformDefaultRoot() const
|
2011-10-16 17:35:40 +00:00
|
|
|
{
|
2016-06-23 13:26:34 +00:00
|
|
|
return SGPath::fromUtf8("..\\data");
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2013-11-19 22:01:11 +00:00
|
|
|
#elif defined(SG_MAC)
|
2013-11-18 15:52:30 +00:00
|
|
|
// platformDefaultRoot defined in CocoaHelpers.mm
|
2011-10-16 17:35:40 +00:00
|
|
|
#else
|
2016-06-21 11:29:04 +00:00
|
|
|
SGPath Options::platformDefaultRoot() const
|
2011-10-16 17:35:40 +00:00
|
|
|
{
|
2017-02-21 11:01:06 +00:00
|
|
|
return SGPath::fromUtf8(PKGLIBDIR);
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2017-02-21 11:01:06 +00:00
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
#endif
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2017-02-21 11:01:06 +00:00
|
|
|
string_list Options::extractOptions() const
|
|
|
|
{
|
|
|
|
string_list result;
|
|
|
|
for (auto opt : p->values) {
|
|
|
|
if (opt.desc == nullptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (!strcmp(opt.desc->option,"prop")) {
|
|
|
|
result.push_back("prop:" + opt.value);
|
|
|
|
} else if (opt.value.empty()) {
|
|
|
|
result.push_back(opt.desc->option);
|
|
|
|
} else {
|
|
|
|
result.push_back(std::string(opt.desc->option) + "=" + opt.value);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return result;
|
|
|
|
}
|
|
|
|
|
2019-01-17 14:52:36 +00:00
|
|
|
OptionResult Options::setupRoot(int argc, char** argv)
|
2011-10-16 17:35:40 +00:00
|
|
|
{
|
2019-11-07 11:11:21 +00:00
|
|
|
SGPath root(globals->get_fg_root());
|
2015-03-06 18:52:06 +00:00
|
|
|
bool usingDefaultRoot = false;
|
|
|
|
|
2019-11-07 11:11:21 +00:00
|
|
|
// root has already been set, so skip the fg_root setting and validation.
|
|
|
|
if (!root.isNull()) {
|
2019-01-17 14:52:36 +00:00
|
|
|
return FG_OPTIONS_OK;
|
2019-11-07 11:11:21 +00:00
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
if (isOptionSet("fg-root")) {
|
2020-03-12 10:51:49 +00:00
|
|
|
root = SGPath::fromUtf8(valueForOption("fg-root")); // easy!
|
2016-06-21 11:29:04 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_INFO, "set from command-line argument: fg_root = " << root );
|
2011-10-16 17:35:40 +00:00
|
|
|
} else {
|
|
|
|
// Next check if fg-root is set as an env variable
|
|
|
|
char *envp = ::getenv( "FG_ROOT" );
|
2018-11-25 14:06:51 +00:00
|
|
|
if ( envp != nullptr ) {
|
2020-03-12 10:51:49 +00:00
|
|
|
root = SGPath::fromEnv("FG_ROOT");
|
2016-06-21 11:29:04 +00:00
|
|
|
SG_LOG(SG_GENERAL, SG_INFO, "set from FG_ROOT env var: fg_root = " << root );
|
2016-01-19 23:32:20 +00:00
|
|
|
} else {
|
2018-06-11 09:35:09 +00:00
|
|
|
#if defined(HAVE_QT)
|
2020-10-06 10:56:58 +00:00
|
|
|
auto restoreResult = SetupRootDialog::restoreUserSelectedRoot(root);
|
|
|
|
if (restoreResult == SetupRootDialog::UserExit) {
|
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
} else if (restoreResult == SetupRootDialog::UseDefault) {
|
|
|
|
root = SGPath{}; // clear any value, so we fall through in root.isNull() below
|
|
|
|
}
|
2016-01-19 23:32:20 +00:00
|
|
|
#endif
|
2020-10-06 10:56:58 +00:00
|
|
|
|
2016-06-21 11:29:04 +00:00
|
|
|
if (root.isNull()) {
|
2016-01-19 23:32:20 +00:00
|
|
|
usingDefaultRoot = true;
|
|
|
|
root = platformDefaultRoot();
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO, "platform default fg_root = " << root );
|
|
|
|
} else {
|
|
|
|
SG_LOG(SG_GENERAL, SG_INFO, "Qt launcher set fg_root = " << root );
|
|
|
|
}
|
2002-06-10 23:30:23 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
}
|
|
|
|
|
2011-10-16 17:35:40 +00:00
|
|
|
globals->set_fg_root(root);
|
2015-03-06 18:52:06 +00:00
|
|
|
string base_version = fgBasePackageVersion(root);
|
|
|
|
|
2017-01-05 10:57:00 +00:00
|
|
|
|
2018-06-11 09:35:09 +00:00
|
|
|
#if defined(HAVE_QT)
|
2017-01-05 10:57:00 +00:00
|
|
|
// only compare major and minor version, not the patch level.
|
|
|
|
const int versionComp = simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, base_version, 2);
|
|
|
|
|
2016-01-19 23:32:20 +00:00
|
|
|
// note we never end up here if restoring a user selected root via
|
2016-01-17 19:10:22 +00:00
|
|
|
// the Qt GUI, since that code pre-validates the path. But if we're using
|
|
|
|
// a command-line, env-var or default root this check can fail and
|
|
|
|
// we still want to use the GUI in that case
|
2017-01-05 10:57:00 +00:00
|
|
|
if (versionComp != 0) {
|
2019-01-17 14:52:36 +00:00
|
|
|
flightgear::initApp(argc, argv);
|
|
|
|
bool ok = SetupRootDialog::runDialog(usingDefaultRoot);
|
|
|
|
if (!ok) {
|
|
|
|
return FG_OPTIONS_EXIT;
|
|
|
|
}
|
2015-03-06 18:52:06 +00:00
|
|
|
}
|
|
|
|
#else
|
2017-04-05 01:01:50 +00:00
|
|
|
SG_UNUSED(usingDefaultRoot);
|
2017-04-09 16:38:38 +00:00
|
|
|
|
2015-03-06 18:52:06 +00:00
|
|
|
// validate it
|
2013-11-06 23:49:58 +00:00
|
|
|
if (base_version.empty()) {
|
2017-04-09 16:38:38 +00:00
|
|
|
flightgear::fatalMessageBoxThenExit(
|
|
|
|
"Base package not found",
|
|
|
|
"Required data files not found, please check your installation.",
|
|
|
|
"Looking for base-package files at: '" + root.str() + "'");
|
2013-11-06 23:49:58 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2017-01-05 10:57:00 +00:00
|
|
|
// only compare major and minor version, not the patch level.
|
|
|
|
const int versionComp = simgear::strutils::compare_versions(FLIGHTGEAR_VERSION, base_version, 2);
|
|
|
|
if (versionComp != 0) {
|
2017-04-09 16:38:38 +00:00
|
|
|
flightgear::fatalMessageBoxThenExit(
|
|
|
|
"Base package version mismatch",
|
|
|
|
"Version check failed, please check your installation.",
|
|
|
|
"Found data files for version '" + base_version + "' at '" +
|
|
|
|
globals->get_fg_root().str() + "', version '" +
|
|
|
|
std::string(FLIGHTGEAR_VERSION) + "' is required.");
|
2011-10-16 17:35:40 +00:00
|
|
|
}
|
2015-03-06 18:52:06 +00:00
|
|
|
#endif
|
2019-01-17 14:52:36 +00:00
|
|
|
return FG_OPTIONS_OK;
|
1998-04-24 00:49:17 +00:00
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2012-09-29 18:02:03 +00:00
|
|
|
bool Options::shouldLoadDefaultConfig() const
|
|
|
|
{
|
|
|
|
return p->shouldLoadDefaultConfig;
|
|
|
|
}
|
2014-02-19 21:53:52 +00:00
|
|
|
|
2016-09-08 10:41:40 +00:00
|
|
|
void Options::setShouldLoadDefaultConfig(bool load)
|
|
|
|
{
|
|
|
|
p->shouldLoadDefaultConfig = load;
|
|
|
|
}
|
|
|
|
|
2014-02-19 21:53:52 +00:00
|
|
|
bool Options::checkForArg(int argc, char* argv[], const char* checkArg)
|
|
|
|
{
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
|
|
char* arg = argv[i];
|
2018-10-12 10:37:39 +00:00
|
|
|
if (arg == nullptr) {
|
2016-01-26 06:47:38 +00:00
|
|
|
continue;
|
2014-02-19 21:53:52 +00:00
|
|
|
}
|
2016-01-26 06:47:38 +00:00
|
|
|
|
|
|
|
if (*arg != '-') { // we only care about args with a leading hypen
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
arg++;
|
|
|
|
if (*arg == '-') { // skip double hypens
|
|
|
|
arg++;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strcmp(arg, checkArg) == 0) {
|
2014-02-19 21:53:52 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
}
|
2016-06-23 13:26:34 +00:00
|
|
|
|
2014-02-19 21:53:52 +00:00
|
|
|
return false;
|
|
|
|
}
|
2011-10-16 17:35:40 +00:00
|
|
|
|
2018-10-12 10:37:39 +00:00
|
|
|
std::string Options::getArgValue(int argc, char* argv[], const char* checkArg)
|
|
|
|
{
|
|
|
|
const auto len = strlen(checkArg);
|
|
|
|
for (int i = 0; i < argc; ++i) {
|
|
|
|
char* arg = argv[i];
|
|
|
|
if (arg == nullptr) {
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (strncmp(arg, checkArg, len) == 0) {
|
|
|
|
const auto alen = strlen(arg);
|
|
|
|
if ((alen - len) < 2)
|
|
|
|
return {}; // no value after the =, or missing = entirely
|
|
|
|
return std::string(arg + len + 1);
|
|
|
|
}
|
|
|
|
} // of args iteration
|
|
|
|
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
2016-06-23 13:26:34 +00:00
|
|
|
} // of namespace flightgear
|