]> git.mxchange.org Git - flightgear.git/blobdiff - src/Main/fg_commands.cxx
apt.dat parser: clearer log and exception messages
[flightgear.git] / src / Main / fg_commands.cxx
index 1e32ef6b2b1efd810d974846322218b7185d37ab..1e93a0b7b70e6cd32a12381c525c1646e5ab3f25 100644 (file)
@@ -23,8 +23,7 @@
 #include <simgear/structure/event_mgr.hxx>
 #include <simgear/sound/soundmgr_openal.hxx>
 #include <simgear/timing/sg_time.hxx>
-#include <simgear/misc/interpolator.hxx>
-#include <simgear/io/HTTPRequest.hxx>
+#include <Network/RemoteXMLRequest.hxx>
 
 #include <FDM/flight.hxx>
 #include <GUI/gui.h>
 #include <GUI/dialog.hxx>
 #include <Aircraft/replay.hxx>
 #include <Scenery/scenery.hxx>
+#include <Scenery/tilemgr.hxx>
 #include <Scripting/NasalSys.hxx>
 #include <Sound/sample_queue.hxx>
 #include <Airports/xmlloader.hxx>
 #include <Network/HTTPClient.hxx>
 #include <Viewer/viewmgr.hxx>
-#include <Viewer/viewer.hxx>
+#include <Viewer/view.hxx>
 #include <Environment/presets.hxx>
+#include <Navaids/NavDataCache.hxx>
 
 #include "fg_init.hxx"
 #include "fg_io.hxx"
 
 #include <boost/scoped_array.hpp>
 
-#if GOOGLE_PERFTOOLS_FOUND == YES
+#if FG_HAVE_GPERFTOOLS
 # include <google/profiler.h>
 #endif
 
+#if defined(HAVE_QT)
+#include <GUI/QtLauncher.hxx>
+#endif
+
 using std::string;
 using std::ifstream;
 using std::ofstream;
@@ -159,7 +164,7 @@ compare_values (SGPropertyNode * value1, SGPropertyNode * value2)
 }
 
 
-\f
+
 ////////////////////////////////////////////////////////////////////////
 // Command implementations.
 ////////////////////////////////////////////////////////////////////////
@@ -205,10 +210,32 @@ do_exit (const SGPropertyNode * arg)
 static bool
 do_reset (const SGPropertyNode * arg)
 {
-    fgReInitSubsystems();
+    fgResetIdleState();
     return true;
 }
 
+
+/**
+ * Change aircraft
+ */
+static bool
+do_switch_aircraft (const SGPropertyNode * arg)
+{
+    fgSetString("/sim/aircraft", arg->getStringValue("aircraft"));
+    // start a reset
+    fgResetIdleState();
+    return true;
+}
+
+/**
+ */
+static bool
+do_reposition (const SGPropertyNode * arg)
+{
+  fgStartReposition();
+  return true;
+}
+
 /**
  * Built-in command: replay the FDR buffer
  */
@@ -225,7 +252,14 @@ do_replay (const SGPropertyNode * arg)
 static bool
 do_pause (const SGPropertyNode * arg)
 {
+    bool forcePause = arg->getBoolValue("force-pause", false );
+    bool forcePlay = arg->getBoolValue("force-play", false );
+
     bool paused = fgGetBool("/sim/freeze/master",true) || fgGetBool("/sim/freeze/clock",true);
+
+    if(forcePause) paused = false;
+    if(forcePlay) paused = true;
+
     if (paused && (fgGetInt("/sim/freeze/replay-state",0)>0))
     {
         do_replay(NULL);
@@ -235,6 +269,8 @@ do_pause (const SGPropertyNode * arg)
         fgSetBool("/sim/freeze/master",!paused);
         fgSetBool("/sim/freeze/clock",!paused);
     }
+  
+    syncPausePopupState();
     return true;
 }
 
