This commit is contained in:
gallaert 2018-07-18 20:48:01 +01:00
commit 5921fd19fb
35 changed files with 1609 additions and 560 deletions

View File

@ -126,7 +126,7 @@ option(ENABLE_RTI "Set to ON to build SimGear with RTI support" OFF)
option(ENABLE_GDAL "Set to ON to build SimGear with GDAL support" OFF)
option(ENABLE_TESTS "Set to OFF to disable building SimGear's test applications" ON)
option(ENABLE_SOUND "Set to OFF to disable building SimGear's sound support" ON)
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" OFF)
option(USE_AEONWAVE "Set to ON to use AeonWave instead of OpenAL" ON)
option(ENABLE_PKGUTIL "Set to ON to build the sg_pkgutil application (default)" ON)
option(ENABLE_DNS "Set to ON to use udns library and DNS service resolver" ON)
option(ENABLE_SIMD "Enable SSE/SSE2 support for x86 compilers" ON)
@ -167,14 +167,22 @@ endif (MSVC)
if (MSVC AND MSVC_3RDPARTY_ROOT)
message(STATUS "3rdparty files located in ${MSVC_3RDPARTY_ROOT}")
string(SUBSTRING ${MSVC_VERSION} 0 2 MSVC_VERSION_MAJOR)
string(SUBSTRING ${MSVC_VERSION} 2 2 MSVC_VERSION_MINOR)
set( OSG_MSVC "msvc" )
if (${MSVC_VERSION} EQUAL 1900)
if (${MSVC_VERSION_MAJOR} EQUAL "19")
if (${MSVC_VERSION_MINOR} EQUAL "00")
set( OSG_MSVC ${OSG_MSVC}140 )
elseif (${MSVC_VERSION} EQUAL 1800)
set( OSG_MSVC ${OSG_MSVC}120 )
else ()
set( OSG_MSVC ${OSG_MSVC}141 )
endif ()
elseif (${MSVC_VERSION_MAJOR} EQUAL "18")
set( OSG_MSVC ${OSG_MSVC}120 )
else ()
message(FATAL_ERROR "Visual Studio 2013/2015 is required now")
message(FATAL_ERROR "Visual Studio 2013/15/17 is required")
endif ()
if (CMAKE_CL_64)
set( OSG_MSVC ${OSG_MSVC}-64 )
set( MSVC_3RDPARTY_DIR 3rdParty.x64 )
@ -222,12 +230,19 @@ else()
if (ENABLE_SOUND)
if (USE_AEONWAVE)
find_package(AAX COMPONENTS aax REQUIRED)
else()
find_package(AAX)
endif()
if(NOT AAX_FOUND)
set(USE_AEONWAVE FALSE)
find_package(OpenAL REQUIRED)
endif()
message(STATUS "Sound support: ENABLED")
if(AAX_FOUND)
message(STATUS "Sound support: AeonWave")
else()
message(STATUS "Sound support: OpenAL")
endif()
endif(ENABLE_SOUND)
find_package(OpenSceneGraph 3.2.0 REQUIRED osgText osgSim osgDB osgParticle osgGA osgViewer osgUtil)

View File

@ -1,61 +1,74 @@
# Locate AAX
# Try to find AAX (AeonWave)
# This module defines
# AAX_LIBRARIES
# AAX_FOUND, if false, do not try to link to AAX
# AAX_INCLUDE_DIR, where to find the headers
#
# AAX_FOUND - if false, do not try to link to AAX
# AAX_INCLUDE_DIR - where to find the headers
# AAX_LIBRARIES - Link these to use AAX
#
# Copyright (C) 2016-2018 by Erik Hofman.
# Copyright (C) 2016-2018 by Adalin B.V.
#
# $AAXDIR is an environment variable that would
# correspond to the ./configure --prefix=$AAXDIR
# used in building AAX.
#
# Created by Erik Hofman.
# This file is Public Domain (www.unlicense.org)
# This is free and unencumbered software released into the public domain.
FIND_PATH(AAX_INCLUDE_DIR aax/aax.h
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/aax
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_SOURCE_DIR}/aax
PATH_SUFFIXES include
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/opt
)
if (AAX_LIBRARY AND AAX_INCLUDE_DIR)
# in cache already
set(AAX_FOUND TRUE)
else()
find_path(AAX_INCLUDE_DIR aax/aax.h
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/aax
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_SOURCE_DIR}/aax
PATH_SUFFIXES include
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/opt
)
FIND_LIBRARY(AAX_LIBRARY
NAMES AAX aax AAX32
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/AAX
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_BUILD_DIR}/aax
PATH_SUFFIXES bin lib lib/${CMAKE_LIBRARY_ARCHITECTURE} lib64 libs64 libs libs/Win32 libs/Win64
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/opt
)
find_library(AAX_LIBRARY
NAMES AAX aax libAAX
HINTS
$ENV{AAXDIR}
$ENV{ProgramFiles}/AAX
$ENV{ProgramFiles}/AeonWave
$ENV{ProgramFiles}/Adalin/AeonWave
${CMAKE_BUILD_DIR}/aax
PATH_SUFFIXES lib64 lib lib/${CMAKE_LIBRARY_ARCHITECTURE} libs64 libs libs/Win32 libs/Win64 bin
PATHS
~/Library/Frameworks
/Library/Frameworks
/usr/local
/usr
/opt
)
IF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
SET(AAX_FOUND "YES")
ELSE(AAX_LIBRARY AND AAX_INCLUDE_DIR)
IF(NOT AAX_INCLUDE_DIR)
MESSAGE(FATAL_ERROR "Unable to find the AAX library development files.")
SET(AAX_FOUND "NO")
ENDIF(NOT AAX_INCLUDE_DIR)
IF(NOT AAX_LIBRARY)
IF(SINGLE_PACKAGE)
SET(AAX_LIBRARY "${aax_BUILD_DIR}/aax/AAX32.dll")
SET(AAX_FOUND "YES")
ELSE(SINGLE_PACKAGE)
ENDIF(SINGLE_PACKAGE)
ENDIF(NOT AAX_LIBRARY)
ENDIF(AAX_LIBRARY AND AAX_INCLUDE_DIR)
set(AAX_DEFINITIONS "")
if (AAX_LIBRARY AND AAX_INCLUDE_DIR)
set(AAX_FOUND TRUE)
endif()
if (AAX_FOUND)
if (NOT Udns_FIND_QUIETLY)
message(STATUS "Found AeonWave: ${AAX_LIBRARIES}")
endif ()
else ()
if (Udns_FIND_REQUIRED)
message(FATAL_ERROR "Could not find AeonWave")
endif ()
endif ()
# show the AAX_INCLUDE_DIRS and AAX_LIBRARIES variables only in the advanced view
mark_as_advanced(AAX_INCLUDE_DIRS AAX_LIBRARIES)
endif()

View File

@ -12,6 +12,11 @@ macro(simgear_component_common name includePath sourcesList sources headers)
set_property(GLOBAL
APPEND PROPERTY PUBLIC_HEADERS "${CMAKE_CURRENT_SOURCE_DIR}/${h}")
set(fh${sourcesList} "${fh${sourcesList}}#${CMAKE_CURRENT_SOURCE_DIR}/${h}")
# also append headers to the sources list, so that IDEs find the files
# correctly (otherwise they are not in the project)
set_property(GLOBAL
APPEND PROPERTY ${sourcesList} "${CMAKE_CURRENT_SOURCE_DIR}/${h}")
endforeach()
set_property(GLOBAL APPEND PROPERTY FG_GROUPS_${sourcesList}_C "${fc${sourcesList}}@")

View File

@ -1,20 +0,0 @@
[This file is mirrored in both the FlightGear and SimGear packages.]
You *must* have the development components of OpenAL installed on your system
to build FlightGear!" You can get a copy here:
http://connect.creativelabs.com/openal/default.aspx
Build notes:
You can download a versioned release of the openal library from
http://www.openal.org/downloads.html. Download the openal source,
release 0.0.8 (dated February 11, 2006) and run:
tar xjvf openal-soft-1.5.304.tar.bz2
cd openal-soft-1.5.304/
ccmake .
[ While running ccmake: press 'c' to configure, press 'c' once more, and
then press 'g' to generate and exit ]

39
README.sound Normal file
View File

@ -0,0 +1,39 @@
[This file is mirrored in both the FlightGear and SimGear packages.]
For Sound support FlightGear requires one of the two following packages:
- OpenAL
- AeonWave
== OpenAL ===
You *must* have the development components of OpenAL installed on your system
to build FlightGear!" You can get a copy here:
http://connect.creativelabs.com/openal/default.aspx
Build notes:
You can download a versioned release of the openal library from
http://www.openal.org/downloads.html. Download the openal source,
release 0.0.8 (dated February 11, 2006) and run:
tar xjvf openal-soft-1.5.304.tar.bz2
cd openal-soft-1.5.304/
ccmake .
[ While running ccmake: press 'c' to configure, press 'c' once more, and
then press 'g' to generate and exit ]
== AeonWave ===
For FlightGear AeonWave has a number of advantages over OpenAL:
* Correct Doppler effect behavior
* Default distance attenuation frequency filtering
* Native support for 29 types of audio formats.
* Native support for wav, mp3, vorbis and raw file formats.
The source code of AeonWave can be found on GitHub:
https://github.com/adalinbv
Optimized binary packages are available at:
http://www.adalin.com/

View File

@ -39,6 +39,75 @@ namespace simgear
{
namespace canvas
{
static int globalinstanceid = 1;
/**
* Camera Callback for moving completed canvas images to subscribed listener.
*/
class CanvasImageCallback : public osg::Camera::DrawCallback {
public:
osg::Image *_rawImage;
CanvasImageCallback(osg::Image *rawImage)
: _min_delta_tick(1.0 / 8.0) {
_previousFrameTick = osg::Timer::instance()->tick();
_rawImage = rawImage;
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImageCallback created. instance is " << instanceid);
}
virtual void operator()(osg::RenderInfo& renderInfo) const {
osg::Timer_t n = osg::Timer::instance()->tick();
double dt = osg::Timer::instance()->delta_s(_previousFrameTick, n);
if (dt < _min_delta_tick)
return;
_previousFrameTick = n;
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImageCallback " << instanceid << ": image available for " << _subscribers.size() << " subscribers. camera is " << renderInfo.getCurrentCamera());
bool hasSubscribers = false;
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
hasSubscribers = !_subscribers.empty();
}
if (hasSubscribers) {
//Make sure image can be overwritten by next frame while it is still returned to the client
osg::Image* image = new osg::Image(*_rawImage, osg::CopyOp::DEEP_COPY_ALL);
{
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
while (!_subscribers.empty()) {
try {
CanvasImageReadyListener *subs = _subscribers.back();
if (subs){
subs->imageReady(image);
}else{
SG_LOG(SG_GENERAL,SG_WARN,"CanvasImageCallback subscriber null");
}
} catch (...) { }
_subscribers.pop_back();
}
}
}
}
void subscribe(CanvasImageReadyListener * subscriber) {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
_subscribers.push_back(subscriber);
}
void unsubscribe(CanvasImageReadyListener * subscriber) {
OpenThreads::ScopedLock<OpenThreads::Mutex> lock(_lock);
_subscribers.remove(subscriber);
}
int getSubscriberCount() {
return _subscribers.size();
}
private:
mutable list<CanvasImageReadyListener*> _subscribers;
mutable OpenThreads::Mutex _lock;
mutable double _previousFrameTick;
double _min_delta_tick;
int instanceid = globalinstanceid++;
};
//----------------------------------------------------------------------------
Canvas::CullCallback::CullCallback(const CanvasWeakPtr& canvas):
@ -248,6 +317,21 @@ namespace canvas
osg::Camera* camera = _texture.getCamera();
string canvasname = _node->getStringValue("name");
int renderToImage = _node->getBoolValue("render-to-image");
if (renderToImage){
CanvasImageCallback *_screenshotCallback = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
if (!_screenshotCallback) {
// no draw callback yet
osg::Image* shot = new osg::Image();
shot->allocateImage(getSizeX(), getSizeY(), 24, GL_RGB, GL_UNSIGNED_BYTE);
camera->attach(osg::Camera::COLOR_BUFFER, shot);
camera->setFinalDrawCallback(new CanvasImageCallback(shot));
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImage: attached image and draw callback to camera " << camera << " for canvas " << canvasname << ". Ready for subscriber now.");
}
}
// TODO Allow custom render order? For now just keep in order with
// property tree.
camera->setRenderOrder(osg::Camera::PRE_RENDER, _node->getIndex());
@ -347,6 +431,41 @@ namespace canvas
}
}
int Canvas::subscribe(CanvasImageReadyListener * subscriber) {
osg::Camera* camera = _texture.getCamera();
const string canvasname = _node->getStringValue("name");
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: subscribe to canvas " << canvasname.c_str() << ", camera ="<< camera);
if (!_node->getBoolValue("render-to-image")) {
SG_LOG(SG_GENERAL,SG_INFO,"CanvasImage: Setting render-to-image");
_node->addChild("render-to-image", 0)->setBoolValue(1);
setStatusFlags(STATUS_DIRTY, true);
}
CanvasImageCallback *_screenshotCallback = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
if (_screenshotCallback) {
// Camera ready for subscriber. Otherwise, draw callback is created by canvas thread later.
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: adding subscriber to camera draw callback");
_screenshotCallback->subscribe(subscriber);
// TODO: check: Is this the correct way to ensure the canvas will be available?
enableRendering(true);
return 1;
}
return 0;
}
int Canvas::unsubscribe(CanvasImageReadyListener * subscriber) {
osg::Camera* camera = _texture.getCamera();
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: unsubscribe");
CanvasImageCallback *cb = dynamic_cast<CanvasImageCallback*> (camera->getFinalDrawCallback());
if (cb) {
SG_LOG(SG_GENERAL,SG_DEBUG,"CanvasImage: unsubscribe from camera " << camera);
cb->unsubscribe(subscriber);
}
return 0;
}
//----------------------------------------------------------------------------
bool Canvas::addEventListener( const std::string& type,
const EventListener& cb )

