Merge branch 'next' of ssh://git.code.sf.net/p/flightgear/simgear into next
This commit is contained in:
+1
-1
@@ -49,7 +49,7 @@ set(CMAKE_OSX_DEPLOYMENT_TARGET "10.7")
|
|||||||
# add a dependency on the versino file
|
# add a dependency on the versino file
|
||||||
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
|
set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS version)
|
||||||
|
|
||||||
set(FIND_LIBRARY_USE_LIB64_PATHS ON)
|
set_property(GLOBAL PROPERTY FIND_LIBRARY_USE_LIB64_PATHS TRUE)
|
||||||
|
|
||||||
# use simgear version also as the SO version (if building SOs)
|
# use simgear version also as the SO version (if building SOs)
|
||||||
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
|
SET(SIMGEAR_SOVERSION ${SIMGEAR_VERSION})
|
||||||
|
|||||||
@@ -47,6 +47,12 @@ namespace canvas
|
|||||||
const std::string GEO = "-geo";
|
const std::string GEO = "-geo";
|
||||||
const std::string HDG = "hdg";
|
const std::string HDG = "hdg";
|
||||||
const std::string Map::TYPE_NAME = "map";
|
const std::string Map::TYPE_NAME = "map";
|
||||||
|
const std::string WEB_MERCATOR = "webmercator";
|
||||||
|
const std::string REF_LAT = "ref-lat";
|
||||||
|
const std::string REF_LON = "ref-lon";
|
||||||
|
const std::string SCREEN_RANGE = "screen-range";
|
||||||
|
const std::string RANGE = "range";
|
||||||
|
const std::string PROJECTION = "projection";
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
void Map::staticInit()
|
void Map::staticInit()
|
||||||
@@ -64,12 +70,16 @@ namespace canvas
|
|||||||
const SGPropertyNode_ptr& node,
|
const SGPropertyNode_ptr& node,
|
||||||
const Style& parent_style,
|
const Style& parent_style,
|
||||||
ElementWeakPtr parent ):
|
ElementWeakPtr parent ):
|
||||||
Group(canvas, node, parent_style, parent),
|
Group(canvas, node, parent_style, parent)
|
||||||
// TODO make projection configurable
|
|
||||||
_projection(new SansonFlamsteedProjection),
|
|
||||||
_projection_dirty(true)
|
|
||||||
{
|
{
|
||||||
staticInit();
|
staticInit();
|
||||||
|
if (node->getChild(PROJECTION) &&
|
||||||
|
(node->getChild(PROJECTION)->getStringValue() == WEB_MERCATOR)) {
|
||||||
|
_projection = (boost::shared_ptr<HorizontalProjection>) new WebMercatorProjection();
|
||||||
|
} else {
|
||||||
|
_projection = (boost::shared_ptr<HorizontalProjection>) new SansonFlamsteedProjection();
|
||||||
|
}
|
||||||
|
_projection_dirty = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
//----------------------------------------------------------------------------
|
//----------------------------------------------------------------------------
|
||||||
@@ -162,10 +172,10 @@ namespace canvas
|
|||||||
if( child->getParent() != _node )
|
if( child->getParent() != _node )
|
||||||
return Group::childChanged(child);
|
return Group::childChanged(child);
|
||||||
|
|
||||||
if( child->getNameString() == "ref-lat"
|
if( child->getNameString() == REF_LAT
|
||||||
|| child->getNameString() == "ref-lon" )
|
|| child->getNameString() == REF_LON )
|
||||||
_projection->setWorldPosition( _node->getDoubleValue("ref-lat"),
|
_projection->setWorldPosition( _node->getDoubleValue(REF_LAT),
|
||||||
_node->getDoubleValue("ref-lon") );
|
_node->getDoubleValue(REF_LON) );
|
||||||
else if( child->getNameString() == HDG )
|
else if( child->getNameString() == HDG )
|
||||||
{
|
{
|
||||||
_projection->setOrientation(child->getFloatValue());
|
_projection->setOrientation(child->getFloatValue());
|
||||||
@@ -174,11 +184,23 @@ namespace canvas
|
|||||||
++it )
|
++it )
|
||||||
hdgNodeChanged(*it);
|
hdgNodeChanged(*it);
|
||||||
}
|
}
|
||||||
else if( child->getNameString() == "range" )
|
else if( child->getNameString() == RANGE )
|
||||||
_projection->setRange(child->getDoubleValue());
|
_projection->setRange(child->getDoubleValue());
|
||||||
else if( child->getNameString() == "screen-range" )
|
else if( child->getNameString() == SCREEN_RANGE )
|
||||||
_projection->setScreenRange(child->getDoubleValue());
|
_projection->setScreenRange(child->getDoubleValue());
|
||||||
else
|
else if( child->getNameString() == PROJECTION ) {
|
||||||
|
if (child->getStringValue() == WEB_MERCATOR) {
|
||||||
|
_projection = (boost::shared_ptr<HorizontalProjection>) new WebMercatorProjection();
|
||||||
|
} else {
|
||||||
|
_projection = (boost::shared_ptr<HorizontalProjection>) new SansonFlamsteedProjection();
|
||||||
|
}
|
||||||
|
_projection->setWorldPosition(_node->getDoubleValue(REF_LAT),
|
||||||
|
_node->getDoubleValue(REF_LON) );
|
||||||
|
_projection->setOrientation(_node->getFloatValue(HDG));
|
||||||
|
_projection->setScreenRange(_node->getDoubleValue(SCREEN_RANGE));
|
||||||
|
_projection->setRange(_node->getDoubleValue(RANGE));
|
||||||
|
_projection_dirty = true;
|
||||||
|
} else
|
||||||
return Group::childChanged(child);
|
return Group::childChanged(child);
|
||||||
|
|
||||||
_projection_dirty = true;
|
_projection_dirty = true;
|
||||||
|
|||||||
@@ -190,6 +190,32 @@ namespace canvas
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* WebMercator projection, relative to the projection center.
|
||||||
|
* Required for Slippy Maps - i.e. openstreetmap
|
||||||
|
*/
|
||||||
|
class WebMercatorProjection:
|
||||||
|
public HorizontalProjection
|
||||||
|
{
|
||||||
|
protected:
|
||||||
|
|
||||||
|
virtual ScreenPosition project(double lat, double lon) const
|
||||||
|
{
|
||||||
|
double d_lat = lat - _ref_lat,
|
||||||
|
d_lon = lon - _ref_lon;
|
||||||
|
double r = 6378137.f / 1852; // Equatorial radius divided by ?
|
||||||
|
|
||||||
|
ScreenPosition pos;
|
||||||
|
|
||||||
|
pos.x = r * d_lon;
|
||||||
|
pos.y = r * (log(tan(d_lat) + 1.0 / cos(d_lat)));
|
||||||
|
//pos.x = lon;
|
||||||
|
//pos.y = log(tan(lat) + 1.0 / cos(lat));
|
||||||
|
return pos;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
} // namespace canvas
|
} // namespace canvas
|
||||||
} // namespace simgear
|
} // namespace simgear
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
include (SimGearComponent)
|
include (SimGearComponent)
|
||||||
|
|
||||||
set(HEADERS EmbeddedResource.hxx EmbeddedResourceManager.hxx)
|
set(HEADERS EmbeddedResource.hxx EmbeddedResourceManager.hxx ResourceProxy.hxx)
|
||||||
set(SOURCES EmbeddedResource.cxx EmbeddedResourceManager.cxx)
|
set(SOURCES EmbeddedResource.cxx EmbeddedResourceManager.cxx ResourceProxy.cxx)
|
||||||
|
|
||||||
simgear_component(embedded_resources embedded_resources
|
simgear_component(embedded_resources embedded_resources
|
||||||
"${SOURCES}" "${HEADERS}")
|
"${SOURCES}" "${HEADERS}")
|
||||||
|
|||||||
@@ -0,0 +1,247 @@
|
|||||||
|
// -*- coding: utf-8 -*-
|
||||||
|
//
|
||||||
|
// ResourceProxy.cxx --- Unified access to real files or embedded resources
|
||||||
|
// Copyright (C) 2017 Florent Rougon
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA.
|
||||||
|
|
||||||
|
#include <simgear_config.h>
|
||||||
|
|
||||||
|
#include <algorithm> // std::find()
|
||||||
|
#include <ios> // std::streamsize
|
||||||
|
#include <istream>
|
||||||
|
#include <limits> // std::numeric_limits
|
||||||
|
#include <memory>
|
||||||
|
#include <vector>
|
||||||
|
#include <string>
|
||||||
|
#include <cstdlib> // std::size_t
|
||||||
|
#include <cassert>
|
||||||
|
|
||||||
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
#include <simgear/misc/strutils.hxx>
|
||||||
|
#include <simgear/io/iostreams/sgstream.hxx>
|
||||||
|
#include <simgear/structure/exception.hxx>
|
||||||
|
#include "EmbeddedResourceManager.hxx"
|
||||||
|
#include "ResourceProxy.hxx"
|
||||||
|
|
||||||
|
using std::string;
|
||||||
|
using std::vector;
|
||||||
|
using std::shared_ptr;
|
||||||
|
using std::unique_ptr;
|
||||||
|
|
||||||
|
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
ResourceProxy::ResourceProxy(const SGPath& realRoot, const string& virtualRoot,
|
||||||
|
bool useEmbeddedResourcesByDefault)
|
||||||
|
: _realRoot(realRoot),
|
||||||
|
_virtualRoot(normalizeVirtualRoot(virtualRoot)),
|
||||||
|
_useEmbeddedResourcesByDefault(useEmbeddedResourcesByDefault)
|
||||||
|
{ }
|
||||||
|
|
||||||
|
SGPath
|
||||||
|
ResourceProxy::getRealRoot() const
|
||||||
|
{ return _realRoot; }
|
||||||
|
|
||||||
|
void
|
||||||
|
ResourceProxy::setRealRoot(const SGPath& realRoot)
|
||||||
|
{ _realRoot = realRoot; }
|
||||||
|
|
||||||
|
string
|
||||||
|
ResourceProxy::getVirtualRoot() const
|
||||||
|
{ return _virtualRoot; }
|
||||||
|
|
||||||
|
void
|
||||||
|
ResourceProxy::setVirtualRoot(const string& virtualRoot)
|
||||||
|
{ _virtualRoot = normalizeVirtualRoot(virtualRoot); }
|
||||||
|
|
||||||
|
bool
|
||||||
|
ResourceProxy::getUseEmbeddedResources() const
|
||||||
|
{ return _useEmbeddedResourcesByDefault; }
|
||||||
|
|
||||||
|
void
|
||||||
|
ResourceProxy::setUseEmbeddedResources(bool useEmbeddedResources)
|
||||||
|
{ _useEmbeddedResourcesByDefault = useEmbeddedResources; }
|
||||||
|
|
||||||
|
// Static method: normalize the 'virtualRoot' argument of the constructor
|
||||||
|
//
|
||||||
|
// The argument must start with a slash and mustn't contain any '.' or '..'
|
||||||
|
// component. The return value never ends with a slash.
|
||||||
|
string
|
||||||
|
ResourceProxy::normalizeVirtualRoot(const string& path)
|
||||||
|
{
|
||||||
|
ResourceProxy::checkPath(__func__, path, false /* allowStartWithColon */);
|
||||||
|
string res = path;
|
||||||
|
|
||||||
|
// Make sure 'res' doesn't end with a '/'.
|
||||||
|
while (!res.empty() && res.back() == '/') {
|
||||||
|
res.pop_back(); // This will ease path concatenation
|
||||||
|
}
|
||||||
|
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Static method
|
||||||
|
void
|
||||||
|
ResourceProxy::checkPath(const string& callerMethod, const string& path,
|
||||||
|
bool allowStartWithColon) {
|
||||||
|
if (path.empty()) {
|
||||||
|
throw sg_format_exception(
|
||||||
|
"Invalid empty path for ResourceProxy::" + callerMethod + "(): '" +
|
||||||
|
path + "'", path);
|
||||||
|
} else if (allowStartWithColon &&
|
||||||
|
!simgear::strutils::starts_with(path, ":/") && path[0] != '/') {
|
||||||
|
throw sg_format_exception(
|
||||||
|
"Invalid path for ResourceProxy::" + callerMethod + "(): it should "
|
||||||
|
"start with either ':/' or '/'", path);
|
||||||
|
} else if (!allowStartWithColon && path[0] != '/') {
|
||||||
|
throw sg_format_exception(
|
||||||
|
"Invalid path for ResourceProxy::" + callerMethod + "(): it should "
|
||||||
|
"start with a slash ('/')", path);
|
||||||
|
} else {
|
||||||
|
const vector<string> components = simgear::strutils::split(path, "/");
|
||||||
|
auto find = [&components](const string& s) -> bool {
|
||||||
|
return (std::find(components.begin(), components.end(), s) !=
|
||||||
|
components.end());
|
||||||
|
};
|
||||||
|
|
||||||
|
if (find(".") || find("..")) {
|
||||||
|
throw sg_format_exception(
|
||||||
|
"Invalid path for ResourceProxy::" + callerMethod + "(): "
|
||||||
|
"'.' and '..' components are not allowed", path);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_ptr<std::istream>
|
||||||
|
ResourceProxy::getIStream(const string& path, bool fromEmbeddedResource) const
|
||||||
|
{
|
||||||
|
ResourceProxy::checkPath(__func__, path, false /* allowStartWithColon */);
|
||||||
|
assert(!path.empty() && path.front() == '/');
|
||||||
|
|
||||||
|
if (fromEmbeddedResource) {
|
||||||
|
const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance();
|
||||||
|
return embeddedResMgr->getIStream(
|
||||||
|
_virtualRoot + path,
|
||||||
|
""); // fetch the default-locale version of the resource
|
||||||
|
} else {
|
||||||
|
const SGPath sgPath = _realRoot / path.substr(std::size_t(1));
|
||||||
|
return unique_ptr<std::istream>(new sg_ifstream(sgPath));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_ptr<std::istream>
|
||||||
|
ResourceProxy::getIStream(const string& path) const
|
||||||
|
{
|
||||||
|
return getIStream(path, _useEmbeddedResourcesByDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
unique_ptr<std::istream>
|
||||||
|
ResourceProxy::getIStreamDecideOnPrefix(const string& path) const
|
||||||
|
{
|
||||||
|
ResourceProxy::checkPath(__func__, path, true /* allowStartWithColon */);
|
||||||
|
|
||||||
|
// 'path' is non-empty
|
||||||
|
if (path.front() == '/') {
|
||||||
|
return getIStream(path, false /* fromEmbeddedResource */);
|
||||||
|
} else if (path.front() == ':') {
|
||||||
|
assert(path.size() >= 2 && path[1] == '/');
|
||||||
|
// Skip the leading ':'
|
||||||
|
return getIStream(path.substr(std::size_t(1)),
|
||||||
|
true /* fromEmbeddedResource */);
|
||||||
|
} else {
|
||||||
|
// The checkPath() call should make it impossible to reach this point.
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
ResourceProxy::getString(const string& path, bool fromEmbeddedResource) const
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
|
||||||
|
ResourceProxy::checkPath(__func__, path, false /* allowStartWithColon */);
|
||||||
|
assert(!path.empty() && path.front() == '/');
|
||||||
|
|
||||||
|
if (fromEmbeddedResource) {
|
||||||
|
const auto& embeddedResMgr = simgear::EmbeddedResourceManager::instance();
|
||||||
|
// Fetch the default-locale version of the resource
|
||||||
|
result = embeddedResMgr->getString(_virtualRoot + path, "");
|
||||||
|
} else {
|
||||||
|
const SGPath sgPath = _realRoot / path.substr(std::size_t(1));
|
||||||
|
result.reserve(sgPath.sizeInBytes());
|
||||||
|
const unique_ptr<std::istream> streamp = getIStream(path,
|
||||||
|
fromEmbeddedResource);
|
||||||
|
std::streamsize nbCharsRead;
|
||||||
|
|
||||||
|
// Allocate a buffer
|
||||||
|
static constexpr std::size_t bufSize = 65536;
|
||||||
|
static_assert(bufSize <= std::numeric_limits<std::streamsize>::max(),
|
||||||
|
"Type std::streamsize is unexpectedly small");
|
||||||
|
static_assert(bufSize <= std::numeric_limits<string::size_type>::max(),
|
||||||
|
"Type std::string::size_type is unexpectedly small");
|
||||||
|
unique_ptr<char[]> buf(new char[bufSize]);
|
||||||
|
|
||||||
|
do {
|
||||||
|
streamp->read(buf.get(), bufSize);
|
||||||
|
nbCharsRead = streamp->gcount();
|
||||||
|
|
||||||
|
if (nbCharsRead > 0) {
|
||||||
|
result.append(buf.get(), nbCharsRead);
|
||||||
|
}
|
||||||
|
} while (*streamp);
|
||||||
|
|
||||||
|
// streamp->fail() would *not* indicate an error, due to the semantics
|
||||||
|
// of std::istream::read().
|
||||||
|
if (streamp->bad()) {
|
||||||
|
throw sg_io_exception("Error reading from file", sg_location(path));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
ResourceProxy::getString(const string& path) const
|
||||||
|
{
|
||||||
|
return getString(path, _useEmbeddedResourcesByDefault);
|
||||||
|
}
|
||||||
|
|
||||||
|
string
|
||||||
|
ResourceProxy::getStringDecideOnPrefix(const string& path) const
|
||||||
|
{
|
||||||
|
string result;
|
||||||
|
|
||||||
|
ResourceProxy::checkPath(__func__, path, true /* allowStartWithColon */);
|
||||||
|
|
||||||
|
// 'path' is non-empty
|
||||||
|
if (path.front() == '/') {
|
||||||
|
result = getString(path, false /* fromEmbeddedResource */);
|
||||||
|
} else if (path.front() == ':') {
|
||||||
|
assert(path.size() >= 2 && path[1] == '/');
|
||||||
|
// Skip the leading ':'
|
||||||
|
result = getString(path.substr(std::size_t(1)),
|
||||||
|
true /* fromEmbeddedResource */);
|
||||||
|
} else {
|
||||||
|
// The checkPath() call should make it impossible to reach this point.
|
||||||
|
std::abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
// -*- coding: utf-8 -*-
|
||||||
|
//
|
||||||
|
// ResourceProxy.hxx --- Unified access to real files or embedded resources
|
||||||
|
// Copyright (C) 2017 Florent Rougon
|
||||||
|
//
|
||||||
|
// This library is free software; you can redistribute it and/or
|
||||||
|
// modify it under the terms of the GNU Library General Public
|
||||||
|
// License as published by the Free Software Foundation; either
|
||||||
|
// version 2 of the License, or (at your option) any later version.
|
||||||
|
//
|
||||||
|
// This library 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
|
||||||
|
// Library General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Library General Public
|
||||||
|
// License along with this library; if not, write to the Free Software
|
||||||
|
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
|
||||||
|
// MA 02110-1301 USA.
|
||||||
|
|
||||||
|
#ifndef FG_RESOURCEPROXY_HXX
|
||||||
|
#define FG_RESOURCEPROXY_HXX
|
||||||
|
|
||||||
|
#include <istream>
|
||||||
|
#include <memory>
|
||||||
|
#include <string>
|
||||||
|
|
||||||
|
#include <simgear/misc/sg_path.hxx>
|
||||||
|
|
||||||
|
// The ResourceProxy class allows one to access real files or embedded
|
||||||
|
// resources in a unified way. When using it, one can switch from one data
|
||||||
|
// source to the other with minimal code changes, possibly even at runtime (in
|
||||||
|
// which case there is obviously no code change at all).
|
||||||
|
//
|
||||||
|
// Sample usage of the ResourceProxy class (from FlightGear):
|
||||||
|
//
|
||||||
|
// simgear::ResourceProxy proxy(globals->get_fg_root(), "/FGData");
|
||||||
|
// std::string s = proxy.getString("/some/path");
|
||||||
|
// std::unique_ptr<std::istream> streamp = proxy.getIStream("/some/path");
|
||||||
|
//
|
||||||
|
// The methods ResourceProxy::getString(const std::string& path) and
|
||||||
|
// ResourceProxy::getIStream(const std::string& path) decide whether to use
|
||||||
|
// embedded resources or real files depending on the boolean value passed to
|
||||||
|
// ResourceProxy::setUseEmbeddedResources() (also available as an optional
|
||||||
|
// parameter to the ResourceProxy constructor, defaulting to true). It is
|
||||||
|
// often most convenient to set this boolean once and then don't worry about
|
||||||
|
// it anymore (it is stored inside ResourceProxy). Otherwise, if you want to
|
||||||
|
// fetch resources some times from real files, other times from embedded
|
||||||
|
// resources, you may use the following methods:
|
||||||
|
//
|
||||||
|
// // Retrieve contents using embedded resources
|
||||||
|
// std:string s = proxy.getString("/some/path", true);
|
||||||
|
// std:string s = proxy.getStringDecideOnPrefix(":/some/path");
|
||||||
|
//
|
||||||
|
// // Retrieve contents using real files
|
||||||
|
// std:string s = proxy.getString("/some/path", false);
|
||||||
|
// std:string s = proxy.getStringDecideOnPrefix("/some/path");
|
||||||
|
//
|
||||||
|
// You can do exactly the same with ResourceProxy::getIStream() and
|
||||||
|
// ResourceProxy::getIStreamDecideOnPrefix(), except they return an
|
||||||
|
// std::unique_ptr<std::istream> instead of an std::string.
|
||||||
|
//
|
||||||
|
// Given how the 'proxy' object was constructed above, each of these calls
|
||||||
|
// will fetch data from either the real file $FG_ROOT/some/path or the
|
||||||
|
// embedded resource whose virtual path is '/FGData/some/path' (more
|
||||||
|
// precisely: the default-locale version of this resource).
|
||||||
|
//
|
||||||
|
// The 'path' argument of ResourceProxy's methods getString(), getIStream(),
|
||||||
|
// getStringDecideOnPrefix() and getIStreamDecideOnPrefix() must:
|
||||||
|
//
|
||||||
|
// - use UTF-8 encoding;
|
||||||
|
//
|
||||||
|
// - start with:
|
||||||
|
// * either '/' or ':/' for the 'DecideOnPrefix' variants;
|
||||||
|
// * only '/' for the other methods.
|
||||||
|
//
|
||||||
|
// - have its components separated by slashes;
|
||||||
|
//
|
||||||
|
// - not contain any '.' or '..' component.
|
||||||
|
//
|
||||||
|
// For the 'DecideOnPrefix' variants:
|
||||||
|
//
|
||||||
|
// - if the path starts with a slash ('/'), a real file access is done;
|
||||||
|
//
|
||||||
|
// - if, on the other hand, it starts with ':/', ResourceProxy uses the
|
||||||
|
// embedded resource whose virtual path is the specified path without its
|
||||||
|
// leading ':' (more precisely: the default-locale version of this
|
||||||
|
// resource).
|
||||||
|
namespace simgear
|
||||||
|
{
|
||||||
|
|
||||||
|
class ResourceProxy
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
// 'virtualRoot' must start with a '/', e.g: '/FGData'. Whether it ends
|
||||||
|
// with a '/' doesn't make a difference.
|
||||||
|
explicit ResourceProxy(const SGPath& realRoot,
|
||||||
|
const std::string& virtualRoot,
|
||||||
|
bool useEmbeddedResourcesByDefault = true);
|
||||||
|
|
||||||
|
// Getters and setters for the corresponding data members
|
||||||
|
SGPath getRealRoot() const;
|
||||||
|
void setRealRoot(const SGPath& realRoot);
|
||||||
|
|
||||||
|
std::string getVirtualRoot() const;
|
||||||
|
void setVirtualRoot(const std::string& virtualRoot);
|
||||||
|
|
||||||
|
bool getUseEmbeddedResources() const;
|
||||||
|
void setUseEmbeddedResources(bool useEmbeddedResources);
|
||||||
|
|
||||||
|
// Get an std::istream to read from a file or from an embedded resource.
|
||||||
|
std::unique_ptr<std::istream>
|
||||||
|
getIStream(const std::string& path, bool fromEmbeddedResource) const;
|
||||||
|
|
||||||
|
std::unique_ptr<std::istream>
|
||||||
|
getIStream(const std::string& path) const;
|
||||||
|
|
||||||
|
std::unique_ptr<std::istream>
|
||||||
|
getIStreamDecideOnPrefix(const std::string& path) const;
|
||||||
|
|
||||||
|
// Get a file or embedded resource contents as a string.
|
||||||
|
std::string
|
||||||
|
getString(const std::string& path, bool fromEmbeddedResource) const;
|
||||||
|
|
||||||
|
std::string
|
||||||
|
getString(const std::string& path) const;
|
||||||
|
|
||||||
|
std::string
|
||||||
|
getStringDecideOnPrefix(const std::string& path) const;
|
||||||
|
|
||||||
|
private:
|
||||||
|
// Check that 'path' starts with either ':/' or '/', and doesn't contain any
|
||||||
|
// '..' component ('path' may only start with ':/' if 'allowStartWithColon'
|
||||||
|
// is true).
|
||||||
|
static void
|
||||||
|
checkPath(const std::string& callerMethod, const std::string& path,
|
||||||
|
bool allowStartWithColon);
|
||||||
|
|
||||||
|
// Normalize the 'virtualRoot' argument of the constructor. The argument
|
||||||
|
// must start with a '/' and mustn't contain any '.' or '..' component. The
|
||||||
|
// return value never ends with a '/'.
|
||||||
|
static std::string
|
||||||
|
normalizeVirtualRoot(const std::string& path);
|
||||||
|
|
||||||
|
SGPath _realRoot;
|
||||||
|
std::string _virtualRoot;
|
||||||
|
bool _useEmbeddedResourcesByDefault;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // of namespace simgear
|
||||||
|
|
||||||
|
#endif // of FG_RESOURCEPROXY_HXX
|
||||||
@@ -33,11 +33,14 @@
|
|||||||
#include <cstddef> // std::size_t
|
#include <cstddef> // std::size_t
|
||||||
|
|
||||||
#include <simgear/misc/test_macros.hxx>
|
#include <simgear/misc/test_macros.hxx>
|
||||||
|
#include <simgear/misc/sg_dir.hxx>
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
#include <simgear/io/iostreams/CharArrayStream.hxx>
|
#include <simgear/io/iostreams/CharArrayStream.hxx>
|
||||||
|
#include <simgear/io/iostreams/sgstream.hxx>
|
||||||
#include <simgear/io/iostreams/zlibstream.hxx>
|
#include <simgear/io/iostreams/zlibstream.hxx>
|
||||||
#include "EmbeddedResource.hxx"
|
#include "EmbeddedResource.hxx"
|
||||||
#include "EmbeddedResourceManager.hxx"
|
#include "EmbeddedResourceManager.hxx"
|
||||||
|
#include "ResourceProxy.hxx"
|
||||||
|
|
||||||
using std::cout;
|
using std::cout;
|
||||||
using std::cerr;
|
using std::cerr;
|
||||||
@@ -395,6 +398,102 @@ void test_getLocaleAndSelectLocale()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Auxiliary function for test_ResourceProxy()
|
||||||
|
void auxTest_ResourceProxy_getIStream(unique_ptr<std::istream> iStream,
|
||||||
|
const string& contents)
|
||||||
|
{
|
||||||
|
cout << "Testing ResourceProxy::getIStream()" << endl;
|
||||||
|
|
||||||
|
iStream->exceptions(std::ios_base::badbit);
|
||||||
|
static constexpr std::size_t bufSize = 65536;
|
||||||
|
unique_ptr<char[]> buf(new char[bufSize]); // intermediate buffer
|
||||||
|
string result;
|
||||||
|
|
||||||
|
do {
|
||||||
|
iStream->read(buf.get(), bufSize);
|
||||||
|
result.append(buf.get(), iStream->gcount());
|
||||||
|
} while (*iStream); // iStream *points* to an std::istream
|
||||||
|
|
||||||
|
// 1) If set, badbit would have caused an exception to be raised (see above).
|
||||||
|
// 2) failbit doesn't necessarily indicate an error here: it is set as soon
|
||||||
|
// as the read() call can't provide the requested number of characters.
|
||||||
|
SG_VERIFY(iStream->eof() && !iStream->bad());
|
||||||
|
SG_CHECK_EQUAL(result, contents);
|
||||||
|
}
|
||||||
|
|
||||||
|
void test_ResourceProxy()
|
||||||
|
{
|
||||||
|
cout << "Testing the ResourceProxy class" << endl;
|
||||||
|
|
||||||
|
// Initialize stuff we need and create two files containing the contents of
|
||||||
|
// the default-locale version of two embedded resources: those with virtual
|
||||||
|
// paths '/path/to/resource1' and '/path/to/resource2'.
|
||||||
|
const auto& resMgr = EmbeddedResourceManager::instance();
|
||||||
|
simgear::Dir tmpDir = simgear::Dir::tempDir("FlightGear");
|
||||||
|
tmpDir.setRemoveOnDestroy();
|
||||||
|
|
||||||
|
const SGPath path1 = tmpDir.path() / "resource1";
|
||||||
|
const SGPath path2 = tmpDir.path() / "resource2";
|
||||||
|
|
||||||
|
sg_ofstream out1(path1);
|
||||||
|
sg_ofstream out2(path2);
|
||||||
|
const string s1 = resMgr->getString("/path/to/resource1", "");
|
||||||
|
// To make sure in these tests that we can tell whether something came from
|
||||||
|
// a real file or from an embedded resource.
|
||||||
|
const string rs1 = s1 + " from real file";
|
||||||
|
const string rlipsum = lipsum + " from real file";
|
||||||
|
|
||||||
|
out1 << rs1;
|
||||||
|
out1.close();
|
||||||
|
if (!out1) {
|
||||||
|
throw sg_io_exception("Error writing to file", sg_location(path1));
|
||||||
|
}
|
||||||
|
|
||||||
|
out2 << rlipsum;
|
||||||
|
out2.close();
|
||||||
|
if (!out2) {
|
||||||
|
throw sg_io_exception("Error writing to file", sg_location(path2));
|
||||||
|
}
|
||||||
|
|
||||||
|
// 'proxy' defaults to using embedded resources
|
||||||
|
const simgear::ResourceProxy proxy(tmpDir.path(),
|
||||||
|
"/path/to",
|
||||||
|
true /* useEmbeddedResourcesByDefault */);
|
||||||
|
simgear::ResourceProxy rproxy(tmpDir.path(), "/path/to");
|
||||||
|
// 'rproxy' defaults to using real files
|
||||||
|
rproxy.setUseEmbeddedResources(false); // could be done from the ctor too
|
||||||
|
|
||||||
|
// Test ResourceProxy::getString()
|
||||||
|
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource1"), rs1);
|
||||||
|
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource1"), s1);
|
||||||
|
SG_CHECK_EQUAL(proxy.getString("/resource1", false), rs1);
|
||||||
|
SG_CHECK_EQUAL(proxy.getString("/resource1", true), s1);
|
||||||
|
SG_CHECK_EQUAL(proxy.getString("/resource1"), s1);
|
||||||
|
SG_CHECK_EQUAL(rproxy.getString("/resource1"), rs1);
|
||||||
|
|
||||||
|
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix("/resource2"), rlipsum);
|
||||||
|
SG_CHECK_EQUAL(proxy.getStringDecideOnPrefix(":/resource2"), lipsum);
|
||||||
|
SG_CHECK_EQUAL(proxy.getString("/resource2", false), rlipsum);
|
||||||
|
SG_CHECK_EQUAL(proxy.getString("/resource2", true), lipsum);
|
||||||
|
SG_CHECK_EQUAL(proxy.getString("/resource2"), lipsum);
|
||||||
|
SG_CHECK_EQUAL(rproxy.getString("/resource2"), rlipsum);
|
||||||
|
|
||||||
|
// Test ResourceProxy::getIStream()
|
||||||
|
auxTest_ResourceProxy_getIStream(proxy.getIStreamDecideOnPrefix("/resource1"),
|
||||||
|
rs1);
|
||||||
|
auxTest_ResourceProxy_getIStream(proxy.getIStreamDecideOnPrefix(":/resource1"),
|
||||||
|
s1);
|
||||||
|
auxTest_ResourceProxy_getIStream(proxy.getIStream("/resource1"), s1);
|
||||||
|
auxTest_ResourceProxy_getIStream(rproxy.getIStream("/resource1"), rs1);
|
||||||
|
|
||||||
|
auxTest_ResourceProxy_getIStream(proxy.getIStream("/resource2", false),
|
||||||
|
rlipsum);
|
||||||
|
auxTest_ResourceProxy_getIStream(proxy.getIStream("/resource2", true),
|
||||||
|
lipsum);
|
||||||
|
auxTest_ResourceProxy_getIStream(proxy.getIStream("/resource2"), lipsum);
|
||||||
|
auxTest_ResourceProxy_getIStream(rproxy.getIStream("/resource2"), rlipsum);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char **argv)
|
int main(int argc, char **argv)
|
||||||
{
|
{
|
||||||
// Initialize the EmbeddedResourceManager instance, add a few resources
|
// Initialize the EmbeddedResourceManager instance, add a few resources
|
||||||
@@ -407,6 +506,7 @@ int main(int argc, char **argv)
|
|||||||
test_addAlreadyExistingResource();
|
test_addAlreadyExistingResource();
|
||||||
test_localeDependencyOfResourceFetching();
|
test_localeDependencyOfResourceFetching();
|
||||||
test_getLocaleAndSelectLocale();
|
test_getLocaleAndSelectLocale();
|
||||||
|
test_ResourceProxy();
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -142,7 +142,7 @@ public:
|
|||||||
void finishedRequest(const RepoRequestPtr& req);
|
void finishedRequest(const RepoRequestPtr& req);
|
||||||
|
|
||||||
HTTPDirectory* getOrCreateDirectory(const std::string& path);
|
HTTPDirectory* getOrCreateDirectory(const std::string& path);
|
||||||
bool deleteDirectory(const std::string& path);
|
bool deleteDirectory(const std::string& relPath, const SGPath& absPath);
|
||||||
|
|
||||||
typedef std::vector<HTTPDirectory*> DirectoryVector;
|
typedef std::vector<HTTPDirectory*> DirectoryVector;
|
||||||
DirectoryVector directories;
|
DirectoryVector directories;
|
||||||
@@ -317,7 +317,8 @@ public:
|
|||||||
ChildInfoList::iterator c = findIndexChild(it->file());
|
ChildInfoList::iterator c = findIndexChild(it->file());
|
||||||
if (c == children.end()) {
|
if (c == children.end()) {
|
||||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "is orphan '" << it->file() << "'" );
|
SG_LOG(SG_TERRASYNC, SG_DEBUG, "is orphan '" << it->file() << "'" );
|
||||||
orphans.push_back(it->file());
|
|
||||||
|
orphans.push_back(it->file());
|
||||||
} else if (c->hash != hash) {
|
} else if (c->hash != hash) {
|
||||||
SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() );
|
SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() );
|
||||||
// file exists, but hash mismatch, schedule update
|
// file exists, but hash mismatch, schedule update
|
||||||
@@ -534,7 +535,7 @@ private:
|
|||||||
|
|
||||||
std::string fpath = _relativePath + "/" + name;
|
std::string fpath = _relativePath + "/" + name;
|
||||||
if (p.isDir()) {
|
if (p.isDir()) {
|
||||||
ok = _repository->deleteDirectory(fpath);
|
ok = _repository->deleteDirectory(fpath, p);
|
||||||
} else {
|
} else {
|
||||||
// remove the hash cache entry
|
// remove the hash cache entry
|
||||||
_repository->updatedFileContents(p, std::string());
|
_repository->updatedFileContents(p, std::string());
|
||||||
@@ -1044,25 +1045,26 @@ HTTPRepository::failure() const
|
|||||||
return d;
|
return d;
|
||||||
}
|
}
|
||||||
|
|
||||||
bool HTTPRepoPrivate::deleteDirectory(const std::string& path)
|
bool HTTPRepoPrivate::deleteDirectory(const std::string& relPath, const SGPath& absPath)
|
||||||
{
|
{
|
||||||
DirectoryWithPath p(path);
|
DirectoryWithPath p(relPath);
|
||||||
DirectoryVector::iterator it = std::find_if(directories.begin(), directories.end(), p);
|
auto it = std::find_if(directories.begin(), directories.end(), p);
|
||||||
if (it != directories.end()) {
|
if (it != directories.end()) {
|
||||||
HTTPDirectory* d = *it;
|
HTTPDirectory* d = *it;
|
||||||
|
assert(d->absolutePath() == absPath);
|
||||||
directories.erase(it);
|
directories.erase(it);
|
||||||
Dir dir(d->absolutePath());
|
|
||||||
bool result = dir.remove(true);
|
|
||||||
|
|
||||||
// update the hash cache too
|
|
||||||
updatedFileContents(d->absolutePath(), std::string());
|
|
||||||
|
|
||||||
delete d;
|
delete d;
|
||||||
|
} else {
|
||||||
|
// we encounter this code path when deleting an orphaned directory
|
||||||
|
}
|
||||||
|
|
||||||
return result;
|
Dir dir(absPath);
|
||||||
}
|
bool result = dir.remove(true);
|
||||||
|
|
||||||
return false;
|
// update the hash cache too
|
||||||
|
updatedFileContents(absPath, std::string());
|
||||||
|
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
void HTTPRepoPrivate::makeRequest(RepoRequestPtr req)
|
void HTTPRepoPrivate::makeRequest(RepoRequestPtr req)
|
||||||
|
|||||||
+41
-14
@@ -28,6 +28,7 @@
|
|||||||
|
|
||||||
#include <zlib.h>
|
#include <zlib.h>
|
||||||
|
|
||||||
|
#include <simgear/sg_inlines.h>
|
||||||
#include <simgear/io/sg_file.hxx>
|
#include <simgear/io/sg_file.hxx>
|
||||||
#include <simgear/misc/sg_dir.hxx>
|
#include <simgear/misc/sg_dir.hxx>
|
||||||
|
|
||||||
@@ -92,7 +93,8 @@ public:
|
|||||||
END_OF_ARCHIVE,
|
END_OF_ARCHIVE,
|
||||||
ERROR_STATE, ///< states above this are error conditions
|
ERROR_STATE, ///< states above this are error conditions
|
||||||
BAD_ARCHIVE,
|
BAD_ARCHIVE,
|
||||||
BAD_DATA
|
BAD_DATA,
|
||||||
|
FILTER_STOPPED
|
||||||
} State;
|
} State;
|
||||||
|
|
||||||
SGPath path;
|
SGPath path;
|
||||||
@@ -110,10 +112,13 @@ public:
|
|||||||
bool haveInitedZLib;
|
bool haveInitedZLib;
|
||||||
bool uncompressedData; // set if reading a plain .tar (not tar.gz)
|
bool uncompressedData; // set if reading a plain .tar (not tar.gz)
|
||||||
uint8_t* headerPtr;
|
uint8_t* headerPtr;
|
||||||
|
TarExtractor* outer;
|
||||||
|
bool skipCurrentEntry = false;
|
||||||
|
|
||||||
TarExtractorPrivate() :
|
TarExtractorPrivate(TarExtractor* o) :
|
||||||
haveInitedZLib(false),
|
haveInitedZLib(false),
|
||||||
uncompressedData(false)
|
uncompressedData(false),
|
||||||
|
outer(o)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -129,7 +134,10 @@ public:
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (state == READING_FILE) {
|
if (state == READING_FILE) {
|
||||||
currentFile->close();
|
if (currentFile) {
|
||||||
|
currentFile->close();
|
||||||
|
currentFile.reset();
|
||||||
|
}
|
||||||
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE;
|
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE;
|
||||||
if (pad) {
|
if (pad) {
|
||||||
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
|
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
|
||||||
@@ -177,26 +185,36 @@ public:
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
skipCurrentEntry = false;
|
||||||
std::string tarPath = std::string(header.prefix) + std::string(header.fileName);
|
std::string tarPath = std::string(header.prefix) + std::string(header.fileName);
|
||||||
|
|
||||||
if (!isSafePath(tarPath)) {
|
if (!isSafePath(tarPath)) {
|
||||||
//state = BAD_ARCHIVE;
|
|
||||||
SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath);
|
SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath);
|
||||||
//return;
|
skipCurrentEntry = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
SGPath p = path;
|
auto result = outer->filterPath(tarPath);
|
||||||
p.append(tarPath);
|
if (result == TarExtractor::Stop) {
|
||||||
|
setState(FILTER_STOPPED);
|
||||||
|
return;
|
||||||
|
} else if (result == TarExtractor::Skipped) {
|
||||||
|
skipCurrentEntry = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
SGPath p = path / tarPath;
|
||||||
if (header.typeflag == DIRTYPE) {
|
if (header.typeflag == DIRTYPE) {
|
||||||
Dir dir(p);
|
if (!skipCurrentEntry) {
|
||||||
dir.create(0755);
|
Dir dir(p);
|
||||||
|
dir.create(0755);
|
||||||
|
}
|
||||||
setState(READING_HEADER);
|
setState(READING_HEADER);
|
||||||
} else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) {
|
} else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) {
|
||||||
currentFileSize = ::strtol(header.size, NULL, 8);
|
currentFileSize = ::strtol(header.size, NULL, 8);
|
||||||
bytesRemaining = currentFileSize;
|
bytesRemaining = currentFileSize;
|
||||||
currentFile.reset(new SGBinaryFile(p));
|
if (!skipCurrentEntry) {
|
||||||
currentFile->open(SG_IO_OUT);
|
currentFile.reset(new SGBinaryFile(p));
|
||||||
|
currentFile->open(SG_IO_OUT);
|
||||||
|
}
|
||||||
setState(READING_FILE);
|
setState(READING_FILE);
|
||||||
} else {
|
} else {
|
||||||
SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag);
|
SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag);
|
||||||
@@ -212,7 +230,9 @@ public:
|
|||||||
|
|
||||||
size_t curBytes = std::min(bytesRemaining, count);
|
size_t curBytes = std::min(bytesRemaining, count);
|
||||||
if (state == READING_FILE) {
|
if (state == READING_FILE) {
|
||||||
currentFile->write(bytes, curBytes);
|
if (currentFile) {
|
||||||
|
currentFile->write(bytes, curBytes);
|
||||||
|
}
|
||||||
bytesRemaining -= curBytes;
|
bytesRemaining -= curBytes;
|
||||||
} else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) {
|
} else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) {
|
||||||
memcpy(headerPtr, bytes, curBytes);
|
memcpy(headerPtr, bytes, curBytes);
|
||||||
@@ -264,7 +284,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
TarExtractor::TarExtractor(const SGPath& rootPath) :
|
TarExtractor::TarExtractor(const SGPath& rootPath) :
|
||||||
d(new TarExtractorPrivate)
|
d(new TarExtractorPrivate(this))
|
||||||
{
|
{
|
||||||
|
|
||||||
d->path = rootPath;
|
d->path = rootPath;
|
||||||
@@ -417,4 +437,11 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto TarExtractor::filterPath(std::string& pathToExtract)
|
||||||
|
-> PathResult
|
||||||
|
{
|
||||||
|
SG_UNUSED(pathToExtract);
|
||||||
|
return Accepted;
|
||||||
|
}
|
||||||
|
|
||||||
} // of simgear
|
} // of simgear
|
||||||
|
|||||||
@@ -43,7 +43,17 @@ public:
|
|||||||
|
|
||||||
bool hasError() const;
|
bool hasError() const;
|
||||||
|
|
||||||
|
protected:
|
||||||
|
enum PathResult {
|
||||||
|
Accepted,
|
||||||
|
Skipped,
|
||||||
|
Modified,
|
||||||
|
Stop
|
||||||
|
};
|
||||||
|
|
||||||
|
virtual PathResult filterPath(std::string& pathToExtract);
|
||||||
private:
|
private:
|
||||||
|
friend class TarExtractorPrivate;
|
||||||
std::unique_ptr<TarExtractorPrivate> d;
|
std::unique_ptr<TarExtractorPrivate> d;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -539,6 +539,33 @@ namespace simgear {
|
|||||||
const std::string& s);
|
const std::string& s);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
// parse a time string ([+/-]%f[:%f[:%f]]) into hours
|
||||||
|
double readTime(const string& time_in)
|
||||||
|
{
|
||||||
|
if (time_in.empty()) {
|
||||||
|
return 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool negativeSign = time_in.front() == '-';
|
||||||
|
const string_list pieces = split(time_in, ":");
|
||||||
|
if (pieces.size() > 3) {
|
||||||
|
throw sg_format_exception("Unable to parse time string, too many pieces", time_in);
|
||||||
|
}
|
||||||
|
|
||||||
|
const int hours = std::abs(to_int(pieces.front()));
|
||||||
|
int minutes = 0, seconds = 0;
|
||||||
|
if (pieces.size() > 1) {
|
||||||
|
minutes = to_int(pieces.at(1));
|
||||||
|
if (pieces.size() > 2) {
|
||||||
|
seconds = to_int(pieces.at(2));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
double result = hours + (minutes / 60.0) + (seconds / 3600.0);
|
||||||
|
return negativeSign ? -result : result;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
int compare_versions(const string& v1, const string& v2, int maxComponents)
|
int compare_versions(const string& v1, const string& v2, int maxComponents)
|
||||||
{
|
{
|
||||||
vector<string> v1parts(split(v1, "."));
|
vector<string> v1parts(split(v1, "."));
|
||||||
|
|||||||
@@ -209,6 +209,16 @@ namespace simgear {
|
|||||||
typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
|
typename = typename std::enable_if<std::is_integral<T>::value, T>::type >
|
||||||
T readNonNegativeInt(const std::string& s);
|
T readNonNegativeInt(const std::string& s);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Read a time value, seperated by colons, as a value in hours.
|
||||||
|
* Allowable input is ([+/-]%f[:%f[:%f]])
|
||||||
|
* i.e 15:04:35 is parsed as 15 + (04 / 60) + (35 / 2600)
|
||||||
|
* This code is moved from flightgear's options.cxx where it was called
|
||||||
|
* parse_time(),
|
||||||
|
*/
|
||||||
|
double readTime(const std::string& s);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Convert a string representing a boolean, to a bool.
|
* Convert a string representing a boolean, to a bool.
|
||||||
* Accepted values include YES, true, 0, 1, false, no, True,
|
* Accepted values include YES, true, 0, 1, false, no, True,
|
||||||
|
|||||||
@@ -19,6 +19,7 @@
|
|||||||
#include <simgear/compiler.h>
|
#include <simgear/compiler.h>
|
||||||
#include <simgear/misc/strutils.hxx>
|
#include <simgear/misc/strutils.hxx>
|
||||||
#include <simgear/structure/exception.hxx>
|
#include <simgear/structure/exception.hxx>
|
||||||
|
#include <simgear/constants.h>
|
||||||
|
|
||||||
using std::string;
|
using std::string;
|
||||||
using std::vector;
|
using std::vector;
|
||||||
@@ -581,6 +582,29 @@ void test_error_string()
|
|||||||
SG_CHECK_GT(strutils::error_string(saved_errno).size(), 0);
|
SG_CHECK_GT(strutils::error_string(saved_errno).size(), 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void test_readTime()
|
||||||
|
{
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime(""), 0.0);
|
||||||
|
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("11"), 11.0);
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("+11"), 11.0);
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("-11"), -11.0);
|
||||||
|
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("11:30"), 11.5);
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("+11:15"), 11.25);
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("-11:45"), -11.75);
|
||||||
|
|
||||||
|
const double seconds = 1 / 3600.0;
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("11:30:00"), 11.5);
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("+11:15:05"), 11.25 + 5 * seconds);
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("-11:45:15"), -(11.75 + 15 * seconds));
|
||||||
|
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("0:0:0"), 0);
|
||||||
|
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("0:0:28"), 28 * seconds);
|
||||||
|
SG_CHECK_EQUAL_EP(strutils::readTime("-0:0:28"), -28 * seconds);
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char* argv[])
|
int main(int argc, char* argv[])
|
||||||
{
|
{
|
||||||
test_strip();
|
test_strip();
|
||||||
@@ -599,6 +623,7 @@ int main(int argc, char* argv[])
|
|||||||
test_md5_hex();
|
test_md5_hex();
|
||||||
test_error_string();
|
test_error_string();
|
||||||
test_propPathMatch();
|
test_propPathMatch();
|
||||||
|
test_readTime();
|
||||||
|
|
||||||
return EXIT_SUCCESS;
|
return EXIT_SUCCESS;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -184,6 +184,16 @@ static naRef f_tan(naContext c, naRef me, int argc, naRef* args)
|
|||||||
return VALIDATE(a);
|
return VALIDATE(a);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static naRef f_atan(naContext c, naRef me, int argc, naRef* args)
|
||||||
|
{
|
||||||
|
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
|
||||||
|
if(naIsNil(a))
|
||||||
|
naRuntimeError(c, "non numeric arguments to tan()");
|
||||||
|
|
||||||
|
a.num = atan(a.num);
|
||||||
|
return VALIDATE(a);
|
||||||
|
}
|
||||||
|
|
||||||
static naRef f_asin(naContext c, naRef me, int argc, naRef* args)
|
static naRef f_asin(naContext c, naRef me, int argc, naRef* args)
|
||||||
{
|
{
|
||||||
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
|
naRef a = naNumValue(argc > 0 ? args[0] : naNil());
|
||||||
@@ -210,6 +220,7 @@ static naCFuncItem funcs[] = {
|
|||||||
{ "pow", f_pow },
|
{ "pow", f_pow },
|
||||||
{ "sqrt", f_sqrt },
|
{ "sqrt", f_sqrt },
|
||||||
{ "atan2", f_atan2 },
|
{ "atan2", f_atan2 },
|
||||||
|
{ "atan", f_atan },
|
||||||
{ "floor", f_floor },
|
{ "floor", f_floor },
|
||||||
{ "ceil", f_ceil },
|
{ "ceil", f_ceil },
|
||||||
{ "fmod", f_fmod },
|
{ "fmod", f_fmod },
|
||||||
|
|||||||
@@ -275,7 +275,7 @@ SGLightFactory::getLights(const SGDirectionalLightBin& lights)
|
|||||||
//stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
//stateSet->setMode(GL_LIGHTING, osg::StateAttribute::OFF);
|
||||||
|
|
||||||
osg::DrawArrays* drawArrays;
|
osg::DrawArrays* drawArrays;
|
||||||
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::TRIANGLES,
|
drawArrays = new osg::DrawArrays(osg::PrimitiveSet::POINTS,
|
||||||
0, vertices->size());
|
0, vertices->size());
|
||||||
geometry->addPrimitiveSet(drawArrays);
|
geometry->addPrimitiveSet(drawArrays);
|
||||||
return geometry;
|
return geometry;
|
||||||
|
|||||||
@@ -19,16 +19,13 @@
|
|||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#ifdef HAVE_CONFIG_H
|
#include <simgear_config.h>
|
||||||
# include <simgear_config.h>
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#include "StateMachine.hxx"
|
#include "StateMachine.hxx"
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
#include <cassert>
|
#include <cassert>
|
||||||
#include <set>
|
#include <set>
|
||||||
#include <boost/foreach.hpp>
|
|
||||||
|
|
||||||
#include <simgear/debug/logstream.hxx>
|
#include <simgear/debug/logstream.hxx>
|
||||||
#include <simgear/structure/SGBinding.hxx>
|
#include <simgear/structure/SGBinding.hxx>
|
||||||
@@ -44,7 +41,7 @@ typedef std::vector<StateMachine::State_ptr> StatePtrVec;
|
|||||||
static void readBindingList(SGPropertyNode* desc, const std::string& name,
|
static void readBindingList(SGPropertyNode* desc, const std::string& name,
|
||||||
SGPropertyNode* root, SGBindingList& result)
|
SGPropertyNode* root, SGBindingList& result)
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(SGPropertyNode* b, desc->getChildren(name)) {
|
for (auto b : desc->getChildren(name)) {
|
||||||
SGBinding* bind = new SGBinding;
|
SGBinding* bind = new SGBinding;
|
||||||
bind->read(b, root);
|
bind->read(b, root);
|
||||||
result.push_back(bind);
|
result.push_back(bind);
|
||||||
@@ -85,7 +82,7 @@ public:
|
|||||||
void computeEligibleTransitions()
|
void computeEligibleTransitions()
|
||||||
{
|
{
|
||||||
_eligible.clear();
|
_eligible.clear();
|
||||||
BOOST_FOREACH(Transition_ptr t, _transitions) {
|
for (Transition_ptr t : _transitions) {
|
||||||
if (t->applicableForState(_currentState)) {
|
if (t->applicableForState(_currentState)) {
|
||||||
_eligible.push_back(t.ptr());
|
_eligible.push_back(t.ptr());
|
||||||
}
|
}
|
||||||
@@ -358,7 +355,7 @@ void StateMachine::update(double aDt)
|
|||||||
|
|
||||||
Transition_ptr trigger;
|
Transition_ptr trigger;
|
||||||
|
|
||||||
BOOST_FOREACH(Transition* trans, d->_eligible) {
|
for (auto trans : d->_eligible) {
|
||||||
if (trans->evaluate()) {
|
if (trans->evaluate()) {
|
||||||
if (trigger != Transition_ptr()) {
|
if (trigger != Transition_ptr()) {
|
||||||
SG_LOG(SG_GENERAL, SG_WARN, "ambiguous transitions! "
|
SG_LOG(SG_GENERAL, SG_WARN, "ambiguous transitions! "
|
||||||
@@ -379,7 +376,7 @@ void StateMachine::update(double aDt)
|
|||||||
|
|
||||||
StateMachine::State_ptr StateMachine::findStateByName(const std::string& aName) const
|
StateMachine::State_ptr StateMachine::findStateByName(const std::string& aName) const
|
||||||
{
|
{
|
||||||
BOOST_FOREACH(State_ptr sp, d->_states) {
|
for (auto sp : d->_states) {
|
||||||
if (sp->name() == aName) {
|
if (sp->name() == aName) {
|
||||||
return sp;
|
return sp;
|
||||||
}
|
}
|
||||||
@@ -435,7 +432,7 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
|
|||||||
assert(d->_root);
|
assert(d->_root);
|
||||||
}
|
}
|
||||||
|
|
||||||
BOOST_FOREACH(SGPropertyNode* stateDesc, desc->getChildren("state")) {
|
for (auto stateDesc : desc->getChildren("state")) {
|
||||||
std::string nm = stateDesc->getStringValue("name");
|
std::string nm = stateDesc->getStringValue("name");
|
||||||
State_ptr st(new State(nm));
|
State_ptr st(new State(nm));
|
||||||
|
|
||||||
@@ -446,7 +443,7 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
|
|||||||
addState(st);
|
addState(st);
|
||||||
} // of states iteration
|
} // of states iteration
|
||||||
|
|
||||||
BOOST_FOREACH(SGPropertyNode* tDesc, desc->getChildren("transition")) {
|
for (auto tDesc : desc->getChildren("transition")) {
|
||||||
std::string nm = tDesc->getStringValue("name");
|
std::string nm = tDesc->getStringValue("name");
|
||||||
State_ptr target = findStateByName(tDesc->getStringValue("target"));
|
State_ptr target = findStateByName(tDesc->getStringValue("target"));
|
||||||
|
|
||||||
@@ -456,7 +453,7 @@ void StateMachine::initFromPlist(SGPropertyNode* desc, SGPropertyNode* root)
|
|||||||
t->setTriggerCondition(cond);
|
t->setTriggerCondition(cond);
|
||||||
|
|
||||||
t->setExcludeTarget(tDesc->getBoolValue("exclude-target", true));
|
t->setExcludeTarget(tDesc->getBoolValue("exclude-target", true));
|
||||||
BOOST_FOREACH(SGPropertyNode* src, tDesc->getChildren("source")) {
|
for (auto src : tDesc->getChildren("source")) {
|
||||||
State_ptr srcState = findStateByName(src->getStringValue());
|
State_ptr srcState = findStateByName(src->getStringValue());
|
||||||
t->addSourceState(srcState);
|
t->addSourceState(srcState);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -212,15 +212,23 @@ void testBindings()
|
|||||||
|
|
||||||
void testParse()
|
void testParse()
|
||||||
{
|
{
|
||||||
const char* xml = "<?xml version=\"1.0\"?>"
|
const char* xml = R"(<?xml version="1.0"?>
|
||||||
"<PropertyList>"
|
<PropertyList>
|
||||||
"<state>"
|
<state>
|
||||||
"<name>one</name>"
|
<name>one</name>
|
||||||
"</state>"
|
<enter>
|
||||||
"<state>"
|
<command>nasal</command>
|
||||||
"<name>two</name>"
|
<script>print('Foo');</script>
|
||||||
"</state>"
|
</enter>
|
||||||
"</PropertyList>";
|
<exit>
|
||||||
|
<command>nasal</command>
|
||||||
|
<script>print('bar');</script>
|
||||||
|
</exit>
|
||||||
|
</state>
|
||||||
|
<state>
|
||||||
|
<name>two</name>
|
||||||
|
</state>
|
||||||
|
</PropertyList>)";
|
||||||
|
|
||||||
SGPropertyNode* desc = new SGPropertyNode;
|
SGPropertyNode* desc = new SGPropertyNode;
|
||||||
readProperties(xml, strlen(xml), desc);
|
readProperties(xml, strlen(xml), desc);
|
||||||
|
|||||||
Reference in New Issue
Block a user