Compare commits

...
Author SHA1 Message Date
Erik Hofman c19cf094a7 Backport the code to be able to display the region name of the file that holds the selected material. 2021-05-27 15:55:02 +02:00
Automatic Release Builder 279179e88d new version: 2020.3.9 2021-05-27 11:09:42 +01:00
James Turner 0ddd3e7f2f Add sg_location to all exceptions, add report flag
Move sg_location member up to the base clase, since it’s potentially
useful in more types.

Allow suppression of the report callback when the exception is thrown
2021-05-05 15:00:23 +01:00
James Turner fe96298be5 Thread-safe error reporting in lowlevel.cxx
Use an exception rather than polling a flag, for checking errors
during BTG reading. This should allow us to give a correctly
identified error, at exactly the point the read fails.
2021-05-05 14:58:37 +01:00
James Turner c6351292dd TerraSync: add a warning file to the root dir.
Try to discourage users from adding custom content underneath the
Terrasync dir, since it can be over-written.
2021-04-23 12:15:34 +01:00
James Turner e2caad3b0b TerraSync: better reporting of permissions failures removing files
Log a ‘failed to remove orphan’ error as an error with the repository,
instead of failing the entire sync
2021-04-23 12:15:34 +01:00
James Turner a095ab684c ASan: fix a leak in GZ extraction 2021-04-23 11:55:02 +01:00
James Turner fe41a03180 Asan: fix leaks in Catalog code 2021-04-23 11:54:56 +01:00
Automatic Release Builder 332d9dfadb new version: 2020.3.8 2021-03-24 11:08:07 +00:00
Automatic Release Builder c05802a498 new version: 2020.3.8 2021-03-24 11:04:18 +00:00
James Turner 0970bb1be2 Fix for local particle update
See issue at:
https://sourceforge.net/p/flightgear/codetickets/2568/
2021-03-24 09:59:13 +00:00
James Turner c9d83fab6c TerraSync: allow an explicit osm2city server
Fixes an error case where a manual TerraSync server is specified; we
would attempt to use an empty string as the OSM2City server, with
hilarious consequences.

Sentry-Id: FLIGHTGEAR-NCZ
2021-03-16 20:14:34 +00:00
James Turner edcce32f24 Remove stray include of std::filesystem 2021-03-07 10:53:36 +00:00
Automatic Release Builder 68d265f0e7 Set correct version files 2021-03-04 22:02:11 +00:00
Automatic Release Builder b985bb5757 new version: 2020.3.7 2021-03-01 12:11:33 +00:00
legoboyvdlp R f030816385 TerraSync: counter to fetch number of bytes which have been extracted from a tarball file 2021-03-01 09:56:48 +00:00
James Turner 6f9f694eff HTTPRepository: improving handling of archives
Avoid hard-coding the archive extension, and ensure the extracted
archive directory is not orphaned on update. Finally, use the
literal filename in the .dirindex when computing the hash, rather
than adding a .tgz extension.
2021-03-01 09:56:48 +00:00
James Turner a0d7f0e172 TerraSync: allow separate OSM2City server
Lookup OSM2City using a separate service profile, and use this server
for requests for OSM2City suffix dirs.
2021-03-01 09:51:24 +00:00
James Turner 9f98e438cb Fix HTTPClient reset() behaviour
Ensure all data members are correctly re-initialzied when doing a reset.

This shoed up as negative ‘bytes downloaded’ counts after a TerraSync
abandon and retry.
2021-02-22 11:29:05 +00:00
James Turner f029ca7b64 Particles: replace use of ParticleSystemUpdater
Extend our own particle manager to replace the OSG particle system
updater. This fixes thread-safety and also timing (better match to
simulation dt values). We also use weak pointers (observer_ptr in
OSG terminology) to ensure particle systems are released once their
frame is gone.
2021-02-22 11:29:05 +00:00
James Turner ca6c6dd6d3 Text-animation: fix missing encoding specification
Ensure we can pass full UTF-8 strings into text animations. Will consider
for back-port after discussion on the devel list.

Ticket-Id: https://sourceforge.net/p/flightgear/codetickets/2512/
2021-02-22 11:10:37 +00:00
Automatic Release Builder 4ba4ea5602 Adding code to see if it improves crash on computeHash.
Also add a test case to verify behaviour on empty files is correct.