View File

@ -44,6 +44,17 @@ namespace canvas
class CanvasMgr;
class MouseEvent;
/**
* A listener interested in completed canvas drawing.
*/
class CanvasImageReadyListener {
public:
virtual void imageReady(osg::ref_ptr<osg::Image>) = 0;
virtual ~CanvasImageReadyListener()
{
}
};
/**
* Canvas to draw onto (to an off-screen render target).
*/
@ -160,7 +171,12 @@ namespace canvas
*/
void enableRendering(bool force = false);
void update(double delta_time_sec);
void update(double delta_time_sec) override;
osg::Camera* getCamera();
int subscribe(CanvasImageReadyListener * subscriber);
int unsubscribe(CanvasImageReadyListener * subscriber);
int getSubscriberCount();
bool addEventListener(const std::string& type, const EventListener& cb);
bool dispatchEvent(const EventPtr& event);

View File

@ -90,8 +90,10 @@ namespace simgear
case HTTPRepository::REPO_ERROR_CANCELLED: return "cancelled";
case HTTPRepository::REPO_PARTIAL_UPDATE: return "partial update (incomplete)";
}
return "Unknown response code";
}
class HTTPRepoPrivate
{
public:
@ -291,7 +293,7 @@ public:
if (!cp.exists()) {
continue;
}
SGBinaryFile src(cp);
SGBinaryFile dst(p);
src.open(SG_IO_IN);
@ -321,32 +323,35 @@ public:
toBeUpdated, orphans;
simgear::Dir d(absolutePath());
PathList fsChildren = d.children(0);
PathList::const_iterator it = fsChildren.begin();
for (; it != fsChildren.end(); ++it) {
ChildInfo info(it->isDir() ? ChildInfo::DirectoryType : ChildInfo::FileType,
it->file(), "");
for (const auto& child : fsChildren) {
const auto& fileName = child.file();
if ((fileName == ".dirindex") || (fileName == ".hashes")) {
continue;
}
ChildInfo info(child.isDir() ? ChildInfo::DirectoryType : ChildInfo::FileType, fileName, "");
std::string hash = hashForChild(info);
ChildInfoList::iterator c = findIndexChild(it->file());
ChildInfoList::iterator c = findIndexChild(fileName);
if (c == children.end()) {
orphans.push_back(it->file());
orphans.push_back(fileName);
} else if (c->hash != hash) {
#if 0
SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << it->file() );
SG_LOG(SG_TERRASYNC, SG_DEBUG, "hash mismatch'" << fileName);
// file exists, but hash mismatch, schedule update
if (!hash.empty()) {
SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists but hash is wrong for:" << it->file() );
SG_LOG(SG_TERRASYNC, SG_DEBUG, "file exists but hash is wrong for:" << fileName);
SG_LOG(SG_TERRASYNC, SG_DEBUG, "on disk:" << hash << " vs in info:" << c->hash);
}
#endif
toBeUpdated.push_back(it->file() );
toBeUpdated.push_back(fileName);
} else {
// file exists and hash is valid. If it's a directory,
// perform a recursive check.
if (c->type == ChildInfo::DirectoryType) {
HTTPDirectory* childDir = childDirectory(it->file());
HTTPDirectory* childDir = childDirectory(fileName);
childDir->updateChildrenBasedOnHash();
}
}
@ -354,7 +359,7 @@ public:
// remove existing file system children from the index list,
// so we can detect new children
// https://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Erase-Remove
indexNames.erase(std::remove(indexNames.begin(), indexNames.end(), it->file()), indexNames.end());
indexNames.erase(std::remove(indexNames.begin(), indexNames.end(), fileName), indexNames.end());
} // of real children iteration
// all remaining names in indexChilden are new children
@ -677,7 +682,7 @@ std::string HTTPRepository::resultCodeAsString(ResultCode code)
{
return innerResultCodeAsString(code);
}
HTTPRepository::ResultCode
HTTPRepository::failure() const
{
@ -746,7 +751,11 @@ HTTPRepository::failure() const
if (responseCode() == -1) {
code = HTTPRepository::REPO_ERROR_CANCELLED;
}
if (file) {
file->close();
}
file.reset();
if (pathInRepo.exists()) {
pathInRepo.remove();
@ -818,7 +827,7 @@ HTTPRepository::failure() const
// dir index data has changed, so write to disk and update
// the hash accordingly
sg_ofstream of(pathInRepo(), std::ios::trunc | std::ios::out);
sg_ofstream of(pathInRepo(), std::ios::trunc | std::ios::out | std::ios::binary);
if (!of.is_open()) {
throw sg_io_exception("Failed to open directory index file for writing", pathInRepo());
}
@ -1084,7 +1093,7 @@ HTTPRepository::failure() const
} else {
// we encounter this code path when deleting an orphaned directory
}
Dir dir(absPath);
bool result = dir.remove(true);
@ -1152,7 +1161,7 @@ HTTPRepository::failure() const
}
SG_LOG(SG_TERRASYNC, SG_WARN, "failed to update repository:" << baseUrl
<< "\n\tchecksum failure for: " << relativePath
<< "\n\tchecksum failure for: " << relativePath
<< "\n\tthis typically indicates the remote repository is corrupt or was being updated during the sync");
} else if (fileStatus == HTTPRepository::REPO_ERROR_CANCELLED) {
// if we were cancelled, don't report or log

BIN
simgear/io/badTar.tgz Normal file

Binary file not shown.

View File

@ -62,7 +62,7 @@ public:
SGFile( int existingFd );
/** Destructor */
~SGFile();
virtual ~SGFile();
// open the file based on specified direction
bool open( const SGProtocolDir dir );

View File

@ -309,7 +309,8 @@ std::string test_computeHashForPath(const SGPath& p)
return std::string();
sha1nfo info;
sha1_init(&info);
char* buf = static_cast<char*>(alloca(1024 * 1024));
char* buf = static_cast<char*>(malloc(1024 * 1024));
assert(buf);
size_t readLen;
SGBinaryFile f(p);
@ -319,6 +320,9 @@ std::string test_computeHashForPath(const SGPath& p)
sha1_write(&info, buf, readLen);
}
f.close();
free(buf);
std::string hashBytes((char*) sha1_result(&info), HASH_LENGTH);
return strutils::encodeHex(hashBytes);
}
@ -433,6 +437,34 @@ void testBasicClone(HTTP::Client* cl)
std::cout << "Passed test: basic clone and update" << std::endl;
}
void testUpdateNoChanges(HTTP::Client* cl)
{
std::unique_ptr<HTTPRepository> repo;
SGPath p(simgear::Dir::current().path());
p.append("http_repo_basic"); // same as before
global_repo->clearRequestCounts();
repo.reset(new HTTPRepository(p, cl));
repo->setBaseUrl("http://localhost:2000/repo");
repo->update();
waitForUpdateComplete(cl, repo.get());
verifyFileState(p, "fileA");
verifyFileState(p, "dirC/subdirA/subsubA/fileCAAA");
verifyRequestCount("dirA", 0);
verifyRequestCount("dirB", 0);
verifyRequestCount("dirB/subdirA", 0);
verifyRequestCount("dirB/subdirA/fileBAA", 0);
verifyRequestCount("dirC", 0);
verifyRequestCount("dirC/fileCA", 0);
std::cout << "Passed test:no changes update" << std::endl;
}
void testModifyLocalFiles(HTTP::Client* cl)
{
std::unique_ptr<HTTPRepository> repo;
@ -469,10 +501,6 @@ void testModifyLocalFiles(HTTP::Client* cl)
std::cout << "Passed test: identify and fix locally modified files" << std::endl;
}
void testNoChangesUpdate()
{
}
void testMergeExistingFileWithoutDownload(HTTP::Client* cl)
{
@ -725,7 +753,7 @@ void testCopyInstalledChildren(HTTP::Client* cl)
verifyRequestCount("dirJ/fileJC", 1);
verifyRequestCount("dirJ/fileJD", 1);
std::cout << "Copy installed children" << std::endl;
std::cout << "passed Copy installed children" << std::endl;
}
int main(int argc, char* argv[])
@ -751,6 +779,7 @@ int main(int argc, char* argv[])
global_repo->defineFile("dirC/subdirA/subsubA/fileCAAA");
testBasicClone(&cl);
testUpdateNoChanges(&cl);
testModifyLocalFiles(&cl);

View File

