+// Parse XML file.
+// parsexml(<path> [, <start-tag> [, <end-tag> [, <data> [, <pi>]]]]);
+//
+// <path> ... absolute path of an XML file
+// <start-tag> ... callback function with two args: tag name, attribute hash
+// <end-tag> ... callback function with one arg: tag name
+// <data> ... callback function with one arg: data
+// <pi> ... callback function with two args: target, data
+// (pi = "processing instruction")
+// All four callback functions are optional and default to nil.
+// The function returns nil on error, and the file name otherwise.
+static naRef f_parsexml(naContext c, naRef me, int argc, naRef* args)
+{
+ if(argc < 1 || !naIsString(args[0]))
+ naRuntimeError(c, "parsexml(): path argument missing or not a string");
+ if(argc > 5) argc = 5;
+ for(int i=1; i<argc; i++)
+ if(!(naIsNil(args[i]) || naIsFunc(args[i])))
+ naRuntimeError(c, "parsexml(): callback argument not a function");
+
+ const char* file = naStr_data(args[0]);
+ std::ifstream input(file);
+ NasalXMLVisitor visitor(c, argc, args);
+ try {
+ readXML(input, visitor);
+ } catch (const sg_exception& e) {
+ string msg = string("parsexml(): file '") + file + "' "
+ + e.getFormattedMessage();
+ naRuntimeError(c, msg.c_str());
+ return naNil();
+ }
+ return args[0];
+}
+
+// Return UNIX epoch time in seconds.
+static naRef f_systime(naContext c, naRef me, int argc, naRef* args)
+{
+#ifdef WIN32
+ FILETIME ft;
+ GetSystemTimeAsFileTime(&ft);
+ double t = (4294967296.0 * ft.dwHighDateTime + ft.dwLowDateTime);
+ // Converts from 100ns units in 1601 epoch to unix epoch in sec
+ return naNum((t * 1e-7) - 11644473600.0);
+#else
+ time_t t;
+ struct timeval td;
+ do { t = time(0); gettimeofday(&td, 0); } while(t != time(0));
+ return naNum(t + 1e-6 * td.tv_usec);
+#endif
+
+}
+
+// Convert a cartesian point to a geodetic lat/lon/altitude.
+static naRef f_carttogeod(naContext c, naRef me, int argc, naRef* args)
+{
+ double lat, lon, alt, xyz[3];
+ if(argc != 3) naRuntimeError(c, "carttogeod() expects 3 arguments");
+ for(int i=0; i<3; i++)
+ xyz[i] = naNumValue(args[i]).num;
+ sgCartToGeod(xyz, &lat, &lon, &alt);
+ lat *= SG_RADIANS_TO_DEGREES;
+ lon *= SG_RADIANS_TO_DEGREES;
+ naRef vec = naNewVector(c);
+ naVec_append(vec, naNum(lat));
+ naVec_append(vec, naNum(lon));
+ naVec_append(vec, naNum(alt));
+ return vec;
+}
+
+// Convert a geodetic lat/lon/altitude to a cartesian point.
+static naRef f_geodtocart(naContext c, naRef me, int argc, naRef* args)
+{
+ if(argc != 3) naRuntimeError(c, "geodtocart() expects 3 arguments");
+ double lat = naNumValue(args[0]).num * SG_DEGREES_TO_RADIANS;
+ double lon = naNumValue(args[1]).num * SG_DEGREES_TO_RADIANS;
+ double alt = naNumValue(args[2]).num;
+ double xyz[3];
+ sgGeodToCart(lat, lon, alt, xyz);
+ naRef vec = naNewVector(c);
+ naVec_append(vec, naNum(xyz[0]));
+ naVec_append(vec, naNum(xyz[1]));
+ naVec_append(vec, naNum(xyz[2]));
+ return vec;
+}
+
+// For given geodetic point return array with elevation, and a material data
+// hash, or nil if there's no information available (tile not loaded). If
+// information about the material isn't available, then nil is returned instead
+// of the hash.
+static naRef f_geodinfo(naContext c, naRef me, int argc, naRef* args)
+{
+#define HASHSET(s,l,n) naHash_set(matdata, naStr_fromdata(naNewString(c),s,l),n)
+ if(argc != 2) naRuntimeError(c, "geodinfo() expects 2 arguments: lat, lon");
+ double lat = naNumValue(args[0]).num;
+ double lon = naNumValue(args[1]).num;
+ double elev;
+ const SGMaterial *mat;
+ if(!globals->get_scenery()->get_elevation_m(lat, lon, 10000.0, elev, &mat))
+ return naNil();
+ naRef vec = naNewVector(c);
+ naVec_append(vec, naNum(elev));
+ naRef matdata = naNil();
+ if(mat) {
+ matdata = naNewHash(c);
+ naRef names = naNewVector(c);
+ const vector<string> n = mat->get_names();
+ for(unsigned int i=0; i<n.size(); i++)
+ naVec_append(names, naStr_fromdata(naNewString(c),
+ const_cast<char*>(n[i].c_str()), n[i].size()));
+ HASHSET("names", 5, names);
+ HASHSET("solid", 5, naNum(mat->get_solid()));
+ HASHSET("friction_factor", 15, naNum(mat->get_friction_factor()));
+ HASHSET("rolling_friction", 16, naNum(mat->get_rolling_friction()));
+ HASHSET("load_resistance", 15, naNum(mat->get_load_resistance()));
+ HASHSET("bumpiness", 9, naNum(mat->get_bumpiness()));
+ HASHSET("light_coverage", 14, naNum(mat->get_light_coverage()));
+ }
+ naVec_append(vec, matdata);
+ return vec;
+#undef HASHSET
+}
+