@@ -247,17 +283,19 @@ do_pause (const SGPropertyNode * arg)
 static bool
 do_load (const SGPropertyNode * arg)
 {
-    string file = arg->getStringValue("file", "fgfs.sav");
-    if (file.size() < 4 || file.substr(file.size() - 4) != ".sav")
-        file += ".sav";
+    SGPath file(arg->getStringValue("file", "fgfs.sav"));
+
+    if (file.extension() != "sav")
+        file.concat(".sav");
 
-    if (!fgValidatePath(file.c_str(), false)) {
+    std::string validated_path = fgValidatePath(file, false);
+    if (validated_path.empty()) {
         SG_LOG(SG_IO, SG_ALERT, "load: reading '" << file << "' denied "
                 "(unauthorized access)");
         return false;
     }
 
-    ifstream input(file.c_str());
+    ifstream input(validated_path.c_str());
     if (input.good() && fgLoadFlight(input)) {
         input.close();
         SG_LOG(SG_INPUT, SG_INFO, "Restored flight from " << file);
@@ -278,11 +316,13 @@ do_load (const SGPropertyNode * arg)
 static bool
 do_save (const SGPropertyNode * arg)
 {
-    string file = arg->getStringValue("file", "fgfs.sav");
-    if (file.size() < 4 || file.substr(file.size() - 4) != ".sav")
-        file += ".sav";
+    SGPath file(arg->getStringValue("file", "fgfs.sav"));
 
-    if (!fgValidatePath(file.c_str(), false)) {
+    if (file.extension() != "sav")
+        file.concat(".sav");
+
+    std::string validated_path = fgValidatePath(file, true);
+    if (validated_path.empty()) {
         SG_LOG(SG_IO, SG_ALERT, "save: writing '" << file << "' denied "
                 "(unauthorized access)");
         return false;
@@ -290,7 +330,7 @@ do_save (const SGPropertyNode * arg)
 
     bool write_all = arg->getBoolValue("write-all", false);
     SG_LOG(SG_INPUT, SG_INFO, "Saving flight");
-    ofstream output(file.c_str());
+    ofstream output(validated_path.c_str());
     if (output.good() && fgSaveFlight(output, write_all)) {
         output.close();
         SG_LOG(SG_INPUT, SG_INFO, "Saved flight to " << file);
@@ -366,17 +406,36 @@ do_preferences_load (const SGPropertyNode * arg)
 }
 
 static void
-do_view_next( bool )
+do_view_next(bool do_it)
 {
+  // Only switch view if really requested to do so (and not for example while
+  // reset/reposition where /command/view/next is set to false)
+  if( do_it )
+  {
     globals->get_current_view()->setHeadingOffset_deg(0.0);
     globals->get_viewmgr()->next_view();
+  }
 }
 
 static void
-do_view_prev( bool )
+do_view_prev(bool do_it)
 {
+  if( do_it )
+  {
     globals->get_current_view()->setHeadingOffset_deg(0.0);
     globals->get_viewmgr()->prev_view();
+  }
+}
+
+/**
+ * An fgcommand to toggle fullscreen mode.
+ * No parameters.
+ */
+static bool
+do_toggle_fullscreen(const SGPropertyNode *arg)
+{
+    fgOSFullScreen();
+    return true;
 }
 
 /**
@@ -454,18 +513,17 @@ do_hires_screen_capture (const SGPropertyNode * arg)
 static bool
 do_tile_cache_reload (const SGPropertyNode * arg)
 {
-    static const SGPropertyNode *master_freeze
-       = fgGetNode("/sim/freeze/master");
+    SGPropertyNode *master_freeze = fgGetNode("/sim/freeze/master");
     bool freeze = master_freeze->getBoolValue();
     SG_LOG(SG_INPUT, SG_INFO, "ReIniting TileCache");
     if ( !freeze ) {
-       fgSetBool("/sim/freeze/master", true);
+        master_freeze->setBoolValue(true);
     }
 
     globals->get_subsystem("tile-manager")->reinit();
 
     if ( !freeze ) {
-       fgSetBool("/sim/freeze/master", false);
+        master_freeze->setBoolValue(false);
     }
     return true;
 }
@@ -473,159 +531,30 @@ do_tile_cache_reload (const SGPropertyNode * arg)
 /**
  * Reload the materials definition
  */
- static bool
- do_materials_reload (const SGPropertyNode * arg)
- {
-   SG_LOG(SG_INPUT, SG_INFO, "Reloading Materials");
-   SGMaterialLib* new_matlib =  new SGMaterialLib;
-   SGPath mpath( globals->get_fg_root() );
-   mpath.append( fgGetString("/sim/rendering/materials-file") );
-   bool loaded = new_matlib->load(globals->get_fg_root(), 
+static bool
+do_materials_reload (const SGPropertyNode * arg)
+{
+    SG_LOG(SG_INPUT, SG_INFO, "Reloading Materials");
+    SGMaterialLib* new_matlib =  new SGMaterialLib;
+    SGPath mpath( globals->get_fg_root() );
+    mpath.append( fgGetString("/sim/rendering/materials-file") );
+    bool loaded = new_matlib->load(globals->get_fg_root(), 
                                   mpath.str(), 
                                   globals->get_props());
-   
-   if ( ! loaded ) {
+
+    if ( ! loaded ) {
        SG_LOG( SG_GENERAL, SG_ALERT,
                "Error loading materials file " << mpath.str() );
        return false;
-   }  
-   
-   globals->set_matlib(new_matlib);    
-   return true;   
- }
-
-
-#if 0
-These do_set_(some-environment-parameters) are deprecated and no longer 
-useful/functional - Torsten Dreyer, January 2011
-/**
- * Set the sea level outside air temperature and assigning that to all
- * boundary and aloft environment layers.
- */
-static bool
-do_set_sea_level_degc ( double temp_sea_level_degc)
-{
-    SGPropertyNode *node, *child;
-
-    // boundary layers
-    node = fgGetNode( "/environment/config/boundary" );
-    if ( node != NULL ) {
-      int i = 0;
-      while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
-       child->setDoubleValue( "temperature-sea-level-degc",
-                              temp_sea_level_degc );
-       ++i;
-      }
-    }
-
-    // aloft layers
-    node = fgGetNode( "/environment/config/aloft" );
-    if ( node != NULL ) {
-      int i = 0;
-      while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
-       child->setDoubleValue( "temperature-sea-level-degc",
-                              temp_sea_level_degc );
-       ++i;
-      }
-    }
-
-    return true;
-}
-
-static bool
-do_set_sea_level_degc (const SGPropertyNode * arg)
-{
-    return do_set_sea_level_degc( arg->getDoubleValue("temp-degc", 15.0) );
-}
-
-
-/**
- * Set the outside air temperature at the "current" altitude by first
- * calculating the corresponding sea level temp, and assigning that to
- * all boundary and aloft environment layers.
- */
-static bool
-do_set_oat_degc (const SGPropertyNode * arg)
-{
-    double oat_degc = arg->getDoubleValue("temp-degc", 15.0);
-    // check for an altitude specified in the arguments, otherwise use
-    // current aircraft altitude.
-    const SGPropertyNode *altitude_ft = arg->getChild("altitude-ft");
-    if ( altitude_ft == NULL ) {
-        altitude_ft = fgGetNode("/position/altitude-ft");
-    }
-
-    FGEnvironment dummy;       // instantiate a dummy so we can leech a method
-    dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
-    dummy.set_temperature_degc( oat_degc );
-    return do_set_sea_level_degc( dummy.get_temperature_sea_level_degc());
-}
-
-/**
- * Set the sea level outside air dewpoint and assigning that to all
- * boundary and aloft environment layers.
- */
-static bool
-do_set_dewpoint_sea_level_degc (double dewpoint_sea_level_degc)
-{
-
-    SGPropertyNode *node, *child;
-
-    // boundary layers
-    node = fgGetNode( "/environment/config/boundary" );
-    if ( node != NULL ) {
-      int i = 0;
-      while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
-       child->setDoubleValue( "dewpoint-sea-level-degc",
-                              dewpoint_sea_level_degc );
-       ++i;
-      }
-    }
-
-    // aloft layers
-    node = fgGetNode( "/environment/config/aloft" );
-    if ( node != NULL ) {
-      int i = 0;
-      while ( ( child = node->getNode( "entry", i ) ) != NULL ) {
-       child->setDoubleValue( "dewpoint-sea-level-degc",
-                              dewpoint_sea_level_degc );
-       ++i;
-      }
-    }
+    }  
 
+    globals->set_matlib(new_matlib);    
+    FGTileMgr* tileManager = static_cast<FGTileMgr*>(globals->get_subsystem("tile-manager"));
+    tileManager->materialLibChanged();
+    
     return true;
 }
 
-static bool
-do_set_dewpoint_sea_level_degc (const SGPropertyNode * arg)
-{
-    return do_set_dewpoint_sea_level_degc(arg->getDoubleValue("dewpoint-degc", 5.0));
-}
-
-/**
- * Set the outside air dewpoint at the "current" altitude by first
- * calculating the corresponding sea level dewpoint, and assigning
- * that to all boundary and aloft environment layers.
- */
-static bool
-do_set_dewpoint_degc (const SGPropertyNode * arg)
-{
-    double dewpoint_degc = arg->getDoubleValue("dewpoint-degc", 5.0);
-
-    // check for an altitude specified in the arguments, otherwise use
-    // current aircraft altitude.
-    const SGPropertyNode *altitude_ft = arg->getChild("altitude-ft");
-    if ( altitude_ft == NULL ) {
-        altitude_ft = fgGetNode("/position/altitude-ft");
-    }
-
-    FGEnvironment dummy;       // instantiate a dummy so we can leech a method
-    dummy.set_elevation_ft( altitude_ft->getDoubleValue() );
-    dummy.set_dewpoint_degc( dewpoint_degc );
-    return do_set_dewpoint_sea_level_degc(dummy.get_dewpoint_sea_level_degc());
-}
-#endif
-
 /**
  * Built-in command: toggle a bool property value.
  *
@@ -816,7 +745,12 @@ static bool
 do_property_cycle (const SGPropertyNode * arg)
 {
     SGPropertyNode * prop = get_prop(arg);
-    vector<SGPropertyNode_ptr> values = arg->getChildren("value");
+    std::vector<SGPropertyNode_ptr> values = arg->getChildren("value");
+    
+    bool wrap = arg->getBoolValue("wrap", true);
+    // compatible with knob/pick animations
+    int offset = arg->getIntValue("offset", 1);
+    
     int selection = -1;
     int nSelections = values.size();
 
@@ -828,15 +762,22 @@ do_property_cycle (const SGPropertyNode * arg)
                                 // Try to find the current selection
     for (int i = 0; i < nSelections; i++) {
         if (compare_values(prop, values[i])) {
-            selection = i + 1;
+            selection = i;
             break;
         }
     }
 
-                                // Default or wrap to the first selection
-    if (selection < 0 || selection >= nSelections)
+    if (selection < 0) { // default to first selection
         selection = 0;
-
+    } else {
+        selection += offset;
+        if (wrap) {
+            selection = (selection + nSelections) % nSelections;
+        } else {
+            SG_CLAMP_RANGE(selection, 0, nSelections - 1);
+        }
+    }
+    
     prop->setUnspecifiedValue(values[selection]->getStringValue());
     return true;
 }
@@ -863,13 +804,15 @@ do_property_randomize (const SGPropertyNode * arg)
  * Built-in command: interpolate a property value over time
  *
  * property:        the name of the property value to interpolate.
+ * type:            the interpolation type ("numeric", "color", etc.)
+ * easing:          name of easing function (see http://easings.net/)
  * value[0..n]      any number of constant values to interpolate
  * time/rate[0..n]  time between each value, number of time elements must
  *                  match those of value elements. Instead of time also rate can
  *                  be used which automatically calculates the time to change
  *                  the property value at the given speed.
  * -or-
- * property[1..n]   any number of target values taken from named properties
+ * property[1..n+1] any number of target values taken from named properties
  * time/rate[0..n]  time between each value, number of time elements must
  *                  match those of value elements. Instead of time also rate can
  *                  be used which automatically calculates the time to change
@@ -878,63 +821,68 @@ do_property_randomize (const SGPropertyNode * arg)
 static bool
 do_property_interpolate (const SGPropertyNode * arg)
 {
-    SGPropertyNode * prop = get_prop(arg);
+  SGPropertyNode * prop = get_prop(arg);
+  if( !prop )
+    return false;
 
-    simgear::PropertyList valueNodes = arg->getChildren( "value" );
-    simgear::PropertyList timeNodes = arg->getChildren( "time" );
-    simgear::PropertyList rateNodes = arg->getChildren( "rate" );
+  simgear::PropertyList time_nodes = arg->getChildren("time");
+  simgear::PropertyList rate_nodes = arg->getChildren("rate");
 
-    if( !timeNodes.empty() && !rateNodes.empty() )
-      // mustn't specify time and rate
-      return false;
+  if( !time_nodes.empty() && !rate_nodes.empty() )
+    // mustn't specify time and rate
+    return false;
 
-    simgear::PropertyList::size_type num_times = timeNodes.empty()
-                                               ? rateNodes.size()
-                                               : timeNodes.size();
+  simgear::PropertyList::size_type num_times = time_nodes.empty()
+                                             ? rate_nodes.size()
+                                             : time_nodes.size();
 
-    boost::scoped_array<double> value;
-    boost::scoped_array<double> time;
+  simgear::PropertyList value_nodes = arg->getChildren("value");
+  if( value_nodes.empty() )
+  {
+    simgear::PropertyList prop_nodes = arg->getChildren("property");
 
-    if( valueNodes.size() > 0 ) {
-        // must match
-        if( num_times != valueNodes.size() )
-            return false;
+    // must have one more property node
+    if( prop_nodes.size() != num_times + 1 )
+      return false;
 
-        value.reset( new double[valueNodes.size()] );
-        for( simgear::PropertyList::size_type n = 0; n < valueNodes.size(); n++ ) {
-            value[n] = valueNodes[n]->getDoubleValue();
-        }
-    } else {
-        valueNodes = arg->getChildren("property");
-        // must have one more property node
-        if( valueNodes.size() - 1 != num_times )
-          return false;
+    value_nodes.reserve(num_times);
+    for( size_t i = 1; i < prop_nodes.size(); ++i )
+      value_nodes.push_back( fgGetNode(prop_nodes[i]->getStringValue()) );
+  }
 
-        value.reset( new double[valueNodes.size()-1] );
-        for( simgear::PropertyList::size_type n = 0; n < valueNodes.size()-1; n++ ) {
-            value[n] = fgGetNode(valueNodes[n+1]->getStringValue(), "/null")->getDoubleValue();
-        }
+  // must match
+  if( value_nodes.size() != num_times )
+    return false;
 
-    }
+  double_list deltas;
+  deltas.reserve(num_times);
 
-    time.reset( new double[num_times] );
-    if( !timeNodes.empty() ) {
-        for( simgear::PropertyList::size_type n = 0; n < num_times; n++ ) {
-            time[n] = timeNodes[n]->getDoubleValue();
-        }
-    } else {
-        for( simgear::PropertyList::size_type n = 0; n < num_times; n++ ) {
-            double delta = value[n]
-                         - (n > 0 ? value[n - 1] : prop->getDoubleValue());
-            time[n] = fabs(delta / rateNodes[n]->getDoubleValue());
-        }
+  if( !time_nodes.empty() )
+  {
+    for( size_t i = 0; i < num_times; ++i )
+      deltas.push_back( time_nodes[i]->getDoubleValue() );
+  }
+  else
+  {
+    for( size_t i = 0; i < num_times; ++i )
+    {
+      // TODO calculate delta based on property type
+      double delta = value_nodes[i]->getDoubleValue()
+                   - ( i > 0
+                     ? value_nodes[i - 1]->getDoubleValue()
+                     : prop->getDoubleValue()
+                     );
+      deltas.push_back( fabs(delta / rate_nodes[i]->getDoubleValue()) );
     }
+  }
 
-    ((SGInterpolator*)globals->get_subsystem_mgr()
-      ->get_group(SGSubsystemMgr::INIT)->get_subsystem("interpolator"))
-      ->interpolate(prop, num_times, value.get(), time.get() );
-
-    return true;
+  return prop->interpolate
+  (
+    arg->getStringValue("type", "numeric"),
+    value_nodes,
+    deltas,
+    arg->getStringValue("easing", "linear")
+  );
 }
 
 /**
@@ -1040,6 +988,21 @@ do_open_browser (const SGPropertyNode * arg)
     return openBrowser(path);
 }
 
+static bool
+do_open_launcher(const SGPropertyNode *)
+{
+#if defined(HAVE_QT)
+    bool ok = flightgear::runInAppLauncherDialog();
+    if (ok) {
+        // start a full reset
+        fgResetIdleState();
+    }
+    return ok;
+#else
+    return false;
+#endif
+}
+
 /**
  * Apply a value in the active XML-configured dialog.
  *
@@ -1098,71 +1061,6 @@ do_add_model (const SGPropertyNode * arg)
     return true;
 }
 
-
-/**
- * Set mouse cursor coordinates and cursor shape.
- */
-static bool
-do_set_cursor (const SGPropertyNode * arg)
-{
-    if (arg->hasValue("x") || arg->hasValue("y")) {
-        SGPropertyNode *mx = fgGetNode("/devices/status/mice/mouse/x", true);
-        SGPropertyNode *my = fgGetNode("/devices/status/mice/mouse/y", true);
-        int x = arg->getIntValue("x", mx->getIntValue());
-        int y = arg->getIntValue("y", my->getIntValue());
-        fgWarpMouse(x, y);
-        mx->setIntValue(x);
-        my->setIntValue(y);
-    }
-
-    SGPropertyNode *cursor = const_cast<SGPropertyNode *>(arg)->getNode("cursor", true);
-    if (cursor->getType() != simgear::props::NONE)
-        fgSetMouseCursor(cursor->getIntValue());
-
-    cursor->setIntValue(fgGetMouseCursor());
-    return true;
-}
-
-
-/**
- * Built-in command: play an audio message (i.e. a wav file) This is
- * fire and forget.  Call this once per message and it will get dumped
- * into a queue.  Messages are played sequentially so they do not
- * overlap.
- */
-static bool
-do_play_audio_sample (const SGPropertyNode * arg)
-{
-    SGSoundMgr *smgr = globals->get_soundmgr();
-    if (!smgr) {
-        SG_LOG(SG_GENERAL, SG_WARN, "play-audio-sample: sound-manager not running");
-        return false;
-    }
-  
-    string path = arg->getStringValue("path");
-    string file = arg->getStringValue("file");
-    float volume = arg->getFloatValue("volume");
-    // cout << "playing " << path << " / " << file << endl;
-    try {
-        static FGSampleQueue *queue = 0;
-        if ( !queue ) {
-           queue = new FGSampleQueue(smgr, "chatter");
-           queue->tie_to_listener();
-        }
-
-        SGSoundSample *msg = new SGSoundSample(file.c_str(), path);
-        msg->set_volume( volume );
-        queue->add( msg );
-
-        return true;
-
-    } catch (const sg_io_exception&) {
-        SG_LOG(SG_GENERAL, SG_ALERT, "play-audio-sample: "
-                "failed to load" << path << '/' << file);
-        return false;
-    }
-}
-
 /**
  * Built-in command: commit presets (read from in /sim/presets/)
  */
@@ -1170,7 +1068,7 @@ static bool
 do_presets_commit (const SGPropertyNode * arg)
 {
     if (fgGetBool("/sim/initialized", false)) {
-      fgReInitSubsystems();
+      fgResetIdleState();
     } else {
       // Nasal can trigger this during initial init, which confuses
       // the logic in ReInitSubsystems, since initial state has not been
@@ -1250,9 +1148,10 @@ do_load_xml_to_proptree(const SGPropertyNode * arg)
         }
     }
     
-    if (!fgValidatePath(file.c_str(), false)) {
+    std::string validated_path = fgValidatePath(file, false);
+    if (validated_path.empty()) {
         SG_LOG(SG_IO, SG_ALERT, "loadxml: reading '" << file.str() << "' denied "
-                "(unauthorized access)");
+                "(unauthorized directory - authorization no longer follows symlinks; to authorize reading additional directories, add them to --fg-aircraft)");
         return false;
     }
 
@@ -1263,7 +1162,7 @@ do_load_xml_to_proptree(const SGPropertyNode * arg)
         targetnode = const_cast<SGPropertyNode *>(arg)->getNode("data", true);
 
     try {
-        readProperties(file.c_str(), targetnode, true);
+        readProperties(validated_path.c_str(), targetnode, true);
     } catch (const sg_exception &e) {
         SG_LOG(SG_IO, SG_WARN, "loadxml: " << e.getFormattedMessage());
         return false;
@@ -1272,68 +1171,15 @@ do_load_xml_to_proptree(const SGPropertyNode * arg)
     return true;
 }
 
-class RemoteXMLRequest : public simgear::HTTP::Request
-{
-public:
-    SGPropertyNode_ptr _complete;
-    SGPropertyNode_ptr _status;
-    SGPropertyNode_ptr _failed;
-    SGPropertyNode_ptr _target;
-    string propsData;
-    
-    RemoteXMLRequest(const std::string& url, SGPropertyNode* targetNode) : 
-        simgear::HTTP::Request(url),
-        _target(targetNode)
-    {
-    }
-    
-    void setCompletionProp(SGPropertyNode_ptr p)
-    {
-        _complete = p;
-    }
-    
-    void setStatusProp(SGPropertyNode_ptr p)
-    {
-        _status = p;
-    }
-    
-    void setFailedProp(SGPropertyNode_ptr p)
-    {
-        _failed = p;
-    }
-protected:
-    virtual void gotBodyData(const char* s, int n)
-    {
-        propsData += string(s, n);
-    }
-    
-    virtual void responseComplete()
-    {
-        int response = responseCode();
-        bool failed = false;
-        if (response == 200) {
-            try {
-                const char* buffer = propsData.c_str();
-                readProperties(buffer, propsData.size(), _target, true);
-            } catch (const sg_exception &e) {
-                SG_LOG(SG_IO, SG_WARN, "parsing XML from remote, failed: " << e.getFormattedMessage());
-                failed = true;
-                response = 406; // 'not acceptable', anything better?
-            }
-        } else {
-            failed = true;
-        }
-    // now the response data is output, signal Nasal / listeners
-        if (_complete) _complete->setBoolValue(true);
-        if (_status) _status->setIntValue(response);
-        if (_failed) _failed->setBoolValue(failed);
-    }
-};
-
-
 static bool
 do_load_xml_from_url(const SGPropertyNode * arg)
 {
+    FGHTTPClient* http = static_cast<FGHTTPClient*>(globals->get_subsystem("http"));
+    if (!http) {
+        SG_LOG(SG_IO, SG_ALERT, "xmlhttprequest: HTTP client not running");
+        return false;
+    }
+  
     std::string url(arg->getStringValue("url"));
     if (url.empty())
         return false;
@@ -1346,6 +1192,9 @@ do_load_xml_from_url(const SGPropertyNode * arg)
     
     RemoteXMLRequest* req = new RemoteXMLRequest(url, targetnode);
     
+    if (arg->hasChild("body"))
+        req->setBodyData(arg->getChild("body"));
+    
 // connect up optional reporting properties
     if (arg->hasValue("complete")) 
         req->setCompletionProp(fgGetNode(arg->getStringValue("complete"), true));
@@ -1354,8 +1203,7 @@ do_load_xml_from_url(const SGPropertyNode * arg)
     if (arg->hasValue("status")) 
         req->setStatusProp(fgGetNode(arg->getStringValue("status"), true));
         
-    FGHTTPClient::instance()->makeRequest(req);
-    
+    http->makeRequest(req);
     return true;
 }
 
@@ -1383,9 +1231,10 @@ do_save_xml_from_proptree(const SGPropertyNode * arg)
     if (file.extension() != "xml")
         file.concat(".xml");
 
-    if (!fgValidatePath(file.c_str(), true)) {
+    std::string validated_path = fgValidatePath(file, true);
+    if (validated_path.empty()) {
         SG_LOG(SG_IO, SG_ALERT, "savexml: writing to '" << file.str() << "' denied "
-                "(unauthorized access)");
+                "(unauthorized directory - authorization no longer follows symlinks)");
         return false;
     }
 
@@ -1398,7 +1247,7 @@ do_save_xml_from_proptree(const SGPropertyNode * arg)
         return false;
 
     try {
-        writeProperties (file.c_str(), sourcenode, true);
+        writeProperties (validated_path.c_str(), sourcenode, true);
     } catch (const sg_exception &e) {
         SG_LOG(SG_IO, SG_WARN, "savexml: " << e.getFormattedMessage());
         return false;
@@ -1453,7 +1302,7 @@ do_release_cockpit_button (const SGPropertyNode *arg)
 // Optional profiling commands using gperftools:
 // http://code.google.com/p/gperftools/
 
-#if GOOGLE_PERFTOOLS_FOUND != YES
+#if !FG_HAVE_GPERFTOOLS
 static void
 no_profiling_support()
 {
@@ -1469,7 +1318,7 @@ no_profiling_support()
 static bool
 do_profiler_start(const SGPropertyNode *arg)
 {
-#if GOOGLE_PERFTOOLS_FOUND == YES
+#if FG_HAVE_GPERFTOOLS
   const char *filename = arg->getStringValue("filename", "fgfs.profile");
   ProfilerStart(filename);
   return true;
@@ -1482,7 +1331,7 @@ do_profiler_start(const SGPropertyNode *arg)
 static bool
 do_profiler_stop(const SGPropertyNode *arg)
 {
-#if GOOGLE_PERFTOOLS_FOUND == YES
+#if FG_HAVE_GPERFTOOLS
   ProfilerStop();
   return true;
 #else
@@ -1491,6 +1340,39 @@ do_profiler_stop(const SGPropertyNode *arg)
 #endif
 }
 
+
+static bool
+do_set_scenery_paths(const SGPropertyNode* arg)
+{
+  globals->clear_fg_scenery();
+  
+  std::string terrasyncPath(fgGetString("/sim/terrasync/scenery-dir"));
+  bool seenTerrasyncPath = false;
+  
+  simgear::PropertyList paths = arg->getChildren("path");
+  for (size_t i = 0; i < paths.size(); ++i) {
+    std::string s = paths[i]->getStringValue();
+    if (s == terrasyncPath) {
+      seenTerrasyncPath = true;
+    }
+    
+    globals->append_fg_scenery(s);
+  }
+  
+  if (fgGetBool("/sim/terrasync/enabled") && !seenTerrasyncPath) {
+    globals->append_fg_scenery(terrasyncPath);
+  }
+  
+  if (paths.empty()) {
+    // no scenery paths set *at all*, use the data in FG_ROOT
+    SGPath root(globals->get_fg_root());
+    root.append("Scenery");
+    globals->append_fg_scenery(root.str());
+  }
+
+  return true;
+}
+
 ////////////////////////////////////////////////////////////////////////
 // Command setup.
 ////////////////////////////////////////////////////////////////////////
@@ -1510,6 +1392,8 @@ static struct {
     { "nasal", do_nasal },
     { "exit", do_exit },
     { "reset", do_reset },
+    { "reposition", do_reposition },
+    { "switch-aircraft", do_switch_aircraft },
     { "pause", do_pause },
     { "load", do_load },
     { "save", do_save },
@@ -1517,6 +1401,7 @@ static struct {
     { "load-tape", do_load_tape },
     { "panel-load", do_panel_load },
     { "preferences-load", do_preferences_load },
+    { "toggle-fullscreen", do_toggle_fullscreen },
     { "view-cycle", do_view_cycle },
     { "screen-capture", do_screen_capture },
     { "hires-screen-capture", do_hires_screen_capture },
@@ -1545,11 +1430,10 @@ static struct {
     { "open-browser", do_open_browser },
     { "gui-redraw", do_gui_redraw },
     { "add-model", do_add_model },
-    { "set-cursor", do_set_cursor },
-    { "play-audio-sample", do_play_audio_sample },
     { "presets-commit", do_presets_commit },
     { "log-level", do_log_level },
     { "replay", do_replay },
+    { "open-launcher", do_open_launcher },
     /*
     { "decrease-visibility", do_decrease_visibility },
     { "increase-visibility", do_increase_visibility },
@@ -1564,11 +1448,10 @@ static struct {
     { "print-visible-scene", do_print_visible_scene_info },
     { "reload-shaders", do_reload_shaders },
     { "reload-materials", do_materials_reload },
-
-#if GOOGLE_PERFTOOLS_FOUND == YES
+    { "set-scenery-paths", do_set_scenery_paths },
+  
     { "profiler-start", do_profiler_start },
     { "profiler-stop",  do_profiler_stop },
-#endif
 
     { 0, 0 }                   // zero-terminated
 };
@@ -1592,6 +1475,9 @@ fgInitCommands ()
   typedef bool (*dummy)();
   fgTie( "/command/view/next", dummy(0), do_view_next );
   fgTie( "/command/view/prev", dummy(0), do_view_prev );
+
+  globals->get_props()->setValueReadOnly( "/sim/debug/profiler-available",
+                                          bool(FG_HAVE_GPERFTOOLS) );
 }
 
 // end of fg_commands.cxx