@ -11,6 +11,7 @@
#include <simgear/misc/test_macros.hxx>
#include <simgear/misc/sg_path.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/io/sg_file.hxx>
@ -31,7 +32,7 @@ void testTarGz()
uint8_t* buf = (uint8_t*) alloca(8192);
size_t bufSize = f.read((char*) buf, 8192);
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
SG_VERIFY(ArchiveExtractor::determineType(buf, bufSize) == ArchiveExtractor::TarData);
f.close();
}
@ -44,18 +45,146 @@ void testPlainTar()
SGBinaryFile f(p);
f.open(SG_IO_IN);
uint8_t* buf = (uint8_t*) alloca(8192);
size_t bufSize = f.read((char*) buf, 8192);
uint8_t* buf = (uint8_t*)alloca(8192);
size_t bufSize = f.read((char*) buf, 8192);
SG_VERIFY(TarExtractor::isTarData(buf, bufSize));
SG_VERIFY(ArchiveExtractor::determineType(buf, bufSize) == ArchiveExtractor::TarData);
f.close();
}
void testExtractStreamed()
{
SGPath p = SGPath(SRC_DIR);
p.append("test.tar.gz");
SGBinaryFile f(p);
f.open(SG_IO_IN);
SGPath extractDir = simgear::Dir::current().path() / "test_extract_streamed";
simgear::Dir pd(extractDir);
pd.removeChildren();
ArchiveExtractor ex(extractDir);
uint8_t* buf = (uint8_t*) alloca(128);
while (!f.eof()) {
size_t bufSize = f.read((char*) buf, 128);
ex.extractBytes(buf, bufSize);
}
ex.flush();
SG_VERIFY(ex.isAtEndOfArchive());
SG_VERIFY(ex.hasError() == false);
SG_VERIFY((extractDir / "testDir/hello.c").exists());
SG_VERIFY((extractDir / "testDir/foo.txt").exists());
}
void testExtractLocalFile()
{
}
void testFilterTar()
{
SGPath p = SGPath(SRC_DIR);
p.append("badTar.tgz");
SGBinaryFile f(p);
f.open(SG_IO_IN);
SGPath extractDir = simgear::Dir::current().path() / "test_filter_tar";
simgear::Dir pd(extractDir);
pd.removeChildren();
ArchiveExtractor ex(extractDir);
uint8_t* buf = (uint8_t*) alloca(128);
while (!f.eof()) {
size_t bufSize = f.read((char*) buf, 128);
ex.extractBytes(buf, bufSize);
}
ex.flush();
SG_VERIFY(ex.isAtEndOfArchive());
SG_VERIFY(ex.hasError() == false);
SG_VERIFY((extractDir / "tarWithBadContent/regular-file.txt").exists());
SG_VERIFY(!(extractDir / "tarWithBadContent/symbolic-linked.png").exists());
SG_VERIFY((extractDir / "tarWithBadContent/screenshot.png").exists());
SG_VERIFY((extractDir / "tarWithBadContent/dirOne/subDirA").exists());
SG_VERIFY(!(extractDir / "tarWithBadContent/dirOne/subDirA/linked.txt").exists());
}
void testExtractZip()
{
SGPath p = SGPath(SRC_DIR);
p.append("zippy.zip");
SGBinaryFile f(p);
f.open(SG_IO_IN);
SGPath extractDir = simgear::Dir::current().path() / "test_extract_zip";
simgear::Dir pd(extractDir);
pd.removeChildren();
ArchiveExtractor ex(extractDir);
uint8_t* buf = (uint8_t*)alloca(128);
while (!f.eof()) {
size_t bufSize = f.read((char*)buf, 128);
ex.extractBytes(buf, bufSize);
}
ex.flush();
SG_VERIFY(ex.isAtEndOfArchive());
SG_VERIFY(ex.hasError() == false);
SG_VERIFY((extractDir / "zippy/dirA/hello.c").exists());
SG_VERIFY((extractDir / "zippy/bar.xml").exists());
SG_VERIFY((extractDir / "zippy/long-named.json").exists());
}
void testPAXAttributes()
{
SGPath p = SGPath(SRC_DIR);
p.append("pax-extended.tar");
SGBinaryFile f(p);
f.open(SG_IO_IN);
SGPath extractDir = simgear::Dir::current().path() / "test_pax_extended";
simgear::Dir pd(extractDir);
pd.removeChildren();
ArchiveExtractor ex(extractDir);
uint8_t* buf = (uint8_t*) alloca(128);
while (!f.eof()) {
size_t bufSize = f.read((char*) buf, 128);
ex.extractBytes(buf, bufSize);
}
ex.flush();
SG_VERIFY(ex.isAtEndOfArchive());
SG_VERIFY(ex.hasError() == false);
}
int main(int ac, char ** av)
{
testTarGz();
testPlainTar();
testFilterTar();
testExtractStreamed();
testExtractZip();
// disabled to avoiding checking in large PAX archive
// testPAXAttributes();
std::cout << "all tests passed" << std::endl;
return 0;
}

View File

