+/**
+ * Built-in command: replay the FDR buffer
+ */
+static bool
+do_replay (const SGPropertyNode * arg)
+{
+ // freeze the master fdm
+ fgSetInt( "/sim/freeze/replay-state", 1 );
+
+ FGReplay *r = (FGReplay *)(globals->get_subsystem( "replay" ));
+
+ fgSetDouble( "/sim/replay/start-time", r->get_start_time() );
+ fgSetDouble( "/sim/replay/end-time", r->get_end_time() );
+ double duration = fgGetDouble( "/sim/replay/duration" );
+ if( duration && duration < (r->get_end_time() - r->get_start_time()) ) {
+ fgSetDouble( "/sim/replay/time", r->get_end_time() - duration );
+ } else {
+ fgSetDouble( "/sim/replay/time", r->get_start_time() );
+ }
+
+ // cout << "start = " << r->get_start_time()
+ // << " end = " << r->get_end_time() << endl;
+
+ return true;
+}
+
+
+static bool
+do_decrease_visibility (const SGPropertyNode * arg)
+{
+ double new_value = fgGetDouble("/environment/visibility-m") * 0.9;
+ fgSetDouble("/environment/visibility-m", new_value);
+ fgDefaultWeatherValue("visibility-m", new_value);
+ globals->get_subsystem("environment")->reinit();
+
+ return true;
+}
+
+static bool
+do_increase_visibility (const SGPropertyNode * arg)
+{
+ double new_value = fgGetDouble("/environment/visibility-m") * 1.1;
+ fgSetDouble("/environment/visibility-m", new_value);
+ fgDefaultWeatherValue("visibility-m", new_value);
+ globals->get_subsystem("environment")->reinit();
+
+ return true;
+}
+
+static bool
+do_hud_init(const SGPropertyNode *)
+{
+ fgHUDInit(0); // minimal HUD
+ return true;
+}
+
+static bool
+do_hud_init2(const SGPropertyNode *)
+{
+ fgHUDInit2(0); // normal HUD
+ return true;
+}
+
+
+/**
+ * An fgcommand to allow loading of xml files via nasal,
+ * the xml file's structure will be made available within
+ * a property tree node defined under argument "targetnode",
+ * or in the given argument tree under "data" otherwise.
+ *
+ * @param filename a string to hold the complete path & filename of an XML file
+ * @param targetnode a string pointing to a location within the property tree
+ * where to store the parsed XML file. If <targetnode> is undefined, then the
+ * file contents are stored under a node <data> in the argument tree.
+ */
+
+static bool
+do_load_xml_to_proptree(const SGPropertyNode * arg)
+{
+ SGPath file(arg->getStringValue("filename"));
+ if (file.str().empty())
+ return false;
+
+ if (file.extension() != "xml")
+ file.concat(".xml");
+
+ if (!fgValidatePath(file.c_str(), false)) {
+ SG_LOG(SG_IO, SG_ALERT, "loadxml: reading '" << file.str() << "' denied "
+ "(unauthorized access)");
+ return false;
+ }
+
+ SGPropertyNode *targetnode;
+ if (arg->hasValue("targetnode"))
+ targetnode = fgGetNode(arg->getStringValue("targetnode"), true);
+ else
+ targetnode = const_cast<SGPropertyNode *>(arg)->getNode("data", true);
+
+ try {
+ readProperties(file.c_str(), targetnode, true);
+ } catch (const sg_exception &e) {
+ SG_LOG(SG_IO, SG_WARN, "loadxml: " << e.getFormattedMessage());
+ return false;
+ }
+
+ return true;
+}
+
+
+/**
+ * An fgcommand to allow saving of xml files via nasal,
+ * the file's structure will be determined based on what's
+ * encountered in the passed (source) property tree node
+ *
+ * @param filename a string to hold the complete path & filename of the (new)
+ * XML file
+ * @param sourcenode a string pointing to a location within the property tree
+ * where to find the nodes that should be written recursively into an XML file
+ * @param data if no sourcenode is given, then the file contents are taken from
+ * the argument tree's "data" node.
+ */
+
+static bool
+do_save_xml_from_proptree(const SGPropertyNode * arg)
+{
+ SGPath file(arg->getStringValue("filename"));
+ if (file.str().empty())
+ return false;
+
+ if (file.extension() != "xml")
+ file.concat(".xml");
+
+ if (!fgValidatePath(file.c_str(), true)) {
+ SG_LOG(SG_IO, SG_ALERT, "savexml: writing to '" << file.str() << "' denied "
+ "(unauthorized access)");
+ return false;
+ }
+
+ SGPropertyNode *sourcenode;
+ if (arg->hasValue("sourcenode"))
+ sourcenode = fgGetNode(arg->getStringValue("sourcenode"), true);
+ else if (arg->getNode("data", false))
+ sourcenode = const_cast<SGPropertyNode *>(arg)->getNode("data");
+ else
+ return false;
+
+ try {
+ writeProperties (file.c_str(), sourcenode, true);
+ } catch (const sg_exception &e) {
+ SG_LOG(SG_IO, SG_WARN, "savexml: " << e.getFormattedMessage());
+ return false;
+ }
+
+ return true;
+}
+
+static bool
+do_press_cockpit_button (const SGPropertyNode *arg)
+{
+ const char *prefix = arg->getStringValue("prefix");
+
+ if (arg->getBoolValue("guarded") && fgGetDouble((string(prefix) + "-guard").c_str()) < 1)
+ return true;
+
+ string prop = string(prefix) + "-button";
+ double value;
+
+ if (arg->getBoolValue("latching"))
+ value = fgGetDouble(prop.c_str()) > 0 ? 0 : 1;
+ else
+ value = 1;
+
+ fgSetDouble(prop.c_str(), value);
+ fgSetBool(arg->getStringValue("discrete"), value > 0);
+
+ return true;
+}
+
+static bool
+do_release_cockpit_button (const SGPropertyNode *arg)
+{
+ const char *prefix = arg->getStringValue("prefix");
+
+ if (arg->getBoolValue("guarded")) {
+ string prop = string(prefix) + "-guard";
+ if (fgGetDouble(prop.c_str()) < 1) {
+ fgSetDouble(prop.c_str(), 1);
+ return true;
+ }
+ }
+
+ if (! arg->getBoolValue("latching")) {
+ fgSetDouble((string(prefix) + "-button").c_str(), 0);
+ fgSetBool(arg->getStringValue("discrete"), false);
+ }
+
+ return true;
+}