Sentry-Id: FLIGHTGEAR-AY2
2021-02-04 10:28:08 +00:00
32 changed files with 1899 additions and 1289 deletions
+1
View File
@@ -286,6 +286,7 @@ else()
find_package(ZLIB 1.2.4 REQUIRED) find_package(ZLIB 1.2.4 REQUIRED)
endif() endif()
find_package(LibLZMA REQUIRED)
find_package(CURL REQUIRED) find_package(CURL REQUIRED)
if (SYSTEM_EXPAT) if (SYSTEM_EXPAT)
+124
View File
@@ -0,0 +1,124 @@
# Distributed under the OSI-approved BSD 3-Clause License. See accompanying
# file Copyright.txt or https://cmake.org/licensing for details.
#[=======================================================================[.rst:
FindLibLZMA
-----------
Find LZMA compression algorithm headers and library.
Imported Targets
^^^^^^^^^^^^^^^^
This module defines :prop_tgt:`IMPORTED` target ``LibLZMA::LibLZMA``, if
liblzma has been found.
Result variables
^^^^^^^^^^^^^^^^
This module will set the following variables in your project:
``LIBLZMA_FOUND``
True if liblzma headers and library were found.
``LIBLZMA_INCLUDE_DIRS``
Directory where liblzma headers are located.
``LIBLZMA_LIBRARIES``
Lzma libraries to link against.
``LIBLZMA_HAS_AUTO_DECODER``
True if lzma_auto_decoder() is found (required).
``LIBLZMA_HAS_EASY_ENCODER``
True if lzma_easy_encoder() is found (required).
``LIBLZMA_HAS_LZMA_PRESET``
True if lzma_lzma_preset() is found (required).
``LIBLZMA_VERSION_MAJOR``
The major version of lzma
``LIBLZMA_VERSION_MINOR``
The minor version of lzma
``LIBLZMA_VERSION_PATCH``
The patch version of lzma
``LIBLZMA_VERSION_STRING``
version number as a string (ex: "5.0.3")
#]=======================================================================]
find_path(LIBLZMA_INCLUDE_DIR lzma.h )
if(NOT LIBLZMA_LIBRARY)
find_library(LIBLZMA_LIBRARY_RELEASE NAMES lzma liblzma NAMES_PER_DIR PATH_SUFFIXES lib)
find_library(LIBLZMA_LIBRARY_DEBUG NAMES lzmad liblzmad NAMES_PER_DIR PATH_SUFFIXES lib)
include(SelectLibraryConfigurations)
select_library_configurations(LIBLZMA)
else()
file(TO_CMAKE_PATH "${LIBLZMA_LIBRARY}" LIBLZMA_LIBRARY)
endif()
if(LIBLZMA_INCLUDE_DIR AND EXISTS "${LIBLZMA_INCLUDE_DIR}/lzma/version.h")
file(STRINGS "${LIBLZMA_INCLUDE_DIR}/lzma/version.h" LIBLZMA_HEADER_CONTENTS REGEX "#define LZMA_VERSION_[A-Z]+ [0-9]+")
string(REGEX REPLACE ".*#define LZMA_VERSION_MAJOR ([0-9]+).*" "\\1" LIBLZMA_VERSION_MAJOR "${LIBLZMA_HEADER_CONTENTS}")
string(REGEX REPLACE ".*#define LZMA_VERSION_MINOR ([0-9]+).*" "\\1" LIBLZMA_VERSION_MINOR "${LIBLZMA_HEADER_CONTENTS}")
string(REGEX REPLACE ".*#define LZMA_VERSION_PATCH ([0-9]+).*" "\\1" LIBLZMA_VERSION_PATCH "${LIBLZMA_HEADER_CONTENTS}")
set(LIBLZMA_VERSION_STRING "${LIBLZMA_VERSION_MAJOR}.${LIBLZMA_VERSION_MINOR}.${LIBLZMA_VERSION_PATCH}")
unset(LIBLZMA_HEADER_CONTENTS)
endif()
# We're using new code known now as XZ, even library still been called LZMA
# it can be found in http://tukaani.org/xz/
# Avoid using old codebase
if (LIBLZMA_LIBRARY)
include(CheckLibraryExists)
set(CMAKE_REQUIRED_QUIET_SAVE ${CMAKE_REQUIRED_QUIET})
set(CMAKE_REQUIRED_QUIET ${LibLZMA_FIND_QUIETLY})
if(NOT LIBLZMA_LIBRARY_RELEASE AND NOT LIBLZMA_LIBRARY_DEBUG)
set(LIBLZMA_LIBRARY_check ${LIBLZMA_LIBRARY})
elseif(LIBLZMA_LIBRARY_RELEASE)
set(LIBLZMA_LIBRARY_check ${LIBLZMA_LIBRARY_RELEASE})
elseif(LIBLZMA_LIBRARY_DEBUG)
set(LIBLZMA_LIBRARY_check ${LIBLZMA_LIBRARY_DEBUG})
endif()
CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY_check} lzma_auto_decoder "" LIBLZMA_HAS_AUTO_DECODER)
CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY_check} lzma_easy_encoder "" LIBLZMA_HAS_EASY_ENCODER)
CHECK_LIBRARY_EXISTS(${LIBLZMA_LIBRARY_check} lzma_lzma_preset "" LIBLZMA_HAS_LZMA_PRESET)
unset(LIBLZMA_LIBRARY_check)
set(CMAKE_REQUIRED_QUIET ${CMAKE_REQUIRED_QUIET_SAVE})
endif ()
include(FindPackageHandleStandardArgs)
find_package_handle_standard_args(LibLZMA REQUIRED_VARS LIBLZMA_LIBRARY
LIBLZMA_INCLUDE_DIR
LIBLZMA_HAS_AUTO_DECODER
LIBLZMA_HAS_EASY_ENCODER
LIBLZMA_HAS_LZMA_PRESET
VERSION_VAR LIBLZMA_VERSION_STRING
)
mark_as_advanced( LIBLZMA_INCLUDE_DIR LIBLZMA_LIBRARY )
if (LIBLZMA_FOUND)
set(LIBLZMA_LIBRARIES ${LIBLZMA_LIBRARY})
set(LIBLZMA_INCLUDE_DIRS ${LIBLZMA_INCLUDE_DIR})
if(NOT TARGET LibLZMA::LibLZMA)
add_library(LibLZMA::LibLZMA UNKNOWN IMPORTED)
set_target_properties(LibLZMA::LibLZMA PROPERTIES
INTERFACE_INCLUDE_DIRECTORIES ${LIBLZMA_INCLUDE_DIR}
IMPORTED_LINK_INTERFACE_LANGUAGES C)
if(LIBLZMA_LIBRARY_RELEASE)
set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY
IMPORTED_CONFIGURATIONS RELEASE)
set_target_properties(LibLZMA::LibLZMA PROPERTIES
IMPORTED_LOCATION_RELEASE "${LIBLZMA_LIBRARY_RELEASE}")
endif()
if(LIBLZMA_LIBRARY_DEBUG)
set_property(TARGET LibLZMA::LibLZMA APPEND PROPERTY
IMPORTED_CONFIGURATIONS DEBUG)
set_target_properties(LibLZMA::LibLZMA PROPERTIES
IMPORTED_LOCATION_DEBUG "${LIBLZMA_LIBRARY_DEBUG}")
endif()
if(NOT LIBLZMA_LIBRARY_RELEASE AND NOT LIBLZMA_LIBRARY_DEBUG)
set_target_properties(LibLZMA::LibLZMA PROPERTIES
IMPORTED_LOCATION "${LIBLZMA_LIBRARY}")
endif()
endif()
endif ()
+1
View File
@@ -1,6 +1,7 @@
include(CMakeFindDependencyMacro) include(CMakeFindDependencyMacro)
find_dependency(ZLIB) find_dependency(ZLIB)
find_dependency(LibLZMA)
find_dependency(Threads) find_dependency(Threads)
# OSG # OSG
+1 -1
View File
@@ -1 +1 @@
2020.3.6 2020.3.9
+3 -1
View File
@@ -168,7 +168,9 @@ target_link_libraries(SimGearCore PRIVATE
${COCOA_LIBRARY} ${COCOA_LIBRARY}
${CURL_LIBRARIES} ${CURL_LIBRARIES}
${WINSOCK_LIBRARY} ${WINSOCK_LIBRARY}
${SHLWAPI_LIBRARY}) ${SHLWAPI_LIBRARY}
LibLZMA::LibLZMA
)
if(SYSTEM_EXPAT) if(SYSTEM_EXPAT)
target_link_libraries(SimGearCore PRIVATE ${EXPAT_LIBRARIES}) target_link_libraries(SimGearCore PRIVATE ${EXPAT_LIBRARIES})
+90
View File
@@ -0,0 +1,90 @@
// Copyright (C) 2021 James Turner - <james@flightgear.org>
//
// 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 General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
//
#pragma once
#include "untar.hxx"
namespace simgear {
class ArchiveExtractorPrivate
{
public:
ArchiveExtractorPrivate(ArchiveExtractor* o) : outer(o)
{
assert(outer);
}
virtual ~ArchiveExtractorPrivate() = default;
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;
}
};
} // namespace simgear
+16 -16
View File
@@ -81,21 +81,8 @@ void Client::ClientPrivate::createCurlMulti() {
#endif #endif
} }
Client::Client() : Client::Client()
d(new ClientPrivate)
{ {
d->proxyPort = 0;
d->maxConnections = 4;
d->maxHostConnections = 4;
d->bytesTransferred = 0;
d->lastTransferRate = 0;
d->timeTransferSample.stamp();
d->totalBytesDownloaded = 0;
d->maxPipelineDepth = 5;
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
static bool didInitCurlGlobal = false; static bool didInitCurlGlobal = false;
static std::mutex initMutex; static std::mutex initMutex;
@@ -105,7 +92,7 @@ Client::Client() :
didInitCurlGlobal = true; didInitCurlGlobal = true;
} }
d->createCurlMulti(); reset();
} }
Client::~Client() Client::~Client()
@@ -139,8 +126,21 @@ void Client::setMaxPipelineDepth(unsigned int depth)
void Client::reset() void Client::reset()
{ {
curl_multi_cleanup(d->curlMulti); if (d.get()) {
curl_multi_cleanup(d->curlMulti);
}
d.reset(new ClientPrivate); d.reset(new ClientPrivate);
d->proxyPort = 0;
d->maxConnections = 4;
d->maxHostConnections = 4;
d->bytesTransferred = 0;
d->lastTransferRate = 0;
d->timeTransferSample.stamp();
d->totalBytesDownloaded = 0;
d->maxPipelineDepth = 5;
setUserAgent("SimGear-" SG_STRINGIZE(SIMGEAR_VERSION));
d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH"); d->tlsCertificatePath = SGPath::fromEnv("SIMGEAR_TLS_CERT_PATH");
d->createCurlMulti(); d->createCurlMulti();
} }
+124 -91
View File
@@ -20,15 +20,15 @@
#include "HTTPRepository.hxx" #include "HTTPRepository.hxx"
#include <iostream>
#include <cassert>
#include <algorithm> #include <algorithm>
#include <sstream> #include <cassert>
#include <cstdlib>
#include <fstream>
#include <iostream>
#include <limits>
#include <map> #include <map>
#include <set> #include <set>
#include <fstream> #include <sstream>
#include <limits>
#include <cstdlib>
#include <fcntl.h> #include <fcntl.h>
@@ -143,8 +143,8 @@ class HTTPDirectory
HTTPRepository::EntryType type; HTTPRepository::EntryType type;
std::string name, hash; std::string name, hash;
size_t sizeInBytes = 0; size_t sizeInBytes = 0;
SGPath path; // absolute path on disk SGPath path; // absolute path on disk
}; };
typedef std::vector<ChildInfo> ChildInfoList; typedef std::vector<ChildInfo> ChildInfoList;
@@ -218,53 +218,66 @@ public:
return; return;
} }
char* buf = nullptr; char* buf = nullptr;
size_t bufSize = 0; size_t bufSize = 0;
for (auto &child : children) { for (auto& child : children) {
if (child.type != HTTPRepository::FileType) if (child.type != HTTPRepository::FileType)
continue;
if (child.path.exists())
continue;
SGPath cp = _repository->installedCopyPath;
cp.append(relativePath());
cp.append(child.name);
if (!cp.exists()) {
continue;
}
SGBinaryFile src(cp);
SGBinaryFile dst(child.path);
src.open(SG_IO_IN);
dst.open(SG_IO_OUT);
if (bufSize < cp.sizeInBytes()) {
bufSize = cp.sizeInBytes();
free(buf);
buf = (char*)malloc(bufSize);
if (!buf) {
continue; continue;
}
}
if (child.path.exists()) src.read(buf, cp.sizeInBytes());
continue; dst.write(buf, cp.sizeInBytes());
src.close();
dst.close();
SGPath cp = _repository->installedCopyPath; // reset caching
cp.append(relativePath()); child.path.set_cached(false);
cp.append(child.name); child.path.set_cached(true);
if (!cp.exists()) {
continue;
}
SGBinaryFile src(cp); std::string hash = computeHashForPath(child.path);
SGBinaryFile dst(child.path); updatedFileContents(child.path, hash);
src.open(SG_IO_IN);
dst.open(SG_IO_OUT);
if (bufSize < cp.sizeInBytes()) {
bufSize = cp.sizeInBytes();
free(buf);
buf = (char *)malloc(bufSize);
if (!buf) {
continue;
}
}
src.read(buf, cp.sizeInBytes());
dst.write(buf, cp.sizeInBytes());
src.close();
dst.close();
// reset caching
child.path.set_cached(false);
child.path.set_cached(true);
std::string hash = computeHashForPath(child.path);
updatedFileContents(child.path, hash);
} }
free(buf); free(buf);
} }
/// helper to check and erase 'fooBar' from paths, if passed fooBar.zip, fooBar.tgz, etc.
void removeExtractedDirectoryFromList(PathList& paths, const std::string& tarballName)
{
const auto directoryName = SGPath::fromUtf8(tarballName).file_base();
auto it = std::find_if(paths.begin(), paths.end(), [directoryName](const SGPath& p) {
return p.isDir() && (p.file() == directoryName);
});
if (it != paths.end()) {
paths.erase(it);
}
}
void updateChildrenBasedOnHash() void updateChildrenBasedOnHash()
{ {
using SAct = HTTPRepository::SyncAction; using SAct = HTTPRepository::SyncAction;
@@ -298,6 +311,11 @@ public:
orphans.end()); orphans.end());
} }
// ensure the extracted directory corresponding to a tarball, is *not* considered an orphan
if (c.type == HTTPRepository::TarballType) {
removeExtractedDirectoryFromList(orphans, c.name);
}
if (_repository->syncPredicate) { if (_repository->syncPredicate) {
const auto pathOnDisk = isNew ? absolutePath() / c.name : *p; const auto pathOnDisk = isNew ? absolutePath() / c.name : *p;
// never handle deletes here, do them at the end // never handle deletes here, do them at the end
@@ -320,7 +338,6 @@ public:
toBeUpdated.push_back(c); toBeUpdated.push_back(c);
} else { } else {
// File/Directory exists and hash is valid. // File/Directory exists and hash is valid.
if (c.type == HTTPRepository::DirectoryType) { if (c.type == HTTPRepository::DirectoryType) {
// If it's a directory,perform a recursive check. // If it's a directory,perform a recursive check.
HTTPDirectory *childDir = childDirectory(c.name); HTTPDirectory *childDir = childDirectory(c.name);
@@ -360,7 +377,12 @@ public:
// We now have a list of entries that need to be updated, and a list // We now have a list of entries that need to be updated, and a list
// of orphan files that should be removed. // of orphan files that should be removed.
removeOrphans(orphans); try {
removeOrphans(orphans);
} catch (sg_exception& e) {
_repository->failedToUpdateChild(_relativePath, HTTPRepository::ResultCode::REPO_ERROR_IO);
}
scheduleUpdates(toBeUpdated); scheduleUpdates(toBeUpdated);
} }
@@ -372,11 +394,10 @@ public:
void removeOrphans(const PathList orphans) void removeOrphans(const PathList orphans)
{ {
PathList::const_iterator it; for (const auto& o : orphans) {
for (it = orphans.begin(); it != orphans.end(); ++it) { if (o.file() == ".dirindex") continue;
if (it->file() == ".dirindex") continue; if (o.file() == ".hash") continue;
if (it->file() == ".hash") continue; removeChild(o);
removeChild(*it);
} }
} }
@@ -432,6 +453,7 @@ public:
return; return;
} }
compressedBytes = p.sizeInBytes();
buffer = (uint8_t *)malloc(bufferSize); buffer = (uint8_t *)malloc(bufferSize);
} }
@@ -444,6 +466,7 @@ public:
} }
size_t rd = file.read((char*)buffer, bufferSize); size_t rd = file.read((char*)buffer, bufferSize);
repo->bytesExtracted += rd;
extractor.extractBytes(buffer, rd); extractor.extractBytes(buffer, rd);
if (file.eof()) { if (file.eof()) {
@@ -470,8 +493,14 @@ public:
return HTTPRepoPrivate::ProcessContinue; return HTTPRepoPrivate::ProcessContinue;
} }
size_t archiveSizeBytes() const
{
return compressedBytes;
}
~ArchiveExtractTask() { free(buffer); } ~ArchiveExtractTask() { free(buffer); }
private: private:
// intentionally small so we extract incrementally on Windows // intentionally small so we extract incrementally on Windows
// where Defender throttles many small files, sorry // where Defender throttles many small files, sorry
@@ -483,6 +512,7 @@ public:
uint8_t *buffer = nullptr; uint8_t *buffer = nullptr;
SGBinaryFile file; SGBinaryFile file;
ArchiveExtractor extractor; ArchiveExtractor extractor;
std::size_t compressedBytes;
}; };
using ArchiveExtractTaskPtr = std::shared_ptr<ArchiveExtractTask>; using ArchiveExtractTaskPtr = std::shared_ptr<ArchiveExtractTask>;
@@ -509,42 +539,42 @@ public:
_repository->totalDownloaded += sz; _repository->totalDownloaded += sz;
SGPath p = SGPath(absolutePath(), file); SGPath p = SGPath(absolutePath(), file);
if ((p.extension() == "tgz") || (p.extension() == "zip")) { if (it->type == HTTPRepository::TarballType) {
// We require that any compressed files have the same filename as the file or directory // We require that any compressed files have the same filename as the file or directory
// they expand to, so we can remove the old file/directory before extracting the new // they expand to, so we can remove the old file/directory before extracting the new
// data. // data.
SGPath removePath = SGPath(p.base()); SGPath removePath = SGPath(p.base());
bool pathAvailable = true; bool pathAvailable = true;
if (removePath.exists()) { if (removePath.exists()) {
if (removePath.isDir()) { if (removePath.isDir()) {
simgear::Dir pd(removePath); simgear::Dir pd(removePath);
pathAvailable = pd.removeChildren(); pathAvailable = pd.removeChildren();
} else { } else {
pathAvailable = removePath.remove(); pathAvailable = removePath.remove();
}
} }
}
if (pathAvailable) { if (pathAvailable) {
// we use a Task helper to extract tarballs incrementally. // we use a Task helper to extract tarballs incrementally.
// without this, archive extraction blocks here, which // without this, archive extraction blocks here, which
// prevents other repositories downloading / updating. // prevents other repositories downloading / updating.
// Unfortunately due Windows AV (Defender, etc) we cna block // Unfortunately due Windows AV (Defender, etc) we cna block
// here for many minutes. // here for many minutes.
// use a lambda to own this shared_ptr; this means when the // use a lambda to own this shared_ptr; this means when the
// lambda is destroyed, the ArchiveExtraTask will get // lambda is destroyed, the ArchiveExtraTask will get
// cleaned up. // cleaned up.
ArchiveExtractTaskPtr t = ArchiveExtractTaskPtr t =
std::make_shared<ArchiveExtractTask>(p, _relativePath); std::make_shared<ArchiveExtractTask>(p, _relativePath);
auto cb = [t](HTTPRepoPrivate *repo) { auto cb = [t](HTTPRepoPrivate* repo) {
return t->run(repo); return t->run(repo);
}; };
_repository->bytesToExtract += t->archiveSizeBytes();
_repository->addTask(cb); _repository->addTask(cb);
} else { } else {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Unable to remove old file/directory " << removePath); SG_LOG(SG_TERRASYNC, SG_ALERT, "Unable to remove old file/directory " << removePath);
} // of pathAvailable } // of pathAvailable
} // of handling tgz files } // of handling archive files
} // of hash matches } // of hash matches
} // of found in child list } // of found in child list
} }
@@ -736,11 +766,9 @@ private:
std::string hashForChild(const ChildInfo& child) const std::string hashForChild(const ChildInfo& child) const
{ {
SGPath p(child.path); SGPath p(child.path);
if (child.type == HTTPRepository::DirectoryType) if (child.type == HTTPRepository::DirectoryType) {
p.append(".dirindex"); p.append(".dirindex");
if (child.type == HTTPRepository::TarballType) }
p.concat(
".tgz"); // For tarballs the hash is against the tarball file itself
return hashForPath(p); return hashForPath(p);
} }
@@ -937,6 +965,11 @@ size_t HTTPRepository::bytesDownloaded() const
return result; return result;
} }
size_t HTTPRepository::bytesToExtract() const
{
return _d->bytesToExtract - _d->bytesExtracted;
}
void HTTPRepository::setInstalledCopyPath(const SGPath& copyPath) void HTTPRepository::setInstalledCopyPath(const SGPath& copyPath)
{ {
_d->installedCopyPath = copyPath; _d->installedCopyPath = copyPath;
@@ -1293,7 +1326,7 @@ HTTPRepository::failure() const
} }
Dir dir(absPath); Dir dir(absPath);
bool result = dir.remove(true); bool result = dir.remove(true);
return result; return result;
} }
+2
View File
@@ -73,6 +73,8 @@ public:
virtual size_t bytesDownloaded() const; virtual size_t bytesDownloaded() const;
virtual size_t bytesToExtract() const;
/** /**
* optionally provide the location of an installer copy of this * optionally provide the location of an installer copy of this
* repository. When a file is missing it will be copied from this tree. * repository. When a file is missing it will be copied from this tree.
+9 -8
View File
@@ -60,22 +60,23 @@ public:
HTTPRepository::FailureVec failures; HTTPRepository::FailureVec failures;
int maxPermittedFailures = 16; int maxPermittedFailures = 16;
HTTPRepoPrivate(HTTPRepository *parent) HTTPRepoPrivate(HTTPRepository* parent)
: p(parent), isUpdating(false), status(HTTPRepository::REPO_NO_ERROR), : p(parent)
totalDownloaded(0) { {
;
} }
~HTTPRepoPrivate(); ~HTTPRepoPrivate();
HTTPRepository *p; // link back to outer HTTPRepository *p; // link back to outer
HTTP::Client *http; HTTP::Client* http = nullptr;
std::string baseUrl; std::string baseUrl;
SGPath basePath; SGPath basePath;
bool isUpdating; bool isUpdating = false;
HTTPRepository::ResultCode status; HTTPRepository::ResultCode status = HTTPRepository::REPO_NO_ERROR;
HTTPDirectory_ptr rootDir; HTTPDirectory_ptr rootDir;
size_t totalDownloaded; size_t totalDownloaded = 0;
size_t bytesToExtract = 0;
size_t bytesExtracted = 0;
HTTPRepository::SyncPredicate syncPredicate; HTTPRepository::SyncPredicate syncPredicate;
HTTP::Request_ptr updateFile(HTTPDirectory *dir, const std::string &name, HTTP::Request_ptr updateFile(HTTPDirectory *dir, const std::string &name,
+69 -41
View File
@@ -23,28 +23,41 @@
// $Id$ // $Id$
#ifdef HAVE_CONFIG_H #include <simgear_config.h>
# include <simgear_config.h>
#endif
#include <string.h> // for memcpy() #include <string.h> // for memcpy()
#include <errno.h>
#include <simgear/structure/exception.hxx>
#include <simgear/misc/strutils.hxx>
#include <simgear/misc/sg_path.hxx>
#include "lowlevel.hxx" #include "lowlevel.hxx"
thread_local SGPath thread_gzPath;
static int read_error = false ; void setThreadLocalSimgearReadPath(const SGPath& path)
static int write_error = false ; {
thread_gzPath = path;
}
void sgClearReadError() { read_error = false; } static std::string gzErrorMessage(gzFile fd)
void sgClearWriteError() { write_error = false; } {
int sgReadError() { return read_error ; } int errNum = 0;
int sgWriteError() { return write_error ; } const char *gzMsg = gzerror(fd, &errNum);
if (errNum == Z_ERRNO) {
return simgear::strutils::error_string(errno);
} else {
return {gzMsg};
}
}
void sgReadChar ( gzFile fd, char *var ) void sgReadChar ( gzFile fd, char *var )
{ {
if ( gzread ( fd, var, sizeof(char) ) != sizeof(char) ) { if ( gzread ( fd, var, sizeof(char) ) != sizeof(char) ) {
read_error = true ; throw sg_io_exception("sgReadChar: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
} }
@@ -52,7 +65,7 @@ void sgReadChar ( gzFile fd, char *var )
void sgWriteChar ( gzFile fd, const char var ) void sgWriteChar ( gzFile fd, const char var )
{ {
if ( gzwrite ( fd, (void *)(&var), sizeof(char) ) != sizeof(char) ) { if ( gzwrite ( fd, (void *)(&var), sizeof(char) ) != sizeof(char) ) {
write_error = true ; throw sg_io_exception("sgWriteChar: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -61,7 +74,8 @@ void sgReadFloat ( gzFile fd, float *var )
{ {
union { float v; uint32_t u; } buf; union { float v; uint32_t u; } buf;
if ( gzread ( fd, &buf.u, sizeof(float) ) != sizeof(float) ) { if ( gzread ( fd, &buf.u, sizeof(float) ) != sizeof(float) ) {
read_error = true ; throw sg_io_exception("sgReadFloat: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
sgEndianSwap( &buf.u ); sgEndianSwap( &buf.u );
@@ -78,7 +92,7 @@ void sgWriteFloat ( gzFile fd, const float var )
sgEndianSwap( &buf.u ); sgEndianSwap( &buf.u );
} }
if ( gzwrite ( fd, (void *)(&buf.u), sizeof(float) ) != sizeof(float) ) { if ( gzwrite ( fd, (void *)(&buf.u), sizeof(float) ) != sizeof(float) ) {
write_error = true ; throw sg_io_exception("sgWriteFloat: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -87,7 +101,8 @@ void sgReadDouble ( gzFile fd, double *var )
{ {
union { double v; uint64_t u; } buf; union { double v; uint64_t u; } buf;
if ( gzread ( fd, &buf.u, sizeof(double) ) != sizeof(double) ) { if ( gzread ( fd, &buf.u, sizeof(double) ) != sizeof(double) ) {
read_error = true ; throw sg_io_exception("sgReadDouble: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
sgEndianSwap( &buf.u ); sgEndianSwap( &buf.u );
@@ -104,7 +119,7 @@ void sgWriteDouble ( gzFile fd, const double var )
sgEndianSwap( &buf.u ); sgEndianSwap( &buf.u );
} }
if ( gzwrite ( fd, (void *)(&buf.u), sizeof(double) ) != sizeof(double) ) { if ( gzwrite ( fd, (void *)(&buf.u), sizeof(double) ) != sizeof(double) ) {
write_error = true ; throw sg_io_exception("sgWriteDouble: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -112,7 +127,8 @@ void sgWriteDouble ( gzFile fd, const double var )
void sgReadUInt ( gzFile fd, unsigned int *var ) void sgReadUInt ( gzFile fd, unsigned int *var )
{ {
if ( gzread ( fd, var, sizeof(unsigned int) ) != sizeof(unsigned int) ) { if ( gzread ( fd, var, sizeof(unsigned int) ) != sizeof(unsigned int) ) {
read_error = true ; throw sg_io_exception("sgReadUInt: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
sgEndianSwap( (uint32_t *)var); sgEndianSwap( (uint32_t *)var);
@@ -128,7 +144,7 @@ void sgWriteUInt ( gzFile fd, const unsigned int var )
if ( gzwrite ( fd, (void *)(&var), sizeof(unsigned int) ) if ( gzwrite ( fd, (void *)(&var), sizeof(unsigned int) )
!= sizeof(unsigned int) ) != sizeof(unsigned int) )
{ {
write_error = true ; throw sg_io_exception("sgWriteUInt: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -136,7 +152,8 @@ void sgWriteUInt ( gzFile fd, const unsigned int var )
void sgReadInt ( gzFile fd, int *var ) void sgReadInt ( gzFile fd, int *var )
{ {
if ( gzread ( fd, var, sizeof(int) ) != sizeof(int) ) { if ( gzread ( fd, var, sizeof(int) ) != sizeof(int) ) {
read_error = true ; throw sg_io_exception("sgReadInt: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
sgEndianSwap( (uint32_t *)var); sgEndianSwap( (uint32_t *)var);
@@ -150,7 +167,7 @@ void sgWriteInt ( gzFile fd, const int var )
sgEndianSwap( (uint32_t *)&var); sgEndianSwap( (uint32_t *)&var);
} }
if ( gzwrite ( fd, (void *)(&var), sizeof(int) ) != sizeof(int) ) { if ( gzwrite ( fd, (void *)(&var), sizeof(int) ) != sizeof(int) ) {
write_error = true ; throw sg_io_exception("sgWriteInt: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -158,7 +175,8 @@ void sgWriteInt ( gzFile fd, const int var )
void sgReadLong ( gzFile fd, int32_t *var ) void sgReadLong ( gzFile fd, int32_t *var )
{ {
if ( gzread ( fd, var, sizeof(int32_t) ) != sizeof(int32_t) ) { if ( gzread ( fd, var, sizeof(int32_t) ) != sizeof(int32_t) ) {
read_error = true ; throw sg_io_exception("sgReadLong: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
sgEndianSwap( (uint32_t *)var); sgEndianSwap( (uint32_t *)var);
@@ -174,7 +192,7 @@ void sgWriteLong ( gzFile fd, const int32_t var )
if ( gzwrite ( fd, (void *)(&var), sizeof(int32_t) ) if ( gzwrite ( fd, (void *)(&var), sizeof(int32_t) )
!= sizeof(int32_t) ) != sizeof(int32_t) )
{ {
write_error = true ; throw sg_io_exception("sgWriteLong: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -182,7 +200,8 @@ void sgWriteLong ( gzFile fd, const int32_t var )
void sgReadLongLong ( gzFile fd, int64_t *var ) void sgReadLongLong ( gzFile fd, int64_t *var )
{ {
if ( gzread ( fd, var, sizeof(int64_t) ) != sizeof(int64_t) ) { if ( gzread ( fd, var, sizeof(int64_t) ) != sizeof(int64_t) ) {
read_error = true ; throw sg_io_exception("sgReadLongLong: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
sgEndianSwap( (uint64_t *)var); sgEndianSwap( (uint64_t *)var);
@@ -198,7 +217,7 @@ void sgWriteLongLong ( gzFile fd, const int64_t var )
if ( gzwrite ( fd, (void *)(&var), sizeof(int64_t) ) if ( gzwrite ( fd, (void *)(&var), sizeof(int64_t) )
!= sizeof(int64_t) ) != sizeof(int64_t) )
{ {
write_error = true ; throw sg_io_exception("sgWriteLongLong: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -206,7 +225,8 @@ void sgWriteLongLong ( gzFile fd, const int64_t var )
void sgReadUShort ( gzFile fd, unsigned short *var ) void sgReadUShort ( gzFile fd, unsigned short *var )
{ {
if ( gzread ( fd, var, sizeof(unsigned short) ) != sizeof(unsigned short) ){ if ( gzread ( fd, var, sizeof(unsigned short) ) != sizeof(unsigned short) ){
read_error = true ; throw sg_io_exception("sgReadUShort: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
sgEndianSwap( (uint16_t *)var); sgEndianSwap( (uint16_t *)var);
@@ -222,7 +242,7 @@ void sgWriteUShort ( gzFile fd, const unsigned short var )
if ( gzwrite ( fd, (void *)(&var), sizeof(unsigned short) ) if ( gzwrite ( fd, (void *)(&var), sizeof(unsigned short) )
!= sizeof(unsigned short) ) != sizeof(unsigned short) )
{ {
write_error = true ; throw sg_io_exception("sgWriteUShort: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -230,7 +250,8 @@ void sgWriteUShort ( gzFile fd, const unsigned short var )
void sgReadShort ( gzFile fd, short *var ) void sgReadShort ( gzFile fd, short *var )
{ {
if ( gzread ( fd, var, sizeof(short) ) != sizeof(short) ) { if ( gzread ( fd, var, sizeof(short) ) != sizeof(short) ) {
read_error = true ; throw sg_io_exception("sgReadShort: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
sgEndianSwap( (uint16_t *)var); sgEndianSwap( (uint16_t *)var);
@@ -244,7 +265,7 @@ void sgWriteShort ( gzFile fd, const short var )
sgEndianSwap( (uint16_t *)&var); sgEndianSwap( (uint16_t *)&var);
} }
if ( gzwrite ( fd, (void *)(&var), sizeof(short) ) != sizeof(short) ) { if ( gzwrite ( fd, (void *)(&var), sizeof(short) ) != sizeof(short) ) {
write_error = true ; throw sg_io_exception("sgWriteShort: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -252,7 +273,8 @@ void sgWriteShort ( gzFile fd, const short var )
void sgReadFloat ( gzFile fd, const unsigned int n, float *var ) void sgReadFloat ( gzFile fd, const unsigned int n, float *var )
{ {
if ( gzread ( fd, var, sizeof(float) * n ) != (int)(sizeof(float) * n) ) { if ( gzread ( fd, var, sizeof(float) * n ) != (int)(sizeof(float) * n) ) {
read_error = true ; throw sg_io_exception("sgReadFloat array: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
for ( unsigned int i = 0; i < n; ++i ) { for ( unsigned int i = 0; i < n; ++i ) {
@@ -276,14 +298,15 @@ void sgWriteFloat ( gzFile fd, const unsigned int n, const float *var )
if ( gzwrite ( fd, (void *)var, sizeof(float) * n ) if ( gzwrite ( fd, (void *)var, sizeof(float) * n )
!= (int)(sizeof(float) * n) ) != (int)(sizeof(float) * n) )
{ {
write_error = true ; throw sg_io_exception("sgWriteFloat array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
void sgReadDouble ( gzFile fd, const unsigned int n, double *var ) void sgReadDouble ( gzFile fd, const unsigned int n, double *var )
{ {
if ( gzread ( fd, var, sizeof(double) * n ) != (int)(sizeof(double) * n) ) { if ( gzread ( fd, var, sizeof(double) * n ) != (int)(sizeof(double) * n) ) {
read_error = true ; throw sg_io_exception("sgReadDouble array: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
for ( unsigned int i = 0; i < n; ++i ) { for ( unsigned int i = 0; i < n; ++i ) {
@@ -307,7 +330,7 @@ void sgWriteDouble ( gzFile fd, const unsigned int n, const double *var )
if ( gzwrite ( fd, (void *)var, sizeof(double) * n ) if ( gzwrite ( fd, (void *)var, sizeof(double) * n )
!= (int)(sizeof(double) * n) ) != (int)(sizeof(double) * n) )
{ {
write_error = true ; throw sg_io_exception("sgWriteDouble array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -315,7 +338,8 @@ void sgReadBytes ( gzFile fd, const unsigned int n, void *var )
{ {
if ( n == 0) return; if ( n == 0) return;
if ( gzread ( fd, var, n ) != (int)n ) { if ( gzread ( fd, var, n ) != (int)n ) {
read_error = true ; throw sg_io_exception("sgReadBytes: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
} }
@@ -323,7 +347,7 @@ void sgWriteBytes ( gzFile fd, const unsigned int n, const void *var )
{ {
if ( n == 0) return; if ( n == 0) return;
if ( gzwrite ( fd, (void *)var, n ) != (int)n ) { if ( gzwrite ( fd, (void *)var, n ) != (int)n ) {
write_error = true ; throw sg_io_exception("sgWriteBytes: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -333,7 +357,8 @@ void sgReadUShort ( gzFile fd, const unsigned int n, unsigned short *var )
if ( gzread ( fd, var, sizeof(unsigned short) * n ) if ( gzread ( fd, var, sizeof(unsigned short) * n )
!= (int)(sizeof(unsigned short) * n) ) != (int)(sizeof(unsigned short) * n) )
{ {
read_error = true ; throw sg_io_exception("sgReadUShort array: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
for ( unsigned int i = 0; i < n; ++i ) { for ( unsigned int i = 0; i < n; ++i ) {
@@ -357,7 +382,7 @@ void sgWriteUShort ( gzFile fd, const unsigned int n, const unsigned short *var
if ( gzwrite ( fd, (void *)var, sizeof(unsigned short) * n ) if ( gzwrite ( fd, (void *)var, sizeof(unsigned short) * n )
!= (int)(sizeof(unsigned short) * n) ) != (int)(sizeof(unsigned short) * n) )
{ {
write_error = true ; throw sg_io_exception("sgWriteUShort array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -368,7 +393,8 @@ void sgReadShort ( gzFile fd, const unsigned int n, short *var )
if ( gzread ( fd, var, sizeof(short) * n ) if ( gzread ( fd, var, sizeof(short) * n )
!= (int)(sizeof(short) * n) ) != (int)(sizeof(short) * n) )
{ {
read_error = true ; throw sg_io_exception("sgReadShort array: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
for ( unsigned int i = 0; i < n; ++i ) { for ( unsigned int i = 0; i < n; ++i ) {
@@ -392,7 +418,7 @@ void sgWriteShort ( gzFile fd, const unsigned int n, const short *var )
if ( gzwrite ( fd, (void *)var, sizeof(short) * n ) if ( gzwrite ( fd, (void *)var, sizeof(short) * n )
!= (int)(sizeof(short) * n) ) != (int)(sizeof(short) * n) )
{ {
write_error = true ; throw sg_io_exception("sgWriteShort array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -402,7 +428,8 @@ void sgReadUInt ( gzFile fd, const unsigned int n, unsigned int *var )
if ( gzread ( fd, var, sizeof(unsigned int) * n ) if ( gzread ( fd, var, sizeof(unsigned int) * n )
!= (int)(sizeof(unsigned int) * n) ) != (int)(sizeof(unsigned int) * n) )
{ {
read_error = true ; throw sg_io_exception("sgReadUInt array: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
for ( unsigned int i = 0; i < n; ++i ) { for ( unsigned int i = 0; i < n; ++i ) {
@@ -426,7 +453,7 @@ void sgWriteUInt ( gzFile fd, const unsigned int n, const unsigned int *var )
if ( gzwrite ( fd, (void *)var, sizeof(unsigned int) * n ) if ( gzwrite ( fd, (void *)var, sizeof(unsigned int) * n )
!= (int)(sizeof(unsigned int) * n) ) != (int)(sizeof(unsigned int) * n) )
{ {
write_error = true ; throw sg_io_exception("sgWriteUInt array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
@@ -437,7 +464,8 @@ void sgReadInt ( gzFile fd, const unsigned int n, int *var )
if ( gzread ( fd, var, sizeof(int) * n ) if ( gzread ( fd, var, sizeof(int) * n )
!= (int)(sizeof(int) * n) ) != (int)(sizeof(int) * n) )
{ {
read_error = true ; throw sg_io_exception("sgReadInt array: GZRead failed:" + gzErrorMessage(fd),
sg_location{thread_gzPath}, nullptr, false);
} }
if ( sgIsBigEndian() ) { if ( sgIsBigEndian() ) {
for ( unsigned int i = 0; i < n; ++i ) { for ( unsigned int i = 0; i < n; ++i ) {
@@ -461,7 +489,7 @@ void sgWriteInt ( gzFile fd, const unsigned int n, const int *var )
if ( gzwrite ( fd, (void *)var, sizeof(int) * n ) if ( gzwrite ( fd, (void *)var, sizeof(int) * n )
!= (int)(sizeof(int) * n) ) != (int)(sizeof(int) * n) )
{ {
write_error = true ; throw sg_io_exception("sgWriteInt array: gzwrite failed:" + gzErrorMessage(fd), {} /* origin */, false);
} }
} }
+8 -4
View File
@@ -35,6 +35,9 @@
#include <simgear/math/SGMath.hxx> #include <simgear/math/SGMath.hxx>
// forward decls
class SGPath;
// Note that output is written in little endian form (and converted as // Note that output is written in little endian form (and converted as
// necessary for big endian machines) // necessary for big endian machines)
@@ -121,9 +124,10 @@ inline void sgWriteGeod ( gzFile fd, const SGGeod& var ) {
sgWriteDouble( fd, var.getElevationM() ); sgWriteDouble( fd, var.getElevationM() );
} }
void sgClearReadError(); /**
void sgClearWriteError(); @ error aid: allow calling code to specify which file path we're reading from, so that erros we
int sgReadError(); throw from sgReadXXXX can have a valid location set.
int sgWriteError(); */
void setThreadLocalSimgearReadPath(const SGPath& path);
#endif // _SG_LOWLEVEL_HXX #endif // _SG_LOWLEVEL_HXX
+344 -361
View File
@@ -454,10 +454,6 @@ void SGBinObject::read_object( gzFile fp,
} }
} }
if ( sgReadError() ) {
throw sg_exception("Error reading object properties");
}
size_t indexCount = std::bitset<32>((int)idx_mask).count(); size_t indexCount = std::bitset<32>((int)idx_mask).count();
if (indexCount == 0) { if (indexCount == 0) {
throw sg_exception("object index mask has no bits set"); throw sg_exception("object index mask has no bits set");
@@ -465,18 +461,10 @@ void SGBinObject::read_object( gzFile fp,
for ( j = 0; j < nelements; ++j ) { for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes ); sgReadUInt( fp, &nbytes );
if ( sgReadError() ) {
throw sg_exception("Error reading element size");
}
buf.resize( nbytes ); buf.resize( nbytes );
char *ptr = buf.get_ptr(); char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr ); sgReadBytes( fp, nbytes, ptr );
if ( sgReadError() ) {
throw sg_exception("Error reading element bytes");
}
int_list vs; int_list vs;
int_list ns; int_list ns;
int_list cs; int_list cs;
@@ -503,314 +491,309 @@ void SGBinObject::read_object( gzFile fp,
// read a binary file and populate the provided structures. // read a binary file and populate the provided structures.
bool SGBinObject::read_bin( const SGPath& file ) { bool SGBinObject::read_bin( const SGPath& file )
SGVec3d p; {
int i, k; gzFile fp = NULL;
size_t j;
unsigned int nbytes;
sgSimpleBuffer buf( 32768 ); // 32 Kb
// zero out structures try {
gbs_center = SGVec3d(0, 0, 0); SGVec3d p;
gbs_radius = 0.0; int i, k;
size_t j;
unsigned int nbytes;
sgSimpleBuffer buf( 32768 ); // 32 Kb
wgs84_nodes.clear(); // zero out structures
normals.clear(); gbs_center = SGVec3d(0, 0, 0);
texcoords.clear(); gbs_radius = 0.0;
pts_v.clear(); wgs84_nodes.clear();
pts_n.clear(); normals.clear();
pts_c.clear(); texcoords.clear();
pts_tcs.clear();
pts_vas.clear();
pt_materials.clear();
tris_v.clear(); pts_v.clear();
tris_n.clear(); pts_n.clear();
tris_c.clear(); pts_c.clear();
tris_tcs.clear(); pts_tcs.clear();
tris_vas.clear(); pts_vas.clear();
tri_materials.clear(); pt_materials.clear();
strips_v.clear(); tris_v.clear();
strips_n.clear(); tris_n.clear();
strips_c.clear(); tris_c.clear();
strips_tcs.clear(); tris_tcs.clear();
strips_vas.clear(); tris_vas.clear();
strip_materials.clear(); tri_materials.clear();
fans_v.clear(); strips_v.clear();
fans_n.clear(); strips_n.clear();
fans_c.clear(); strips_c.clear();
fans_tcs.clear(); strips_tcs.clear();
fans_vas.clear(); strips_vas.clear();
fan_materials.clear(); strip_materials.clear();
gzFile fp = gzFileFromSGPath(file, "rb"); fans_v.clear();
if ( fp == NULL ) { fans_n.clear();
SGPath withGZ = file; fans_c.clear();
withGZ.concat(".gz"); fans_tcs.clear();
fp = gzFileFromSGPath(withGZ, "rb"); fans_vas.clear();
if (fp == nullptr) { fan_materials.clear();
SG_LOG( SG_EVENT, SG_ALERT,
"ERROR: opening " << file << " or " << withGZ << " for reading!");
throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file)); gzFile fp = gzFileFromSGPath(file, "rb");
if ( fp == NULL ) {
SGPath withGZ = file;
withGZ.concat(".gz");
fp = gzFileFromSGPath(withGZ, "rb");
if (fp == nullptr) {
throw sg_io_exception("Error opening for reading (and .gz)", sg_location(file), {}, false);
}
} }
}
sgClearReadError(); setThreadLocalSimgearReadPath(file);
// read headers // read headers
unsigned int header; unsigned int header;
sgReadUInt( fp, &header ); sgReadUInt( fp, &header );
if (sgReadError()) {
int code = 0;
const char* gzErrorString = gzerror(fp, &code);
gzclose(fp);
throw sg_io_exception("Unable to read BTG header: " + string{gzErrorString} + ", code =" + std::to_string(code), sg_location(file));
}
if ( ((header & 0xFF000000) >> 24) == 'S' && if ( ((header & 0xFF000000) >> 24) == 'S' &&
((header & 0x00FF0000) >> 16) == 'G' ) { ((header & 0x00FF0000) >> 16) == 'G' ) {
// read file version // read file version
version = (header & 0x0000FFFF); version = (header & 0x0000FFFF);
} else { } else {
// close the file before we return throw sg_io_exception("Bad BTG magic/version", sg_location(file), {}, false);
gzclose(fp); }
throw sg_io_exception("Bad BTG magic/version", sg_location(file));
}
// read creation time // read creation time
unsigned int foo_calendar_time; unsigned int foo_calendar_time;
sgReadUInt( fp, &foo_calendar_time ); sgReadUInt( fp, &foo_calendar_time );
#if 0 #if 0
time_t calendar_time = foo_calendar_time; time_t calendar_time = foo_calendar_time;
// The following code has a global effect on the host application // The following code has a global effect on the host application
// and can screws up the time elsewhere. It should be avoided // and can screws up the time elsewhere. It should be avoided
// unless you need this for debugging in which case you should // unless you need this for debugging in which case you should
// disable it again once the debugging task is finished. // disable it again once the debugging task is finished.
struct tm *local_tm; struct tm *local_tm;
local_tm = localtime( &calendar_time ); local_tm = localtime( &calendar_time );
char time_str[256]; char time_str[256];
strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm); strftime( time_str, 256, "%a %b %d %H:%M:%S %Z %Y", local_tm);
SG_LOG( SG_EVENT, SG_DEBUG, "File created on " << time_str); SG_LOG( SG_EVENT, SG_DEBUG, "File created on " << time_str);
#endif #endif
// read number of top level objects // read number of top level objects
int nobjects; int nobjects;
if ( version >= 10) { // version 10 extends everything to be 32-bit if ( version >= 10) { // version 10 extends everything to be 32-bit
sgReadInt( fp, &nobjects ); sgReadInt( fp, &nobjects );
} else if ( version >= 7 ) {
uint16_t v;
sgReadUShort( fp, &v );
nobjects = v;
} else {
int16_t v;
sgReadShort( fp, &v );
nobjects = v;
}
SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects);
if ( sgReadError() ) {
throw sg_io_exception("Error reading BTG file header", sg_location(file));
}
// read in objects
for ( i = 0; i < nobjects; ++i ) {
// read object header
char obj_type;
uint32_t nproperties, nelements;
sgReadChar( fp, &obj_type );
if ( version >= 10 ) {
sgReadUInt( fp, &nproperties );
sgReadUInt( fp, &nelements );
} else if ( version >= 7 ) { } else if ( version >= 7 ) {
uint16_t v; uint16_t v;
sgReadUShort( fp, &v ); sgReadUShort( fp, &v );
nproperties = v; nobjects = v;
sgReadUShort( fp, &v );
nelements = v;
} else { } else {
int16_t v; int16_t v;
sgReadShort( fp, &v ); sgReadShort( fp, &v );
nproperties = v; nobjects = v;
sgReadShort( fp, &v );
nelements = v;
} }
SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i << SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin Total objects to read = " << nobjects);
" = " << (int)obj_type << " props = " << nproperties <<
" elements = " << nelements);
if ( obj_type == SG_BOUNDING_SPHERE ) {
// read bounding sphere properties
read_properties( fp, nproperties );
// read bounding sphere elements // read in objects
for ( j = 0; j < nelements; ++j ) { for ( i = 0; i < nobjects; ++i ) {
sgReadUInt( fp, &nbytes ); // read object header
buf.resize( nbytes ); char obj_type;
buf.reset(); uint32_t nproperties, nelements;
char *ptr = buf.get_ptr(); sgReadChar( fp, &obj_type );
sgReadBytes( fp, nbytes, ptr ); if ( version >= 10 ) {
gbs_center = buf.readVec3d(); sgReadUInt( fp, &nproperties );
gbs_radius = buf.readFloat(); sgReadUInt( fp, &nelements );
} else if ( version >= 7 ) {
uint16_t v;
sgReadUShort( fp, &v );
nproperties = v;
sgReadUShort( fp, &v );
nelements = v;
} else {
int16_t v;
sgReadShort( fp, &v );
nproperties = v;
sgReadShort( fp, &v );
nelements = v;
} }
} else if ( obj_type == SG_VERTEX_LIST ) {
// read vertex list properties
read_properties( fp, nproperties );
// read vertex list elements SG_LOG(SG_IO, SG_DEBUG, "SGBinObject::read_bin object " << i <<
for ( j = 0; j < nelements; ++j ) { " = " << (int)obj_type << " props = " << nproperties <<
sgReadUInt( fp, &nbytes ); " elements = " << nelements);
buf.resize( nbytes );
buf.reset(); if ( obj_type == SG_BOUNDING_SPHERE ) {
char *ptr = buf.get_ptr(); // read bounding sphere properties
sgReadBytes( fp, nbytes, ptr ); read_properties( fp, nproperties );
int count = nbytes / (sizeof(float) * 3);
wgs84_nodes.reserve( count ); // read bounding sphere elements
for ( k = 0; k < count; ++k ) { for ( j = 0; j < nelements; ++j ) {
SGVec3f v = buf.readVec3f(); sgReadUInt( fp, &nbytes );
// extend from float to double, hmmm buf.resize( nbytes );
wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) ); buf.reset();
char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr );
gbs_center = buf.readVec3d();
gbs_radius = buf.readFloat();
} }
} } else if ( obj_type == SG_VERTEX_LIST ) {
} else if ( obj_type == SG_COLOR_LIST ) { // read vertex list properties
// read color list properties read_properties( fp, nproperties );
read_properties( fp, nproperties );
// read color list elements // read vertex list elements
for ( j = 0; j < nelements; ++j ) { for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes ); sgReadUInt( fp, &nbytes );
buf.resize( nbytes ); buf.resize( nbytes );
buf.reset(); buf.reset();
char *ptr = buf.get_ptr(); char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr ); sgReadBytes( fp, nbytes, ptr );
int count = nbytes / (sizeof(float) * 4); int count = nbytes / (sizeof(float) * 3);
colors.reserve(count); wgs84_nodes.reserve( count );
for ( k = 0; k < count; ++k ) { for ( k = 0; k < count; ++k ) {
colors.push_back( buf.readVec4f() ); SGVec3f v = buf.readVec3f();
// extend from float to double, hmmm
wgs84_nodes.push_back( SGVec3d(v[0], v[1], v[2]) );
}
} }
} } else if ( obj_type == SG_COLOR_LIST ) {
} else if ( obj_type == SG_NORMAL_LIST ) { // read color list properties
// read normal list properties read_properties( fp, nproperties );
read_properties( fp, nproperties );
// read normal list elements // read color list elements
for ( j = 0; j < nelements; ++j ) { for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes ); sgReadUInt( fp, &nbytes );
buf.resize( nbytes ); buf.resize( nbytes );
buf.reset(); buf.reset();
unsigned char *ptr = (unsigned char *)(buf.get_ptr()); char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr ); sgReadBytes( fp, nbytes, ptr );
int count = nbytes / 3; int count = nbytes / (sizeof(float) * 4);
normals.reserve( count ); colors.reserve(count);
for ( k = 0; k < count; ++k ) {
for ( k = 0; k < count; ++k ) { colors.push_back( buf.readVec4f() );
SGVec3f normal( (ptr[0]) / 127.5 - 1.0, }
(ptr[1]) / 127.5 - 1.0,
(ptr[2]) / 127.5 - 1.0);
normals.push_back(normalize(normal));
ptr += 3;
} }
} } else if ( obj_type == SG_NORMAL_LIST ) {
} else if ( obj_type == SG_TEXCOORD_LIST ) { // read normal list properties
// read texcoord list properties read_properties( fp, nproperties );
read_properties( fp, nproperties );
// read texcoord list elements // read normal list elements
for ( j = 0; j < nelements; ++j ) { for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes ); sgReadUInt( fp, &nbytes );
buf.resize( nbytes ); buf.resize( nbytes );
buf.reset(); buf.reset();
char *ptr = buf.get_ptr(); unsigned char *ptr = (unsigned char *)(buf.get_ptr());
sgReadBytes( fp, nbytes, ptr ); sgReadBytes( fp, nbytes, ptr );
int count = nbytes / (sizeof(float) * 2); int count = nbytes / 3;
texcoords.reserve(count); normals.reserve( count );
for ( k = 0; k < count; ++k ) {
texcoords.push_back( buf.readVec2f() ); for ( k = 0; k < count; ++k ) {
SGVec3f normal( (ptr[0]) / 127.5 - 1.0,
(ptr[1]) / 127.5 - 1.0,
(ptr[2]) / 127.5 - 1.0);
normals.push_back(normalize(normal));
ptr += 3;
}
} }
} } else if ( obj_type == SG_TEXCOORD_LIST ) {
} else if ( obj_type == SG_VA_FLOAT_LIST ) { // read texcoord list properties
// read vertex attribute (float) properties read_properties( fp, nproperties );
read_properties( fp, nproperties );
// read vertex attribute list elements // read texcoord list elements
for ( j = 0; j < nelements; ++j ) { for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes ); sgReadUInt( fp, &nbytes );
buf.resize( nbytes ); buf.resize( nbytes );
buf.reset(); buf.reset();
char *ptr = buf.get_ptr(); char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr ); sgReadBytes( fp, nbytes, ptr );
int count = nbytes / (sizeof(float)); int count = nbytes / (sizeof(float) * 2);
va_flt.reserve(count); texcoords.reserve(count);
for ( k = 0; k < count; ++k ) { for ( k = 0; k < count; ++k ) {
va_flt.push_back( buf.readFloat() ); texcoords.push_back( buf.readVec2f() );
}
} }
} } else if ( obj_type == SG_VA_FLOAT_LIST ) {
} else if ( obj_type == SG_VA_INTEGER_LIST ) { // read vertex attribute (float) properties
// read vertex attribute (integer) properties read_properties( fp, nproperties );
read_properties( fp, nproperties );
// read vertex attribute list elements // read vertex attribute list elements
for ( j = 0; j < nelements; ++j ) { for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes ); sgReadUInt( fp, &nbytes );
buf.resize( nbytes ); buf.resize( nbytes );
buf.reset(); buf.reset();
char *ptr = buf.get_ptr(); char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr ); sgReadBytes( fp, nbytes, ptr );
int count = nbytes / (sizeof(unsigned int)); int count = nbytes / (sizeof(float));
va_int.reserve(count); va_flt.reserve(count);
for ( k = 0; k < count; ++k ) { for ( k = 0; k < count; ++k ) {
va_int.push_back( buf.readInt() ); va_flt.push_back( buf.readFloat() );
}
} }
} } else if ( obj_type == SG_VA_INTEGER_LIST ) {
} else if ( obj_type == SG_POINTS ) { // read vertex attribute (integer) properties
// read point elements read_properties( fp, nproperties );
read_object( fp, SG_POINTS, nproperties, nelements,
pts_v, pts_n, pts_c, pts_tcs,
pts_vas, pt_materials );
} else if ( obj_type == SG_TRIANGLE_FACES ) {
// read triangle face properties
read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements,
tris_v, tris_n, tris_c, tris_tcs,
tris_vas, tri_materials );
} else if ( obj_type == SG_TRIANGLE_STRIPS ) {
// read triangle strip properties
read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements,
strips_v, strips_n, strips_c, strips_tcs,
strips_vas, strip_materials );
} else if ( obj_type == SG_TRIANGLE_FANS ) {
// read triangle fan properties
read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements,
fans_v, fans_n, fans_c, fans_tcs,
fans_vas, fan_materials );
} else {
// unknown object type, just skip
read_properties( fp, nproperties );
// read elements // read vertex attribute list elements
for ( j = 0; j < nelements; ++j ) { for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes ); sgReadUInt( fp, &nbytes );
// cout << "element size = " << nbytes << endl; buf.resize( nbytes );
if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); } buf.reset();
char *ptr = buf.get_ptr(); char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr ); sgReadBytes( fp, nbytes, ptr );
int count = nbytes / (sizeof(unsigned int));
va_int.reserve(count);
for ( k = 0; k < count; ++k ) {
va_int.push_back( buf.readInt() );
}
}
} else if ( obj_type == SG_POINTS ) {
// read point elements
read_object( fp, SG_POINTS, nproperties, nelements,
pts_v, pts_n, pts_c, pts_tcs,
pts_vas, pt_materials );
} else if ( obj_type == SG_TRIANGLE_FACES ) {
// read triangle face properties
read_object( fp, SG_TRIANGLE_FACES, nproperties, nelements,
tris_v, tris_n, tris_c, tris_tcs,
tris_vas, tri_materials );
} else if ( obj_type == SG_TRIANGLE_STRIPS ) {
// read triangle strip properties
read_object( fp, SG_TRIANGLE_STRIPS, nproperties, nelements,
strips_v, strips_n, strips_c, strips_tcs,
strips_vas, strip_materials );
} else if ( obj_type == SG_TRIANGLE_FANS ) {
// read triangle fan properties
read_object( fp, SG_TRIANGLE_FANS, nproperties, nelements,
fans_v, fans_n, fans_c, fans_tcs,
fans_vas, fan_materials );
} else {
// unknown object type, just skip
read_properties( fp, nproperties );
// read elements
for ( j = 0; j < nelements; ++j ) {
sgReadUInt( fp, &nbytes );
// cout << "element size = " << nbytes << endl;
if ( nbytes > buf.get_size() ) { buf.resize( nbytes ); }
char *ptr = buf.get_ptr();
sgReadBytes( fp, nbytes, ptr );
}
} }
} }
if ( sgReadError() ) { gzclose(fp);
throw sg_io_exception("Error while reading object", sg_location(file, i)); fp = NULL;
} catch (std::exception&) {
if (fp) {
// close the file
gzclose(fp);
} }
throw; // re-throw
} }
// close the file
gzclose(fp);
return true; return true;
} }
@@ -984,112 +967,112 @@ bool SGBinObject::write_bin_file(const SGPath& file)
return false; return false;
} }
sgClearWriteError(); try {
SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size()
<< " pt_materials = " << pt_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size()
<< " tri_materials = " << tri_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size()
<< " strip_materials = " << strip_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size()
<< " fan_materials = " << fan_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "points size = " << pts_v.size() SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() );
<< " pt_materials = " << pt_materials.size() ); SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() );
SG_LOG(SG_IO, SG_DEBUG, "triangles size = " << tris_v.size() SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() );
<< " tri_materials = " << tri_materials.size() ); SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() );
SG_LOG(SG_IO, SG_DEBUG, "strips size = " << strips_v.size()
<< " strip_materials = " << strip_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "fans size = " << fans_v.size()
<< " fan_materials = " << fan_materials.size() );
SG_LOG(SG_IO, SG_DEBUG, "nodes = " << wgs84_nodes.size() ); version = 10;
SG_LOG(SG_IO, SG_DEBUG, "colors = " << colors.size() ); bool shortMaterialsRanges =
SG_LOG(SG_IO, SG_DEBUG, "normals = " << normals.size() ); (max_object_size(pt_materials) < VERSION_7_MATERIAL_LIMIT) &&
SG_LOG(SG_IO, SG_DEBUG, "tex coords = " << texcoords.size() ); (max_object_size(fan_materials) < VERSION_7_MATERIAL_LIMIT) &&
(max_object_size(strip_materials) < VERSION_7_MATERIAL_LIMIT) &&
(max_object_size(tri_materials) < VERSION_7_MATERIAL_LIMIT);
version = 10; if ((wgs84_nodes.size() < 0xffff) &&
bool shortMaterialsRanges = (normals.size() < 0xffff) &&
(max_object_size(pt_materials) < VERSION_7_MATERIAL_LIMIT) && (texcoords.size() < 0xffff) &&
(max_object_size(fan_materials) < VERSION_7_MATERIAL_LIMIT) && shortMaterialsRanges) {
(max_object_size(strip_materials) < VERSION_7_MATERIAL_LIMIT) && version = 7; // use smaller indices if possible
(max_object_size(tri_materials) < VERSION_7_MATERIAL_LIMIT); }
if ((wgs84_nodes.size() < 0xffff) && // write header magic
(normals.size() < 0xffff) &&
(texcoords.size() < 0xffff) &&
shortMaterialsRanges) {
version = 7; // use smaller indices if possible
}
// write header magic /** Magic Number for our file format */
#define SG_FILE_MAGIC_NUMBER ( ('S'<<24) + ('G'<<16) + version )
/** Magic Number for our file format */ sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER );
#define SG_FILE_MAGIC_NUMBER ( ('S'<<24) + ('G'<<16) + version ) time_t calendar_time = time(NULL);
sgWriteLong( fp, (int32_t)calendar_time );
sgWriteUInt( fp, SG_FILE_MAGIC_NUMBER ); // calculate and write number of top level objects
time_t calendar_time = time(NULL); int nobjects = 5; // gbs, vertices, colors, normals, texcoords
sgWriteLong( fp, (int32_t)calendar_time ); nobjects += count_objects(pt_materials);
nobjects += count_objects(tri_materials);
nobjects += count_objects(strip_materials);
nobjects += count_objects(fan_materials);
// calculate and write number of top level objects SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects);
int nobjects = 5; // gbs, vertices, colors, normals, texcoords
nobjects += count_objects(pt_materials);
nobjects += count_objects(tri_materials);
nobjects += count_objects(strip_materials);
nobjects += count_objects(fan_materials);
SG_LOG(SG_IO, SG_DEBUG, "total top level objects = " << nobjects); if (version == 7) {
sgWriteUShort( fp, (uint16_t) nobjects );
} else {
sgWriteInt( fp, nobjects );
}
if (version == 7) { // write bounding sphere
sgWriteUShort( fp, (uint16_t) nobjects ); write_header( fp, SG_BOUNDING_SPHERE, 0, 1);
} else { sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes
sgWriteInt( fp, nobjects ); sgWritedVec3( fp, gbs_center );
} sgWriteFloat( fp, gbs_radius );
// write bounding sphere // dump vertex list
write_header( fp, SG_BOUNDING_SPHERE, 0, 1); write_header( fp, SG_VERTEX_LIST, 0, 1);
sgWriteUInt( fp, sizeof(double) * 3 + sizeof(float) ); // nbytes sgWriteUInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes
sgWritedVec3( fp, gbs_center ); for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) {
sgWriteFloat( fp, gbs_radius ); sgWriteVec3( fp, toVec3f(wgs84_nodes[i] - gbs_center));
}
// dump vertex list // dump vertex color list
write_header( fp, SG_VERTEX_LIST, 0, 1); write_header( fp, SG_COLOR_LIST, 0, 1);
sgWriteUInt( fp, wgs84_nodes.size() * sizeof(float) * 3 ); // nbytes sgWriteUInt( fp, colors.size() * sizeof(float) * 4 ); // nbytes
for ( i = 0; i < (int)wgs84_nodes.size(); ++i ) { for ( i = 0; i < (int)colors.size(); ++i ) {
sgWriteVec3( fp, toVec3f(wgs84_nodes[i] - gbs_center)); sgWriteVec4( fp, colors[i]);
} }
// dump vertex color list // dump vertex normal list
write_header( fp, SG_COLOR_LIST, 0, 1); write_header( fp, SG_NORMAL_LIST, 0, 1);
sgWriteUInt( fp, colors.size() * sizeof(float) * 4 ); // nbytes sgWriteUInt( fp, normals.size() * 3 ); // nbytes
for ( i = 0; i < (int)colors.size(); ++i ) { char normal[3];
sgWriteVec4( fp, colors[i]); for ( i = 0; i < (int)normals.size(); ++i ) {
} SGVec3f p = normals[i];
normal[0] = (unsigned char)((p.x() + 1.0) * 127.5);
normal[1] = (unsigned char)((p.y() + 1.0) * 127.5);
normal[2] = (unsigned char)((p.z() + 1.0) * 127.5);
sgWriteBytes( fp, 3, normal );
}
// dump vertex normal list // dump texture coordinates
write_header( fp, SG_NORMAL_LIST, 0, 1); write_header( fp, SG_TEXCOORD_LIST, 0, 1);
sgWriteUInt( fp, normals.size() * 3 ); // nbytes sgWriteUInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes
char normal[3]; for ( i = 0; i < (int)texcoords.size(); ++i ) {
for ( i = 0; i < (int)normals.size(); ++i ) { sgWriteVec2( fp, texcoords[i]);
SGVec3f p = normals[i]; }
normal[0] = (unsigned char)((p.x() + 1.0) * 127.5);
normal[1] = (unsigned char)((p.y() + 1.0) * 127.5);
normal[2] = (unsigned char)((p.z() + 1.0) * 127.5);
sgWriteBytes( fp, 3, normal );
}
// dump texture coordinates write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials);
write_header( fp, SG_TEXCOORD_LIST, 0, 1); write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials);
sgWriteUInt( fp, texcoords.size() * sizeof(float) * 2 ); // nbytes write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials);
for ( i = 0; i < (int)texcoords.size(); ++i ) { write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials);
sgWriteVec2( fp, texcoords[i]);
}
write_objects(fp, SG_POINTS, pts_v, pts_n, pts_c, pts_tcs, pts_vas, pt_materials); // close the file
write_objects(fp, SG_TRIANGLE_FACES, tris_v, tris_n, tris_c, tris_tcs, tris_vas, tri_materials); gzclose(fp);
write_objects(fp, SG_TRIANGLE_STRIPS, strips_v, strips_n, strips_c, strips_tcs, strips_vas, strip_materials); fp = NULL;
write_objects(fp, SG_TRIANGLE_FANS, fans_v, fans_n, fans_c, fans_tcs, fans_vas, fan_materials); } catch (std::exception&) {
if (fp) {
// close the file gzclose(fp);
gzclose(fp); }
if ( sgWriteError() ) {
cout << "Error while writing file " << file << endl;
return false; return false;
} }
return true; return true;
} }
+3 -1
View File
@@ -82,7 +82,9 @@ std::string SGFile::computeHash()
[](char* p) { free(p); }}; [](char* p) { free(p); }};
if (!buf) { if (!buf) {
SG_LOG(SG_IO, SG_ALERT, "Failed to malloc buffer for SHA1 check"); // @TODO report out of memory error
SG_LOG(SG_IO, SG_ALERT, "Failed to malloc buffer for SHA1 check:" << file_name);
return {};
} }
size_t readLen; size_t readLen;
Binary file not shown.
+23
View File
@@ -901,6 +901,27 @@ void testPersistentSocketFailure(HTTP::Client *cl) {
verifyRequestCount("dirD/subdirDB/fileDBA", 1); verifyRequestCount("dirD/subdirDB/fileDBA", 1);
} }
void testHashOnEmptyFile()
{
std::unique_ptr<HTTPRepository> repo;
SGPath p(simgear::Dir::current().path());
p.append("sgfile_compute_hash");
simgear::Dir pd(p);
if (pd.exists()) {
pd.removeChildren();
} else {
pd.create(0700);
}
SGPath fPath = p / "test_empty_file";
SGFile file(fPath);
file.open(SG_IO_OUT);
file.close();
const auto hash = file.computeHash();
}
int main(int argc, char* argv[]) int main(int argc, char* argv[])
{ {
sglog().setLogLevels( SG_ALL, SG_INFO ); sglog().setLogLevels( SG_ALL, SG_INFO );
@@ -951,6 +972,8 @@ int main(int argc, char* argv[])
testRetryAfterSocketFailure(&cl); testRetryAfterSocketFailure(&cl);
testPersistentSocketFailure(&cl); testPersistentSocketFailure(&cl);
testHashOnEmptyFile();
std::cout << "all tests passed ok" << std::endl; std::cout << "all tests passed ok" << std::endl;
return 0; return 0;
} }
+30 -1
View File
@@ -32,7 +32,7 @@ void testTarGz()
uint8_t* buf = (uint8_t*) alloca(8192); uint8_t* buf = (uint8_t*) alloca(8192);
size_t bufSize = f.read((char*) buf, 8192); size_t bufSize = f.read((char*) buf, 8192);
SG_VERIFY(ArchiveExtractor::determineType(buf, bufSize) == ArchiveExtractor::TarData); SG_VERIFY(ArchiveExtractor::determineType(buf, bufSize) == ArchiveExtractor::GZData);
f.close(); f.close();
} }
@@ -174,6 +174,34 @@ void testPAXAttributes()
} }
void testExtractXZ()
{
SGPath p = SGPath(SRC_DIR);
p.append("test.tar.xz");
SGBinaryFile f(p);
f.open(SG_IO_IN);
SGPath extractDir = simgear::Dir::current().path() / "test_extract_xz";
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());
}
int main(int ac, char ** av) int main(int ac, char ** av)
{ {
testTarGz(); testTarGz();
@@ -181,6 +209,7 @@ int main(int ac, char ** av)
testFilterTar(); testFilterTar();
testExtractStreamed(); testExtractStreamed();
testExtractZip(); testExtractZip();
testExtractXZ();
// disabled to avoiding checking in large PAX archive // disabled to avoiding checking in large PAX archive
// testPAXAttributes(); // testPAXAttributes();
+408 -371
View File
@@ -38,82 +38,11 @@
#include <simgear/package/unzip.h> #include <simgear/package/unzip.h>
#include <simgear/structure/exception.hxx> #include <simgear/structure/exception.hxx>
#include "ArchiveExtractor_private.hxx"
namespace simgear namespace simgear
{ {
class ArchiveExtractorPrivate
{
public:
ArchiveExtractorPrivate(ArchiveExtractor* o) :
outer(o)
{
assert(outer);
}
virtual ~ArchiveExtractorPrivate() = default;
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;
/* tar Header Block, from POSIX 1003.1-1990. */
typedef struct typedef struct
{ {
@@ -153,320 +82,413 @@ typedef struct
#define FIFOTYPE '6' /* FIFO special */ #define FIFOTYPE '6' /* FIFO special */
#define CONTTYPE '7' /* reserved */ #define CONTTYPE '7' /* reserved */
const char PAX_GLOBAL_HEADER = 'g'; const char PAX_GLOBAL_HEADER = 'g';
const char PAX_FILE_ATTRIBUTES = 'x'; const char PAX_FILE_ATTRIBUTES = 'x';
class TarExtractorPrivate : public ArchiveExtractorPrivate ///////////////////////////////////////////////////////////////////////////////////////////////////
const int ZLIB_DECOMPRESS_BUFFER_SIZE = 32 * 1024;
const int ZLIB_INFLATE_WINDOW_BITS = MAX_WBITS;
const int ZLIB_DECODE_GZIP_HEADER = 16;
/* tar Header Block, from POSIX 1003.1-1990. */
class TarExtractorPrivate : public ArchiveExtractorPrivate
{
public:
union {
UstarHeaderBlock header;
uint8_t headerBytes[TAR_HEADER_BLOCK_SIZE];
};
size_t bytesRemaining;
std::unique_ptr<SGFile> currentFile;
size_t currentFileSize;
uint8_t* headerPtr;
bool skipCurrentEntry = false;
std::string paxAttributes;
std::string paxPathName;
TarExtractorPrivate(ArchiveExtractor* o) : ArchiveExtractorPrivate(o)
{
setState(TarExtractorPrivate::READING_HEADER);
}
~TarExtractorPrivate() = default;
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) {
return;
}
if (state == READING_FILE) {
if (currentFile) {
currentFile->close();
currentFile.reset();
}
readPaddingIfRequired();
} else if (state == READING_HEADER) {
processHeader();
} else if (state == PRE_END_OF_ARCHVE) {
if (headerIsAllZeros()) {
setState(END_OF_ARCHIVE);
} 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);
}
}
void setState(State newState)
{
if ((newState == READING_HEADER) || (newState == PRE_END_OF_ARCHVE)) {
bytesRemaining = TAR_HEADER_BLOCK_SIZE;
headerPtr = headerBytes;
}
state = newState;
}
void extractBytes(const uint8_t* bytes, size_t count) override
{
// uncompressed, just pass through directly
processBytes((const char*)bytes, count);
}
void flush() override
{
// no-op for tar files, we process everything greedily
}
void processHeader()
{
if (headerIsAllZeros()) {
if (state == PRE_END_OF_ARCHVE) {
setState(END_OF_ARCHIVE);
} else {
setState(PRE_END_OF_ARCHVE);
}
return;
}
if (strncmp(header.magic, TMAGIC, TMAGLEN) != 0) {
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, "unsafe tar path, skipping::" << tarPath);
skipCurrentEntry = true;
}
auto result = filterPath(tarPath);
if (result == ArchiveExtractor::Stop) {
setState(FILTER_STOPPED);
return;
} else if (result == ArchiveExtractor::Skipped) {
skipCurrentEntry = true;
}
SGPath p = extractRootPath() / tarPath;
if (header.typeflag == DIRTYPE) {
if (!skipCurrentEntry) {
Dir dir(p);
dir.create(0755);
}
setState(READING_HEADER);
} else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) {
currentFileSize = ::strtol(header.size, NULL, 8);
bytesRemaining = currentFileSize;
if (!skipCurrentEntry) {
currentFile.reset(new SGBinaryFile(p));
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;
}
}
void processBytes(const char* bytes, size_t count)
{
if ((state >= ERROR_STATE) || (state == END_OF_ARCHIVE)) {
return;
}
size_t curBytes = std::min(bytesRemaining, count);
if (state == READING_FILE) {
if (currentFile) {
currentFile->write(bytes, curBytes);
}
bytesRemaining -= curBytes;
} else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) {
memcpy(headerPtr, bytes, curBytes);
bytesRemaining -= curBytes;
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();
if (count > curBytes) {
// recurse with unprocessed bytes
processBytes(bytes + curBytes, count - curBytes);
}
}
bool headerIsAllZeros() const
{
char* headerAsChar = (char*)&header;
for (size_t i = 0; i < offsetof(UstarHeaderBlock, magic); ++i) {
if (*headerAsChar++ != 0) {
return false;
}
}
return true;
}
// https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxa500/paxex.htm#paxex
void parsePAXAttributes(bool areGlobal)
{
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;
}
}
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;
}
}
};
///////////////////////////////////////////////////////////////////////////////
class GZTarExtractor : public TarExtractorPrivate
{ {
public: public:
GZTarExtractor(ArchiveExtractor* outer) : TarExtractorPrivate(outer)
union {
UstarHeaderBlock header;
uint8_t headerBytes[TAR_HEADER_BLOCK_SIZE];
};
size_t bytesRemaining;
std::unique_ptr<SGFile> currentFile;
size_t currentFileSize;
z_stream zlibStream;
uint8_t* zlibOutput;
bool haveInitedZLib = false;
bool uncompressedData = false; // set if reading a plain .tar (not tar.gz)
uint8_t* headerPtr;
bool skipCurrentEntry = false;
std::string paxAttributes;
std::string paxPathName;
TarExtractorPrivate(ArchiveExtractor* o) :
ArchiveExtractorPrivate(o)
{ {
memset(&zlibStream, 0, sizeof(z_stream)); memset(&zlibStream, 0, sizeof(z_stream));
zlibOutput = (unsigned char*)malloc(ZLIB_DECOMPRESS_BUFFER_SIZE); zlibOutput = (unsigned char*)malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
zlibStream.zalloc = Z_NULL; zlibStream.zalloc = Z_NULL;
zlibStream.zfree = Z_NULL; zlibStream.zfree = Z_NULL;
zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE; zlibStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
zlibStream.next_out = zlibOutput; zlibStream.next_out = zlibOutput;
} }
~TarExtractorPrivate() ~GZTarExtractor()
{ {
if (haveInitedZLib) {
inflateEnd(&zlibStream);
}
free(zlibOutput); free(zlibOutput);
} }
void readPaddingIfRequired() void extractBytes(const uint8_t* bytes, size_t count) override
{ {
size_t pad = currentFileSize % TAR_HEADER_BLOCK_SIZE; zlibStream.next_in = (uint8_t*)bytes;
if (pad) { zlibStream.avail_in = count;
bytesRemaining = TAR_HEADER_BLOCK_SIZE - pad;
setState(READING_PADDING);
} else {
setState(READING_HEADER);
}
}
void checkEndOfState() if (!haveInitedZLib) {
{ // now we have data, see if we're dealing with GZ-compressed data or not
if (bytesRemaining > 0) { if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) {
return; // GZIP identification bytes
} if (inflateInit2(&zlibStream, ZLIB_INFLATE_WINDOW_BITS | ZLIB_DECODE_GZIP_HEADER) != Z_OK) {
SG_LOG(SG_IO, SG_WARN, "inflateInit2 failed");
if (state == READING_FILE) { state = TarExtractorPrivate::BAD_DATA;
if (currentFile) { return;
currentFile->close(); }
currentFile.reset();
}
readPaddingIfRequired();
} else if (state == READING_HEADER) {
processHeader();
} else if (state == PRE_END_OF_ARCHVE) {
if (headerIsAllZeros()) {
setState(END_OF_ARCHIVE);
} else { } else {
// what does the spec say here? // set error state
state = TarExtractorPrivate::BAD_DATA;
return;
} }
} 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);
}
}
void setState(State newState) haveInitedZLib = true;
{ setState(TarExtractorPrivate::READING_HEADER);
if ((newState == READING_HEADER) || (newState == PRE_END_OF_ARCHVE)) { } // of init on first-bytes case
bytesRemaining = TAR_HEADER_BLOCK_SIZE;
headerPtr = headerBytes;
}
state = newState; 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
void extractBytes(const uint8_t* bytes, size_t count) override } else if (result == Z_BUF_ERROR) {
{ // transient error, fall through
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()) {
if (state == PRE_END_OF_ARCHVE) {
setState(END_OF_ARCHIVE);
} else { } else {
setState(PRE_END_OF_ARCHVE); // _error = result;
SG_LOG(SG_IO, SG_WARN, "Permanent ZLib error:" << zlibStream.msg);
state = TarExtractorPrivate::BAD_DATA;
return;
} }
return;
}
if (strncmp(header.magic, TMAGIC, TMAGLEN) != 0) { writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - zlibStream.avail_out;
SG_LOG(SG_IO, SG_WARN, "Untar: magic is wrong"); if (writtenSize > 0) {
state = BAD_ARCHIVE; processBytes((const char*)zlibOutput, writtenSize);
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, "unsafe tar path, skipping::" << tarPath);
skipCurrentEntry = true;
}
auto result = filterPath(tarPath);
if (result == ArchiveExtractor::Stop) {
setState(FILTER_STOPPED);
return;
} else if (result == ArchiveExtractor::Skipped) {
skipCurrentEntry = true;
}
SGPath p = extractRootPath() / tarPath;
if (header.typeflag == DIRTYPE) {
if (!skipCurrentEntry) {
Dir dir(p);
dir.create(0755);
} }
setState(READING_HEADER);
} else if ((header.typeflag == REGTYPE) || (header.typeflag == AREGTYPE)) {
currentFileSize = ::strtol(header.size, NULL, 8);
bytesRemaining = currentFileSize;
if (!skipCurrentEntry) {
currentFile.reset(new SGBinaryFile(p));
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;
}
}
void processBytes(const char* bytes, size_t count) if (result == Z_STREAM_END) {
{
if ((state >= ERROR_STATE) || (state == END_OF_ARCHIVE)) {
return;
}
size_t curBytes = std::min(bytesRemaining, count);
if (state == READING_FILE) {
if (currentFile) {
currentFile->write(bytes, curBytes);
}
bytesRemaining -= curBytes;
} else if ((state == READING_HEADER) || (state == PRE_END_OF_ARCHVE) || (state == END_OF_ARCHIVE)) {
memcpy(headerPtr, bytes, curBytes);
bytesRemaining -= curBytes;
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();
if (count > curBytes) {
// recurse with unprocessed bytes
processBytes(bytes + curBytes, count - curBytes);
}
}
bool headerIsAllZeros() const
{
char* headerAsChar = (char*) &header;
for (size_t i=0; i < offsetof(UstarHeaderBlock, magic); ++i) {
if (*headerAsChar++ != 0) {
return false;
}
}
return true;
}
// https://www.ibm.com/support/knowledgecenter/en/SSLTBW_2.3.0/com.ibm.zos.v2r3.bpxa500/paxex.htm#paxex
void parsePAXAttributes(bool areGlobal)
{
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; break;
} }
} while ((zlibStream.avail_in > 0) || (writtenSize > 0));
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;
}
} }
void processPAXAttribute(bool isGlobalAttr, const std::string& attrName, const std::string& data) private:
{ z_stream zlibStream;
if (!isGlobalAttr && (attrName == "path")) { uint8_t* zlibOutput;
// data is UTF-8 encoded path name bool haveInitedZLib = false;
paxPathName = data;
}
}
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
#if 1 || defined(HAVE_XZ)
#include <lzma.h>
class XZTarExtractor : public TarExtractorPrivate
{
public:
XZTarExtractor(ArchiveExtractor* outer) : TarExtractorPrivate(outer)
{
_xzStream = LZMA_STREAM_INIT;
_outputBuffer = (uint8_t*)malloc(ZLIB_DECOMPRESS_BUFFER_SIZE);
auto ret = lzma_stream_decoder(&_xzStream, UINT64_MAX, LZMA_TELL_ANY_CHECK);
if (ret != LZMA_OK) {
setState(BAD_ARCHIVE);
return;
}
}
~XZTarExtractor()
{
free(_outputBuffer);
}
void extractBytes(const uint8_t* bytes, size_t count) override
{
lzma_action action = LZMA_RUN;
_xzStream.next_in = bytes;
_xzStream.avail_in = count;
size_t writtenSize;
do {
_xzStream.avail_out = ZLIB_DECOMPRESS_BUFFER_SIZE;
_xzStream.next_out = _outputBuffer;
const auto ret = lzma_code(&_xzStream, action);
writtenSize = ZLIB_DECOMPRESS_BUFFER_SIZE - _xzStream.avail_out;
if (writtenSize > 0) {
processBytes((const char*)_outputBuffer, writtenSize);
}
if (ret == LZMA_GET_CHECK) {
//
} else if (ret == LZMA_STREAM_END) {
setState(END_OF_ARCHIVE);
break;
} else if (ret != LZMA_OK) {
setState(BAD_ARCHIVE);
break;
}
} while ((_xzStream.avail_in > 0) || (writtenSize > 0));
}
void flush() override
{
const auto ret = lzma_code(&_xzStream, LZMA_FINISH);
if (ret != LZMA_STREAM_END) {
setState(BAD_ARCHIVE);
}
}
private:
lzma_stream _xzStream;
uint8_t* _outputBuffer = nullptr;
};
#endif
///////////////////////////////////////////////////////////////////////////////
extern "C" { extern "C" {
void fill_memory_filefunc(zlib_filefunc_def*); void fill_memory_filefunc(zlib_filefunc_def*);
} }
@@ -637,17 +659,19 @@ void ArchiveExtractor::extractBytes(const uint8_t* bytes, size_t count)
if (r == TarData) { if (r == TarData) {
d.reset(new TarExtractorPrivate(this)); d.reset(new TarExtractorPrivate(this));
} } else if (r == GZData) {
else if (r == ZipData) { d.reset(new GZTarExtractor(this));
d.reset(new ZipExtractorPrivate(this)); } else if (r == XZData) {
} d.reset(new XZTarExtractor(this));
else { } else if (r == ZipData) {
SG_LOG(SG_IO, SG_WARN, "Invalid archive type"); d.reset(new ZipExtractorPrivate(this));
} else {
SG_LOG(SG_IO, SG_WARN, "Invalid archive type");
_invalidDataType = true; _invalidDataType = true;
return; return;
} }
// if hit here, we created the extractor. Feed the prefbuffer // if hit here, we created the extractor. Feed the prefbuffer
// bytes through it // bytes through it
d->extractBytes((uint8_t*) _prebuffer.data(), _prebuffer.size()); d->extractBytes((uint8_t*) _prebuffer.data(), _prebuffer.size());
_prebuffer.clear(); _prebuffer.clear();
@@ -699,9 +723,18 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::determineType(const uint8_t*
return ZipData; return ZipData;
} }
auto r = isTarData(bytes, count); if (count < 6) {
if ((r == TarData) || (r == InsufficientData)) return InsufficientData;
return r; }
const uint8_t XZ_HEADER[6] = {0xFD, '7', 'z', 'X', 'Z', 0x00};
if (memcmp(bytes, XZ_HEADER, 6) == 0) {
return XZData;
}
auto r = isTarData(bytes, count);
if ((r == TarData) || (r == InsufficientData) || (r == GZData))
return r;
return Invalid; return Invalid;
} }
@@ -714,6 +747,8 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* byt
} }
UstarHeaderBlock* header = 0; UstarHeaderBlock* header = 0;
DetermineResult result = InsufficientData;
if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) { if ((bytes[0] == 0x1f) && (bytes[1] == 0x8b)) {
// GZIP identification bytes // GZIP identification bytes
z_stream z; z_stream z;
@@ -731,11 +766,11 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* byt
return Invalid; return Invalid;
} }
int result = inflate(&z, Z_SYNC_FLUSH); int zResult = inflate(&z, Z_SYNC_FLUSH);
if ((result == Z_OK) || (result == Z_STREAM_END)) { if ((zResult == Z_OK) || (zResult == Z_STREAM_END)) {
// all good // all good
} else { } else {
SG_LOG(SG_IO, SG_WARN, "isTarData: Zlib inflate failed:" << result); SG_LOG(SG_IO, SG_WARN, "isTarData: Zlib inflate failed:" << zResult);
inflateEnd(&z); inflateEnd(&z);
return Invalid; // not tar data return Invalid; // not tar data
} }
@@ -748,6 +783,7 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* byt
header = reinterpret_cast<UstarHeaderBlock*>(zlibOutput); header = reinterpret_cast<UstarHeaderBlock*>(zlibOutput);
inflateEnd(&z); inflateEnd(&z);
result = GZData;
} else { } else {
// uncompressed tar // uncompressed tar
if (count < TAR_HEADER_BLOCK_SIZE) { if (count < TAR_HEADER_BLOCK_SIZE) {
@@ -755,13 +791,14 @@ ArchiveExtractor::DetermineResult ArchiveExtractor::isTarData(const uint8_t* byt
} }
header = (UstarHeaderBlock*) bytes; header = (UstarHeaderBlock*) bytes;
result = TarData;
} }
if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) { if (strncmp(header->magic, TMAGIC, TMAGLEN) != 0) {
return Invalid; return Invalid;
} }
return TarData; return result;
} }
void ArchiveExtractor::extractLocalFile(const SGPath& archiveFile) void ArchiveExtractor::extractLocalFile(const SGPath& archiveFile)
+9 -8
View File
@@ -36,15 +36,16 @@ public:
ArchiveExtractor(const SGPath& rootPath); ArchiveExtractor(const SGPath& rootPath);
virtual ~ArchiveExtractor(); virtual ~ArchiveExtractor();
enum DetermineResult enum DetermineResult {
{ Invalid,
Invalid, InsufficientData,
InsufficientData, TarData,
TarData, ZipData,
ZipData GZData, // Gzipped-tar
}; XZData // XZ (aka LZMA) tar
};
static DetermineResult determineType(const uint8_t* bytes, size_t count); static DetermineResult determineType(const uint8_t* bytes, size_t count);
/** /**
* @brief API to extract a local zip or tar.gz * @brief API to extract a local zip or tar.gz
+4 -2
View File
@@ -619,6 +619,8 @@ void Catalog::setUserEnabled(bool b)
void Catalog::processAlternate(SGPropertyNode_ptr alt) void Catalog::processAlternate(SGPropertyNode_ptr alt)
{ {
m_refreshRequest.reset();
std::string altId; std::string altId;
const auto idPtr = alt->getStringValue("id"); const auto idPtr = alt->getStringValue("id");
if (idPtr) { if (idPtr) {
@@ -694,8 +696,8 @@ void Catalog::processAlternate(SGPropertyNode_ptr alt)
SG_LOG(SG_GENERAL, SG_INFO, "Migrating catalog " << id() << " to new URL:" << altUrl); SG_LOG(SG_GENERAL, SG_INFO, "Migrating catalog " << id() << " to new URL:" << altUrl);
setUrl(altUrl); setUrl(altUrl);
Downloader* dl = new Downloader(this, altUrl); m_refreshRequest = new Downloader(this, altUrl);
root()->makeHTTPRequest(dl); root()->makeHTTPRequest(m_refreshRequest);
} }
int Catalog::markPackagesForInstallation(const string_list &packageIds) { int Catalog::markPackagesForInstallation(const string_list &packageIds) {
+3 -3
View File
@@ -166,7 +166,8 @@ int parseTest()
{ {
SGPath rootPath = simgear::Dir::current().path(); SGPath rootPath = simgear::Dir::current().path();
rootPath.append("testRoot"); rootPath.append("testRoot");
pkg::Root* root = new pkg::Root(rootPath, "8.1.12");
pkg::RootRef root(new pkg::Root(rootPath, "8.1.2"));
root->setLocale("de"); root->setLocale("de");
pkg::CatalogRef cat = pkg::Catalog::createFromPath(root, SGPath(SRC_DIR "/catalogTest1")); pkg::CatalogRef cat = pkg::Catalog::createFromPath(root, SGPath(SRC_DIR "/catalogTest1"));
@@ -344,7 +345,6 @@ int parseTest()
SG_CHECK_EQUAL(urls.size(), 3); SG_CHECK_EQUAL(urls.size(), 3);
SG_CHECK_EQUAL(urls.at(1), "http://localhost:2000/mirrorB/b737.tar.gz"); SG_CHECK_EQUAL(urls.at(1), "http://localhost:2000/mirrorB/b737.tar.gz");
delete root;
return EXIT_SUCCESS; return EXIT_SUCCESS;
} }
@@ -953,7 +953,7 @@ int parseInvalidTest()
{ {
SGPath rootPath = simgear::Dir::current().path(); SGPath rootPath = simgear::Dir::current().path();
rootPath.append("testRoot"); rootPath.append("testRoot");
pkg::Root* root = new pkg::Root(rootPath, "8.1.12"); pkg::RootRef root(new pkg::Root(rootPath, "8.1.12"));
pkg::CatalogRef cat = pkg::Catalog::createFromPath(root, SGPath(SRC_DIR "/catalogTestInvalid")); pkg::CatalogRef cat = pkg::Catalog::createFromPath(root, SGPath(SRC_DIR "/catalogTestInvalid"));
SG_VERIFY(cat.valid()); SG_VERIFY(cat.valid());
+6 -2
View File
@@ -97,11 +97,13 @@ SGMaterial::SGMaterial( const SGReaderWriterOptions* options,
const SGPropertyNode *props, const SGPropertyNode *props,
SGPropertyNode *prop_root, SGPropertyNode *prop_root,
AreaList *a, AreaList *a,
SGSharedPtr<const SGCondition> c) SGSharedPtr<const SGCondition> c,
const std::string& n)
{ {
init(); init();
areas = a; areas = a;
condition = c; condition = c;
region = n;
read_properties( options, props, prop_root ); read_properties( options, props, prop_root );
buildEffectProperties(options); buildEffectProperties(options);
} }
@@ -110,12 +112,14 @@ SGMaterial::SGMaterial( const osgDB::Options* options,
const SGPropertyNode *props, const SGPropertyNode *props,
SGPropertyNode *prop_root, SGPropertyNode *prop_root,
AreaList *a, AreaList *a,
SGSharedPtr<const SGCondition> c) SGSharedPtr<const SGCondition> c,
const std::string& n)
{ {
osg::ref_ptr<SGReaderWriterOptions> opt; osg::ref_ptr<SGReaderWriterOptions> opt;
opt = SGReaderWriterOptions::copyOrCreate(options); opt = SGReaderWriterOptions::copyOrCreate(options);
areas = a; areas = a;
condition = c; condition = c;
region = n;
init(); init();
read_properties(opt.get(), props, prop_root); read_properties(opt.get(), props, prop_root);
buildEffectProperties(opt.get()); buildEffectProperties(opt.get());
+12 -2
View File
@@ -97,14 +97,16 @@ public:
const SGPropertyNode *props, const SGPropertyNode *props,
SGPropertyNode *prop_root, SGPropertyNode *prop_root,
AreaList *a, AreaList *a,
SGSharedPtr<const SGCondition> c); SGSharedPtr<const SGCondition> c,
const std::string& n);
SGMaterial(const simgear::SGReaderWriterOptions*, SGMaterial(const simgear::SGReaderWriterOptions*,
const SGPropertyNode *props, const SGPropertyNode *props,
SGPropertyNode *prop_root, SGPropertyNode *prop_root,
AreaList *a, AreaList *a,
SGSharedPtr<const SGCondition> c); SGSharedPtr<const SGCondition> c,
const std::string& n);
/** /**
* Destructor. * Destructor.
@@ -123,6 +125,11 @@ public:
simgear::Effect* get_one_effect(int texIndex); simgear::Effect* get_one_effect(int texIndex);
simgear::Effect* get_effect(); simgear::Effect* get_effect();
/**
* Get the region Name.
*/
const std::string get_region_name() const { return region; }
/** /**
* Get the Effect Name. * Get the Effect Name.
*/ */
@@ -487,6 +494,9 @@ private:
SGVec4f ambient, diffuse, specular, emission; SGVec4f ambient, diffuse, specular, emission;
double shininess; double shininess;
// region of this material
std::string region;
// effect for this material // effect for this material
std::string effect; std::string effect;
+2 -1
View File
@@ -128,12 +128,13 @@ bool SGMaterialLib::load( const SGPath &fg_root, const SGPath& mpath,
// Now build all the materials for this set of areas and conditions // Now build all the materials for this set of areas and conditions
const std::string region = node->getStringValue("name");
const simgear::PropertyList materials = node->getChildren("material"); const simgear::PropertyList materials = node->getChildren("material");
simgear::PropertyList::const_iterator materials_iter = materials.begin(); simgear::PropertyList::const_iterator materials_iter = materials.begin();
for (; materials_iter != materials.end(); materials_iter++) { for (; materials_iter != materials.end(); materials_iter++) {
const SGPropertyNode *node = materials_iter->get(); const SGPropertyNode *node = materials_iter->get();
SGSharedPtr<SGMaterial> m = SGSharedPtr<SGMaterial> m =
new SGMaterial(options.get(), node, prop_root, arealist, condition); new SGMaterial(options.get(), node, prop_root, arealist, condition, region);
std::vector<SGPropertyNode_ptr>names = node->getChildren("name"); std::vector<SGPropertyNode_ptr>names = node->getChildren("name");
for ( unsigned int j = 0; j < names.size(); j++ ) { for ( unsigned int j = 0; j < names.size(); j++ ) {
+1 -1
View File
@@ -80,7 +80,7 @@ void SGText::UpdateCallback::operator()(osg::Node * node, osg::NodeVisitor *nv )
// be lazy and set the text only if the property has changed. // be lazy and set the text only if the property has changed.
// update() computes the glyph representation which looks // update() computes the glyph representation which looks
// more expensive than a the above string compare. // more expensive than a the above string compare.
text->setText( buf ); text->setText( buf, osgText::String::ENCODING_UTF8 );
text->update(); text->update();
} }
traverse( node, nv ); traverse( node, nv );
+233 -71
View File
@@ -36,10 +36,10 @@
#include <osgParticle/MultiSegmentPlacer> #include <osgParticle/MultiSegmentPlacer>
#include <osgParticle/SectorPlacer> #include <osgParticle/SectorPlacer>
#include <osgParticle/ConstantRateCounter> #include <osgParticle/ConstantRateCounter>
#include <osgParticle/ParticleSystemUpdater>
#include <osgParticle/ParticleSystem> #include <osgParticle/ParticleSystem>
#include <osgParticle/FluidProgram> #include <osgParticle/FluidProgram>
#include <osgUtil/CullVisitor>
#include <osg/Geode> #include <osg/Geode>
#include <osg/Group> #include <osg/Group>
#include <osg/MatrixTransform> #include <osg/MatrixTransform>
@@ -48,67 +48,250 @@
#include <simgear/scene/model/animation.hxx> #include <simgear/scene/model/animation.hxx>
using ParticleSystemRef = osg::ref_ptr<osgParticle::ParticleSystem>;
namespace simgear namespace simgear
{ {
class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback class ParticlesGlobalManager::ParticlesGlobalManagerPrivate : public osg::NodeCallback
{ {
public: public:
ParticlesGlobalManagerPrivate() : _updater(new osgParticle::ParticleSystemUpdater), ParticlesGlobalManagerPrivate();
_commonGeode(new osg::Geode)
void operator()(osg::Node* node, osg::NodeVisitor* nv) override;
// only call this with the lock held!
osg::Group* internalGetCommonRoot();
void updateParticleSystemsFromCullCallback(int currentFrameNumber, osg::NodeVisitor* nv);
void addParticleSystem(osgParticle::ParticleSystem* ps, const osg::ref_ptr<osg::Group>& frame);
void registerNewLocalParticleSystem(osg::Node* node, ParticleSystemRef ps);
void registerNewWorldParticleSystem(osg::Node* node, ParticleSystemRef ps, osg::Group* frame);
std::mutex _lock;
bool _frozen = false;
double _simulationDt = 0.0;
osg::ref_ptr<osg::Group> _commonRoot;
osg::ref_ptr<osg::Geode> _commonGeode;
osg::Vec3 _wind;
bool _enabled = true;
osg::Vec3 _gravity;
// osg::Vec3 _localWind;
SGGeod _currentPosition;
SGConstPropertyNode_ptr _enabledNode;
using ParticleSystemWeakRef = osg::observer_ptr<osgParticle::ParticleSystem>;
using ParticleSystemsWeakRefVec = std::vector<ParticleSystemWeakRef>;
using ParticleSystemsStrongRefVec = std::vector<ParticleSystemRef>;
ParticleSystemsWeakRefVec _systems;
osg::ref_ptr<ParticlesGlobalManager::UpdaterCallback> _cullCallback;
using GroupRefVec = std::vector<osg::ref_ptr<osg::Group>>;
GroupRefVec _newWorldParticles;
};
/**
@brief this class replaces the need to use osgParticle::ParticleSystemUpdater, which has some
thread-safety and ownership complications in our us case
*/
class ParticlesGlobalManager::UpdaterCallback : public osg::NodeCallback
{
public:
UpdaterCallback(ParticlesGlobalManagerPrivate* p) : _manager(p)
{ {
} }
void operator()(osg::Node* node, osg::NodeVisitor* nv) override void operator()(osg::Node* node, osg::NodeVisitor* nv) override
{ {
std::lock_guard<std::mutex> g(_lock); osgUtil::CullVisitor* cv = dynamic_cast<osgUtil::CullVisitor*>(nv);
_enabled = !_enabledNode || _enabledNode->getBoolValue(); if (cv && nv->getFrameStamp()) {
if (_frameNumber < nv->getFrameStamp()->getFrameNumber()) {
if (!_enabled) _frameNumber = nv->getFrameStamp()->getFrameNumber();
return; _manager->updateParticleSystemsFromCullCallback(_frameNumber, nv);
}
const auto q = SGQuatd::fromLonLatDeg(_longitudeNode->getFloatValue(), _latitudeNode->getFloatValue());
osg::Matrix om(toOsg(q));
osg::Vec3 v(0, 0, 9.81);
_gravity = om.preMult(v);
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
// const osg::Vec3& zUpWind = _wind;
// osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
// _localWind = om.preMult(w);
}
// only call this with the lock held!
osg::Group* internalGetCommonRoot()
{
if (!_commonRoot.valid()) {
SG_LOG(SG_PARTICLES, SG_DEBUG, "Particle common root called.");
_commonRoot = new osg::Group;
_commonRoot->setName("common particle system root");
_commonGeode->setName("common particle system geode");
_commonRoot->addChild(_commonGeode);
_commonRoot->addChild(_updater);
_commonRoot->setNodeMask(~simgear::MODELLIGHT_BIT);
} }
return _commonRoot.get();
// note, callback is responsible for scenegraph traversal so
// they must call traverse(node,nv) to ensure that the
// scene graph subtree (and associated callbacks) are traversed.
traverse(node, nv);
} }
std::mutex _lock; unsigned int _frameNumber = 0;
bool _frozen = false; ParticlesGlobalManagerPrivate* _manager;
osg::ref_ptr<osgParticle::ParticleSystemUpdater> _updater;
osg::ref_ptr<osg::Group> _commonRoot;
osg::ref_ptr<osg::Geode> _commonGeode;
osg::Vec3 _wind;
bool _globalCallbackRegistered = false;
bool _enabled = true;
osg::Vec3 _gravity;
// osg::Vec3 _localWind;
SGConstPropertyNode_ptr _enabledNode;
SGConstPropertyNode_ptr _longitudeNode, _latitudeNode;
}; };
/**
single-shot node callback, used to register a particle system with the global manager. Once run, removes
itself. We used this to avoid updating a particle system until the load is complete and merged into the
main scene.
*/
class ParticlesGlobalManager::RegistrationCallback : public osg::NodeCallback
{
public:
void operator()(osg::Node* node, osg::NodeVisitor* nv) override
{
auto d = ParticlesGlobalManager::instance()->d;
d->addParticleSystem(_system, _frame);
node->removeUpdateCallback(this); // suicide
}
ParticleSystemRef _system;
osg::ref_ptr<osg::Group> _frame;
};
ParticlesGlobalManager::ParticlesGlobalManagerPrivate::ParticlesGlobalManagerPrivate() : _commonGeode(new osg::Geode),
_cullCallback(new UpdaterCallback(this))
{
// callbacks are registered in initFromMainThread : depending on timing,
// this constructor might be called from an osgDB thread
}
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::addParticleSystem(osgParticle::ParticleSystem* ps, const osg::ref_ptr<osg::Group>& frame)
{
std::lock_guard<std::mutex> g(_lock);
_systems.push_back(ps);
// we're inside an update callback here, so better not modify the
// scene structure; defer it to later
if (frame.get()) {
_newWorldParticles.push_back(frame);
}
}
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::registerNewLocalParticleSystem(osg::Node* node, ParticleSystemRef ps)
{
auto cb = new RegistrationCallback;
cb->_system = ps;
node->addUpdateCallback(cb);
}
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::registerNewWorldParticleSystem(osg::Node* node, ParticleSystemRef ps, osg::Group* frame)
{
auto cb = new RegistrationCallback;
cb->_system = ps;
cb->_frame = frame;
node->addUpdateCallback(cb);
}
// this is called from the main thread during scenery init
// after this, we beign updarting particle systems
void ParticlesGlobalManager::initFromMainThread()
{
std::lock_guard<std::mutex> g(d->_lock);
d->internalGetCommonRoot();
d->_commonRoot->addUpdateCallback(d.get());
d->_commonRoot->setCullingActive(false);
d->_commonRoot->addCullCallback(d->_cullCallback);
}
void ParticlesGlobalManager::update(double dt, const SGGeod& pos)
{
std::lock_guard<std::mutex> g(d->_lock);
d->_simulationDt = dt;
d->_currentPosition = pos;
for (auto f : d->_newWorldParticles) {
d->internalGetCommonRoot()->addChild(f);
}
d->_newWorldParticles.clear();
}
// this is called from the main thread, since it's an update callback
// lock any state used by updateParticleSystemsFromCullCallback, which
// runs during culling, potentialy on a different thread
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::operator()(osg::Node* node, osg::NodeVisitor* nv)
{
std::lock_guard<std::mutex> g(_lock);
_enabled = !_enabledNode || _enabledNode->getBoolValue();
if (!_enabled)
return;
const auto q = SGQuatd::fromLonLat(_currentPosition);
osg::Matrix om(toOsg(q));
osg::Vec3 v(0, 0, 9.81);
_gravity = om.preMult(v);
// NOTE: THIS WIND COMPUTATION DOESN'T SEEM TO AFFECT PARTICLES
// const osg::Vec3& zUpWind = _wind;
// osg::Vec3 w(zUpWind.y(), zUpWind.x(), -zUpWind.z());
// _localWind = om.preMult(w);
// while we have the lock, remove all expired systems
auto firstInvalid = std::remove_if(_systems.begin(), _systems.end(), [](const ParticleSystemWeakRef& weak) {
return !weak.valid();
});
_systems.erase(firstInvalid, _systems.end());
}
// only call this with the lock held!
osg::Group* ParticlesGlobalManager::ParticlesGlobalManagerPrivate::internalGetCommonRoot()
{
if (!_commonRoot.valid()) {
_commonRoot = new osg::Group;
_commonRoot->setName("common particle system root");
_commonGeode->setName("common particle system geode");
_commonRoot->addChild(_commonGeode);
_commonRoot->setNodeMask(~simgear::MODELLIGHT_BIT);
}
return _commonRoot.get();
}
void ParticlesGlobalManager::ParticlesGlobalManagerPrivate::updateParticleSystemsFromCullCallback(int frameNumber, osg::NodeVisitor* nv)
{
ParticleSystemsStrongRefVec activeSystems;
double dt = 0.0;
// begin locked section
{
std::lock_guard<std::mutex> g(_lock);
activeSystems.reserve(_systems.size());
for (const auto& psref : _systems) {
ParticleSystemRef owningRef;
if (!psref.lock(owningRef)) {
// pointed to system is gone, skip it
// we will clean these up in the update callback, don't
// worry about that here
continue;
}
// add to the list we will update now
activeSystems.push_back(owningRef);
}
dt = _simulationDt;
} // of locked section
// from here on, don't access class data; copy it all to local variables
// before this line. this is important so we're not holding _lock during
// culling, which might block osgDB threads for example.
if (dt <= 0.0) {
return;
}
for (const auto& ps : activeSystems) {
// code inside here is copied from osgParticle::ParticleSystemUpdate
osgParticle::ParticleSystem::ScopedWriteLock lock(*(ps->getReadWriteMutex()));
// We need to allow at least 2 frames difference, because the particle system's lastFrameNumber
// is updated in the draw thread which may not have completed yet.
if (!ps->isFrozen() &&
(!ps->getFreezeOnCull() || ((frameNumber - ps->getLastFrameNumber()) <= 2))) {
ps->update(dt, *nv);
}
// end of code copied from osgParticle::ParticleSystemUpdate
}
}
static std::mutex static_managerLock; static std::mutex static_managerLock;
static std::unique_ptr<ParticlesGlobalManager> static_instance; static std::unique_ptr<ParticlesGlobalManager> static_instance;
@@ -132,14 +315,6 @@ ParticlesGlobalManager::ParticlesGlobalManager() : d(new ParticlesGlobalManagerP
{ {
} }
ParticlesGlobalManager::~ParticlesGlobalManager()
{
if (d->_globalCallbackRegistered) {
// is this actually necessary? possibly not
d->_updater->setUpdateCallback(nullptr);
}
}
bool ParticlesGlobalManager::isEnabled() const bool ParticlesGlobalManager::isEnabled() const
{ {
std::lock_guard<std::mutex> g(d->_lock); std::lock_guard<std::mutex> g(d->_lock);
@@ -728,23 +903,10 @@ osg::ref_ptr<osg::Group> ParticlesGlobalManager::appendParticles(const SGPropert
emitter->setUpdateCallback(callback.get()); emitter->setUpdateCallback(callback.get());
} }
// touch shared data now (and not before) if (attach == "local") {
d->registerNewLocalParticleSystem(align, particleSys);
{ } else {
std::lock_guard<std::mutex> g(d->_lock); d->registerNewWorldParticleSystem(align, particleSys, callback()->particleFrame);
d->_updater->addParticleSystem(particleSys);
if (attach != "local") {
d->internalGetCommonRoot()->addChild(callback()->particleFrame);
}
if (!d->_globalCallbackRegistered) {
SG_LOG(SG_PARTICLES, SG_INFO, "Registering global particles callback");
d->_globalCallbackRegistered = true;
d->_longitudeNode = modelRoot->getNode("/position/longitude-deg", true);
d->_latitudeNode = modelRoot->getNode("/position/latitude-deg", true);
d->_updater->setUpdateCallback(d.get());
}
} }
return align; return align;
+9 -4
View File
@@ -41,7 +41,6 @@ class NodeVisitor;
namespace osgParticle namespace osgParticle
{ {
class ParticleSystem; class ParticleSystem;
class ParticleSystemUpdater;
} }
#include <simgear/scene/util/SGNodeMasks.hxx> #include <simgear/scene/util/SGNodeMasks.hxx>
@@ -151,7 +150,7 @@ protected:
class ParticlesGlobalManager class ParticlesGlobalManager
{ {
public: public:
~ParticlesGlobalManager(); ~ParticlesGlobalManager() = default;
static ParticlesGlobalManager* instance(); static ParticlesGlobalManager* instance();
static void clear(); static void clear();
@@ -161,14 +160,18 @@ public:
osg::ref_ptr<osg::Group> appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options); osg::ref_ptr<osg::Group> appendParticles(const SGPropertyNode* configNode, SGPropertyNode* modelRoot, const osgDB::Options* options);
osg::Group* getCommonRoot(); osg::Group* getCommonRoot();
osg::Geode* getCommonGeode(); osg::Geode* getCommonGeode();
osgParticle::ParticleSystemUpdater* getPSU(); void initFromMainThread();
void setFrozen(bool e); void setFrozen(bool e);
bool isFrozen() const; bool isFrozen() const;
/**
@brief update function: call from the main thread , outside of OSG calls.
*/
void update(double dt, const SGGeod& pos);
void setSwitchNode(const SGPropertyNode* n); void setSwitchNode(const SGPropertyNode* n);
/** /**
@@ -185,6 +188,8 @@ private:
ParticlesGlobalManager(); ParticlesGlobalManager();
class ParticlesGlobalManagerPrivate; class ParticlesGlobalManagerPrivate;
class UpdaterCallback;
class RegistrationCallback;
// because Private inherits NodeCallback, we need to own it // because Private inherits NodeCallback, we need to own it
// via an osg::ref_ptr // via an osg::ref_ptr
+173 -70
View File
@@ -109,13 +109,13 @@ bool hasWhitespace(string path)
class SyncItem class SyncItem
{ {
public: public:
enum Type enum Type {
{ Stop = 0, ///< special item indicating to stop the SVNThread
Stop = 0, ///< special item indicating to stop the SVNThread
Tile, Tile,
AirportData, AirportData,
SharedModels, SharedModels,
AIData AIData,
OSMTile ///< OSm2City per-Tile data
}; };
enum Status enum Status
@@ -165,13 +165,16 @@ public:
SGTimeStamp stamp; SGTimeStamp stamp;
bool busy = false; ///< is the slot working or idle bool busy = false; ///< is the slot working or idle
unsigned int pendingKBytes = 0; unsigned int pendingKBytes = 0;
unsigned int pendingExtractKBytes = 0;
unsigned int nextWarnTimeout = 0; unsigned int nextWarnTimeout = 0;
}; };
static const int SYNC_SLOT_TILES = 0; ///< Terrain and Objects sync static const int SYNC_SLOT_TILES = 0; ///< Terrain and Objects sync
static const int SYNC_SLOT_SHARED_DATA = 1; /// shared Models and Airport data static const int SYNC_SLOT_SHARED_DATA = 1; /// shared Models and Airport data
static const int SYNC_SLOT_AI_DATA = 2; /// AI traffic and models static const int SYNC_SLOT_AI_DATA = 2; /// AI traffic and models
static const unsigned int NUM_SYNC_SLOTS = 3; static const int SYNC_SLOT_OSM_TILE_DATA = 3;
static const unsigned int NUM_SYNC_SLOTS = 4;
/** /**
* @brief translate a sync item type into one of the available slots. * @brief translate a sync item type into one of the available slots.
@@ -186,7 +189,8 @@ static unsigned int syncSlotForType(SyncItem::Type ty)
return SYNC_SLOT_SHARED_DATA; return SYNC_SLOT_SHARED_DATA;
case SyncItem::AIData: case SyncItem::AIData:
return SYNC_SLOT_AI_DATA; return SYNC_SLOT_AI_DATA;
case SyncItem::OSMTile:
return SYNC_SLOT_OSM_TILE_DATA;
default: default:
return SYNC_SLOT_SHARED_DATA; return SYNC_SLOT_SHARED_DATA;
} }
@@ -194,18 +198,18 @@ static unsigned int syncSlotForType(SyncItem::Type ty)
struct TerrasyncThreadState struct TerrasyncThreadState
{ {
TerrasyncThreadState() : TerrasyncThreadState() : _busy(false),
_busy(false), _stalled(false),
_stalled(false), _hasServer(false),
_hasServer(false), _fail_count(0),
_fail_count(0), _updated_tile_count(0),
_updated_tile_count(0), _success_count(0),
_success_count(0), _consecutive_errors(0),
_consecutive_errors(0), _cache_hits(0),
_cache_hits(0), _transfer_rate(0),
_transfer_rate(0), _total_kb_downloaded(0),
_total_kb_downloaded(0), _totalKbPending(0),
_totalKbPending(0) _extractTotalKbPending(0)
{} {}
bool _busy; bool _busy;
@@ -220,6 +224,7 @@ struct TerrasyncThreadState
// kbytes, not bytes, because bytes might overflow 2^31 // kbytes, not bytes, because bytes might overflow 2^31
int _total_kb_downloaded; int _total_kb_downloaded;
unsigned int _totalKbPending; unsigned int _totalKbPending;
unsigned int _extractTotalKbPending;
}; };
/////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////////////////////////////////////////////
@@ -275,9 +280,10 @@ public:
SyncItem getNewTile() { return _freshTiles.pop_front();} SyncItem getNewTile() { return _freshTiles.pop_front();}
void setHTTPServer(const std::string& server) void setHTTPServer(const std::string& server, const std::string& osmServer)
{ {
_httpServer = stripPath(server); _httpServer = stripPath(server);
_osmCityServer = stripPath(osmServer);
_isAutomaticServer = (server == "automatic"); _isAutomaticServer = (server == "automatic");
} }
@@ -296,6 +302,11 @@ public:
_sceneryVersion = simgear::strutils::strip(sceneryVersion); _sceneryVersion = simgear::strutils::strip(sceneryVersion);
} }
void setOSMCityVersion(const std::string& osmCityVersion)
{
_osmCityService = osmCityVersion;
}
void setLocalDir(string dir) { _local_dir = stripPath(dir);} void setLocalDir(string dir) { _local_dir = stripPath(dir);}
string getLocalDir() { return _local_dir;} string getLocalDir() { return _local_dir;}
@@ -322,10 +333,12 @@ public:
void setCachePath(const SGPath &p) { _persistentCachePath = p; } void setCachePath(const SGPath &p) { _persistentCachePath = p; }
private: private:
void incrementCacheHits() std::string dnsSelectServerForService(const std::string& service);
{
std::lock_guard<std::mutex> g(_stateLock); void incrementCacheHits()
_state._cache_hits++; {
std::lock_guard<std::mutex> g(_stateLock);
_state._cache_hits++;
} }
virtual void run(); virtual void run();
@@ -334,9 +347,9 @@ public:
void runInternal(); void runInternal();
void updateSyncSlot(SyncSlot& slot); void updateSyncSlot(SyncSlot& slot);
void beginSyncAirports(SyncSlot& slot); bool beginSyncAirports(SyncSlot& slot);
void beginSyncTile(SyncSlot& slot); bool beginSyncTile(SyncSlot& slot);
void beginNormalSync(SyncSlot& slot); bool beginNormalSync(SyncSlot& slot);
void drainWaitingTiles(); void drainWaitingTiles();
@@ -363,6 +376,9 @@ public:
string _local_dir; string _local_dir;
SGPath _persistentCachePath; SGPath _persistentCachePath;
string _httpServer; string _httpServer;
string _osmCityServer;
string _osmCityService = "o2c";
bool _isAutomaticServer; bool _isAutomaticServer;
SGPath _installRoot; SGPath _installRoot;
string _sceneryVersion; string _sceneryVersion;
@@ -474,8 +490,19 @@ bool SGTerraSync::WorkerThread::findServer()
{ {
if ( false == _isAutomaticServer ) return true; if ( false == _isAutomaticServer ) return true;
_httpServer = dnsSelectServerForService(MakeQService(_protocol, _sceneryVersion));
if (!_osmCityService.empty()) {
_osmCityServer = dnsSelectServerForService(_osmCityService);
}
return !_httpServer.empty();
}
std::string SGTerraSync::WorkerThread::dnsSelectServerForService(const std::string& service)
{
DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(_dnsdn); DNS::NAPTRRequest * naptrRequest = new DNS::NAPTRRequest(_dnsdn);
naptrRequest->qservice = MakeQService(_protocol, _sceneryVersion); naptrRequest->qservice = service;
naptrRequest->qflags = "U"; naptrRequest->qflags = "U";
DNS::Request_ptr r(naptrRequest); DNS::Request_ptr r(naptrRequest);
@@ -483,14 +510,15 @@ bool SGTerraSync::WorkerThread::findServer()
DNS::Client dnsClient; DNS::Client dnsClient;
dnsClient.makeRequest(r); dnsClient.makeRequest(r);
SG_LOG(SG_TERRASYNC,SG_DEBUG,"DNS NAPTR query for '" << _dnsdn << "' '" << naptrRequest->qservice << "'" ); SG_LOG(SG_TERRASYNC,SG_DEBUG,"DNS NAPTR query for '" << _dnsdn << "' '" << naptrRequest->qservice << "'" );
while( !r->isComplete() && !r->isTimeout() ) while (!r->isComplete() && !r->isTimeout()) {
dnsClient.update(0); dnsClient.update(0);
}
if( naptrRequest->entries.empty() ) { if( naptrRequest->entries.empty() ) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Warning: no DNS entry found for '" << _dnsdn << "' '" << naptrRequest->qservice << "'" ); SG_LOG(SG_TERRASYNC, SG_ALERT, "Warning: no DNS entry found for '" << _dnsdn << "' '" << naptrRequest->qservice << "'" );
_httpServer = ""; return {};
return false;
} }
// walk through responses, they are ordered by 1. order and 2. preference // walk through responses, they are ordered by 1. order and 2. preference
// For now, only take entries with lowest order // For now, only take entries with lowest order
// TODO: try all available servers in the order given by preferenc and order // TODO: try all available servers in the order given by preferenc and order
@@ -498,38 +526,33 @@ bool SGTerraSync::WorkerThread::findServer()
// get all servers with this order and the same (for now only lowest preference) // get all servers with this order and the same (for now only lowest preference)
DNS::NAPTRRequest::NAPTR_list availableServers; DNS::NAPTRRequest::NAPTR_list availableServers;
for( DNS::NAPTRRequest::NAPTR_list::const_iterator it = naptrRequest->entries.begin(); for (const auto& entry : naptrRequest->entries) {
it != naptrRequest->entries.end(); if (entry->order != order)
++it ) {
if( (*it)->order != order )
continue; continue;
string regex = (*it)->regexp; const string regex = entry->regexp;
if( false == simgear::strutils::starts_with( (*it)->regexp, "!^.*$!" ) ) { if (false == simgear::strutils::starts_with(regex, "!^.*$!")) {
SG_LOG(SG_TERRASYNC,SG_WARN, "ignoring unsupported regexp: " << (*it)->regexp ); SG_LOG(SG_TERRASYNC, SG_WARN, "ignoring unsupported regexp: " << regex);
continue; continue;
} }
if( false == simgear::strutils::ends_with( (*it)->regexp, "!" ) ) { if (false == simgear::strutils::ends_with(regex, "!")) {
SG_LOG(SG_TERRASYNC,SG_WARN, "ignoring unsupported regexp: " << (*it)->regexp ); SG_LOG(SG_TERRASYNC, SG_WARN, "ignoring unsupported regexp: " << regex);
continue; continue;
} }
// always use first entry // always use first entry
if( availableServers.empty() || (*it)->preference == availableServers[0]->preference) { if (availableServers.empty() || entry->preference == availableServers[0]->preference) {
SG_LOG(SG_TERRASYNC,SG_DEBUG, "available server regexp: " << (*it)->regexp ); SG_LOG(SG_TERRASYNC, SG_DEBUG, "available server regexp: " << regex);
availableServers.push_back( *it ); availableServers.push_back(entry);
} }
} }
// now pick a random entry from the available servers // now pick a random entry from the available servers
DNS::NAPTRRequest::NAPTR_list::size_type idx = sg_random() * availableServers.size(); auto idx = static_cast<int>(sg_random() * availableServers.size());
_httpServer = availableServers[idx]->regexp; const auto server = availableServers.at(idx)->regexp;
_httpServer = _httpServer.substr( 6, _httpServer.length()-7 ); // strip search pattern and separators SG_LOG(SG_TERRASYNC, SG_INFO, "picking entry # " << idx << ", server is " << server.substr(6, server.length() - 7););
return server.substr(6, server.length() - 7);
SG_LOG(SG_TERRASYNC,SG_INFO, "picking entry # " << idx << ", server is " << _httpServer );
return true;
} }
void SGTerraSync::WorkerThread::run() void SGTerraSync::WorkerThread::run()
@@ -562,6 +585,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
#endif #endif
// convert bytes to kbytes here // convert bytes to kbytes here
slot.pendingKBytes = (slot.repository->bytesToDownload() >> 10); slot.pendingKBytes = (slot.repository->bytesToDownload() >> 10);
slot.pendingExtractKBytes = (slot.repository->bytesToExtract() >> 10);
return; // easy, still working return; // easy, still working
} }
@@ -569,6 +593,9 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
HTTPRepository::ResultCode res = slot.repository->failure(); HTTPRepository::ResultCode res = slot.repository->failure();
if (res == HTTPRepository::REPO_ERROR_NOT_FOUND) { if (res == HTTPRepository::REPO_ERROR_NOT_FOUND) {
// not founds should never happen any more (unless the server-
// side data is incorrect), since we now check top-down that
// a 1x1 dir exists or not.
notFound(slot.currentItem); notFound(slot.currentItem);
} else if (res != HTTPRepository::REPO_NO_ERROR) { } else if (res != HTTPRepository::REPO_NO_ERROR) {
fail(slot.currentItem); fail(slot.currentItem);
@@ -591,6 +618,7 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
slot.busy = false; slot.busy = false;
slot.repository.reset(); slot.repository.reset();
slot.pendingKBytes = 0; slot.pendingKBytes = 0;
slot.pendingExtractKBytes = 0;
slot.currentItem = {}; slot.currentItem = {};
} }
@@ -604,12 +632,23 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
slot.isNewDirectory = !path.exists(); slot.isNewDirectory = !path.exists();
const auto type = slot.currentItem._type; const auto type = slot.currentItem._type;
bool ok = false;
if (type == SyncItem::AirportData) { if (type == SyncItem::AirportData) {
beginSyncAirports(slot); ok = beginSyncAirports(slot);
} else if (type == SyncItem::OSMTile) {
ok = beginSyncTile(slot);
} else if (type == SyncItem::Tile) { } else if (type == SyncItem::Tile) {
beginSyncTile(slot); ok = beginSyncTile(slot);
} else { } else {
beginNormalSync(slot); ok = beginNormalSync(slot);
}
if (!ok) {
SG_LOG(SG_TERRASYNC, SG_INFO, "sync of " << slot.currentItem._dir << " failed to start");
fail(slot.currentItem);
slot.busy = false;
slot.repository.reset();
return;
} }
try { try {
@@ -626,17 +665,17 @@ void SGTerraSync::WorkerThread::updateSyncSlot(SyncSlot &slot)
slot.nextWarnTimeout = 30 * 1000; slot.nextWarnTimeout = 30 * 1000;
slot.stamp.stamp(); slot.stamp.stamp();
slot.busy = true; slot.busy = true;
slot.pendingKBytes = slot.repository->bytesToDownload(); slot.pendingKBytes = slot.repository->bytesToDownload() >> 10;
slot.pendingExtractKBytes = slot.repository->bytesToExtract() >> 10;
SG_LOG(SG_TERRASYNC, SG_INFO, "sync of " << slot.repository->baseUrl() << " started, queue size is " << slot.queue.size()); SG_LOG(SG_TERRASYNC, SG_INFO, "sync of " << slot.repository->baseUrl() << ":" << slot.currentItem._dir << " started, queue size is " << slot.queue.size());
} }
} }
void SGTerraSync::WorkerThread::beginSyncAirports(SyncSlot& slot) bool SGTerraSync::WorkerThread::beginSyncAirports(SyncSlot& slot)
{ {
if (!slot.isNewDirectory) { if (!slot.isNewDirectory) {
beginNormalSync(slot); return beginNormalSync(slot);
return;
} }
SG_LOG(SG_TERRASYNC, SG_INFO, "doing Airports download via tarball"); SG_LOG(SG_TERRASYNC, SG_INFO, "doing Airports download via tarball");
@@ -656,9 +695,10 @@ void SGTerraSync::WorkerThread::beginSyncAirports(SyncSlot& slot)
}; };
slot.repository->setFilter(f); slot.repository->setFilter(f);
return true;
} }
void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot) bool SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
{ {
// avoid 404 requests by doing a sync which excludes all paths // avoid 404 requests by doing a sync which excludes all paths
// except our tile path. In the case of a missing 1x1 tile, we will // except our tile path. In the case of a missing 1x1 tile, we will
@@ -667,8 +707,7 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
auto comps = strutils::split(slot.currentItem._dir, "/"); auto comps = strutils::split(slot.currentItem._dir, "/");
if (comps.size() != 3) { if (comps.size() != 3) {
SG_LOG(SG_TERRASYNC, SG_ALERT, "Bad tile path:" << slot.currentItem._dir); SG_LOG(SG_TERRASYNC, SG_ALERT, "Bad tile path:" << slot.currentItem._dir);
beginNormalSync(slot); return false;
return;
} }
const auto tileCategory = comps.front(); const auto tileCategory = comps.front();
@@ -677,7 +716,17 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
const auto path = SGPath::fromUtf8(_local_dir) / tileCategory; const auto path = SGPath::fromUtf8(_local_dir) / tileCategory;
slot.repository.reset(new HTTPRepository(path, &_http)); slot.repository.reset(new HTTPRepository(path, &_http));
slot.repository->setBaseUrl(_httpServer + "/" + tileCategory);
if (slot.currentItem._type == SyncItem::OSMTile) {
if (_osmCityServer.empty()) {
SG_LOG(SG_TERRASYNC, SG_WARN, "No OSM2City server defined for:" << slot.currentItem._dir);
return false;
}
slot.repository->setBaseUrl(_osmCityServer + "/" + tileCategory);
} else {
slot.repository->setBaseUrl(_httpServer + "/" + tileCategory);
}
if (_installRoot.exists()) { if (_installRoot.exists()) {
SGPath p = _installRoot / tileCategory; SGPath p = _installRoot / tileCategory;
@@ -685,6 +734,7 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
} }
const auto dirPrefix = tenByTenDir + "/" + oneByOneDir; const auto dirPrefix = tenByTenDir + "/" + oneByOneDir;
using simgear::strutils::starts_with;
// filter callback to *only* sync the 1x1 dir we want, if it exists // filter callback to *only* sync the 1x1 dir we want, if it exists
// if doesn't, we'll simply stop, which is what we want // if doesn't, we'll simply stop, which is what we want
@@ -693,7 +743,10 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
if (item.directory.empty()) { if (item.directory.empty()) {
return item.filename == tenByTenDir; return item.filename == tenByTenDir;
} else if (item.directory == tenByTenDir) { } else if (item.directory == tenByTenDir) {
return item.filename == oneByOneDir; // allow 10x10 dir to contain 1x1_dir.tgz/.zip and still be accepted
// this does mean we'd also download oneByOneDir_foobar but that
// doesn't seem unreasonable either
return starts_with(item.filename, oneByOneDir);
} }
// allow arbitrary children below dirPrefix, including sub-dirs // allow arbitrary children below dirPrefix, including sub-dirs
@@ -706,9 +759,10 @@ void SGTerraSync::WorkerThread::beginSyncTile(SyncSlot& slot)
}; };
slot.repository->setFilter(f); slot.repository->setFilter(f);
return true;
} }
void SGTerraSync::WorkerThread::beginNormalSync(SyncSlot& slot) bool SGTerraSync::WorkerThread::beginNormalSync(SyncSlot& slot)
{ {
SGPath path(_local_dir); SGPath path(_local_dir);
path.append(slot.currentItem._dir); path.append(slot.currentItem._dir);
@@ -720,6 +774,8 @@ void SGTerraSync::WorkerThread::beginNormalSync(SyncSlot& slot)
p.append(slot.currentItem._dir); p.append(slot.currentItem._dir);
slot.repository->setInstalledCopyPath(p); slot.repository->setInstalledCopyPath(p);
} }
return true;
} }
void SGTerraSync::WorkerThread::runInternal() void SGTerraSync::WorkerThread::runInternal()
@@ -762,20 +818,24 @@ void SGTerraSync::WorkerThread::runInternal()
bool anySlotBusy = false; bool anySlotBusy = false;
unsigned int newPendingCount = 0; unsigned int newPendingCount = 0;
unsigned int newExtractCount = 0; // how much is left to extract
// update each sync slot in turn // update each sync slot in turn
for (unsigned int slot=0; slot < NUM_SYNC_SLOTS; ++slot) { for (unsigned int slot=0; slot < NUM_SYNC_SLOTS; ++slot) {
updateSyncSlot(_syncSlots[slot]); updateSyncSlot(_syncSlots[slot]);
newPendingCount += _syncSlots[slot].pendingKBytes; newPendingCount += _syncSlots[slot].pendingKBytes;
newExtractCount += _syncSlots[slot].pendingExtractKBytes;
anySlotBusy |= _syncSlots[slot].busy; anySlotBusy |= _syncSlots[slot].busy;
} }
{ {
std::lock_guard<std::mutex> g(_stateLock); std::lock_guard<std::mutex> g(_stateLock);
_state._totalKbPending = newPendingCount; // approximately atomic update _state._totalKbPending = newPendingCount; // approximately atomic update
_state._extractTotalKbPending = newExtractCount;
_state._busy = anySlotBusy; _state._busy = anySlotBusy;
} }
if (!anySlotBusy) { if (!anySlotBusy) {
// wait on the blocking deque here, otherwise we spin // wait on the blocking deque here, otherwise we spin
// the loop very fast, since _http::update with no connections // the loop very fast, since _http::update with no connections
@@ -811,12 +871,19 @@ void SGTerraSync::WorkerThread::fail(SyncItem failedItem)
{ {
std::lock_guard<std::mutex> g(_stateLock); std::lock_guard<std::mutex> g(_stateLock);
time_t now = time(0); time_t now = time(0);
_state._consecutive_errors++;
_state._fail_count++; if (_osmCityServer.empty() && (failedItem._type == SyncItem::OSMTile)) {
// don't count these as errors, otherwise normla sync will keep
// being abandoned
} else {
_state._consecutive_errors++;
_state._fail_count++;
}
failedItem._status = SyncItem::Failed; failedItem._status = SyncItem::Failed;
_freshTiles.push_back(failedItem); _freshTiles.push_back(failedItem);
// not we also end up here for partial syncs // not we also end up here for partial syncs
SG_LOG(SG_TERRASYNC,SG_INFO, SG_LOG(SG_TERRASYNC, SG_WARN,
"Failed to sync'" << failedItem._dir << "'"); "Failed to sync'" << failedItem._dir << "'");
_completedTiles[ failedItem._dir ] = now + UpdateInterval::FailedAttempt; _completedTiles[ failedItem._dir ] = now + UpdateInterval::FailedAttempt;
} }
@@ -828,6 +895,8 @@ void SGTerraSync::WorkerThread::notFound(SyncItem item)
// we don't spam the server with lookups for models that don't // we don't spam the server with lookups for models that don't
// exist // exist
SG_LOG(SG_TERRASYNC, SG_WARN, "Not found for: '" << item._dir << "'");
time_t now = time(0); time_t now = time(0);
item._status = SyncItem::NotFound; item._status = SyncItem::NotFound;
_freshTiles.push_back(item); _freshTiles.push_back(item);
@@ -1040,8 +1109,11 @@ void SGTerraSync::reinit()
if (enabled) if (enabled)
{ {
_availableNode->setBoolValue(true); _availableNode->setBoolValue(true);
_workerThread->setHTTPServer( _terraRoot->getStringValue("http-server","automatic") ); _workerThread->setHTTPServer(
_terraRoot->getStringValue("http-server", "automatic"),
_terraRoot->getStringValue("osm2city-server", ""));
_workerThread->setSceneryVersion( _terraRoot->getStringValue("scenery-version","ws20") ); _workerThread->setSceneryVersion( _terraRoot->getStringValue("scenery-version","ws20") );
_workerThread->setOSMCityVersion(_terraRoot->getStringValue("osm2city-version", "o2c"));
_workerThread->setProtocol( _terraRoot->getStringValue("protocol","") ); _workerThread->setProtocol( _terraRoot->getStringValue("protocol","") );
#if 1 #if 1
// leave it hardcoded for now, not sure about the security implications for now // leave it hardcoded for now, not sure about the security implications for now
@@ -1052,6 +1124,9 @@ void SGTerraSync::reinit()
SGPath sceneryRoot{_terraRoot->getStringValue("scenery-dir", "")}; SGPath sceneryRoot{_terraRoot->getStringValue("scenery-dir", "")};
_workerThread->setLocalDir(sceneryRoot.utf8Str()); _workerThread->setLocalDir(sceneryRoot.utf8Str());
if (sceneryRoot.exists()) {
writeWarningFile(sceneryRoot);
}
SGPath installPath(_terraRoot->getStringValue("installation-dir")); SGPath installPath(_terraRoot->getStringValue("installation-dir"));
_workerThread->setInstalledDir(installPath); _workerThread->setInstalledDir(installPath);
@@ -1100,6 +1175,7 @@ void SGTerraSync::bind()
_transferRateBytesSecNode = _terraRoot->getNode("transfer-rate-bytes-sec", true); _transferRateBytesSecNode = _terraRoot->getNode("transfer-rate-bytes-sec", true);
_pendingKbytesNode = _terraRoot->getNode("pending-kbytes", true); _pendingKbytesNode = _terraRoot->getNode("pending-kbytes", true);
_downloadedKBtesNode = _terraRoot->getNode("downloaded-kbytes", true); _downloadedKBtesNode = _terraRoot->getNode("downloaded-kbytes", true);
_extractPendingKbytesNode = _terraRoot->getNode("extract-pending-kbytes", true);
_enabledNode = _terraRoot->getNode("enabled", true); _enabledNode = _terraRoot->getNode("enabled", true);
_availableNode = _terraRoot->getNode("available", true); _availableNode = _terraRoot->getNode("available", true);
_maxErrorsNode = _terraRoot->getNode("max-errors", true); _maxErrorsNode = _terraRoot->getNode("max-errors", true);
@@ -1125,6 +1201,7 @@ void SGTerraSync::unbind()
_transferRateBytesSecNode.clear(); _transferRateBytesSecNode.clear();
_pendingKbytesNode.clear(); _pendingKbytesNode.clear();
_downloadedKBtesNode.clear(); _downloadedKBtesNode.clear();
_extractPendingKbytesNode.clear();
_enabledNode.clear(); _enabledNode.clear();
_availableNode.clear(); _availableNode.clear();
_maxErrorsNode.clear(); _maxErrorsNode.clear();
@@ -1161,6 +1238,7 @@ void SGTerraSync::update(double)
_transferRateBytesSecNode->setIntValue(copiedState._transfer_rate); _transferRateBytesSecNode->setIntValue(copiedState._transfer_rate);
_pendingKbytesNode->setIntValue(copiedState._totalKbPending); _pendingKbytesNode->setIntValue(copiedState._totalKbPending);
_downloadedKBtesNode->setIntValue(copiedState._total_kb_downloaded); _downloadedKBtesNode->setIntValue(copiedState._total_kb_downloaded);
_extractPendingKbytesNode->setIntValue(copiedState._extractTotalKbPending);
_stalledNode->setBoolValue(_workerThread->isStalled()); _stalledNode->setBoolValue(_workerThread->isStalled());
_activeNode->setBoolValue(worker_running); _activeNode->setBoolValue(worker_running);
@@ -1210,6 +1288,10 @@ string_list SGTerraSync::getSceneryPathSuffixes() const
return scenerySuffixes; return scenerySuffixes;
} }
bool isOSMSuffix(const std::string& suffix)
{
return (suffix == "Buildings") || (suffix == "Roads") || (suffix == "Pylons") || (suffix == "Details");
}
void SGTerraSync::syncAreaByPath(const std::string& aPath) void SGTerraSync::syncAreaByPath(const std::string& aPath)
{ {
@@ -1222,8 +1304,7 @@ void SGTerraSync::syncAreaByPath(const std::string& aPath)
if (_workerThread->isDirActive(dir)) { if (_workerThread->isDirActive(dir)) {
continue; continue;
} }
SyncItem w(dir, isOSMSuffix(suffix) ? SyncItem::OSMTile : SyncItem::Tile);
SyncItem w(dir, SyncItem::Tile);
_workerThread->request( w ); _workerThread->request( w );
} }
} }
@@ -1242,6 +1323,11 @@ bool SGTerraSync::isTileDirPending(const std::string& sceneryDir) const
} }
for (const auto& suffix : getSceneryPathSuffixes()) { for (const auto& suffix : getSceneryPathSuffixes()) {
// don't wait on OSM dirs, even if enabled
if (isOSMSuffix(suffix)) {
continue;
}
const auto s = suffix + "/" + sceneryDir; const auto s = suffix + "/" + sceneryDir;
if (_workerThread->isDirActive(s)) { if (_workerThread->isDirActive(s)) {
return true; return true;
@@ -1279,6 +1365,23 @@ void SGTerraSync::reposition()
// stub, remove // stub, remove
} }
void SGTerraSync::writeWarningFile(const SGPath& sceneryDir)
{
SGPath p = sceneryDir / "TerraSync-WARNING.txt";
if (p.exists())
return;
sg_ofstream os(p, std::ios::out | std::ios::trunc);
os << "This folder is managed by FlightGear's download system.\n";
os << "Any changes you make here or in sub-folders will be overwritten when TerraSync\n";
os << "downloads updates.\n";
os << "\n";
os << "To use custom scenery or data with FlightGear, put it in a differnet location\n";
os << "on your computer, then add the location using either the launcher 'Add-ons' page, or by\n";
os << "passing '--fg-scenery=<location>' on the command line.";
os << endl;
}
// Register the subsystem. // Register the subsystem.
SGSubsystemMgr::Registrant<SGTerraSync> registrantSGTerraSync( SGSubsystemMgr::Registrant<SGTerraSync> registrantSGTerraSync(
+4
View File
@@ -90,6 +90,9 @@ protected:
class WorkerThread; class WorkerThread;
private:
void writeWarningFile(const SGPath& sceneryDir);
private: private:
WorkerThread* _workerThread; WorkerThread* _workerThread;
SGPropertyNode_ptr _terraRoot; SGPropertyNode_ptr _terraRoot;
@@ -107,6 +110,7 @@ private:
SGPropertyNode_ptr _transferRateBytesSecNode; SGPropertyNode_ptr _transferRateBytesSecNode;
SGPropertyNode_ptr _pendingKbytesNode; SGPropertyNode_ptr _pendingKbytesNode;
SGPropertyNode_ptr _downloadedKBtesNode; SGPropertyNode_ptr _downloadedKBtesNode;
SGPropertyNode_ptr _extractPendingKbytesNode;
SGPropertyNode_ptr _maxErrorsNode; SGPropertyNode_ptr _maxErrorsNode;
// we manually bind+init TerraSync during early startup // we manually bind+init TerraSync during early startup
+110 -157
View File
@@ -19,66 +19,65 @@ static ThrowCallback static_callback;
// Implementation of sg_location class. // Implementation of sg_location class.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
sg_location::sg_location () sg_location::sg_location() noexcept
: _line(-1), : _line(-1),
_column(-1), _column(-1),
_byte(-1) _byte(-1)
{ {
_path[0] = '\0'; _path[0] = '\0';
} }
sg_location::sg_location (const std::string& path, int line, int column) sg_location::sg_location(const std::string& path, int line, int column) noexcept
: _line(line), : _line(line),
_column(column), _column(column),
_byte(-1) _byte(-1)
{ {
setPath(path.c_str()); setPath(path.c_str());
} }
sg_location::sg_location (const SGPath& path, int line, int column) sg_location::sg_location(const SGPath& path, int line, int column) noexcept
: _line(line), : _line(line),
_column(column), _column(column),
_byte(-1) _byte(-1)
{ {
setPath(path.utf8Str().c_str()); setPath(path.utf8Str().c_str());
} }
sg_location::sg_location (const char* path, int line, int column) sg_location::sg_location(const char* path, int line, int column) noexcept
: _line(line), : _line(line),
_column(column), _column(column),
_byte(-1) _byte(-1)
{ {
setPath(path); setPath(path);
} }
void sg_location::setPath(const char *path) { void sg_location::setPath(const char* path) noexcept
if (path) { {
strncpy(_path, path, max_path); if (path) {
_path[max_path -1] = '\0'; strncpy(_path, path, max_path);
} else { _path[max_path - 1] = '\0';
_path[0] = '\0'; } else {
} _path[0] = '\0';
}
} }
const char *sg_location::getPath() const { return _path; } const char* sg_location::getPath() const noexcept { return _path; }
int sg_location::getLine() const { return _line; } int sg_location::getLine() const noexcept { return _line; }
int int sg_location::getColumn() const noexcept
sg_location::getColumn () const
{ {
return _column; return _column;
} }
int int sg_location::getByte() const noexcept
sg_location::getByte () const
{ {
return _byte; return _byte;
} }
std::string std::string
sg_location::asString () const sg_location::asString() const noexcept
{ {
std::ostringstream out; std::ostringstream out;
if (_path[0]) { if (_path[0]) {
@@ -97,46 +96,48 @@ sg_location::asString () const
return out.str(); return out.str();
} }
bool sg_location::isValid() const { return strlen(_path) > 0; } bool sg_location::isValid() const noexcept { return strlen(_path) > 0; }
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Implementation of sg_throwable class. // Implementation of sg_throwable class.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
sg_throwable::sg_throwable () sg_throwable::sg_throwable() noexcept
{ {
_message[0] = '\0'; _message[0] = '\0';
_origin[0] = '\0'; _origin[0] = '\0';
} }
sg_throwable::sg_throwable(const char *message, const char *origin, sg_throwable::sg_throwable(const char* message, const char* origin,
const sg_location &loc) { const sg_location& loc, bool report) noexcept : _location(loc)
setMessage(message);
setOrigin(origin);
if (static_callback) {
static_callback(_message, _origin, loc);
}
}
sg_throwable::~sg_throwable ()
{ {
setMessage(message);
setOrigin(origin);
if (static_callback && report) {
static_callback(_message, _origin, loc);
}
} }
const char* const char*
sg_throwable::getMessage () const sg_throwable::getMessage() const noexcept
{ {
return _message; return _message;
} }
const std::string std::string
sg_throwable::getFormattedMessage () const sg_throwable::getFormattedMessage() const noexcept
{ {
return std::string(getMessage()); std::string ret = getMessage();
const std::string loc = getLocation().asString();
if (loc.length()) {
ret += "\n at ";
ret += loc;
}
return ret;
} }
void void sg_throwable::setMessage(const char* message) noexcept
sg_throwable::setMessage (const char* message)
{ {
strncpy(_message, message, MAX_TEXT_LEN); strncpy(_message, message, MAX_TEXT_LEN);
_message[MAX_TEXT_LEN - 1] = '\0'; _message[MAX_TEXT_LEN - 1] = '\0';
@@ -144,13 +145,12 @@ sg_throwable::setMessage (const char* message)
} }
const char* const char*
sg_throwable::getOrigin () const sg_throwable::getOrigin() const noexcept
{ {
return _origin; return _origin;
} }
void void sg_throwable::setOrigin(const char* origin) noexcept
sg_throwable::setOrigin (const char* origin)
{ {
if (origin) { if (origin) {
strncpy(_origin, origin, MAX_TEXT_LEN); strncpy(_origin, origin, MAX_TEXT_LEN);
@@ -170,27 +170,27 @@ const char* sg_throwable::what() const noexcept
} }
} }
sg_location sg_throwable::getLocation() const noexcept
{
return _location;
}
void sg_throwable::setLocation(const sg_location& location) noexcept
{
_location = location;
}
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Implementation of sg_error class. // Implementation of sg_error class.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
sg_error::sg_error () sg_error::sg_error(const char* message, const char* origin, bool report) noexcept
: sg_throwable () : sg_throwable(message, origin, {}, report)
{ {
} }
sg_error::sg_error (const char* message, const char *origin) sg_error::sg_error(const std::string& message, const std::string& origin, bool report) noexcept
: sg_throwable(message, origin) : sg_throwable(message.c_str(), origin.c_str(), {}, report)
{
}
sg_error::sg_error(const std::string& message, const std::string& origin)
: sg_throwable(message.c_str(), origin.c_str())
{
}
sg_error::~sg_error ()
{ {
} }
@@ -198,121 +198,81 @@ sg_error::~sg_error ()
// Implementation of sg_exception class. // Implementation of sg_exception class.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
sg_exception::sg_exception () sg_exception::sg_exception(const char* message, const char* origin,
: sg_throwable () const sg_location& loc, bool report) noexcept
{ : sg_throwable(message, origin, loc, report) {}
}
sg_exception::sg_exception(const char *message, const char *origin, sg_exception::sg_exception(const std::string& message,
const sg_location &loc) const std::string& origin, const sg_location& loc,
: sg_throwable(message, origin, loc) {} bool report) noexcept
: sg_throwable(message.c_str(), origin.c_str(), loc, report) {}
sg_exception::sg_exception(const std::string &message,
const std::string &origin, const sg_location &loc)
: sg_throwable(message.c_str(), origin.c_str(), loc) {}
sg_exception::~sg_exception ()
{
}
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Implementation of sg_io_exception. // Implementation of sg_io_exception.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
sg_io_exception::sg_io_exception () sg_io_exception::sg_io_exception(const char* message, const char* origin, bool report)
: sg_exception() : sg_exception(message, origin, {}, report)
{ {
} }
sg_io_exception::sg_io_exception (const char* message, const char* origin) sg_io_exception::sg_io_exception(const char* message,
: sg_exception(message, origin) const sg_location& location,
const char* origin,
bool report)
: sg_exception(message, origin, location, report) {}
sg_io_exception::sg_io_exception(const std::string& message,
const std::string& origin,
bool report)
: sg_exception(message, origin, {}, report)
{ {
} }
sg_io_exception::sg_io_exception(const char *message, sg_io_exception::sg_io_exception(const std::string& message,
const sg_location &location, const sg_location& location,
const char *origin) const std::string& origin,
: sg_exception(message, origin, location), _location(location) {} bool report)
: sg_exception(message, origin, location, report) {}
sg_io_exception::sg_io_exception( const std::string& message,
const std::string& origin )
: sg_exception(message, origin)
{
}
sg_io_exception::sg_io_exception(const std::string &message,
const sg_location &location,
const std::string &origin)
: sg_exception(message, origin, location), _location(location) {}
sg_io_exception::~sg_io_exception ()
{
}
const std::string
sg_io_exception::getFormattedMessage () const
{
std::string ret = getMessage();
std::string loc = getLocation().asString();
if (loc.length()) {
ret += "\n at ";
ret += loc;
}
return ret;
}
const sg_location &
sg_io_exception::getLocation () const
{
return _location;
}
void
sg_io_exception::setLocation (const sg_location &location)
{
_location = location;
}
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
// Implementation of sg_format_exception. // Implementation of sg_format_exception.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
sg_format_exception::sg_format_exception () sg_format_exception::sg_format_exception() noexcept
: sg_exception() : sg_exception()
{ {
_text[0] = '\0'; _text[0] = '\0';
} }
sg_format_exception::sg_format_exception (const char* message, sg_format_exception::sg_format_exception(const char* message,
const char* text, const char* text,
const char* origin) const char* origin,
: sg_exception(message, origin) bool report)
: sg_exception(message, origin, {}, report)
{ {
setText(text); setText(text);
} }
sg_format_exception::sg_format_exception( const std::string& message, sg_format_exception::sg_format_exception(const std::string& message,
const std::string& text, const std::string& text,
const std::string& origin ) const std::string& origin,
: sg_exception(message, origin) bool report)
: sg_exception(message, origin, {}, report)
{ {
setText(text.c_str()); setText(text.c_str());
} }
sg_format_exception::~sg_format_exception ()
{
}
const char* const char*
sg_format_exception::getText () const sg_format_exception::getText() const noexcept
{ {
return _text; return _text;
} }
void void sg_format_exception::setText(const char* text) noexcept
sg_format_exception::setText (const char* text)
{ {
if (text) { if (text) {
strncpy(_text, text, MAX_TEXT_LEN); strncpy(_text, text, MAX_TEXT_LEN);
@@ -328,26 +288,19 @@ sg_format_exception::setText (const char* text)
// Implementation of sg_range_exception. // Implementation of sg_range_exception.
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
sg_range_exception::sg_range_exception () sg_range_exception::sg_range_exception(const char* message,
: sg_exception() const char* origin, bool report)
{ : sg_exception(message, origin, {}, report)
}
sg_range_exception::sg_range_exception (const char* message,
const char* origin)
: sg_exception(message, origin)
{ {
} }
sg_range_exception::sg_range_exception(const std::string& message, sg_range_exception::sg_range_exception(const std::string& message,
const std::string& origin) const std::string& origin,
: sg_exception(message, origin) bool report)
: sg_exception(message, origin, {}, report)
{ {
} }
sg_range_exception::~sg_range_exception ()
{
}
//////////////////////////////////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////
+63 -59
View File
@@ -28,23 +28,23 @@ class sg_location
{ {
public: public:
enum {max_path = 1024}; enum {max_path = 1024};
sg_location (); sg_location() noexcept;
sg_location(const std::string& path, int line = -1, int column = -1); sg_location(const std::string& path, int line = -1, int column = -1) noexcept;
sg_location(const SGPath& path, int line = -1, int column = -1); sg_location(const SGPath& path, int line = -1, int column = -1) noexcept;
explicit sg_location(const char* path, int line = -1, int column = -1); explicit sg_location(const char* path, int line = -1, int column = -1) noexcept;
~sg_location() = default; ~sg_location() = default;
const char *getPath() const; const char* getPath() const noexcept;
int getLine() const; int getLine() const noexcept;
int getColumn() const; int getColumn() const noexcept;
int getByte() const; int getByte() const noexcept;
std::string asString() const; std::string asString() const noexcept;
bool isValid() const; bool isValid() const noexcept;
private: private:
void setPath(const char *p); void setPath(const char* p) noexcept;
char _path[max_path]; char _path[max_path];
int _line; int _line;
@@ -60,20 +60,26 @@ class sg_throwable : public std::exception
{ {
public: public:
enum {MAX_TEXT_LEN = 1024}; enum {MAX_TEXT_LEN = 1024};
sg_throwable (); sg_throwable() noexcept;
sg_throwable(const char *message, const char *origin = 0, sg_throwable(const char* message, const char* origin = 0,
const sg_location &loc = {}); const sg_location& loc = {}, bool report = true) noexcept;
virtual ~sg_throwable (); virtual ~sg_throwable() noexcept = default;
virtual const char* getMessage () const;
virtual const std::string getFormattedMessage () const; virtual const char* getMessage() const noexcept;
virtual void setMessage (const char* message); std::string getFormattedMessage() const noexcept;
virtual const char* getOrigin () const; virtual void setMessage(const char* message) noexcept;
virtual void setOrigin (const char *origin); virtual const char* getOrigin() const noexcept;
virtual void setOrigin(const char* origin) noexcept;
virtual const char* what() const noexcept; virtual const char* what() const noexcept;
private:
sg_location getLocation() const noexcept;
void setLocation(const sg_location& location) noexcept;
private:
char _message[MAX_TEXT_LEN]; char _message[MAX_TEXT_LEN];
char _origin[MAX_TEXT_LEN]; char _origin[MAX_TEXT_LEN];
sg_location _location;
}; };
@@ -90,10 +96,10 @@ private:
class sg_error : public sg_throwable class sg_error : public sg_throwable
{ {
public: public:
sg_error (); sg_error() noexcept = default;
sg_error (const char* message, const char* origin = 0); sg_error(const char* message, const char* origin = 0, bool report = true) noexcept;
sg_error(const std::string &message, const std::string &origin = {}); sg_error(const std::string& message, const std::string& origin = {}, bool report = true) noexcept;
virtual ~sg_error (); virtual ~sg_error() noexcept = default;
}; };
@@ -114,12 +120,12 @@ public:
class sg_exception : public sg_throwable class sg_exception : public sg_throwable
{ {
public: public:
sg_exception (); sg_exception() noexcept = default;
sg_exception(const char *message, const char *origin = 0, sg_exception(const char* message, const char* origin = 0,
const sg_location &loc = {}); const sg_location& loc = {}, bool report = true) noexcept;
sg_exception(const std::string &message, const std::string & = {}, sg_exception(const std::string& message, const std::string& = {},
const sg_location &loc = {}); const sg_location& loc = {}, bool report = true) noexcept;
virtual ~sg_exception (); virtual ~sg_exception() noexcept = default;
}; };
@@ -137,20 +143,15 @@ public:
class sg_io_exception : public sg_exception class sg_io_exception : public sg_exception
{ {
public: public:
sg_io_exception (); sg_io_exception() noexcept = default;
sg_io_exception (const char* message, const char* origin = 0); sg_io_exception(const char* message, const char* origin = 0, bool report = true);
sg_io_exception (const char* message, const sg_location &location, sg_io_exception(const char* message, const sg_location& location,
const char* origin = 0); const char* origin = 0, bool report = true);
sg_io_exception (const std::string &message, const std::string &origin = ""); sg_io_exception(const std::string& message, const std::string& origin = {}, bool report = true);
sg_io_exception (const std::string &message, const sg_location &location, sg_io_exception(const std::string& message, const sg_location& location,
const std::string &origin = ""); const std::string& origin = {}, bool report = true);
virtual ~sg_io_exception (); virtual ~sg_io_exception() noexcept = default;
virtual const std::string getFormattedMessage () const;
virtual const sg_location &getLocation () const;
virtual void setLocation (const sg_location &location);
private:
sg_location _location;
}; };
@@ -168,14 +169,15 @@ private:
class sg_format_exception : public sg_exception class sg_format_exception : public sg_exception
{ {
public: public:
sg_format_exception (); sg_format_exception() noexcept;
sg_format_exception (const char* message, const char* text, sg_format_exception(const char* message, const char* text,
const char* origin = 0); const char* origin = 0, bool report = true);
sg_format_exception (const std::string& message, const std::string& text, sg_format_exception(const std::string& message, const std::string& text,
const std::string& origin = ""); const std::string& origin = {}, bool report = true);
virtual ~sg_format_exception ();
virtual const char* getText () const; const char* getText() const noexcept;
virtual void setText (const char* text); void setText(const char* text) noexcept;
private: private:
char _text[MAX_TEXT_LEN]; char _text[MAX_TEXT_LEN];
}; };
@@ -193,12 +195,14 @@ private:
class sg_range_exception : public sg_exception class sg_range_exception : public sg_exception
{ {
public: public:
sg_range_exception (); sg_range_exception() noexcept = default;
sg_range_exception (const char* message, sg_range_exception(const char* message,
const char* origin = 0); const char* origin = 0,
sg_range_exception (const std::string& message, bool report = true);
const std::string& origin = ""); sg_range_exception(const std::string& message,
virtual ~sg_range_exception (); const std::string& origin = {},
bool report = true);
virtual ~sg_range_exception() noexcept = default;
}; };
using ThrowCallback = std::function<void( using ThrowCallback = std::function<void(
+1
View File
@@ -0,0 +1 @@
2020.3.8