@ -31,12 +31,80 @@
#include <simgear/sg_inlines.h>
#include <simgear/io/sg_file.hxx>
#include <simgear/misc/sg_dir.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
#include <simgear/debug/logstream.hxx>
#include <simgear/package/unzip.h>
#include <simgear/structure/exception.hxx>
namespace simgear
{
class ArchiveExtractorPrivate
{
public:
ArchiveExtractorPrivate(ArchiveExtractor* o) :
outer(o)
{
assert(outer);
}
typedef enum {
INVALID = 0,
READING_HEADER,
READING_FILE,
READING_PADDING,
READING_PAX_GLOBAL_ATTRIBUTES,
READING_PAX_FILE_ATTRIBUTES,
PRE_END_OF_ARCHVE,
END_OF_ARCHIVE,
ERROR_STATE, ///< states above this are error conditions
BAD_ARCHIVE,
BAD_DATA,
FILTER_STOPPED
} State;
State state = INVALID;
ArchiveExtractor* outer = nullptr;
virtual void extractBytes(const uint8_t* bytes, size_t count) = 0;
virtual void flush() = 0;
SGPath extractRootPath()
{
return outer->_rootPath;
}
ArchiveExtractor::PathResult filterPath(std::string& pathToExtract)
{
return outer->filterPath(pathToExtract);
}
bool isSafePath(const std::string& p) const
{
if (p.empty()) {
return false;
}
// reject absolute paths
if (p.at(0) == '/') {
return false;
}
// reject paths containing '..'
size_t doubleDot = p.find("..");
if (doubleDot != std::string::npos) {
return false;
}
// on POSIX could use realpath to sanity check
return true;
}
};
///////////////////////////////////////////////////////////////////////////////////////////////////
const int ZLIB_DECOMPRESS_BUFFER_SIZE = 32 * 1024;
const int ZLIB_INFLATE_WINDOW_BITS = MAX_WBITS;
const int ZLIB_DECODE_GZIP_HEADER = 16;
@ -81,24 +149,14 @@ typedef struct
#define FIFOTYPE '6' /* FIFO special */
#define CONTTYPE '7' /* reserved */
class TarExtractorPrivate
const char PAX_GLOBAL_HEADER = 'g';
const char PAX_FILE_ATTRIBUTES = 'x';
class TarExtractorPrivate : public ArchiveExtractorPrivate
{
public:
typedef enum {
INVALID = 0,
READING_HEADER,
READING_FILE,
READING_PADDING,
PRE_END_OF_ARCHVE,
END_OF_ARCHIVE,
ERROR_STATE, ///< states above this are error conditions
BAD_ARCHIVE,
BAD_DATA,
FILTER_STOPPED
} State;
SGPath path;
State state;
union {
UstarHeaderBlock header;
uint8_t headerBytes[TAR_HEADER_BLOCK_SIZE];
@ -109,17 +167,22 @@ public:
size_t currentFileSize;
z_stream zlibStream;
uint8_t* zlibOutput;
bool haveInitedZLib;
bool uncompressedData; // set if reading a plain .tar (not tar.gz)
bool haveInitedZLib = false;
bool uncompressedData = false; // set if reading a plain .tar (not tar.gz)
uint8_t* headerPtr;
TarExtractor* outer;
bool skipCurrentEntry = false;
std::string paxAttributes;
std::string paxPathName;
TarExtractorPrivate(TarExtractor* o) :
haveInitedZLib(false),
uncompressedData(false),
outer(o)
TarExtractorPrivate(ArchiveExtractor* o) :
ArchiveExtractorPrivate(o)
{
memset(&zlibStream, 0, sizeof(z_stream));
zlibOutput = (unsigned char*)malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
zlibStream.zalloc = Z_NULL;
zlibStream.zfree = Z_NULL;
zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
zlibStream.next_out = zlibOutput;
}
~TarExtractorPrivate()
@ -127,6 +190,17 @@ public:
free(zlibOutput);
}
void readPaddingIfRequired()
{
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE;
if (pad) {
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
setState(READING_PADDING);
} else {
setState(READING_HEADER);
}
}
void checkEndOfState()
{
if (bytesRemaining > 0) {
@ -138,13 +212,7 @@ public:
currentFile->close();
currentFile.reset();
}
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE;
if (pad) {
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
setState(READING_PADDING);
} else {
setState(READING_HEADER);
}
readPaddingIfRequired();
} else if (state == READING_HEADER) {
processHeader();
} else if (state == PRE_END_OF_ARCHVE) {
@ -153,6 +221,12 @@ public:
} else {
// what does the spec say here?
}
} else if (state == READING_PAX_GLOBAL_ATTRIBUTES) {
parsePAXAttributes(true);
readPaddingIfRequired();
} else if (state == READING_PAX_FILE_ATTRIBUTES) {
parsePAXAttributes(false);
readPaddingIfRequired();
} else if (state == READING_PADDING) {
setState(READING_HEADER);
}
@ -168,6 +242,77 @@ public:
state = newState;
}
void extractBytes(const uint8_t* bytes, size_t count) override
{
zlibStream.next_in = (uint8_t*) bytes;
zlibStream.avail_in = count;
if (!haveInitedZLib) {
// now we have data, see if we're dealing with GZ-compressed data or not
if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) {
// GZIP identification bytes
if (inflateInit2(&zlibStream, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
state = TarExtractorPrivate::BAD_DATA;
return;
}
} else {
UstarHeaderBlock* header = (UstarHeaderBlock*)bytes;
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
SG_LOG(SG_IO, SG_WARN, "didn't find tar magic in header");
state = TarExtractorPrivate::BAD_DATA;
return;
}
uncompressedData = true;
}
haveInitedZLib = true;
setState(TarExtractorPrivate::READING_HEADER);
} // of init on first-bytes case
if (uncompressedData) {
processBytes((const char*) bytes, count);
} else {
size_t writtenSize;
// loop, running zlib() inflate and sending output bytes to
// our request body handler. Keep calling inflate until no bytes are
// written, and ZLIB has consumed all available input
do {
zlibStream.next_out = zlibOutput;
zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
int result = inflate(&zlibStream, Z_NO_FLUSH);
if (result == Z_OK || result == Z_STREAM_END) {
// nothing to do
}
else if (result == Z_BUF_ERROR) {
// transient error, fall through
}
else {
// _error = result;
SG_LOG(SG_IO, SG_WARN, "Permanent ZLib error:" << zlibStream.msg);
state = TarExtractorPrivate::BAD_DATA;
return;
}
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - zlibStream.avail_out;
if (writtenSize > 0) {
processBytes((const char*) zlibOutput, writtenSize);
}
if (result == Z_STREAM_END) {
break;
}
} while ((zlibStream.avail_in > 0) || (writtenSize > 0));
} // of Zlib-compressed data
}
void flush() override
{
// no-op for tar files, we process everything greedily
}
void processHeader()
{
if (headerIsAllZeros()) {
@ -180,28 +325,32 @@ public:
}
if (strncmp(header.magic, TMAGIC, TMAGLEN) != 0) {
SG_LOG(SG_IO, SG_WARN, "magic is wrong");
SG_LOG(SG_IO, SG_WARN, "Untar: magic is wrong");
state = BAD_ARCHIVE;
return;
}
skipCurrentEntry = false;
std::string tarPath = std::string(header.prefix) + std::string(header.fileName);
if (!paxPathName.empty()) {
tarPath = paxPathName;
paxPathName.clear(); // clear for next file
}
if (!isSafePath(tarPath)) {
SG_LOG(SG_IO, SG_WARN, "bad tar path:" << tarPath);
skipCurrentEntry = true;
}
auto result = outer->filterPath(tarPath);
if (result == TarExtractor::Stop) {
auto result = filterPath(tarPath);
if (result == ArchiveExtractor::Stop) {
setState(FILTER_STOPPED);
return;
} else if (result == TarExtractor::Skipped) {
} else if (result == ArchiveExtractor::Skipped) {
skipCurrentEntry = true;
}
SGPath p = path / tarPath;
SGPath p = extractRootPath() / tarPath;
if (header.typeflag == DIRTYPE) {
if (!skipCurrentEntry) {
Dir dir(p);
@ -216,6 +365,20 @@ public:
currentFile->open(SG_IO_OUT);
}
setState(READING_FILE);
} else if (header.typeflag == PAX_GLOBAL_HEADER) {
setState(READING_PAX_GLOBAL_ATTRIBUTES);
currentFileSize = ::strtol(header.size, NULL, 8);
bytesRemaining = currentFileSize;
paxAttributes.clear();
} else if (header.typeflag == PAX_FILE_ATTRIBUTES) {
setState(READING_PAX_FILE_ATTRIBUTES);
currentFileSize = ::strtol(header.size, NULL, 8);
bytesRemaining = currentFileSize;
paxAttributes.clear();
} else if ((header.typeflag == SYMTYPE) || (header.typeflag == LNKTYPE)) {
SG_LOG(SG_IO, SG_WARN, "Tarball contains a link or symlink, will be skipped:" << tarPath);
skipCurrentEntry = true;
setState(READING_HEADER);
} else {
SG_LOG(SG_IO, SG_WARN, "Unsupported tar file type:" << header.typeflag);
state = BAD_ARCHIVE;
@ -240,6 +403,9 @@ public:
headerPtr += curBytes;
} else if (state == READING_PADDING) {
bytesRemaining -= curBytes;
} else if ((state == READING_PAX_FILE_ATTRIBUTES) || (state == READING_PAX_GLOBAL_ATTRIBUTES)) {
bytesRemaining -= curBytes;
paxAttributes.append(bytes, curBytes);
}
checkEndOfState();
@ -261,132 +427,283 @@ public:
return true;
}
bool isSafePath(const std::string& p) const
// https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxa500/paxex.htm#paxex
void parsePAXAttributes(bool areGlobal)
{
if (p.empty()) {
return false;
auto lineStart = 0;
for (;;) {
auto firstSpace = paxAttributes.find(' ', lineStart);
auto firstEq = paxAttributes.find('=', lineStart);
if ((firstEq == std::string::npos) || (firstSpace == std::string::npos)) {
SG_LOG(SG_IO, SG_WARN, "Malfroemd PAX attributes in tarfile");
break;
}
uint32_t lengthBytes = std::stoul(paxAttributes.substr(lineStart, firstSpace));
uint32_t dataBytes = lengthBytes - (firstEq + 1);
std::string name = paxAttributes.substr(firstSpace+1, firstEq - (firstSpace + 1));
// dataBytes - 1 here to trim off the trailing newline
std::string data = paxAttributes.substr(firstEq+1, dataBytes - 1);
processPAXAttribute(areGlobal, name, data);
lineStart += lengthBytes;
}
// reject absolute paths
if (p.at(0) == '/') {
return false;
}
void processPAXAttribute(bool isGlobalAttr, const std::string& attrName, const std::string& data)
{
if (!isGlobalAttr && (attrName == "path")) {
// data is UTF-8 encoded path name
paxPathName = data;
}
// reject paths containing '..'
size_t doubleDot = p.find("..");
if (doubleDot != std::string::npos) {
return false;
}
// on POSIX could use realpath to sanity check
return true;
}
};
TarExtractor::TarExtractor(const SGPath& rootPath) :
d(new TarExtractorPrivate(this))
{
///////////////////////////////////////////////////////////////////////////////
d->path = rootPath;
d->state = TarExtractorPrivate::INVALID;
memset(&d->zlibStream, 0, sizeof(z_stream));
d->zlibOutput = (unsigned char*) malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
d->zlibStream.zalloc = Z_NULL;
d->zlibStream.zfree = Z_NULL;
d->zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
d->zlibStream.next_out = d->zlibOutput;
extern "C" {
void fill_memory_filefunc(zlib_filefunc_def*);
}
TarExtractor::~TarExtractor()
class ZipExtractorPrivate : public ArchiveExtractorPrivate
{
public:
std::string m_buffer;
ZipExtractorPrivate(ArchiveExtractor* outer) :
ArchiveExtractorPrivate(outer)
{
}
~ZipExtractorPrivate()
{
}
void extractBytes(const uint8_t* bytes, size_t count) override
{
// becuase the .zip central directory is at the end of the file,
// we have no choice but to simply buffer bytes here until flush()
// is called
m_buffer.append((const char*) bytes, count);
}
void flush() override
{
zlib_filefunc_def memoryAccessFuncs;
fill_memory_filefunc(&memoryAccessFuncs);
char bufferName[128];
#if defined(SG_WINDOWS)
::snprintf(bufferName, 128, "%p+%llx", m_buffer.data(), m_buffer.size());
#else
::snprintf(bufferName, 128, "%p+%lx", m_buffer.data(), m_buffer.size());
#endif
unzFile zip = unzOpen2(bufferName, &memoryAccessFuncs);
const size_t BUFFER_SIZE = 32 * 1024;
void* buf = malloc(BUFFER_SIZE);
try {
int result = unzGoToFirstFile(zip);
if (result != UNZ_OK) {
throw sg_exception("failed to go to first file in archive");
}
while (true) {
extractCurrentFile(zip, (char*)buf, BUFFER_SIZE);
if (state == FILTER_STOPPED) {
break;
}
result = unzGoToNextFile(zip);
if (result == UNZ_END_OF_LIST_OF_FILE) {
break;
}
else if (result != UNZ_OK) {
throw sg_io_exception("failed to go to next file in the archive");
}
}
state = END_OF_ARCHIVE;
}
catch (sg_exception&) {
state = BAD_ARCHIVE;
}
free(buf);
unzClose(zip);
}
void extractCurrentFile(unzFile zip, char* buffer, size_t bufferSize)
{
unz_file_info fileInfo;
unzGetCurrentFileInfo(zip, &fileInfo,
buffer, bufferSize,
NULL, 0, /* extra field */
NULL, 0 /* comment field */);
std::string name(buffer);
if (!isSafePath(name)) {
throw sg_format_exception("Bad zip path", name);
}
auto filterResult = filterPath(name);
if (filterResult == ArchiveExtractor::Stop) {
state = FILTER_STOPPED;
return;
}
else if (filterResult == ArchiveExtractor::Skipped) {
return;
}
if (fileInfo.uncompressed_size == 0) {
// assume it's a directory for now
// since we create parent directories when extracting
// a path, we're done here
return;
}
int result = unzOpenCurrentFile(zip);
if (result != UNZ_OK) {
throw sg_io_exception("opening current zip file failed", sg_location(name));
}
sg_ofstream outFile;
bool eof = false;
SGPath path = extractRootPath() / name;
// create enclosing directory heirarchy as required
Dir parentDir(path.dir());
if (!parentDir.exists()) {
bool ok = parentDir.create(0755);
if (!ok) {
throw sg_io_exception("failed to create directory heirarchy for extraction", path);
}
}
outFile.open(path, std::ios::binary | std::ios::trunc | std::ios::out);
if (outFile.fail()) {
throw sg_io_exception("failed to open output file for writing", path);
}
while (!eof) {
int bytes = unzReadCurrentFile(zip, buffer, bufferSize);
if (bytes < 0) {
throw sg_io_exception("unzip failure reading curent archive", sg_location(name));
}
else if (bytes == 0) {
eof = true;
}
else {
outFile.write(buffer, bytes);
}
}
outFile.close();
unzCloseCurrentFile(zip);
}
};
//////////////////////////////////////////////////////////////////////////////
ArchiveExtractor::ArchiveExtractor(const SGPath& rootPath) :
_rootPath(rootPath)
{
}
ArchiveExtractor::~ArchiveExtractor()
{
}
void TarExtractor::extractBytes(const char* bytes, size_t count)
void ArchiveExtractor::extractBytes(const uint8_t* bytes, size_t count)
{
if (d->state >= TarExtractorPrivate::ERROR_STATE) {
if (!d) {
_prebuffer.append((char*) bytes, count);
auto r = determineType((uint8_t*) _prebuffer.data(), _prebuffer.size());
if (r == InsufficientData) {
return;
}
if (r == TarData) {
d.reset(new TarExtractorPrivate(this));
}
else if (r == ZipData) {
d.reset(new ZipExtractorPrivate(this));
}
else {
SG_LOG(SG_IO, SG_ALERT, "Invcalid archive type");
_invalidDataType = true;
return;
}
// if hit here, we created the extractor. Feed the prefbuffer
// bytes through it
d->extractBytes((uint8_t*) _prebuffer.data(), _prebuffer.size());
_prebuffer.clear();
return;
}
if (d->state >= ArchiveExtractorPrivate::ERROR_STATE) {
return;
}
d->zlibStream.next_in = (uint8_t*) bytes;
d->zlibStream.avail_in = count;
if (!d->haveInitedZLib) {
// now we have data, see if we're dealing with GZ-compressed data or not
uint8_t* ubytes = (uint8_t*) bytes;
if ((ubytes[0] == 0x1f) && (ubytes[1] == 0x8b)) {
// GZIP identification bytes
if (inflateInit2(&d->zlibStream, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
d->state = TarExtractorPrivate::BAD_DATA;
return;
}
} else {
UstarHeaderBlock* header = (UstarHeaderBlock*) bytes;
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
SG_LOG(SG_IO, SG_WARN, "didn't find tar magic in header");
d->state = TarExtractorPrivate::BAD_DATA;
return;
}
d->uncompressedData = true;
}
d->haveInitedZLib = true;
d->setState(TarExtractorPrivate::READING_HEADER);
} // of init on first-bytes case
if (d->uncompressedData) {
d->processBytes(bytes, count);
} else {
size_t writtenSize;
// loop, running zlib() inflate and sending output bytes to
// our request body handler. Keep calling inflate until no bytes are
// written, and ZLIB has consumed all available input
do {
d->zlibStream.next_out = d->zlibOutput;
d->zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
int result = inflate(&d->zlibStream, Z_NO_FLUSH);
if (result == Z_OK || result == Z_STREAM_END) {
// nothing to do
} else if (result == Z_BUF_ERROR) {
// transient error, fall through
} else {
// _error = result;
SG_LOG(SG_IO, SG_WARN, "Permanent ZLib error:" << d->zlibStream.msg);
d->state = TarExtractorPrivate::BAD_DATA;
return;
}
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - d->zlibStream.avail_out;
if (writtenSize > 0) {
d->processBytes((const char*) d->zlibOutput, writtenSize);
}
if (result == Z_STREAM_END) {
break;
}
} while ((d->zlibStream.avail_in > 0) || (writtenSize > 0));
} // of Zlib-compressed data
d->extractBytes(bytes, count);
}
bool TarExtractor::isAtEndOfArchive() const
void ArchiveExtractor::flush()
{
return (d->state == TarExtractorPrivate::END_OF_ARCHIVE);
if (!d)
return;
d->flush();
}
bool TarExtractor::hasError() const
bool ArchiveExtractor::isAtEndOfArchive() const
{
return (d->state >= TarExtractorPrivate::ERROR_STATE);
if (!d)
return false;
return (d->state == ArchiveExtractorPrivate::END_OF_ARCHIVE);
}
bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
bool ArchiveExtractor::hasError() const
{
if (_invalidDataType)
return true;
if (!d)
return false;
return (d->state >= ArchiveExtractorPrivate::ERROR_STATE);
}
ArchiveExtractor::DetermineResult ArchiveExtractor::determineType(const uint8_t* bytes, size_t count)
{
// check for ZIP
if (count < 4) {
return InsufficientData;
}
if (memcmp(bytes, "PK\x03\x04", 4) == 0) {
return ZipData;
}
auto r = isTarData(bytes, count);
if ((r == TarData) || (r == InsufficientData))
return r;
return Invalid;
}
ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* bytes, size_t count)
{
if (count < 2) {
return false;
return InsufficientData;
}
UstarHeaderBlock* header = 0;
@ -404,21 +721,20 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
if (inflateInit2(&z, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
inflateEnd(&z);
return false;
return Invalid;
}
int result = inflate(&z, Z_SYNC_FLUSH);
if (result != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflate failed:" << result);
inflateEnd(&z);
return false; // not tar data
return Invalid; // not tar data
}
size_t written = 4096 - z.avail_out;
if (written < TAR_HEADER_BLOCK_SIZE) {
SG_LOG(SG_IO, SG_WARN, "insufficient data for header");
inflateEnd(&z);
return false;
return InsufficientData;
}
header = reinterpret_cast<UstarHeaderBlock*>(zlibOutput);
@ -426,22 +742,25 @@ bool TarExtractor::isTarData(const uint8_t* bytes, size_t count)
} else {
// uncompressed tar
if (count < TAR_HEADER_BLOCK_SIZE) {
SG_LOG(SG_IO, SG_WARN, "insufficient data for header");
return false;
return InsufficientData;
}
header = (UstarHeaderBlock*) bytes;
}
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
SG_LOG(SG_IO, SG_WARN, "not a tar file");
return false;
return Invalid;
}
return true;
return TarData;
}
auto TarExtractor::filterPath(std::string& pathToExtract)
void ArchiveExtractor::extractLocalFile(const SGPath& archiveFile)
{
}
auto ArchiveExtractor::filterPath(std::string& pathToExtract)
-> PathResult
{
SG_UNUSED(pathToExtract);

View File

@ -21,40 +21,68 @@
#include <memory>
#include <cstdlib>
#include <stdint.h> // for uint8_t
#include <cstdint>
#include <simgear/misc/sg_path.hxx>
namespace simgear
{
class TarExtractorPrivate;
class ArchiveExtractorPrivate;
class TarExtractor
class ArchiveExtractor
{
public:
TarExtractor(const SGPath& rootPath);
~TarExtractor();
ArchiveExtractor(const SGPath& rootPath);
~ArchiveExtractor();
static bool isTarData(const uint8_t* bytes, size_t count);
enum DetermineResult
{
Invalid,
InsufficientData,
TarData,
ZipData
};
void extractBytes(const char* bytes, size_t count);
static DetermineResult determineType(const uint8_t* bytes, size_t count);
/**
* @brief API to extract a local zip or tar.gz
*/
void extractLocalFile(const SGPath& archiveFile);
/**
* @brief API to extract from memory - this can be called multiple
* times for streamking from a network socket etc
*/
void extractBytes(const uint8_t* bytes, size_t count);
void flush();
bool isAtEndOfArchive() const;
bool hasError() const;
enum PathResult {
Accepted,
Skipped,
Modified,
Stop
};
protected:
enum PathResult {
Accepted,
Skipped,
Modified,
Stop
};
virtual PathResult filterPath(std::string& pathToExtract);
private:
friend class TarExtractorPrivate;
std::unique_ptr<TarExtractorPrivate> d;
static DetermineResult isTarData(const uint8_t* bytes, size_t count);
friend class ArchiveExtractorPrivate;
std::unique_ptr<ArchiveExtractorPrivate> d;
SGPath _rootPath;
std::string _prebuffer; // store bytes before type is determined
bool _invalidDataType = false;
};
} // of namespace simgear

BIN
simgear/io/zippy.zip Normal file

Binary file not shown.

View File

@ -142,10 +142,10 @@ public:
T (&sg(void))[4][4]
{ return _data.ptr(); }
/// Readonly raw storage interface
const simd4x4_t<T,4> (&simd4x4(void) const)
const simd4x4_t<T,4> &simd4x4(void) const
{ return _data; }
/// Readonly raw storage interface
simd4x4_t<T,4> (&simd4x4(void))
simd4x4_t<T,4> &simd4x4(void)
{ return _data; }

View File

@ -87,10 +87,10 @@ public:
/// Access raw data
T (&data(void))[2]
{ return _data.ptr(); }
const simd4_t<T,2> (&simd2(void) const)
const simd4_t<T,2> &simd2(void) const
{ return _data; }
/// Readonly raw storage interface
simd4_t<T,2> (&simd2(void))
simd4_t<T,2> &simd2(void)
{ return _data; }
/// Inplace addition

View File

@ -106,10 +106,10 @@ public:
T (&data(void))[3]
{ return _data.ptr(); }
/// Readonly raw storage interface
const simd4_t<T,3> (&simd3(void) const)
const simd4_t<T,3> &simd3(void) const
{ return _data; }
/// Readonly raw storage interface
simd4_t<T,3> (&simd3(void))
simd4_t<T,3> &simd3(void)
{ return _data; }
/// Inplace addition

View File

@ -99,10 +99,10 @@ public:
T (&data(void))[4]
{ return _data.ptr(); }
/// Readonly raw storage interface
const simd4_t<T,4> (&simd4(void) const)
const simd4_t<T,4> &simd4(void) const
{ return _data; }
/// Readonly raw storage interface
simd4_t<T,4> (&simd4(void))
simd4_t<T,4> &simd4(void)
{ return _data; }
/// Inplace addition

View File

@ -352,10 +352,10 @@ public:
simd4_t(const simd4_t<float,2>& v) { simd4 = v.v4(); }
simd4_t(const __m128& v) { simd4 = v; }
inline const __m128 (&v4(void) const) {
inline const __m128 &v4(void) const {
return simd4;
}
inline __m128 (&v4(void)) {
inline __m128 &v4(void) {
return simd4;
}
@ -1120,11 +1120,11 @@ public:
simd4_t(const simd4_t<int,2>& v) { simd4 = v.v4(); }
simd4_t(const __m128i& v) { simd4 = v; }
inline __m128i (&v4(void)) {
inline __m128i &v4(void) {
return simd4;
}
inline const __m128i (&v4(void) const) {
inline const __m128i &v4(void) const {
return simd4;
}

View File

@ -17,14 +17,23 @@
//
// $Id$
#include <assert.h>
#include <algorithm>
#include <simgear_config.h>
#include <simgear/misc/ResourceManager.hxx>
#include <simgear/debug/logstream.hxx>
namespace simgear
{
static ResourceManager* static_manager = NULL;
static ResourceManager* static_manager = nullptr;
ResourceProvider::~ResourceProvider()
{
// pin to this compilation unit
}
ResourceManager::ResourceManager()
{
@ -40,13 +49,20 @@ ResourceManager* ResourceManager::instance()
return static_manager;
}
ResourceManager::~ResourceManager()
{
assert(this == static_manager);
static_manager = nullptr;
std::for_each(_providers.begin(), _providers.end(),
[](ResourceProvider* p) { delete p; });
}
/**
* trivial provider using a fixed base path
*/
class BasePathProvider : public ResourceProvider
{
public:
BasePathProvider(const SGPath& aBase, int aPriority) :
BasePathProvider(const SGPath& aBase, ResourceManager::Priority aPriority) :
ResourceProvider(aPriority),
_base(aBase)
{
@ -69,6 +85,8 @@ void ResourceManager::addBasePath(const SGPath& aPath, Priority aPriority)
void ResourceManager::addProvider(ResourceProvider* aProvider)
{
assert(aProvider);
ProviderVec::iterator it = _providers.begin();
for (; it != _providers.end(); ++it) {
if (aProvider->priority() > (*it)->priority()) {
@ -81,6 +99,16 @@ void ResourceManager::addProvider(ResourceProvider* aProvider)
_providers.push_back(aProvider);
}
void ResourceManager::removeProvider(ResourceProvider* aProvider)
{
assert(aProvider);
auto it = std::find(_providers.begin(), _providers.end(), aProvider);
if (it == _providers.end()) {
SG_LOG(SG_GENERAL, SG_DEV_ALERT, "unknown provider doing remove");
return;
}
}
SGPath ResourceManager::findPath(const std::string& aResource, SGPath aContext)
{
if (!aContext.isNull()) {
@ -90,9 +118,8 @@ SGPath ResourceManager::findPath(const std::string& aResource, SGPath aContext)
}
}
ProviderVec::iterator it = _providers.begin();
for (; it != _providers.end(); ++it) {
SGPath path = (*it)->resolve(aResource, aContext);
for (auto provider : _providers) {
SGPath path = provider->resolve(aResource, aContext);
if (!path.isNull()) {
return path;
}

View File

@ -36,6 +36,8 @@ class ResourceProvider;
class ResourceManager
{
public:
~ResourceManager();
typedef enum {
PRIORITY_DEFAULT = 0,
PRIORITY_FALLBACK = -100,
@ -55,6 +57,8 @@ public:
*/
void addProvider(ResourceProvider* aProvider);
void removeProvider(ResourceProvider* aProvider);
/**
* given a resource name (or path), find the appropriate real resource
* path.
@ -75,17 +79,19 @@ class ResourceProvider
public:
virtual SGPath resolve(const std::string& aResource, SGPath& aContext) const = 0;
virtual int priority() const
virtual ~ResourceProvider();
virtual ResourceManager::Priority priority() const
{
return _priority;
}
protected:
ResourceProvider(int aPriority) :
ResourceProvider(ResourceManager::Priority aPriority) :
_priority(aPriority)
{}
int _priority;
ResourceManager::Priority _priority = ResourceManager::PRIORITY_DEFAULT;
};
} // of simgear namespace

View File

@ -107,7 +107,7 @@ static SGPath pathForKnownFolder(REFKNOWNFOLDERID folderId, const SGPath& def)
// system call will allocate dynamic memory... which we must release when done
wchar_t* localFolder = 0;
if (pSHGetKnownFolderPath(folderId, KF_FLAG_DEFAULT_PATH, NULL, &localFolder) == S_OK) {
if (pSHGetKnownFolderPath(folderId, KF_FLAG_DONT_VERIFY, NULL, &localFolder) == S_OK) {
SGPath folder_path = SGPath(localFolder, def.getPermissionChecker());
// release dynamic memory
CoTaskMemFree(static_cast<void*>(localFolder));

View File

@ -12,65 +12,50 @@
// $Id$
//////////////////////////////////////////////////////////////////////
//
// There are many sick systems out there:
//
// check for sizeof(float) and sizeof(double)
// if sizeof(float) != 4 this code must be patched
// if sizeof(double) != 8 this code must be patched
//
// Those are comments I fetched out of glibc source:
// - s390 is big-endian
// - Sparc is big-endian, but v9 supports endian conversion
// on loads/stores and GCC supports such a mode. Be prepared.
// - The MIPS architecture has selectable endianness.
// - x86_64 is little-endian.
// - CRIS is little-endian.
// - m68k is big-endian.
// - Alpha is little-endian.
// - PowerPC can be little or big endian.
// - SH is bi-endian but with a big-endian FPU.
// - hppa1.1 big-endian.
// - ARM is (usually) little-endian but with a big-endian FPU.
//
//////////////////////////////////////////////////////////////////////
#include <cstdint>
#include <cstdlib> // for _byteswap_foo on Win32
#ifdef _MSC_VER
typedef signed char int8_t;
typedef signed short int16_t;
typedef signed int int32_t;
typedef signed __int64 int64_t;
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned __int64 uint64_t;
typedef int ssize_t;
#elif defined(sgi) || defined(__sun)
# include <sys/types.h>
#else
# include <stdint.h>
#if defined(_MSC_VER)
using ssize_t = int64_t; // this is a POSIX type, not a C one
#endif
inline uint16_t sg_bswap_16(uint16_t x) {
#if defined(__llvm__) || \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 8)) && !defined(__ICC)
return __builtin_bswap16(x);
#elif defined(_MSC_VER) && !defined(_DEBUG)
return _byteswap_ushort(x);
#else
x = (x >> 8) | (x << 8);
return x;
#endif
}
inline uint32_t sg_bswap_32(uint32_t x) {
#if defined(__llvm__) || \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && !defined(__ICC)
return __builtin_bswap32(x);
#elif defined(_MSC_VER) && !defined(_DEBUG)
return _byteswap_ulong(x);
#else
x = ((x >> 8) & 0x00FF00FFL) | ((x << 8) & 0xFF00FF00L);
x = (x >> 16) | (x << 16);
return x;
#endif
}
inline uint64_t sg_bswap_64(uint64_t x) {
#if defined(__llvm__) || \
(__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 3)) && !defined(__ICC)
return __builtin_bswap64(x);
#elif defined(_MSC_VER) && !defined(_DEBUG)
return _byteswap_uint64(x);
#else
x = ((x >> 8) & 0x00FF00FF00FF00FFLL) | ((x << 8) & 0xFF00FF00FF00FF00LL);
x = ((x >> 16) & 0x0000FFFF0000FFFFLL) | ((x << 16) & 0xFFFF0000FFFF0000LL);
x = (x >> 32) | (x << 32);
return x;
#endif
}

View File

@ -37,7 +37,7 @@
#include <simgear/package/md5.h>
#include <simgear/compiler.h> // SG_WINDOWS
#include <simgear/structure/exception.hxx>
#include <simgear/math/SGGeod.hxx>
#if defined(SG_WINDOWS)
#include <windows.h>
@ -1133,6 +1133,266 @@ bool matchPropPathToTemplate(const std::string& path, const std::string& templat
// unreachable
}
bool parseStringAsLatLonValue(const std::string& s, double& degrees)
{
string ss = simplify(s);
auto spacePos = ss.find_first_of(" *");
if (spacePos == std::string::npos) {
degrees = std::stod(ss);
} else {
degrees = std::stod(ss.substr(0, spacePos));
double minutes = 0.0, seconds = 0.0;
// check for minutes marker
auto quotePos = ss.find('\'');
if (quotePos == std::string::npos) {
const auto minutesStr = ss.substr(spacePos+1);
if (!minutesStr.empty()) {
minutes = std::stod(minutesStr);
}
} else {
minutes = std::stod(ss.substr(spacePos+1, quotePos - spacePos));
const auto secondsStr = ss.substr(quotePos+1);
if (!secondsStr.empty()) {
seconds = std::stod(secondsStr);
}
}
if ((seconds < 0.0) || (minutes < 0.0)) {
// don't allow sign information in minutes or seconds
return false;
}
double offset = (minutes / 60.0) + (seconds / 3600.0);
degrees += (degrees >= 0.0) ? offset : -offset;
}
// since we simplified, any trailing N/S/E/W must be the last char
const char lastChar = ::toupper(ss.back());
if ((lastChar == 'W') || (lastChar == 'S')) {
degrees = -degrees;
}
return true;
}
namespace {
bool isLatString(const std::string &s)
{
const char lastChar = ::toupper(s.back());
return (lastChar == 'N') || (lastChar == 'S');
}
bool isLonString(const std::string &s)
{
const char lastChar = ::toupper(s.back());
return (lastChar == 'E') || (lastChar == 'W');
}
} // of anonymous namespace
bool parseStringAsGeod(const std::string& s, SGGeod* result, bool assumeLonLatOrder)
{
if (s.empty())
return false;
const auto commaPos = s.find(',');
if (commaPos == string::npos) {
return false;
}
auto termA = simplify(s.substr(0, commaPos)),
termB = simplify(s.substr(commaPos+1));
double valueA, valueB;
if (!parseStringAsLatLonValue(termA, valueA) || !parseStringAsLatLonValue(termB, valueB)) {
return false;
}
if (result) {
// explicit ordering
if (isLatString(termA) && isLonString(termB)) {
*result = SGGeod::fromDeg(valueB, valueA);
} else if (isLonString(termA) && isLatString(termB)) {
*result = SGGeod::fromDeg(valueA, valueB);
} else {
// implicit ordering
// SGGeod wants longitude, latitude
*result = assumeLonLatOrder ? SGGeod::fromDeg(valueA, valueB)
: SGGeod::fromDeg(valueB, valueA);
}
}
return true;
}
namespace {
const char* static_degreeSymbols[] = {
"*",
" ",
"\xB0", // Latin-1 B0 codepoint
"\xC2\xB0" // UTF-8 equivalent
};
} // of anonymous namespace
std::string formatLatLonValueAsString(double deg, LatLonFormat format,
char c,
DegreeSymbol degreeSymbol)
{
double min, sec;
const int sign = deg < 0.0 ? -1 : 1;
deg = fabs(deg);
char buf[128];
const char* degSym = static_degreeSymbols[static_cast<int>(degreeSymbol)];
switch (format) {
case LatLonFormat::DECIMAL_DEGREES:
::snprintf(buf, sizeof(buf), "%3.6f%c", deg, c);
break;
case LatLonFormat::DEGREES_MINUTES:
// d mm.mmm' (DMM format) -- uses a round-off factor tailored to the
// required precision of the minutes field (three decimal places),
// preventing minute values of 60.
min = (deg - int(deg)) * 60.0;
if (min >= 59.9995) {
min -= 60.0;
deg += 1.0;
}
snprintf(buf, sizeof(buf), "%d%s%06.3f'%c", int(deg), degSym, fabs(min), c);
break;
case LatLonFormat::DEGREES_MINUTES_SECONDS:
// d mm'ss.s" (DMS format) -- uses a round-off factor tailored to the
// required precision of the seconds field (one decimal place),
// preventing second values of 60.
min = (deg - int(deg)) * 60.0;
sec = (min - int(min)) * 60.0;
if (sec >= 59.95) {
sec -= 60.0;
min += 1.0;
if (min >= 60.0) {
min -= 60.0;
deg += 1.0;
}
}
::snprintf(buf, sizeof(buf), "%d%s%02d'%04.1f\"%c", int(deg), degSym,
int(min), fabs(sec), c);
break;
case LatLonFormat::SIGNED_DECIMAL_DEGREES:
// d.dddddd' (signed DDD format).
::snprintf(buf, sizeof(buf), "%3.6f", sign*deg);
break;
case LatLonFormat::SIGNED_DEGREES_MINUTES:
// d mm.mmm' (signed DMM format).
min = (deg - int(deg)) * 60.0;
if (min >= 59.9995) {
min -= 60.0;
deg += 1.0;
}
if (sign == 1) {
snprintf(buf, sizeof(buf), "%d%s%06.3f'", int(deg), degSym, fabs(min));
} else {
snprintf(buf, sizeof(buf), "-%d%s%06.3f'", int(deg), degSym, fabs(min));
}
break;
case LatLonFormat::SIGNED_DEGREES_MINUTES_SECONDS:
// d mm'ss.s" (signed DMS format).
min = (deg - int(deg)) * 60.0;
sec = (min - int(min)) * 60.0;
if (sec >= 59.95) {
sec -= 60.0;
min += 1.0;
if (min >= 60.0) {
min -= 60.0;
deg += 1.0;
}
}
if (sign == 1) {
snprintf(buf, sizeof(buf), "%d%s%02d'%04.1f\"", int(deg), degSym, int(min), fabs(sec));
} else {
snprintf(buf, sizeof(buf), "-%d%s%02d'%04.1f\"", int(deg), degSym, int(min), fabs(sec));
}
break;
case LatLonFormat::ZERO_PAD_DECIMAL_DEGRESS:
// dd.dddddd X, ddd.dddddd X (zero padded DDD format).
if (c == 'N' || c == 'S') {
snprintf(buf, sizeof(buf), "%09.6f%c", deg, c);
} else {
snprintf(buf, sizeof(buf), "%010.6f%c", deg, c);
}
break;
case LatLonFormat::ZERO_PAD_DEGREES_MINUTES:
// dd mm.mmm' X, ddd mm.mmm' X (zero padded DMM format).
min = (deg - int(deg)) * 60.0;
if (min >= 59.9995) {
min -= 60.0;
deg += 1.0;
}
if (c == 'N' || c == 'S') {
snprintf(buf, sizeof(buf), "%02d%s%06.3f'%c", int(deg), degSym, fabs(min), c);
} else {
snprintf(buf, sizeof(buf), "%03d%s%06.3f'%c", int(deg), degSym, fabs(min), c);
}
break;
case LatLonFormat::ZERO_PAD_DEGREES_MINUTES_SECONDS:
// dd mm'ss.s" X, dd mm'ss.s" X (zero padded DMS format).
min = (deg - int(deg)) * 60.0;
sec = (min - int(min)) * 60.0;
if (sec >= 59.95) {
sec -= 60.0;
min += 1.0;
if (min >= 60.0) {
min -= 60.0;
deg += 1.0;
}
}
if (c == 'N' || c == 'S') {
snprintf(buf, sizeof(buf), "%02d%s%02d'%04.1f\"%c", int(deg), degSym, int(min), fabs(sec), c);
} else {
snprintf(buf, sizeof(buf), "%03d%s%02d'%04.1f\"%c", int(deg), degSym, int(min), fabs(sec), c);
}
break;
case LatLonFormat::TRINITY_HOUSE:
// dd* mm'.mmm X, ddd* mm'.mmm X (Trinity House Navigation standard).
min = (deg - int(deg)) * 60.0;
if (min >= 59.9995) {
min -= 60.0;
deg += 1.0;
}
if (c == 'N' || c == 'S') {
snprintf(buf, sizeof(buf), "%02d* %02d'.%03d%c", int(deg), int(min), int(SGMisc<double>::round((min-int(min))*1000)), c);
} else {
snprintf(buf, sizeof(buf), "%03d* %02d'.%03d%c", int(deg), int(min), int(SGMisc<double>::round((min-int(min))*1000)), c);
}
break;
case LatLonFormat::DECIMAL_DEGREES_SYMBOL:
::snprintf(buf, sizeof(buf), "%3.6f%s%c", deg, degSym, c);
break;
}
return std::string(buf);
}
std::string formatGeodAsString(const SGGeod& geod, LatLonFormat format,
DegreeSymbol degreeSymbol)
{
const char ns = (geod.getLatitudeDeg() > 0.0) ? 'N' : 'S';
const char ew = (geod.getLongitudeDeg() > 0.0) ? 'E' : 'W';
return formatLatLonValueAsString(geod.getLatitudeDeg(), format, ns, degreeSymbol) + ","
+ formatLatLonValueAsString(geod.getLongitudeDeg(), format, ew, degreeSymbol);
}
} // end namespace strutils

View File

@ -36,6 +36,9 @@
typedef std::vector < std::string > string_list;
// forward decls
class SGGeod;
namespace simgear {
namespace strutils {
@ -354,6 +357,67 @@ namespace simgear {
* /views[0]/view[4]/fig, /views[0]/view[1000]/flight
*/
bool matchPropPathToTemplate(const std::string& path, const std::string& templatePath);
bool parseStringAsLatLonValue(const std::string& s, double& result);
/**
* Attempt to parse a string as a latitude,longitude input. Returns true
* or false based on success, and returns the SGGeod by pointer. Leading,
* trailing and internal white-space is skipped / ignored.
*
* Supported formats:
* <signed decimal degrees latitude>,<signed decimal degress longitude>
* <unsigned decimal degrees>[NS],<unsigned decimal degrees>[EW]
* <degrees>*<decimal minutes>'[NS],<degrees>*<decimal minutes>'[EW]
*
* Latitude and longitude are parsed seperately so the formats for each
* do not need to agree. Latitude is assumed to precede longitude
* unless assumeLonLatOrder = true
*
* When NSEW characters are used, the order can be swapped and will be
* fixed correctly (longitude then latitude).
*/
bool parseStringAsGeod(const std::string& string,
SGGeod* result = nullptr,
bool assumeLonLatOrder = false);
// enum values here correspond to existing lon-lat format codes inside
// FlightGear (property: /sim/lon-lat-format )
// Don't re-order, just add new ones, or things may break
enum class LatLonFormat
{
DECIMAL_DEGREES = 0, ///< 88.4N,4.54W,
DEGREES_MINUTES, ///< 88 24.6'N, 4 30.5'W
DEGREES_MINUTES_SECONDS,
SIGNED_DECIMAL_DEGREES, ///< 88.4,-4.54
SIGNED_DEGREES_MINUTES,
SIGNED_DEGREES_MINUTES_SECONDS,
ZERO_PAD_DECIMAL_DEGRESS,
ZERO_PAD_DEGREES_MINUTES,
ZERO_PAD_DEGREES_MINUTES_SECONDS,
TRINITY_HOUSE, ///< dd* mm'.mmm X, ddd* mm'.mmm X (Trinity House Navigation standard).
DECIMAL_DEGREES_SYMBOL ///< 88.4*N,4.54*W
};
enum class DegreeSymbol
{
ASTERISK = 0,
SPACE,
LATIN1_DEGREE,
UTF8_DEGREE
};
std::string formatLatLonValueAsString(double deg,
LatLonFormat format, char c,
DegreeSymbol degreeSymbol = DegreeSymbol::ASTERISK);
/**
* Format an SGGeod as a string according to the provided rule.
* if the SGGeod is invalid (default constructed), will return an empty string
*/
std::string formatGeodAsString(const SGGeod& geod,
LatLonFormat format = LatLonFormat::DECIMAL_DEGREES,
DegreeSymbol degreeSymbol = DegreeSymbol::ASTERISK);
} // end namespace strutils
} // end namespace simgear

View File

@ -20,6 +20,7 @@
#include <simgear/misc/strutils.hxx>
#include <simgear/structure/exception.hxx>
#include <simgear/constants.h>
#include <simgear/math/SGGeod.hxx>
using std::string;
using std::vector;
@ -619,6 +620,84 @@ void test_utf8Convert()
SG_VERIFY(a == aRoundTrip);
}
void test_parseGeod()
{
SGGeod a;
SG_VERIFY(strutils::parseStringAsGeod("56.12,-3.0", &a));
SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -3.0);
SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), 56.12);
SG_VERIFY(strutils::parseStringAsGeod("56.12345678s,3.12345678w", &a));
SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -3.12345678);
SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -56.12345678);
// trailing degrees
SG_VERIFY(strutils::parseStringAsGeod("56.12*,-3.0*", &a));
SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -3.0);
SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), 56.12);
// embedded whitepace, DMS notation, NSEW notation
SG_VERIFY(strutils::parseStringAsGeod("\t40 30'50\"S, 12 34'56\"W ", &a));
SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -12.58222222);
SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -40.5138888);
// embedded whitepace, DMS notation, NSEW notation, degrees symbol
SG_VERIFY(strutils::parseStringAsGeod("\t40*30'50\"S, 12*34'56\"W ", &a));
SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -12.58222222);
SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -40.5138888);
// signed degrees-minutes
SG_VERIFY(strutils::parseStringAsGeod("-45 27.89,-12 34.56", &a));
SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -12.576);
SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -45.464833333);
SG_VERIFY(strutils::parseStringAsGeod("") == false);
SG_VERIFY(strutils::parseStringAsGeod("aaaaaaaa") == false);
// ordering tests
// normal default order, but explicitly pass as lon,lat
// (should work)
SG_VERIFY(strutils::parseStringAsGeod("3.12345678w, 56.12345678s", &a));
SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -3.12345678);
SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -56.12345678);
// different default order
// also some embedded whitespace for fun
SG_VERIFY(strutils::parseStringAsGeod(" -12 34.56,\n-45 27.89 ", &a, true));
SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -12.576);
SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -45.464833333);
// differnet default order, but still set explicitly so should
// use the lat,lon order
SG_VERIFY(strutils::parseStringAsGeod("\t40 30'50\"S, 12 34'56\"W ", &a, true));
SG_CHECK_EQUAL_EP(a.getLongitudeDeg(), -12.58222222);
SG_CHECK_EQUAL_EP(a.getLatitudeDeg(), -40.5138888);
}
void test_formatGeod()
{
SGGeod a = SGGeod::fromDeg(-3.46, 55.45);
SG_CHECK_EQUAL(strutils::formatGeodAsString(a, strutils::LatLonFormat::SIGNED_DECIMAL_DEGREES), "55.450000,-3.460000");
SG_CHECK_EQUAL(strutils::formatGeodAsString(a, strutils::LatLonFormat::DEGREES_MINUTES_SECONDS),
"55*27'00.0\"N,3*27'36.0\"W");
const auto s = strutils::formatGeodAsString(a,
strutils::LatLonFormat::ZERO_PAD_DEGREES_MINUTES,
strutils::DegreeSymbol::LATIN1_DEGREE);
SG_CHECK_EQUAL(s, "55\xB0" "27.000'N,003\xB0" "27.600'W");
// Jakarta, if you care
SGGeod b = SGGeod::fromDeg(106.8278, -6.1568);
const auto s2 = strutils::formatGeodAsString(b,
strutils::LatLonFormat::DECIMAL_DEGREES_SYMBOL,
strutils::DegreeSymbol::UTF8_DEGREE);
SG_CHECK_EQUAL(s2, "6.156800\xC2\xB0S,106.827800\xC2\xB0" "E");
}
int main(int argc, char* argv[])
{
test_strip();
@ -639,6 +718,8 @@ int main(int argc, char* argv[])
test_propPathMatch();
test_readTime();
test_utf8Convert();
test_parseGeod();
test_formatGeod();
return EXIT_SUCCESS;
}

View File

@ -36,10 +36,6 @@
#include <simgear/misc/strutils.hxx>
#include <simgear/io/iostreams/sgstream.hxx>
extern "C" {
void fill_memory_filefunc (zlib_filefunc_def*);
}
namespace simgear {
namespace pkg {
@ -57,9 +53,9 @@ public:
throw sg_exception("no package download URLs");
}
if (m_owner->package()->properties()->hasChild("archive-type")) {
setArchiveTypeFromExtension(m_owner->package()->properties()->getStringValue("archive-type"));
}
// if (m_owner->package()->properties()->hasChild("archive-type")) {
// setArchiveTypeFromExtension(m_owner->package()->properties()->getStringValue("archive-type"));
//}
// TODO randomise order of m_urls
@ -110,17 +106,17 @@ protected:
Dir d(m_extractPath);
d.create(0755);
m_extractor.reset(new ArchiveExtractor(m_extractPath));
memset(&m_md5, 0, sizeof(SG_MD5_CTX));
SG_MD5Init(&m_md5);
}
virtual void gotBodyData(const char* s, int n)
{
m_buffer += std::string(s, n);
SG_MD5Update(&m_md5, (unsigned char*) s, n);
m_downloaded = m_buffer.size();
m_owner->installProgress(m_buffer.size(), responseLength());
const uint8_t* ubytes = (uint8_t*) s;
SG_MD5Update(&m_md5, ubytes, n);
m_owner->installProgress(m_downloaded, responseLength());
m_extractor->extractBytes(ubytes, n);
}
virtual void onDone()
@ -151,7 +147,8 @@ protected:
return;
}
if (!extract()) {
m_extractor->flush();
if (m_extractor->hasError() || !m_extractor->isAtEndOfArchive()) {
SG_LOG(SG_GENERAL, SG_WARN, "archive extraction failed");
doFailure(Delegate::FAIL_EXTRACT);
return;
@ -207,151 +204,6 @@ protected:
}
private:
void setArchiveTypeFromExtension(const std::string& ext)
{
if (ext.empty())
return;
if (ext == "zip") {
m_archiveType = ZIP;
return;
}
if ((ext == "tar.gz") || (ext == "tgz")) {
m_archiveType = TAR_GZ;
return;
}
}
void extractCurrentFile(unzFile zip, char* buffer, size_t bufferSize)
{
unz_file_info fileInfo;
unzGetCurrentFileInfo(zip, &fileInfo,
buffer, bufferSize,
NULL, 0, /* extra field */
NULL, 0 /* comment field */);
std::string name(buffer);
// no absolute paths, no 'up' traversals
// we could also look for suspicious file extensions here (forbid .dll, .exe, .so)
if ((name[0] == '/') || (name.find("../") != std::string::npos) || (name.find("..\\") != std::string::npos)) {
throw sg_format_exception("Bad zip path", name);
}
if (fileInfo.uncompressed_size == 0) {
// assume it's a directory for now
// since we create parent directories when extracting
// a path, we're done here
return;
}
int result = unzOpenCurrentFile(zip);
if (result != UNZ_OK) {
throw sg_io_exception("opening current zip file failed", sg_location(name));
}
sg_ofstream outFile;
bool eof = false;
SGPath path(m_extractPath);
path.append(name);
// create enclosing directory heirarchy as required
Dir parentDir(path.dir());
if (!parentDir.exists()) {
bool ok = parentDir.create(0755);
if (!ok) {
throw sg_io_exception("failed to create directory heirarchy for extraction", path);
}
}
outFile.open(path, std::ios::binary | std::ios::trunc | std::ios::out);
if (outFile.fail()) {
throw sg_io_exception("failed to open output file for writing", path);
}
while (!eof) {
int bytes = unzReadCurrentFile(zip, buffer, bufferSize);
if (bytes < 0) {
throw sg_io_exception("unzip failure reading curent archive", sg_location(name));
} else if (bytes == 0) {
eof = true;
} else {
outFile.write(buffer, bytes);
}
}
outFile.close();
unzCloseCurrentFile(zip);
}
bool extract()
{
const std::string u(url());
const size_t ul(u.length());
if (m_archiveType == AUTO_DETECT) {
if (u.rfind(".zip") == (ul - 4)) {
m_archiveType = ZIP;
} else if (u.rfind(".tar.gz") == (ul - 7)) {
m_archiveType = TAR_GZ;
}
// we will fall through to the error case now
}
if (m_archiveType == ZIP) {
return extractUnzip();
} else if (m_archiveType == TAR_GZ) {
return extractTar();
}
SG_LOG(SG_IO, SG_WARN, "unsupported archive format:" << u);
return false;
}
bool extractUnzip()
{
bool result = true;
zlib_filefunc_def memoryAccessFuncs;
fill_memory_filefunc(&memoryAccessFuncs);
char bufferName[128];
snprintf(bufferName, 128, "%p+%lx", m_buffer.data(), m_buffer.size());
unzFile zip = unzOpen2(bufferName, &memoryAccessFuncs);
const size_t BUFFER_SIZE = 32 * 1024;
void* buf = malloc(BUFFER_SIZE);
try {
int result = unzGoToFirstFile(zip);
if (result != UNZ_OK) {
throw sg_exception("failed to go to first file in archive");
}
while (true) {
extractCurrentFile(zip, (char*) buf, BUFFER_SIZE);
result = unzGoToNextFile(zip);
if (result == UNZ_END_OF_LIST_OF_FILE) {
break;
} else if (result != UNZ_OK) {
throw sg_io_exception("failed to go to next file in the archive");
}
}
} catch (sg_exception& ) {
result = false;
}
free(buf);
unzClose(zip);
return result;
}
bool extractTar()
{
TarExtractor tx(m_extractPath);
tx.extractBytes(m_buffer.data(), m_buffer.size());
return !tx.hasError() && tx.isAtEndOfArchive();
}
void doFailure(Delegate::StatusCode aReason)
{
Dir dir(m_extractPath);
@ -364,19 +216,13 @@ private:
m_owner->installResult(aReason);
}
enum ArchiveType {
AUTO_DETECT = 0,
ZIP,
TAR_GZ
};
InstallRef m_owner;
ArchiveType m_archiveType = AUTO_DETECT;
string_list m_urls;
SG_MD5_CTX m_md5;
std::string m_buffer;
SGPath m_extractPath;
size_t m_downloaded;
std::unique_ptr<ArchiveExtractor> m_extractor;
};
////////////////////////////////////////////////////////////////////

View File

@ -125,7 +125,7 @@ SGMesh::SGMesh( const SGDemPtr dem,
::std::vector<SGGeod> geodes(grid_width*grid_height);
// session can't be paralell yet - save alts in geode array
// session can't be parallel yet - save alts in geode array
fprintf( stderr, "SGMesh::SGMesh - create session - num dem roots is %d\n", dem->getNumRoots() );
SGDemSession s = dem->openSession( wo, so, eo, no, lvl, true );
s.getGeods( wo, so, eo, no, grid_width, grid_height, skipx, skipy, geodes, Debug1, Debug2 );
@ -159,10 +159,10 @@ SGMesh::SGMesh( const SGDemPtr dem,
index.push_back( src_idx );
}
// we can convert to cartesian in paralell
unsigned int nv = geodes.size();
// we can convert to cartesian in parallel
long nv = geodes.size();
#pragma omp parallel for
for (unsigned int i = 0; i < nv; i++) {
for (long i = 0; i < nv; i++) {
(*vertices)[i].set( toOsg( SGVec3f::fromGeod( geodes[i] ) ) );
}
@ -195,7 +195,7 @@ SGMesh::SGMesh( const SGDemPtr dem,
// translate pos after normals computed
#pragma omp parallel for
for ( unsigned int i=0; i < nv; i++ ) {
for ( long i=0; i < nv; i++ ) {
(*vertices)[i].set( transform.preMult( (*vertices)[i]) );
}
@ -245,7 +245,7 @@ SGMesh::SGMesh( const SGDemPtr dem,
osg::Geometry* geometry = new osg::Geometry;
char geoName[64];
snprintf( geoName, sizeof(geoName), "tilemesh (%u,%u)-(%u,%u):level%d,%d",
snprintf( geoName, sizeof(geoName), "tilemesh (%u,%u)-(%u,%u):level%u,%u",
wo, so, eo, no,
widthLevel, heightLevel );
geometry->setName(geoName);
@ -364,9 +364,9 @@ void SGMesh::need_normals()
// need_faces();
if ( !faces.empty() ) {
// Compute from faces
int nf = faces.size();
long nf = faces.size();
#pragma omp parallel for
for (int i = 0; i < nf; i++) {
for (long i = 0; i < nf; i++) {
const osg::Vec3 &p0 = (*vertices)[faces[i][0]];
const osg::Vec3 &p1 = (*vertices)[faces[i][1]];
const osg::Vec3 &p2 = (*vertices)[faces[i][2]];
@ -384,9 +384,9 @@ void SGMesh::need_normals()
}
// Make them all unit-length
unsigned int nn = normals->size();
long nn = normals->size();
#pragma omp parallel for
for (unsigned int i = 0; i < nn; i++)
for (long i = 0; i < nn; i++)
(*normals)[i].normalize();
}
}

View File

@ -161,33 +161,68 @@ SGModelLib::loadDeferredModel(const string &path, SGPropertyNode *prop_root,
return proxyNode;
}
/*
* Load a set of models at different LOD range_nearest
*
*/
osg::PagedLOD*
SGModelLib::loadPagedModel(const string &path, SGPropertyNode *prop_root,
SGModelData *data)
SGModelLib::loadPagedModel(SGPropertyNode *prop_root, SGModelData *data, SGModelLOD model_lods)
{
osg::PagedLOD *plod = new osg::PagedLOD;
plod->setName("Paged LOD for \"" + path + "\"");
plod->setFileName(0, path);
plod->setRange(0, 0.0, 50.0*SG_NM_TO_METER);
plod->setMinimumExpiryTime( 0, prop_root->getDoubleValue("/sim/rendering/plod-minimum-expiry-time-secs", 180.0 ) );
osg::ref_ptr<SGReaderWriterOptions> opt;
opt = SGReaderWriterOptions::copyOrCreate(osgDB::Registry::instance()->getOptions());
opt->setPropertyNode(prop_root ? prop_root: static_propRoot.get());
opt->setModelData(data);
opt->setLoadPanel(static_panelFunc);
std::string lext = SGPath(path).lower_extension();
if ((lext == "ac") || (lext == "obj")) {
opt->setInstantiateEffects(true);
}
if (!prop_root || prop_root->getBoolValue("/sim/rendering/cache", true))
opt->setObjectCacheHint(osgDB::Options::CACHE_ALL);
else
opt->setObjectCacheHint(osgDB::Options::CACHE_NONE);
for(unsigned int i = 0; i < model_lods.getNumLODs(); i++) {
SGModelLOD::ModelLOD lod = model_lods.getModelLOD(i);
plod->setName("Paged LOD for \"" + lod.path + "\"");
plod->setFileName(i, lod.path);
plod->setRange(i, lod.min_range, lod.max_range);
plod->setMinimumExpiryTime(i, prop_root->getDoubleValue("/sim/rendering/plod-minimum-expiry-time-secs", 180.0 ) );
std::string lext = SGPath(lod.path).lower_extension();
// We can only have one set of ReaderWriterOptions for the PagedLOD, so
// we will just have to assume that if one of the defined models can
// handle instantiated effects, then the other can as well.
if ((lext == "ac") || (lext == "obj")) {
opt->setInstantiateEffects(true);
}
}
plod->setDatabaseOptions(opt.get());
return plod;
}
osg::PagedLOD*
SGModelLib::loadPagedModel(const string &path, SGPropertyNode *prop_root,
SGModelData *data)
{
SGModelLOD model_lods;
model_lods.insert(path, 0.0, 50.0*SG_NM_TO_METER);
return SGModelLib::loadPagedModel(prop_root, data, model_lods);
}
osg::PagedLOD*
SGModelLib::loadPagedModel(std::vector<string> paths, SGPropertyNode *prop_root,
SGModelData *data)
{
SGModelLOD model_lods;
for(unsigned int i = 0; i < paths.size(); i++) {
// We don't have any range data, so simply set them all up to full range.
// Some other code will update the LoD ranges later. (AIBase::updateLOD)
model_lods.insert(paths[i], 0.0, 50.0*SG_NM_TO_METER);
}
return SGModelLib::loadPagedModel(prop_root, data, model_lods);
}
// end of modellib.cxx

View File

@ -39,6 +39,7 @@ namespace osg {
namespace simgear {
class SGModelData; // defined below
class SGModelLOD; // defined below
/**
* Class for loading and managing models with XML wrappers.
@ -51,9 +52,9 @@ public:
static void init(const std::string &root_dir, SGPropertyNode* root);
static void resetPropertyRoot();
static void setPanelFunc(panel_func pf);
// Load a 3D model (any format)
// data->modelLoaded() will be called after the model is loaded
static osg::Node* loadModel(const std::string &path,
@ -72,17 +73,24 @@ public:
// the model file. Once the viewer steps onto that node the
// model will be loaded. When the viewer does no longer reference this
// node for a long time the node is unloaded again.
static osg::PagedLOD* loadPagedModel(SGPropertyNode *prop_root,
SGModelData *data,
SGModelLOD model_lods);
static osg::PagedLOD* loadPagedModel(const std::string &path,
SGPropertyNode *prop_root = NULL,
SGModelData *data=0);
static std::string findDataFile(const std::string& file,
static osg::PagedLOD* loadPagedModel(std::vector<string> paths,
SGPropertyNode *prop_root = NULL,
SGModelData *data=0);
static std::string findDataFile(const std::string& file,
const osgDB::Options* opts = NULL,
SGPath currentDir = SGPath());
SGPath currentDir = SGPath());
protected:
SGModelLib();
~SGModelLib ();
private:
static SGPropertyNode_ptr static_propRoot;
static panel_func static_panelFunc;
@ -102,6 +110,39 @@ public:
virtual SGModelData* clone() const = 0;
};
/*
* Data for a model with multiple LoD versions
*/
class SGModelLOD {
public:
struct ModelLOD {
ModelLOD(const string &p, float minrange, float maxrange) :
path(p), min_range(minrange), max_range(maxrange)
{ }
const string &path;
float min_range;
float max_range;
};
typedef std::vector<ModelLOD> ModelLODList;
void insert(const ModelLOD& model)
{
_models.push_back(model);
}
void insert(const string &p, float minrange, float maxrange)
{
insert(ModelLOD(p, minrange, maxrange));
}
unsigned getNumLODs() const { return _models.size(); }
const ModelLOD& getModelLOD(unsigned i) const { return _models[i]; }
private:
ModelLODList _models;
};
}
#endif // _SG_MODEL_LIB_HXX

View File

@ -25,5 +25,6 @@
#cmakedefine SYSTEM_EXPAT
#cmakedefine ENABLE_SOUND
#cmakedefine USE_AEONWAVE
#cmakedefine ENABLE_SIMD
#cmakedefine ENABLE_GDAL

View File

@ -314,7 +314,7 @@ public:
/**
* Get a list of available playback devices.
*/
std::vector<const char*> get_available_devices();
std::vector<std::string> get_available_devices();
/**
* Get the current OpenAL vendor or rendering backend.

View File

@ -75,10 +75,6 @@ public:
~SoundManagerPrivate()
{
for (auto it = _devices.begin(); it != _devices.end(); ++it) {
free((void*)*it);
}
_devices.clear();
_sample_groups.clear();
}
@ -125,8 +121,6 @@ public:
}
sample_group_map _sample_groups;
std::vector<const char*> _devices;
};
@ -444,7 +438,7 @@ unsigned int SGSoundMgr::request_buffer(SGSoundSample *sample)
std::string sample_name = sample->get_sample_name();
aax::Buffer& buf = d->_aax.buffer(sample_name);
if (!buf) {
if (sample->is_file() && !buf) {
SG_LOG(SG_SOUND, SG_ALERT,
"Unable to create buffer: " << sample_name);
sample->set_buffer( SGSoundMgr::FAILED_BUFFER );
@ -561,6 +555,7 @@ void SGSoundMgr::sample_play( SGSoundSample *sample )
if (bufid == SGSoundMgr::FAILED_BUFFER ||
bufid == SGSoundMgr::NO_BUFFER)
{
printf("A: release: %i, bufid: %i (%i, %i)\n", sample->get_source(), bufid, SGSoundMgr::FAILED_BUFFER, SGSoundMgr::NO_BUFFER);
release_source(sample->get_source());
return;
}
@ -646,39 +641,46 @@ void SGSoundMgr::update_sample_config( SGSoundSample *sample, SGVec3d& position,
aax::Emitter& emitter = d->get_emitter(sample->get_source());
aax::dsp dsp;
aax::Vector64 pos = position.data();
aax::Vector ori = orientation.data();
aax::Vector vel = velocity.data();
if (emitter != d->nullEmitter)
{
aax::Vector64 pos = position.data();
aax::Vector ori = orientation.data();
aax::Vector vel = velocity.data();
aax::Matrix64 mtx(pos, ori);
TRY( emitter.matrix(mtx) );
TRY( emitter.velocity(vel) );
aax::Matrix64 mtx(pos, ori);
TRY( emitter.matrix(mtx) );
TRY( emitter.velocity(vel) );
dsp = emitter.get(AAX_VOLUME_FILTER);
TRY( dsp.set(AAX_GAIN, sample->get_volume()) );
TRY( emitter.set(dsp) );
dsp = emitter.get(AAX_PITCH_EFFECT);
TRY( dsp.set(AAX_PITCH, sample->get_pitch()) );
TRY( emitter.set(dsp) );
if ( sample->has_static_data_changed() ) {
dsp = emitter.get(AAX_ANGULAR_FILTER);
TRY( dsp.set(AAX_INNER_ANGLE, sample->get_innerangle()) );
TRY( dsp.set(AAX_OUTER_ANGLE, sample->get_outerangle()) );
TRY( dsp.set(AAX_OUTER_GAIN, sample->get_outergain()) );
dsp = emitter.get(AAX_VOLUME_FILTER);
TRY( dsp.set(AAX_GAIN, sample->get_volume()) );
TRY( emitter.set(dsp) );
dsp = emitter.get(AAX_DISTANCE_FILTER);
TRY( dsp.set(AAX_REF_DISTANCE, sample->get_reference_dist()) );
TRY( dsp.set(AAX_MAX_DISTANCE, sample->get_max_dist()) );
dsp = emitter.get(AAX_PITCH_EFFECT);
TRY( dsp.set(AAX_PITCH, sample->get_pitch()) );
TRY( emitter.set(dsp) );
if ( sample->has_static_data_changed() ) {
dsp = emitter.get(AAX_ANGULAR_FILTER);
TRY( dsp.set(AAX_INNER_ANGLE, sample->get_innerangle()) );
TRY( dsp.set(AAX_OUTER_ANGLE, sample->get_outerangle()) );
TRY( dsp.set(AAX_OUTER_GAIN, sample->get_outergain()) );
TRY( emitter.set(dsp) );
dsp = emitter.get(AAX_DISTANCE_FILTER);
TRY( dsp.set(AAX_REF_DISTANCE, sample->get_reference_dist()) );
TRY( dsp.set(AAX_MAX_DISTANCE, sample->get_max_dist()) );
TRY( emitter.set(dsp) );
}
}
else {
SG_LOG( SG_SOUND, SG_ALERT, "Error: source: " << sample->get_source() << " not found");
}
}
vector<const char*> SGSoundMgr::get_available_devices()
vector<std::string> SGSoundMgr::get_available_devices()
{
vector<std::string> devices;
#ifdef ENABLE_SOUND
std::string on = " on ";
std::string colon = ": ";
@ -690,12 +692,12 @@ vector<const char*> SGSoundMgr::get_available_devices()
else if (*r) name += on + r;
else if (*i) name += colon + i;
d->_devices.push_back( strdup(name.c_str()) );
devices.push_back( name );
}
}
}
#endif
return d->_devices;
return devices;
}

View File

@ -821,9 +821,9 @@ bool SGSoundMgr::load( const std::string &samplepath,
return true;
}
vector<const char*> SGSoundMgr::get_available_devices()
vector<std::string> SGSoundMgr::get_available_devices()
{
vector<const char*> devices;
vector<std::string> devices;
#ifdef ENABLE_SOUND
const ALCchar *s;